/* ============================================================
   霍格沃茨：我在魔法界白手起家 · 视觉系统 v2
   「烛光礼堂 · 浮页手抄本」

   三层，各司其职：
     1. 底图（stage）   —— 一整张照片，钉在视口上，永远在。
     2. 玻璃层           —— 状态栏、文游对话框。墨色半透明，不抢戏。
     3. 羊皮纸层         —— 卡片、选项、名牌、手抄本。只出现在该出现的地方。
   页面本身不再是「一本羊皮纸书」，而是一层浮在礼堂上的纸与光。
   ============================================================ */

/* --- 字体：只引叶子文件（整包 woff2），不引 fontsource 的 index.css --- */
@font-face {
  font-family: 'Noto Serif SC';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('https://cdn.jsdmirror.com/npm/@fontsource/noto-serif-sc@5.3.0/files/noto-serif-sc-chinese-simplified-400-normal.woff2') format('woff2');
}
@font-face {
  font-family: 'Noto Serif SC';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('https://cdn.jsdmirror.com/npm/@fontsource/noto-serif-sc@5.3.0/files/noto-serif-sc-chinese-simplified-700-normal.woff2') format('woff2');
}
@font-face {
  font-family: 'IM Fell English';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('https://cdn.jsdmirror.com/npm/@fontsource/im-fell-english@5.3.0/files/im-fell-english-latin-400-normal.woff2') format('woff2');
}

/* --- design token：之后一律只引用 token --- */
:root {
  /* 纸与墨 */
  --paper:      #f2e7d0;   /* 羊皮纸底 */
  --paper-2:    #e6d5b4;   /* 深一档纸 */
  --paper-3:    #d5bd92;   /* 纸边 / 分隔线 */
  --ink:        #40301f;   /* 墨水棕，正文 */
  --ink-soft:   #7d6849;   /* 次级文字 */
  --red:        #8c2f2f;   /* 红套印：唯一主强调 */
  --leaf:       #55654a;   /* 药剂橄榄绿：第二强调，面积极小 */
  --bright:     #fffaf0;   /* 纸上最亮的面 */

  /* 照片上的一族暖色（纸上的墨压在照片上看不清，故设亮字变体） */
  --cine-text:  #f4e9d4;
  --cine-dim:   #bda98a;
  --cine-gold:  #e0b264;
  --cine-red:   #d4695f;

  /* 健康条：深红。比纸上的套印（--red）亮一点，黑底上才看得见 */
  --bar-hp:     #ab332c;

  /*
   * 钱的三档：加隆金、西可银灰、纳特古铜。
   * 这里给的是一套亮色（状态栏这种压在照片上的地方）；
   * 落到纸上会换一套墨色（见下面 .card / .btn 那一族）。
   */
  --coin-g:     #e6b44f;
  --coin-s:     #d2d5d9;
  --coin-k:     #c98250;

  /* 墨色玻璃：状态栏 / 对话框 / 悬浮条 */
  --glass:      rgba(18, 13, 9, 0.72);
  --glass-2:    rgba(26, 19, 13, 0.86);
  --glass-line: rgba(224, 178, 100, 0.24);
  --glass-line-2: rgba(224, 178, 100, 0.46);

  /* 字号阶（≥1.25× 跳） */
  --fs-xs: 12px;
  --fs-s:  14px;
  --fs-m:  17px;
  --fs-l:  21px;
  --fs-xl: 27px;
  --fs-2xl: 36px;

  /* 8px 间距阶 */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px;

  /* 1 个圆角 + 2 种阴影（纸 / 玻璃） */
  --r: 3px;
  --sh-paper: 0 2px 0 rgba(120, 96, 62, 0.55), 0 14px 32px rgba(0, 0, 0, 0.5);
  --sh-glass: 0 22px 54px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 235, 200, 0.09);

  --font-body: 'Noto Serif SC', 'Songti SC', 'STSong', serif;
  --font-hand: 'IM Fell English', 'Kaiti SC', 'KaiTi', 'STKaiti', 'Noto Serif SC', serif;
}

* { box-sizing: border-box; }

html, body { margin: 0; padding: 0; }

body {
  background: #0b0806;
  font-family: var(--font-body);
  font-size: var(--fs-m);
  line-height: 1.85;
  /*
   * 全套前景色都走这四个变量：默认是「照片上」的亮色，
   * 一进到纸里（.card / .sheet / .panel / .letter / .btn）就换成墨色。
   * 于是同一段「小字」，在照片上是暗金、在纸上是灰褐，不用每个界面各调一次。
   */
  --fg: var(--cine-text);
  --fg-dim: var(--cine-dim);
  --fg-accent: var(--cine-gold);
  --fg-danger: var(--cine-red);
  color: var(--fg);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9), 0 2px 16px rgba(0, 0, 0, 0.6);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  overflow-x: hidden;
}

/* ============================================================
   1 · 舞台（整屏底图）
   ============================================================ */

.stage {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: #0b0806;
}

.stage-img {
  position: absolute;
  inset: -2%;
  background-position: center 42%;
  background-size: cover;
  background-repeat: no-repeat;
  background-color: #170f0a;
  animation: hallDrift 52s ease-in-out infinite alternate;
  will-change: transform;
}
/* 极慢的推移：一潭死水的背景会显得是张壁纸，动一点就「在场」了 */
@keyframes hallDrift {
  from { transform: scale(1.03) translateY(0); }
  to   { transform: scale(1.10) translateY(-1.6%); }
}

/* 上暗 → 中间亮（把画面留给立绘）→ 下暗（给对话框留得住字） */
.stage-scrim {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(115% 75% at 50% 8%, rgba(255, 226, 175, 0.10), transparent 60%),
    linear-gradient(180deg,
      rgba(9, 7, 5, 0.62) 0%,
      rgba(9, 7, 5, 0.16) 16%,
      rgba(9, 7, 5, 0.10) 42%,
      rgba(9, 7, 5, 0.52) 74%,
      rgba(9, 7, 5, 0.9) 100%),
    radial-gradient(135% 105% at 50% 50%, transparent 52%, rgba(9, 7, 5, 0.7) 100%);
}

@media (prefers-reduced-motion: reduce) {
  .stage-img { animation: none; }
}

/* ============================================================
   2 · 页面骨架（浮在底图上的窄栏）
   ============================================================ */

.app {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 760px;
  margin: 0 auto;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  padding: 0 var(--sp-4);
}

/* ---------- 顶栏账目（玻璃条，滚动时钉在顶上） ---------- */
.hud {
  position: sticky;
  top: 0;
  z-index: 6;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-2);
  margin: 0 calc(var(--sp-4) * -1);
  padding: calc(var(--sp-3) + env(safe-area-inset-top)) var(--sp-4) var(--sp-3);
  background: linear-gradient(180deg, rgba(9, 7, 5, 0.86) 0%, rgba(9, 7, 5, 0.55) 62%, rgba(9, 7, 5, 0) 100%);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.9);
}
.hud::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--glass-line) 18%, var(--glass-line) 82%, transparent);
}
.hud[hidden] { display: none; }

.hud-date { font-family: var(--font-hand); }
.hud-day-row {
  display: flex;
  align-items: center;
  gap: 6px;
}
.hud-day {
  display: block;
  font-size: var(--fs-xl);
  line-height: 1.1;
  letter-spacing: 0.02em;
  color: #fff6e4;
}
.hud-week {
  display: block;
  font-size: 11px;
  font-family: var(--font-body);
  color: var(--cine-dim);
  letter-spacing: 0.18em;
  line-height: 1.5;
}
/* 三行：钱 / 健康 / 精力。行距压到 3px，两根条才算是「贴着放」的 */
.hud-stats { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; }
/*
 * 钱那一行不写「钱袋」两个字——三档各有各的颜色，本身就是在报自己是谁。
 * 只在它和下面两根条之间留一条缝，免得几个数字和条子糊成一坨。
 */
.stat-row.purse-row { margin-bottom: 2px; }
/*
 * 行高要压住：正文是 1.85，照抄下来三行会白占一截高度，
 * 顶栏跟着长高、把画面往下推——这里每一行都按内容贴紧。
 */
.stat-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-s);
  line-height: 1.25;
}
.stat-row .tiny { color: var(--cine-dim); }
/*
 * 可点的那两行（健康 / 精力）。
 *
 * 藏起来的入口不该长得像按钮——那会天天勾着玩家花钱。
 * 但也不能完全没痕迹：鼠标划过给一点回应、按下去收一下，
 * 玩家伸手点的时候就知道这里按得动。触屏看不到 hover，
 * 所以反馈主要落在 :active 上（按住那一下）。
 */
.stat-row.tappable {
  cursor: pointer;
  border-radius: var(--r);
  padding: 1px 4px;
  margin: -1px -4px;
  transition: background 0.14s ease;
}
.stat-row.tappable:hover { background: rgba(255, 246, 228, 0.1); }
.stat-row.tappable:active { background: rgba(255, 246, 228, 0.18); }
.stat-row.tappable:focus-visible {
  outline: 1px solid var(--cine-gold);
  outline-offset: 1px;
}
/* inline-block：行内元素上的 transform 不生效，跳动要看得见 */
.stat-row b {
  font-family: var(--font-hand);
  font-size: var(--fs-m);
  font-weight: 400;
  color: #fff6e4;
  display: inline-block;
  transition: color 0.35s ease;
}

/*
 * 一行钱：三档各是各的颜色，中间一个小圆点隔开。
 * 「加隆金 · 西可银灰 · 纳特古铜」——不用读数字也知道他数的是哪一档。
 * 全是零的那一档压暗一点，三档并排时不至于三个数一样抢眼。
 */
.coin {
  display: inline-block;
  white-space: nowrap;
  font-family: var(--font-hand);
  font-size: var(--fs-m);
  line-height: 1.25;
  letter-spacing: 0.01em;
  transition: color 0.35s ease;
}
/* 顶栏那一行钱要写得下三档，字号压小一档；分隔点也收紧 */
.hud-stats .coin { font-size: var(--fs-s); letter-spacing: 0; }
.hud-stats .coin .c-sep { margin: 0; }
.coin .c-g { color: var(--coin-g); }
.coin .c-s { color: var(--coin-s); }
.coin .c-k { color: var(--coin-k); }
.coin .zero { opacity: 0.5; }
.coin .c-sep { margin: 0 1px; color: var(--fg-dim); opacity: 0.7; }
/*
 * 钱刚变过：整行闪一下。写在 .coin 上再落到子元素——
 * 子元素各自有颜色，得让这一条盖过它们（选择器比 .c-g 更具体，不用 !important）。
 */
.coin.flash-up span { color: #a8c08c; opacity: 1; }
.coin.flash-down span { color: #d4695f; opacity: 1; }

/* 当前时段：一枚小金签，跟在「第几周」后面（不再占条子那一行的位置） */
.hud-slot {
  display: inline-block;
  margin-left: 6px;
  font-family: var(--font-hand);
  font-size: 10px;
  letter-spacing: 0.12em;
  line-height: 1.5;
  color: #1b130c;
  background: var(--cine-gold);
  border-radius: 2px;
  padding: 0 5px;
  vertical-align: 1px;
  text-shadow: none;
}

/* ---------- 两根条：健康在上、精力在下，尺寸一样，贴着放 ---------- */
.bar { display: flex; gap: 2px; }
.bar i {
  width: 6px; height: 11px;
  background: rgba(20, 14, 10, 0.55);
  border: 1px solid rgba(240, 228, 205, 0.42);
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease, opacity 0.2s ease;
}
/* 精力：金。玩家最该盯的一根 */
.bar.stam i.on { background: var(--cine-gold); border-color: var(--cine-gold); }
/* 健康：深红 */
.bar.hp i.on { background: var(--bar-hp); border-color: var(--bar-hp); }
/* 见底警告（两根共用）：换成亮红。放在 .on 之后，同权重后者胜 */
.bar.stam i.on.low,
.bar.hp i.on.low { background: var(--cine-red); border-color: var(--cine-red); }
/* 血跌到危险线：整根条轻轻呼吸，别只是一格一格少下去 */
.bar.hp.alert i.on { animation: barPulse 1.1s ease-in-out infinite; }
@keyframes barPulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}
.bar.bump i { transform: scaleY(1.4); }

/* ---------- 主区域：浮着的一张张羊皮纸 ---------- */
.view {
  position: relative;
  z-index: 1;
  flex: 1;
  /*
   * 顶部只留一小格。这一段原本是 sp-4，再叠上 .scene 的下边距和屋子的地面高度，
   * 第一排按钮就掉到要滚一下才看得见的位置了（创作者点过这个问题）。
   * 往下收这一层，比去挪每个按钮自己的坐标干净——坐标是按照片里的位置定的。
   */
  padding: var(--sp-2) 0 var(--sp-5);
  color: var(--fg);
}

/*
 * 压在照片上的文字需要一层柔和暗底。
 * 礼堂这张图上半部偏亮，白字直接搁上去会发飘；
 * 但也不能压成一块黑板——所以用无边界的径向渐变，只把文字这一带压下去。
 * 纸面（.card / .panel / .sheet）是不透明的，会自然盖住它，互不干扰。
 */
.view::before {
  content: '';
  position: absolute;
  inset: -12px -20px;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(140% 78% at 50% 42%,
    rgba(9, 7, 5, 0.5) 0%,
    rgba(9, 7, 5, 0.32) 48%,
    rgba(9, 7, 5, 0) 100%);
}
.view:empty { display: none; }

/* 状态栏隐掉的那几屏（通知书 / 剧情），安全区得由内容自己让出来——
   顶栏在的时候它已经占过这一段，别重复加 */
.hud[hidden] ~ .view { padding-top: calc(var(--sp-4) + env(safe-area-inset-top)); }

/* ---------- 底部行动托盘（吸底，滚动时也够得着） ---------- */
.tray {
  position: sticky;
  bottom: 0;
  z-index: 6;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: stretch;
  gap: var(--sp-2);
  margin: 0 calc(var(--sp-4) * -1);
  padding: var(--sp-6) var(--sp-4) calc(var(--sp-4) + env(safe-area-inset-bottom));
  background: linear-gradient(0deg, rgba(9, 7, 5, 0.94) 42%, rgba(9, 7, 5, 0.7) 72%, rgba(9, 7, 5, 0) 100%);
}
.tray:empty { display: none; }
.tray .btn { flex: 0 1 auto; width: auto; min-width: 9.5rem; max-width: 100%; }
/*
 * 托盘里的「主按钮」（今天的日程 / 离开）占满一整行——
 * 门牌已经单独占了一行，主按钮再窄窄一条就不成套了。
 * 门牌不是托盘的直接子元素（在 .entry-row 里），所以不受这条影响。
 */
.tray > .btn { flex: 1 1 100%; min-width: 0; width: 100%; }
.tray .acts { display: flex; }
.tray .acts > * { flex: 1 1 0; }

/* ============================================================
   3 · 排版
   ============================================================ */

h1, h2, h3 { margin: 0 0 var(--sp-3); line-height: 1.3; color: var(--fg); }
h1 { font-family: var(--font-hand); font-size: var(--fs-2xl); font-weight: 400; letter-spacing: 0.01em; }
h2 { font-size: var(--fs-l); font-weight: 700; }
h3 { font-size: var(--fs-m); font-weight: 700; letter-spacing: 0.06em; }
p { margin: 0 0 var(--sp-4); }
.small { font-size: var(--fs-s); }
.tiny { font-size: var(--fs-xs); letter-spacing: 0.1em; }
.muted { color: var(--fg-dim); }
.rule-red { color: var(--fg-danger); }
.hand { font-family: var(--font-hand); }

/* 小标题：短横 + 大字距。默认是纸上的红套印；直接在照片上时换成金 */
.chapter {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: var(--sp-5) 0 var(--sp-3);
  font-size: var(--fs-xs);
  letter-spacing: 0.34em;
  color: var(--fg-accent);
  text-transform: uppercase;
}
.chapter::after {
  content: '';
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, currentColor, transparent);
  opacity: 0.5;
}
/*
 * 分区标题右边挂东西时（手抄本「关于你自己」那两颗按钮）：
 * 靠 flex 的 order 把生成的那道横线排到按钮**前面**——默认顺序是
 * [标题][英文][::after 横线]，按钮会挤在横线左边；给横线 order 1、右边那串 order 2，
 * 横线照旧把剩下的地方占满，按钮稳稳落在行尾。
 */
.chapter .ch-right {
  order: 2;
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  margin-left: var(--sp-2);
}
.chapter::after { order: 1; }
/* 标题那一行字距很大（0.34em），里头那两颗按钮得还回原样 */
.chapter .ch-right .btn {
  letter-spacing: 0.06em;
  text-transform: none;
}
.chapter:first-child { margin-top: 0; }

/*
 * 场景抬头：压在照片上的场次标签 + 标题。
 * 底下那段留白从 sp-5 收到 sp-3：抬头和按钮之间不需要一整个空段，
 * 而地图那几屏就是靠这一段+地面高度一路把按钮顶到折叠线以下的。
 */
.scene { margin: var(--sp-1) 0 var(--sp-3); }
.scene .label {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.3em;
  color: var(--cine-red);
  text-transform: uppercase;
}
.scene .name {
  font-size: var(--fs-xl);
  line-height: 1.25;
  margin-top: var(--sp-1);
  color: #fff6e4;
}
.scene .name::after {
  content: '';
  display: block;
  width: 34px;
  height: 2px;
  margin-top: var(--sp-3);
  background: var(--cine-red);
  opacity: 0.85;
}
.scene .note { font-size: var(--fs-s); color: var(--cine-dim); margin-top: var(--sp-3); }
/*
 * 名字那一行：名字占满剩下的地方，退出键（如有）贴在**行尾**。
 * 退出键从托盘挪到这儿之后（托盘要留给「开始剧情 / 今天的日程」那两条），
 * 它就该和标题同高、在标题这一行的右端——不压标题，也不另起一行。
 */
.scene .scene-row { display: flex; align-items: flex-start; gap: var(--sp-3); }
.scene .scene-row .name { flex: 1 1 auto; min-width: 0; }
.scene .scene-row .btn.exit { flex: none; }

/*
 * 屋里那句补充（「要等熄灯之后才有正事」「三年级才许去」这一类）。
 * 它和抬头一样落在照片上，所以走 cine 那一套亮字 + 暗影；
 * 位置放在抬头下面一小截，读起来是「刚才那句话的注脚」，不是另一段正文。
 */
.place-hint {
  font-size: var(--fs-xs);
  line-height: 1.7;
  color: var(--cine-dim);
  letter-spacing: 0.04em;
  margin: 0 0 var(--sp-3);
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.9), 0 0 14px rgba(0, 0, 0, 0.82);
}

/* ============================================================
   4 · 羊皮纸（容器一族）
   ============================================================ */

/*
 * 凡是「一张纸」的东西，都在这里统一换成纸上的墨色——
 * 于是在照片上读得清，在纸上也读得清，不用每个界面各自调。
 */
.card,
.panel,
.sheet,
.letter,
.btn,
.place,
.vn-choice {
  --fg: var(--ink);
  --fg-dim: var(--ink-soft);
  --fg-accent: var(--red);
  --fg-danger: var(--red);
  /* 钱落到纸上：金变赭金、银灰压深、古铜偏褐——同一套色，换个亮度 */
  --coin-g: #9c6b16;
  --coin-s: #7c828b;
  --coin-k: #97552a;
  color: var(--fg);
  text-shadow: none;
}

.card {
  background:
    radial-gradient(80% 50% at 14% 4%, rgba(255, 255, 255, 0.55), transparent 62%),
    radial-gradient(120% 90% at 52% 112%, rgba(140, 47, 47, 0.07), transparent 72%),
    linear-gradient(180deg, #fbf4e4, #eee1c4);
  border: 1px solid rgba(64, 48, 31, 0.34);
  border-bottom-color: rgba(64, 48, 31, 0.5);
  border-radius: var(--r);
  box-shadow: var(--sh-paper);
  padding: var(--sp-4);
  margin: 0 0 var(--sp-3);
}
.card > *:last-child { margin-bottom: 0; }

/* 纸上再摊纸就别各带一份厚影了——压平，读起来才是一张本子里的两页 */
.panel-body .card,
.sheet .card {
  background: rgba(255, 251, 240, 0.5);
  border-color: var(--paper-3);
  box-shadow: none;
}

/* ---------- 列表 ---------- */
.row {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  padding: var(--sp-2) 0;
  border-bottom: 1px dotted rgba(64, 48, 31, 0.22);
}
.row:last-child { border-bottom: none; padding-bottom: 0; }
.row .grow { flex: 1; min-width: 0; }
.row .val { font-family: var(--font-hand); white-space: nowrap; }
.row.dim { opacity: 0.5; }

/*
 * 图鉴详情那几张纸（ui/codex.js 的 .codex-rows）与家里升级那一列权益（ui/home.js 的 .home-gains）：
 * **整句的值**（用料、吃下去会怎样、"16 格（现 10）"那种）要能换行。.row .val 默认 nowrap
 * （钱、数量那种短数不该进第二行），可一整句塞进去会把左边的标签挤成一列竖字
 * （创作者报过：凤凰尾鱼宴那张纸上「吃下去会怎样」竖着排）。
 * 所以这两档松开换行、把标签还回它的自然宽度。
 */
.codex-rows .row, .home-gains .row { align-items: flex-start; }
.codex-rows .row .grow, .home-gains .row .grow { flex: 0 0 auto; }
.codex-rows .row .val, .home-gains .row .val { white-space: normal; text-align: right; line-height: 1.55; }

/*
 * 药房柜台上那三段的小标题（.shop-group / .sg-zh / .sg-en）随这一批货格
 * 一起撤了：那三段现在是 .chapter + 货格，「每样一天最多几个」写在货格上面
 * 那一行 .mkt-line 里。留着一段没人挂的类是给自己挖坑，所以整段删掉。
 */

/* ---------- 叙事 ---------- */
.narr { font-size: var(--fs-m); line-height: 1.9; }
.narr em { font-style: normal; color: var(--fg-danger); }

/* ---------- 人物小头像 ---------- */
.who-face {
  width: 34px; height: 34px;
  flex: none;
  border-radius: 50%;
  border: 1px solid var(--glass-line-2);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
  display: grid;
  place-items: center;
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  color: #fff6e4;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
.cast-row { display: flex; gap: 6px; margin-top: calc(var(--sp-3) * -1); margin-bottom: var(--sp-4); }
/*
 * 带小像的那一种（手抄本的人物簿）：小像自己就带着描金画框、自己是透明底，
 * 所以这枚**不画圆底、不填色、不描边**，只把图按它自己的比例撑满。
 */
.who-face.art {
  background: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
  display: block;
  overflow: visible;
}
.who-face.art img { display: block; width: 100%; height: 100%; object-fit: contain; }

/* 好感度条：和精力条同一套读法——十格竖条，满格 100 */
.hearts { display: flex; gap: 2px; flex: none; }
.hearts i {
  width: 7px; height: 15px;
  background: var(--paper-3);
  border: 1px solid var(--ink-soft);
  transition: background 0.2s ease, transform 0.2s ease;
}
.hearts i.on { background: var(--red); border-color: var(--red); }

/* ============================================================
   5 · 按钮（羊皮纸）
   ============================================================ */

.acts { display: flex; flex-direction: column; gap: var(--sp-2); }
.acts.row { flex-direction: row; flex-wrap: wrap; }
.acts.row > * { flex: 1 1 140px; }

/*
 * 弹页底下那条按钮栏（收起来 / 知道了 / 存下 / 主角人格…）：**居中、按内容宽**——
 * 从前它们是一条占满整张纸的长杠，三个字横贯一整个纸面
 * （创作者 2026-09-29 点掉的：「类似的大长条按钮不要出现」）。
 * 这一条只够到弹页与小窗**自己的按钮栏**（.sheet / .panel 的直接子节点）；
 * 纸里面那些行动组（日程那几颗、禁林那排选项）不归它管——那些本来就该占满一行。
 */
.sheet > .acts {
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
}
.sheet > .acts > * { flex: 0 1 auto; }
.sheet > .acts > .btn { width: auto; }

/*
 * 日程里那一格格子。
 *
 * 「做事」的选项排两列，一条条对齐——一眼扫得完，也好比较各自费多少精力。
 * 能把这一格直接收掉的那一个（眼下只剩晚上那颗「睡觉」）不在这里，它整行排在前面，
 * 因为那一下是「我不挑了」，不该混在候选里一起看。
 */
.acts-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-2);
}
.acts-grid > .btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-width: 0;
  min-height: 4em;
  padding: var(--sp-3) var(--sp-2);
  text-align: center;
  /* 名字长的（有求必应屋那种）宁可换行，也不要撞出格子 */
  overflow-wrap: anywhere;
  /* 整行按钮那条左侧红边，在居中的格子里会看着像错位——改压在上沿 */
  border-left-width: 1px;
}
.acts-grid > .btn.primary,
.acts-grid > .btn.danger { border-top: 3px solid var(--red); }
.acts-grid > .btn .cost {
  float: none;
  font-size: var(--fs-xs);
  line-height: 1.3;
}
/* 手机上两列各自更窄，名字收小一号：宁可字小，也不让一格高出一截错位 */
@media (max-width: 420px) {
  .acts-grid > .btn { font-size: var(--fs-s); padding: var(--sp-2) 6px; }
}

/* 「昨夜」那一段：醒来弹过一次，日程纸上留着随时翻回来读 */
.night-note {
  display: flex;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-left: 2px solid rgba(140, 47, 47, 0.45);
  background: rgba(140, 47, 47, 0.05);
}
.night-note .nn-label {
  flex: none;
  font-size: var(--fs-xs);
  letter-spacing: 0.2em;
  color: var(--red);
  opacity: 0.8;
}
.night-note .nn-text {
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  line-height: 1.75;
  color: var(--ink-soft);
}

/*
 * 身上那点伤（见 ui/screens-day.js 的 hurtLine）。
 * 带伤那一档只是提醒（左边一道浅痕）；血见底那一档是**这一格真的什么都做不了**，
 * 所以给它整个底色压深一档——它是这张纸上唯一一条「今天到此为止」的话。
 */
.day-hurt {
  margin: 0 0 var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-left: 2px solid rgba(140, 47, 47, 0.35);
  font-size: var(--fs-s);
  line-height: 1.7;
  color: var(--ink-soft);
}
.day-hurt.bad {
  border-left-color: var(--red);
  background: rgba(140, 47, 47, 0.09);
  color: var(--ink);
}

.btn {
  display: block;
  width: 100%;
  text-align: left;
  font-family: var(--font-body);
  font-size: var(--fs-m);
  color: var(--ink);
  background:
    radial-gradient(80% 60% at 14% 4%, rgba(255, 255, 255, 0.5), transparent 62%),
    linear-gradient(180deg, #fbf4e4, #efe1c4);
  border: 1px solid rgba(64, 48, 31, 0.4);
  border-radius: var(--r);
  padding: var(--sp-3) var(--sp-4);
  cursor: pointer;
  box-shadow: 0 3px 0 rgba(50, 37, 24, 0.38), 0 10px 24px rgba(0, 0, 0, 0.42);
  transition: transform 0.14s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.14s ease, border-color 0.14s ease;
}
.btn:hover { border-color: var(--red); transform: translateY(-2px); box-shadow: 0 5px 0 rgba(50, 37, 24, 0.34), 0 14px 28px rgba(0, 0, 0, 0.5); }
.btn:active { transform: translateY(1px) scale(0.99); box-shadow: 0 1px 0 rgba(50, 37, 24, 0.38); }
.btn:disabled { opacity: 0.42; cursor: not-allowed; transform: none; box-shadow: 0 2px 0 rgba(50, 37, 24, 0.3); }
.btn .cost { float: right; font-family: var(--font-hand); font-size: var(--fs-s); color: var(--ink-soft); }
/*
 * 中英两行的那种按钮（只有给了 en 的才是，见 shell.js 的 btn）。
 * 中文那一行就是按钮本来的字号（继承 .btn），英文这行小一号、手写体、压暗。
 * 别把 .btn 整体改成 flex：底下几百颗按钮都是「一行字 + 右边一个 cost」，
 * 动布局等于一次动全部。
 */
.btn.duo .btn-zh { display: block; }
.btn.duo .btn-en {
  display: block;
  margin-top: 1px;
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  letter-spacing: 0.12em;
  line-height: 1.3;
  color: var(--ink-soft);
}
.btn.primary { border-left: 4px solid var(--red); }
.btn.danger { border-left: 4px solid var(--red); }

/*
 * 「今天的日程」那颗（`.btn.day`）。
 *
 * 它和上面那些纸色按钮不是一件事：那些是「把这一格给出去」的正事，
 * 这一颗只是**把那张纸摊开看一眼**——所以它长得像一条压在地图照片上的窄杠：
 * 扁（上下只留一点内边距）、深棕半透明（照片在底下透出来）、浅灰黄的字。
 * 中英并排：中文照旧是主角，英文小一号跟在后面，中间一个点隔开。
 *
 * ⚠️ 一定要把 .btn 那几条「纸按钮」的默认盖掉（底色、投影、左边那道红边），
 *    不然它会顶着一张纸色的脸混在照片上——override 就得写全，别只写一半。
 */
.btn.day {
  padding: calc(var(--sp-1) + 1px) var(--sp-3);
  font-size: var(--fs-s);
  line-height: 1.5;
  text-align: center;
  letter-spacing: 0.08em;
  color: #d8cfae;
  background: rgba(46, 30, 18, 0.72);
  border: 1px solid rgba(224, 178, 100, 0.26);
  border-left-width: 1px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(6px);
}
.btn.day:hover {
  color: #f0e7ca;
  background: rgba(66, 43, 25, 0.86);
  border-color: rgba(224, 178, 100, 0.55);
  transform: translateY(-1px);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.46);
}
.btn.day:active { transform: translateY(0) scale(0.995); box-shadow: 0 1px 6px rgba(0, 0, 0, 0.42); }
.btn.day .db { display: inline-flex; align-items: baseline; gap: 5px; }
.btn.day .db-zh { font-size: var(--fs-s); }
.btn.day .db-sep { opacity: 0.45; }
.btn.day .db-en {
  font-family: var(--font-hand);
  font-size: 11px;
  letter-spacing: 0.04em;
  opacity: 0.78;
}
/*
 * 【开始剧情】那颗：跟日程同款，只把中文字挑出一点金。
 * 它比日程重要一档（一个管"这一格的活"、一个管"这一天怎么过"），
 * 但这一屏的主角是那幅城堡的画——所以只给一个字色，不给尺寸、不给底、不给边框。
 */
.btn.day.story { color: #e6cf9a; }
.btn.day.story:hover { color: #fbf0d2; }
.btn.day.story .db-en { color: #c9ad76; opacity: 0.9; }

/*
 * 【开始剧情】那道岔（main.js 的 chooseStory）：
 * 一张纸上并排两条主线，每条一张「牌子」——标题一行、底下小字说清它是什么。
 * 自由主线（眼下真能走的那条）挂一道红边，一眼看得出哪个是正路。
 */
/*
 * 两张卡片（固定 / 自由剧情）排成一列。
 * 间距比别处宽一档（12px）：卡片自己带一道向下的投影，8px 时上那张的影子
 * 会压在下那张的边线上，看着像两张抹在一起（创作者点过这个问题）。
 */
.story-pick { display: flex; flex-direction: column; gap: var(--sp-3); }
.btn.sp {
  text-align: left;
  padding: var(--sp-3) var(--sp-4);
  /* 投影也收一档：这两张是竖着排的，.btn 那道 24px 的尾巴会越到下一张的边线上 */
  box-shadow: 0 3px 0 rgba(50, 37, 24, 0.34), 0 7px 14px rgba(0, 0, 0, 0.34);
}
.btn.sp .sp-zh {
  display: block;
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: 0.1em;
}
.btn.sp .sp-note {
  display: block;
  margin-top: 4px;
  font-size: var(--fs-xs);
  line-height: 1.6;
  color: var(--ink-soft);
}
.btn.sp.free { border-left: 4px solid var(--red); }
.btn.sp.fixed { opacity: 0.9; }

/* 卡片 + 末尾那颗【设置】（2026-09-28 加）：设置是那一头的事，就该长在那一头 */
.story-row { display: flex; align-items: stretch; gap: var(--sp-2); }
.story-row .btn.sp { flex: 1 1 auto; min-width: 0; }
.btn.sp-set {
  /* 一颗**小方块**（创作者定的）：.btn 生来是 width:100% 的整行块，
     摆在这一行里会把卡片挤成一根竖条——所以宽高都自己定死 */
  flex: none;
  align-self: center;
  width: 40px;
  height: 40px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-xs);
  letter-spacing: 0;
  text-align: center;
  /* 小方块配小影子：.btn 那两道 24px 的大投影压在 40px 的小块上会糊成一团 */
  box-shadow: 0 2px 0 rgba(50, 37, 24, 0.34), 0 5px 12px rgba(0, 0, 0, 0.3);
}
.btn.sp-set:hover {
  transform: translateY(-1px);
  box-shadow: 0 3px 0 rgba(50, 37, 24, 0.3), 0 8px 14px rgba(0, 0, 0, 0.34);
}
.btn.sp-set:active {
  transform: translateY(1px);
  box-shadow: 0 1px 0 rgba(50, 37, 24, 0.34);
}

/* ---------- 固定剧情：章节目录（ui/story-menu.js） ---------- */
/* 支线底下那两栏的小标题（好感 / 其他）：一行小字 + 一道细线，不是一张卡片 */
.ch-group {
  margin: 12px var(--sp-3) 2px;
  padding-bottom: 4px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.18em;
  color: var(--ink-soft);
  border-bottom: 1px solid rgba(64, 48, 31, 0.18);
}
.ch-group:first-child { margin-top: 2px; }
.ch-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 10px var(--sp-3);
  border-bottom: 1px dashed rgba(64, 48, 31, 0.22);
}
.ch-row:last-of-type { border-bottom: 0; }
.ch-row.done { opacity: 0.72; }
.ch-no {
  flex: none;
  width: 3.6em;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  color: var(--ink-soft);
}
.ch-tx { flex: 1 1 auto; min-width: 0; }
.ch-title { font-size: var(--fs-m); font-weight: 700; letter-spacing: 0.06em; color: var(--ink); }
.ch-note { margin-top: 3px; font-size: var(--fs-xs); line-height: 1.6; color: var(--ink-soft); }
/*
 * 右边那一格给一个最低高：只写「暂未触发」的那几行没章也没按钮，比旁边带【回看】的矮两像素
 * （同一张列表里一行 52、一行 54 看着就是没对齐）。33px 正好是那颗小按钮的高，
 * 带【已经历】红章的那几行本来就有 35 那一枚压着——整张列表归到 54，一行都不用长高。
 */
.ch-act { flex: none; display: flex; align-items: center; gap: var(--sp-2); min-height: 33px; }
/* 已经历那枚红章（它自己点不动，旁边那颗【回看】才是动作） */
.ch-stamp {
  display: inline-block;
  padding: 3px 10px 4px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  color: var(--ink-soft);
  border: 1px dashed rgba(64, 48, 31, 0.4);
  border-radius: 999px;
}
.ch-stamp.done {
  color: var(--red);
  border: 1px solid rgba(140, 47, 47, 0.65);
  background: rgba(140, 47, 47, 0.08);
  transform: rotate(-4deg);
}

/* ---------- 开关（固定剧情 · 设置） ---------- */
.sw-row { display: flex; align-items: center; gap: var(--sp-3); }
.sw-tx { flex: 1 1 auto; min-width: 0; }
.sw-zh { font-size: var(--fs-m); font-weight: 700; letter-spacing: 0.06em; color: var(--ink); }
.sw-note { margin-top: 3px; font-size: var(--fs-xs); line-height: 1.6; color: var(--ink-soft); }
.sw {
  flex: none;
  position: relative;
  width: 46px;
  height: 26px;
  padding: 0;
  border-radius: 999px;
  border: 1px solid rgba(64, 48, 31, 0.44);
  background: rgba(64, 48, 31, 0.18);
  cursor: pointer;
  transition: background 0.16s ease, border-color 0.16s ease;
}
.sw .sw-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: linear-gradient(180deg, #fbf4e4, #ded0b0);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
  transition: transform 0.16s cubic-bezier(0.16, 1, 0.3, 1);
}
.sw.on { background: rgba(140, 47, 47, 0.55); border-color: rgba(140, 47, 47, 0.7); }
.sw.on .sw-knob { transform: translateX(20px); }

/*
 * 退出键（「离开 / 回小屋 / 回大厅 / 回街上」这一类）。
 *
 * 它**从托盘挪到了标题行尾**（见 shell.js 的 sceneHead 第 5 个参数、本文件的 .scene-row）：
 * 托盘那一层现在留给【开始剧情 / 今天的日程】那两条，世界里每一屏都站着。
 *
 * 长相（创作者定的）：**深棕半透明**——就是那两条日程杠同一套底色，
 * 一眼认得出「这是退出去的键」，又不跟标题、不跟地图抢眼。
 * 所以它必须盖掉 .btn 那副羊皮纸身板：宽度收成内容宽、不左对齐。
 * 后来创作者又点了一句：这是**最常用的一个键**，可以比原样大一点点——
 * 于是字从 xs 提一档到 s、上下各给了 5px 的肉，点起来不再像在戳一颗芝麻。
 */
.btn.exit {
  width: auto;
  text-align: center;
  white-space: nowrap;
  padding: 5px var(--sp-3);
  font-size: var(--fs-s);
  line-height: 1.45;
  letter-spacing: 0.08em;
  color: #d3c5a2;
  background: rgba(46, 30, 18, 0.74);
  border: 1px solid rgba(224, 178, 100, 0.28);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(6px);
}
.btn.exit:hover {
  color: #f0e7ca;
  background: rgba(66, 43, 25, 0.86);
  border-color: rgba(224, 178, 100, 0.5);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.44);
}
.btn.exit:active { transform: translateY(0) scale(0.99); box-shadow: 0 1px 5px rgba(0, 0, 0, 0.36); }

/*
 * 托盘里的退出键（「离开」「回走廊」「回小屋」这类）：**按内容宽的小键，居中**，
 * 不收成一条整杠。创作者 2026-09-29 连点两回：先要深棕磨砂的底（浅色太突兀），
 * 再不要长条（「类似的大长条按钮不要出现」）。颜色、磨砂、投影都是 .btn.exit
 * 自己那一套；这儿只把托盘那条「占满一行」（flex-basis 100%）按回去。
 */
.tray > .btn.exit { flex: 0 1 auto; width: auto; }
/* 退出键上那行英文：深底上得换浅色——.btn.duo 默认给的是墨色，压在深棕上看不见 */
.btn.exit.duo .btn-en { color: rgba(233, 205, 152, 0.78); }

/*
 * 退出键**悬浮**（创作者定的）：一屏内容再长、往下拉多少，它都钉在右上角同一个位置上——
 * 最常用的一个键，不该被下面的内容推着一起滚上去。
 *
 * 两处挂它的地方（shell.js 的 sceneHead 行尾、screens-map.js 的 mapCap）都收在这条规矩里，
 * 所以以后新开的屏只要还用这两个抬头，自己就是悬浮的，不用一屏一屏去写。
 *
 * 顶栏（.hud）是 sticky 的，键就停在它下面那一行——顶栏多高由 JS 量出来写进 --hud-h
 * （见 ui/shell.js 的 renderHud 那一段），这里取不到时退回 0 也不至于叠上去。
 * 右边让出居中栏（.app 的 760px）外那圈空白：窗口拉宽了，它跟着栏子走，不飘到天边去。
 */
.scene .scene-row .btn.exit,
.map-cap.has-exit .btn.exit {
  position: fixed;
  right: max(var(--sp-4), calc((100% - 760px) / 2 + var(--sp-4)));
  top: calc(max(var(--hud-h, 0px), env(safe-area-inset-top)) + var(--sp-2));
  z-index: 5;
}

/* 玻璃底上的小按钮（跳过 / 不聊了 / 还剩几次） */
.btn.ghost {
  background: rgba(16, 11, 8, 0.6);
  color: var(--cine-text);
  border-color: var(--glass-line);
  border-left-width: 1px;
  box-shadow: none;
  backdrop-filter: blur(6px);
  font-size: var(--fs-s);
  width: auto;
  padding: var(--sp-2) var(--sp-3);
}
.btn.ghost:hover { border-color: var(--cine-gold); color: #fff6e4; }

/* ─────────────────────────────
   滚条与滚动边缘（创作者 2026-09-29 定的两件事）
   ───────────────────────────── */

/*
 * 1) 滚条：细、深棕、平时几乎看不见——它压在羊皮纸边上，不该比纸上的字更抢眼。
 *    Chrome / Edge / Safari 走 ::-webkit-scrollbar 那一套（8px、圆头、上下两颗小箭头撤掉）。
 *    ⚠️ 标准那两条（scrollbar-width / scrollbar-color）**只能包在 @supports 里给 Firefox**：
 *       Chromium 一旦看到这两条不是 auto，就把上面那套 webkit 皮整个丢掉，
 *       只剩它自己那根（实测 15px）——两套同时写等于没写。
 */
@supports not selector(::-webkit-scrollbar) {
  :root {
    scrollbar-width: thin;
    scrollbar-color: rgba(58, 42, 28, 0.42) transparent;
  }
}
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(58, 42, 28, 0.42); border-radius: 999px; }
::-webkit-scrollbar-thumb:hover { background: rgba(58, 42, 28, 0.66); }
::-webkit-scrollbar-corner { background: transparent; }
::-webkit-scrollbar-button { display: none; }

/*
 * 2) 滚动边缘渐隐：内容滚到边上时**渐渐隐**，不再是硬切一刀。
 *    --fade-t / --fade-b = 「这一头还藏着内容」——由 shell.js 的 wireScrollFade
 *    盯着 scrollTop 维护：能滚、且这一头还有才给一段雾，滚到头就收回去（所以
 *    停在顶上时纸的上沿照旧是清清爽爽的）。三条只列在真会滚的三处：
 *    弹页 / 就地小窗的身子 / 对话框的记录区。
 */
.sheet,
.panel-body,
.vn-scroll {
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 var(--fade-t, 0px), #000 calc(100% - var(--fade-b, 0px)), transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0, #000 var(--fade-t, 0px), #000 calc(100% - var(--fade-b, 0px)), transparent 100%);
}

/* ============================================================
   6 · 弹页（居中摊开的一张纸）
   ============================================================ */

.overlay {
  position: fixed;
  inset: 0;
  background: rgba(9, 7, 5, 0.66);
  backdrop-filter: blur(2px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-4);
  z-index: 56;
  animation: fade 0.2s ease both;
}
.overlay[hidden] { display: none; }

/*
 * 一张弹页：底就是**长方形羊皮纸**那张图（assets/ui/长方形羊皮纸底图.png，
 * 路径登记在 data/assets.js，由 shell.js 塞进根上的 --parchment-wide）。
 * 2026-09-29 创作者定的：所有默认皮跟【通讯】一样铺这张纸——
 * 从前那层奶油色渐变底不再画；深绿 / 暗红那两套皮各走各的（见 .sheet.green / .red）。
 */
.sheet {
  width: 100%;
  max-width: 560px;
  max-height: 82vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  background-image: var(--parchment-wide);
  background-size: 100% 100%;
  background-position: center;
  background-repeat: no-repeat;
  border: 0;
  border-radius: var(--r);
  box-shadow: 0 28px 70px rgba(0, 0, 0, 0.66);
  padding: var(--sp-5);
  animation: sheetIn 0.3s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.sheet > *:last-child { margin-bottom: 0; }

/*
 * 紧凑档：给「一次成交」那种小面板用（回满精力 / 快速成熟）。
 *
 * 它们只有两三行字和两颗按钮，摊一张 560px 的大纸会空得难看，
 * 也会把视线从「按哪颗」上带走。这儿把纸收窄、留白与字号各降一档，
 * 头那条红线收细——紧凑不等于潦草，只是不再摆排场。
 */
/* 顶上那排分类（和设置那张纸的 .set-tabs 同一种手感） */
.mail-tabs {
  display: flex;
  gap: var(--sp-1);
  margin: 2px 0 var(--sp-3);
  padding-bottom: 6px;
  border-bottom: 1px solid rgba(64, 48, 31, 0.28);
}
.mail-tab {
  flex: 1 1 0;
  padding: 4px 2px 5px;
  font-family: var(--font-body);
  font-size: var(--fs-s);
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  background: transparent;
  border: 0;
  border-radius: var(--r);
  cursor: pointer;
  transition: color 0.14s ease, background 0.14s ease;
}
.mail-tab:hover { color: var(--ink); background: rgba(255, 255, 255, 0.35); }
.mail-tab.on { color: var(--red); font-weight: 700; background: rgba(140, 47, 47, 0.09); }

/* 通讯录：一行一个人（头像 / 名字 / 到哪一步 / 十格好感），整行可点 */
.mail-contact {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  padding: 9px var(--sp-2);
  text-align: left;
  font-family: var(--font-body);
  background: transparent;
  border: 0;
  border-bottom: 1px dashed rgba(64, 48, 31, 0.22);
  cursor: pointer;
  transition: background 0.14s ease;
}
.mail-contact:last-of-type { border-bottom: 0; }
.mail-contact:hover { background: rgba(255, 255, 255, 0.38); }
.mc-tx { flex: 1 1 auto; min-width: 0; }
.mc-name { font-size: var(--fs-m); font-weight: 700; letter-spacing: 0.06em; color: var(--ink); }
.mc-en { margin-top: 1px; font-size: 9px; letter-spacing: 0.04em; color: var(--ink-soft); opacity: .72; }
.mc-note { margin-top: 3px; font-size: var(--fs-xs); color: var(--ink-soft); }

.sheet.compact {
  max-width: 384px;
  padding: var(--sp-4);
}
.sheet.compact .sheet-head {
  margin-bottom: var(--sp-3);
  padding-bottom: var(--sp-2);
  border-bottom-width: 1px;
  box-shadow: none;
}
.sheet.compact .sheet-head h2 { font-size: var(--fs-l); }
.sheet.compact .acts { margin-top: var(--sp-3) !important; gap: var(--sp-2); }
.sheet.compact .acts > * { flex: 0 1 auto; }

.sheet-head {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
  padding-bottom: var(--sp-3);
  border-bottom: 2px solid var(--ink);
  box-shadow: 0 1px 0 rgba(140, 47, 47, 0.45);
}
.sheet-head h2 { flex: 1; margin: 0; }

/*
 * 关掉它的那个叉：图形是现成的一张（assets/ui/关闭按钮.png —— 一块画着叉的小羊皮方牌，
 * 按 artNode 的规矩由 shell.js 的 closeButton 画成 <img>）。
 * 从前那枚内联 SVG 叉不画了——图形自带一个，键本身只剩个头寸。
 */
.x-btn {
  flex: none;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  padding: 0;
  background: transparent;
  border: 0;
  cursor: pointer;
  transition: filter 0.14s ease, transform 0.14s cubic-bezier(0.16, 1, 0.3, 1);
}
.x-btn .x-img { width: 100%; height: 100%; display: block; }
.x-btn:hover { filter: brightness(1.09) drop-shadow(0 2px 5px rgba(0, 0, 0, 0.35)); }
.x-btn:active { transform: scale(0.9); }

/*
 * 原地重画（openSheet 的 inPlace）：这张纸已经摊着，只是换了内容。
 * 入场动画挂在 .sheet 上，写死 none 是为了"这一类情况下绝不重播"——
 * 真正常开一张纸时这个类不在身上，动画照旧会播。
 */
.sheet.in-place { animation: none; }

/* ============================================================
   7 · 就地摊开的小窗（手抄本 / 今天的日程）
   ============================================================ */

.panel-layer {
  position: fixed;
  inset: 0;
  z-index: 52;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: var(--sp-4);
  animation: fade 0.18s ease both;
}
.panel-scrim { position: absolute; inset: 0; background: rgba(9, 7, 5, 0.5); }
/* 就地摊开那张纸（背包 / 手抄本…）：底图同弹页，也是长方形羊皮纸（见 .sheet 上面那段） */
.panel {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 560px;
  max-height: 78vh;
  background-image: var(--parchment-wide);
  background-size: 100% 100%;
  background-position: center;
  background-repeat: no-repeat;
  border: 0;
  border-radius: var(--r);
  box-shadow: 0 26px 64px rgba(0, 0, 0, 0.66);
  animation: rise 0.3s cubic-bezier(0.16, 1, 0.3, 1) both;
  overflow: hidden;
}
.panel-head {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-3) var(--sp-3) var(--sp-4);
  border-bottom: 2px solid var(--ink);
  box-shadow: 0 1px 0 rgba(140, 47, 47, 0.45);
}
.panel-title {
  flex: 1;
  display: flex;
  align-items: baseline;
  gap: 7px;
  font-size: var(--fs-l);
  font-weight: 700;
  letter-spacing: 0.04em;
}
/* 抬头里那句英文：小字、手写体、压暗一档（中文大、英文小） */
.panel-title .pt-en {
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  font-weight: 400;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  opacity: 0.75;
}

/*
 * 中英并排的一对字：中文大、英文小。
 *
 * 这一部里到处是这个样子（栏目名、格子名），在这儿只写一份：
 * 需要的地方包一层 .duo，里面两个 span 自己排。
 * 字号不写死——外层想多大就多大，英文跟着中文缩一档半（用 em，随外层走）。
 */
.duo {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  min-width: 0;
}
.duo-zh { line-height: 1.3; letter-spacing: 0.04em; }
.duo-en {
  font-family: var(--font-hand);
  /* 0.72em：跟着外层的中文字号缩，不另立一档字号 token */
  font-size: 0.72em;
  line-height: 1.3;
  letter-spacing: 0.02em;
  opacity: 0.7;
}
.panel-body {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--sp-4);
}
/* 小窗展开时会盖住底下的托盘，所以入口在小窗里也要够得着 */
.panel-acts { margin-top: var(--sp-4); flex-direction: row; justify-content: center; }
.panel-acts > * { flex: 0 1 auto; }
.panel-acts > .btn { width: auto; }
/* 门牌（手抄本 / 背包）不跟着拉伸，保持方块比例 */
.panel-acts > .paper-entry { flex: 0 0 auto; }

@media (max-width: 560px) {
  .panel-layer { padding: 0 0 env(safe-area-inset-bottom); }
  .panel { max-width: none; max-height: 84vh; border-radius: var(--r) var(--r) 0 0; }
  .panel-body { padding: var(--sp-3) var(--sp-4) var(--sp-5); }
}

/* ============================================================
   8 · 文游对话层
   贴着下沿的对话框；羊皮纸选项悬在它正上方。
   中间那片空着——立绘以后就站在那儿。
   ============================================================ */

.vn-host { position: fixed; inset: 0; z-index: 44; pointer-events: none; }
.vn-host:empty { display: none; }

/*
 * 对话在场时，页面那一层（.view 的抬头与牌、.tray 的托盘按钮）整个让位。
 *
 * 为什么必须让：这一层是铺满整屏的 fixed 层，可它只压暗**下半屏**
 * （.vn::before，上面那片得留给立绘），所以底下那一屏的牌子会从人头顶和
 * 两侧露出来——看着就是「进了对话，刚才那屏的 UI 还在」。
 *
 * 为什么是 visibility 而不是 display:none：留着占位，页面高度不塌，
 * 换回来的时候底图与滚动位置都不会跳一下。
 * class 由对话层自己挂/摘（ui/dialog.js 的 createDialog / destroy），
 * 所以对话期间就算有谁触发了重画，重画出来的页面层照样是藏着的。
 */
body.vn-live .view,
body.vn-live .tray { visibility: hidden; }

.vn {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  gap: var(--sp-2);
  padding: 0 var(--sp-3) calc(var(--sp-3) + env(safe-area-inset-bottom));
  pointer-events: auto;
  cursor: pointer;
  --fg: var(--cine-text);
  --fg-dim: var(--cine-dim);
  --fg-accent: var(--cine-gold);
  --fg-danger: var(--cine-red);
  color: var(--fg);
  animation: fade 0.3s ease both;
}
/* 只压暗下半屏：上面那片留给立绘，别糊住 */
.vn::before {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 62%;
  pointer-events: none;
  background: linear-gradient(180deg, transparent 0%, rgba(9, 7, 5, 0.42) 46%, rgba(9, 7, 5, 0.8) 100%);
}
.vn > * { position: relative; }

/*
 * 顶部一行：左边场次抬头，右边工具按钮（跳过 / 不聊了 / 还剩几次 / 手抄本）。
 * 它必须和对话框同属一层——抬头压在照片上、还要和按钮抢同一行，
 * 拆成两层写迟早叠在一起。顶栏多高由 JS 量出来写进 --hud-h。
 */
.vn-top {
  /* 立绘 z-index:1，这三层必须都在它之上：状态栏不能被人的头顶走 */
  z-index: 3;
  margin-bottom: auto;
  width: 100%;
  max-width: 760px;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-2);
  padding-top: calc(max(var(--hud-h, 0px), env(safe-area-inset-top)) + var(--sp-2));
}
/* 没有状态栏时（理论上不会），按钮自己靠右站 */
.vn-top.no-status { justify-content: flex-end; }

/* 状态栏占满左边，地点那一行才有地方居中 */
.vn-status { flex: 1; min-width: 0; }

.vn-tools {
  flex: none;
  display: flex;
  justify-content: flex-end;
  align-items: flex-start;
  gap: var(--sp-2);
  flex-wrap: wrap;
}

/* 窄屏上状态栏和按钮排不开，就上下落，别挤成一团 */
@media (max-width: 460px) {
  .vn-top { flex-direction: column; gap: var(--sp-2); }
  .vn-tools { order: 1; }
  .vn-status { order: 2; }
  .vn-status .row3 { text-align: left; }
}

/* ---------- 立绘：人站在场景里，脚踩到屏幕底 ----------
 *
 * 这一层是铺满整屏的一层（不参与 .vn 那条 flex），人从屏幕底沿往上长。
 * 所以头顶到接近状态栏、下半身被对话框盖住——这是文游该有的分量：
 * 人和那张场景照片在同一个空间里，而不是挂在半空的一张卡片。
 * 素材是抠好的透明 PNG，所以不加底、不加框、不垫任何 UI。
 * 绘制顺序由 DOM 决定：立绘排在选项与对话框之前，于是那两层自然压住他。
 */
.vn > .vn-cast {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: clamp(0px, 2vw, 22px);
  padding: 0 var(--sp-2);
  /* 人不是按钮：点击一律漏给下面，点他照样翻下一句 */
  pointer-events: none;
  /* 压在暗化层之上（人物该亮），但排在状态栏 / 选项 / 对话框之下——
     所以他的下半身是被框沿盖住的，这正是文游那种「人站在台词后面」的层次 */
  z-index: 1;
  transition: opacity 0.28s ease, filter 0.28s ease;
  /* 一个人时尽量撑满，两人同场各退一步（下面按 data-count 切换）。
     高是给桌面的上限，宽是给竖屏的上限——两个夹着，素材什么比例都不会崩。 */
  --sprite-h: 92vh;
  --sprite-w: min(84vw, 660px);
}
.vn-cast[hidden] { display: none; }
.vn > .vn-cast[data-count="2"] {
  --sprite-h: 84vh;
  --sprite-w: min(46vw, 430px);
}
/* 张开看记录的时候，人让位给字 */
.vn[data-expanded="true"] .vn-cast { opacity: 0.2; filter: saturate(0.5); }

.sprite {
  position: relative;
  /* 盒子按两个上限夹出来，图在盒子里按原比例贴住底边摆——
     既不拉伸也不裁头，素材比例换了也不用回来改这里 */
  width: var(--sprite-w);
  height: var(--sprite-h);
  /* 让脚伸出屏幕一点点：人被地面截断比「站在空中」更像现场 */
  margin-bottom: calc(-1 * var(--sp-3));
  transition: transform 0.34s cubic-bezier(0.16, 1, 0.3, 1), filter 0.34s ease, opacity 0.34s ease;
  /* 影子贴在轮廓上（透明 PNG 才做得到）：人和照片之间的那道界线就靠它 */
  filter: drop-shadow(0 14px 26px rgba(6, 4, 3, 0.55));
}

/*
 * 入场（他走进画面的那一下）——**只在 data-enter 还在的时候播**。
 *
 * ⚠️ 它从前是直接写在 .sprite 上的，这是「立绘一闪一闪」的头号来源：
 *    挂在元素上 = 只要那条规则被重新生效一次、或者节点被重新插回 DOM 一次，
 *    浏览器就把这段动画从头再放一遍（from 那一帧是 opacity:0 —— 于是人闪一下）。
 *    两种触发在这工程里都有：每翻一句台词都会重画立绘层，而重画从前会给
 *    已经在场的节点重新 appendChild（= 摘下来再插回去）。
 * 现在两道都堵上了：这条规则只有新节点带 data-enter 时才命中，
 * 而 data-enter 由 ui/sprites.js 在 480ms 后摘掉——**一辈子只播一次**。
 */
.sprite[data-enter="1"] { animation: spriteIn 0.44s cubic-bezier(0.16, 1, 0.3, 1); }

.sprite-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: 50% 100%;
  -webkit-user-drag: none;
  user-select: none;
  /*
   * 呼吸：人活着 —— **只要他站在画面里就一直在呼吸**（理由见下面「四种状态」那一段）。
   * 它挂在这条**基础**规则上，不挂在某个状态上：挂状态上就会跟着状态一次次重新开始，
   * 玩家每点一下都要看它从头「吸」一下。
   */
  animation: breathe 5.4s ease-in-out infinite alternate;
}

/*
 * 四种状态（sprites.js 报过来）：
 *   speaking 开口的那位
 *   neutral  这一刻没人开口（旁白、或轮到玩家打字）
 *   idle     别人在说：往暗处走一点——一眼看出这话不是他讲的
 *   leaving  这一场散了 / 位子要让给别人：淡掉（JS 等这个时长过了才摘节点）
 * 压暗只写在 idle 上——自由对话刚进来还没人说话，不该一上来就暗着。
 * 幅度都收得很轻：这是两个人站在同一束光里，不是聚光灯下的独舞。
 *
 * ⚠️ speaking / neutral / idle **三个的姿势和位置一模一样**，差别只在光和深浅。
 *    为什么这么定：旁白与台词本来就是一句一句交替的，玩家点一下就翻一句、
 *    也就换一次状态。要是让开口的那位「往前站半步」，每点一下人都要上下窜一下——
 *    读起来不是「他开口了」，是「这个画面在抖」；自由对话里更明显：
 *    你打完字、他回一句，一来说一回，坐着的人跟着一前一后地晃。
 *    谁在开口，交给名牌和对话框里那句话去说，不靠人挪位置。
 *    **只有 leaving 真的动**——那是「这一场散了」，一回戏只有一次，动得越多越像回事。
 */
/* speaking 与 neutral 都是 .sprite 的原样：不写 transform，站在同一个位置 */
.sprite[data-state="idle"] {
  filter: drop-shadow(0 10px 18px rgba(6, 4, 3, 0.45)) brightness(0.78) saturate(0.82);
  opacity: 0.88;
}
.sprite[data-state="leaving"] {
  /* 退场只化开——不带位移/缩放（创作者：「不要点一下弹跳一下」） */
  filter: drop-shadow(0 6px 12px rgba(6, 4, 3, 0.35)) brightness(0.72) saturate(0.85);
  opacity: 0;
}

/*
 * 呼吸：就挂在上面那条基础规则（.sprite-img）上，**不挂状态**。
 *
 * ⚠️ 从前它是挂在 speaking 上的，那正是「点一下立绘就蹦跶一下」的另一半原因：
 *    挂在状态上，这条规则每翻一句就被换成另一条、再换回来，动画于是被**重新开始**，
 *    每次点击都从关键帧的头「吸」一下。它和上面那条位移凑在一起，人就蹦了。
 * 呼吸说的是「这个人活着」，不是「这一刻他在说话」——
 * 所以他进了画面就一直呼吸；谁在说话，交给光去分（见上面 idle 那条）。
 */

@keyframes spriteIn {
  /* 入场只淡入——不位移、不缩放（creator：「不要点一下弹跳一下」） */
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes breathe {
  from { transform: translateY(0) scale(1); }
  to   { transform: translateY(-0.9%) scale(1.014); }
}

/* 晕动症的玩家不该被一个自己会动的人盯着 */
@media (prefers-reduced-motion: reduce) {
  .sprite { animation: none; }
  .sprite-img { animation: none; }
  .sprite[data-enter="1"] { animation: none; }
  .sprite, .vn-cast { transition-duration: 0.01ms; }
}

/*
 * 窄屏（手机竖屏）：宽度已经快占满，两人同场就别并排了——
 * 收掉间距、让后面那位往前那位身上靠一点，靠重叠站住，不靠缩小。
 */
@media (max-width: 460px) {
  .vn > .vn-cast { padding: 0; gap: 0; }
  .vn > .vn-cast[data-count="2"] { --sprite-w: 54vw; }
  .vn-cast[data-count="2"] .sprite[data-slot="1"] { margin-left: -7vw; }
}

/* ---------- 羊皮纸选项（悬在对话框上方） ---------- */
.vn-stack {
  z-index: 4;
  width: 100%;
  max-width: 620px;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.vn-stack[hidden] { display: none; }
.vn-prompt {
  font-size: var(--fs-xs);
  letter-spacing: 0.32em;
  text-align: center;
  color: var(--cine-gold);
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.9);
}
.vn-choice {
  --tilt: -0.35deg;
  position: relative;      /* 序号牌要钉在这张纸上，不是钉在屏幕上 */
  display: block;
  width: 100%;
  text-align: left;
  font-family: var(--font-body);
  background:
    radial-gradient(80% 60% at 14% 4%, rgba(255, 255, 255, 0.5), transparent 62%),
    linear-gradient(180deg, #fbf4e4, #eee1c4);
  border: 1px solid rgba(64, 48, 31, 0.44);
  border-radius: var(--r);
  padding: var(--sp-3) var(--sp-4) var(--sp-3) 2.9em;
  cursor: pointer;
  transform: rotate(var(--tilt));
  box-shadow: 0 3px 0 rgba(50, 37, 24, 0.42), 0 12px 26px rgba(0, 0, 0, 0.52);
  transition: transform 0.16s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.16s ease, border-color 0.16s ease;
  animation: choiceIn 0.36s cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: calc(var(--i, 0) * 55ms);
}
.vn-choice:nth-child(even) { --tilt: 0.3deg; }
.vn-choice:hover {
  border-color: var(--red);
  transform: rotate(var(--tilt)) translateY(-2px);
  box-shadow: 0 6px 0 rgba(50, 37, 24, 0.34), 0 16px 30px rgba(0, 0, 0, 0.55);
}
.vn-choice:active { transform: rotate(var(--tilt)) translateY(1px) scale(0.99); }
/* 序号牌：压在纸边上一枚红圈小章 */
.vn-choice .num {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  width: 1.55em;
  height: 1.55em;
  display: grid;
  place-items: center;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  color: var(--red);
  border: 1px solid rgba(140, 47, 47, 0.42);
  border-radius: 50%;
  background: rgba(255, 251, 240, 0.6);
}
.vn-choice .lbl { display: block; font-size: var(--fs-m); color: var(--ink); }
.vn-choice .note { display: block; font-size: var(--fs-s); color: var(--ink-soft); margin-top: 2px; line-height: 1.6; }

/*
 * 「收下」那张结果条（禁林末尾的「（获得蘑菇x3）」）。
 *
 * 它**不是一道选择题**，是这一趟的账——玩家得点它才走得下去，但它不该长得像岔路：
 * 不摆序号章，左侧那段给序号的位置也收掉；纸压暗一档、边线改虚线；字小一号、偏灰，
 * 读起来像账目而不是选项。仍然点得动（hover 抬一下），因为它是停住剧情的那一颗。
 */
.vn-choice.settle {
  --tilt: 0;
  padding-left: var(--sp-4);
  background:
    radial-gradient(80% 60% at 14% 4%, rgba(255, 255, 255, 0.34), transparent 62%),
    linear-gradient(180deg, #f3ead6, #e6d9bd);
  border-style: dashed;
  border-color: rgba(64, 48, 31, 0.34);
  box-shadow: 0 2px 0 rgba(50, 37, 24, 0.3), 0 8px 18px rgba(0, 0, 0, 0.42);
}
.vn-choice.settle:hover {
  border-color: rgba(64, 48, 31, 0.5);
  transform: translateY(-1px);
  box-shadow: 0 4px 0 rgba(50, 37, 24, 0.26), 0 12px 22px rgba(0, 0, 0, 0.46);
}
.vn-choice.settle .lbl { font-size: var(--fs-s); color: var(--ink-soft); }

/* ---------- 对话框本体 ---------- */
.vn-box {
  position: relative;
  z-index: 5;
  width: 100%;
  max-width: 760px;
  min-height: 8.4em;
  display: flex;
  flex-direction: column;
  background: linear-gradient(180deg, rgba(24, 16, 11, 0.78), rgba(10, 7, 5, 0.92));
  -webkit-backdrop-filter: blur(10px) saturate(1.06);
  backdrop-filter: blur(10px) saturate(1.06);
  border: 1px solid var(--glass-line);
  border-radius: var(--r);
  box-shadow: var(--sh-glass);
  padding: 22px var(--sp-4) var(--sp-3);
  /* 怎么长都不许顶出屏幕：框有上限，读不完的部分交给里面的滚动区 */
  max-height: 74vh;
}
.vn-box[data-expanded="true"] { animation: boxGrow 0.28s cubic-bezier(0.16, 1, 0.3, 1) both; }

/* 翻记录的时候先把手上的选项收起来——那会儿玩家要读，不要选 */
.vn[data-expanded="true"] .vn-stack { display: none; }

/* 说话人名牌：一片小羊皮，坐在框的上沿 */
.vn-plate {
  position: absolute;
  top: -13px;
  left: var(--sp-4);
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  letter-spacing: 0.14em;
  color: var(--ink);
  background: linear-gradient(180deg, #fbf4e4, #eddfc0);
  border: 1px solid rgba(64, 48, 31, 0.4);
  border-radius: var(--r);
  padding: 2px 12px;
  box-shadow: 0 5px 16px rgba(0, 0, 0, 0.5);
}
.vn-plate[hidden] { display: none; }
/* 玩家自己那一句：红墨写的，一眼和别人分开 */
.vn-plate[data-kind="picked"] {
  color: var(--red);
  border-color: rgba(140, 47, 47, 0.5);
  background: linear-gradient(180deg, #fdf2e6, #f2e0cb);
}

/* 展开 / 收起 */
.vn-fold {
  position: absolute;
  top: -13px;
  right: var(--sp-4);
  display: flex;
  align-items: center;
  gap: 5px;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  letter-spacing: 0.14em;
  color: var(--cine-dim);
  background: rgba(16, 11, 8, 0.86);
  border: 1px solid var(--glass-line);
  border-radius: var(--r);
  padding: 3px 10px;
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.vn-fold:hover { color: #fff6e4; border-color: var(--glass-line-2); }
.vn-fold .arrow { font-size: 10px; line-height: 1; }

/* 记录区：收起时只有当前一句，展开时全部记录可滚 */
.vn-scroll {
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 2px 4px 2px 0;
  max-height: 26vh;
}
.vn-box[data-expanded="true"] .vn-scroll { max-height: 46vh; }
.vn-box[data-expanded="false"] .vn-entries .vn-entry:not(.is-current) { display: none; }

.vn-entry { margin: 0 0 var(--sp-3); animation: lineIn 0.3s cubic-bezier(0.16, 1, 0.3, 1) both; }
.vn-entry:last-child { margin-bottom: 0; }
.vn-entry .who {
  display: block;
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  letter-spacing: 0.14em;
  color: var(--fg-accent);
  margin-bottom: 2px;
}
/* 收起时名牌已经贴在框沿上了，条目里就不再重复一遍 */
.vn-box[data-expanded="false"] .vn-entry .who { display: none; }

.vn-entry .txt {
  font-size: var(--fs-l);
  line-height: 1.95;
  color: var(--cine-text);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85), 0 2px 14px rgba(0, 0, 0, 0.55);
  white-space: pre-wrap;
  word-break: break-word;
}
.vn-entry.narr .txt { font-size: var(--fs-m); color: var(--cine-dim); }
.vn-entry.inner .txt { color: var(--cine-gold); font-style: italic; font-size: var(--fs-m); }
.vn-entry.note .txt {
  font-size: var(--fs-s);
  color: var(--cine-dim);
  border-left: 2px solid var(--glass-line-2);
  padding-left: 10px;
}
.vn-entry.picked .who { color: var(--cine-red); }

/* 打字光标 */
.vn-entry.typing .txt::after {
  content: '▍';
  margin-left: 1px;
  opacity: 0.55;
  animation: caret 0.9s steps(1) infinite;
}
@keyframes caret { 0%, 60% { opacity: 0.55; } 61%, 100% { opacity: 0; } }

/* 底部：提示 + 输入行 */
.vn-foot {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  min-height: 1.3em;
  margin-top: var(--sp-1);
}
.vn-hint {
  font-size: var(--fs-xs);
  letter-spacing: 0.2em;
  color: var(--cine-dim);
  animation: hintPulse 1.8s ease-in-out infinite;
}
.vn-hint[hidden] { display: none; }

.vn-bar { display: flex; gap: var(--sp-2); align-items: flex-end; margin-top: var(--sp-2); }
.vn-bar[hidden] { display: none; }
.vn-bar textarea {
  flex: 1;
  resize: none;
  min-height: 42px;
  max-height: 104px;
  font-family: var(--font-body);
  font-size: var(--fs-m);
  line-height: 1.6;
  color: var(--cine-text);
  background: rgba(255, 247, 232, 0.06);
  border: 1px solid var(--glass-line);
  border-radius: var(--r);
  padding: 10px 12px;
}
.vn-bar textarea:focus { outline: none; border-color: var(--glass-line-2); background: rgba(255, 247, 232, 0.1); }
.vn-bar textarea::placeholder { color: rgba(189, 169, 138, 0.75); letter-spacing: 0.04em; }
.vn-bar[data-off="true"] textarea { opacity: 0.5; }
.vn-send {
  flex: none;
  font-family: var(--font-body);
  font-size: var(--fs-m);
  color: var(--ink);
  background: linear-gradient(180deg, #fbf4e4, #eddfc0);
  border: 1px solid rgba(64, 48, 31, 0.4);
  border-radius: var(--r);
  padding: 11px var(--sp-4);
  cursor: pointer;
  box-shadow: 0 3px 0 rgba(50, 37, 24, 0.4);
  transition: transform 0.14s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.14s ease;
}
.vn-send:hover { border-color: var(--red); }
.vn-send:active { transform: translateY(1px); }
.vn-send:disabled { opacity: 0.45; cursor: not-allowed; }

@media (max-width: 560px) {
  .vn { padding: 0 var(--sp-2) calc(var(--sp-2) + env(safe-area-inset-bottom)); gap: var(--sp-2); }
  .vn-box { padding: 20px var(--sp-3) var(--sp-2); min-height: 9em; }
  .vn-entry .txt { font-size: var(--fs-m); }
  .vn-scroll { max-height: 30vh; }
  .vn-box[data-expanded="true"] .vn-scroll { max-height: 44vh; }
  .vn-stack { max-width: none; }
  /* 左边那一条要让给序号牌，窄屏也不能压住字 */
  .vn-choice { padding: var(--sp-3) var(--sp-3) var(--sp-3) 2.7em; }
  .vn-choice .num { left: 8px; }
}

@media (prefers-reduced-motion: reduce) {
  .vn-hint { animation: none; opacity: 0.8; }
  .vn-entry.typing .txt::after { animation: none; }
}

/* ============================================================
   9 · 大地图（牌子直接钉在城堡上）
   ============================================================ */

/*
 * 地图整块往上提：负边距吃掉 .view 顶部那段留白，于是第一排牌子就贴在
 * 状态栏底下（创作者要的「从靠近状态栏的位置开始放」）。
 * 行距也收一档——三行牌子原来能一路排到折叠线以下。
 */
.map-top {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  margin-top: calc(var(--sp-2) * -1);
}
/* 日期与时段只在顶部状态栏出现一次，地图上不再重复 */
.map-btns { display: flex; flex-direction: column; gap: var(--sp-2); padding-top: 0; }
.map-row { display: flex; gap: var(--sp-2); flex-wrap: wrap; justify-content: center; }

/*
 * 这一层地图的名字，压在照片上部。
 * 落在照片上的字一律取暖白 + 暗影，别用纸上那套墨色。
 */
.map-cap {
  text-align: center;
  /*
   * 底下一片淡到看不见的暗雾：照片上半常常是亮天或亮窗，只靠文字阴影撑不住。
   * 用径向渐变、不用直线渐变——四边都化开，才不会露出一块方方的暗斑。
   */
  /* 这一层以前上下共吃掉 sp-2 + sp-4 + sp-2，光一个标题就把牌子推到折叠线以下 */
  margin: 0;
  padding: var(--sp-1) 0 var(--sp-2);
  background: radial-gradient(120% 92% at 50% 26%,
    rgba(9, 7, 5, 0.42), rgba(9, 7, 5, 0) 72%);
}
.map-cap .mc-zh {
  display: block;
  font-family: var(--font-hand);
  font-size: var(--fs-xl);
  letter-spacing: 0.14em;
  color: var(--cine-text);
  text-shadow: 0 2px 14px rgba(0, 0, 0, 0.92);
}
.map-cap .mc-en {
  display: block;
  margin-top: 1px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.3em;
  color: var(--cine-gold);
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.92);
}
/*
 * 地图标题那一行要挂退出键时（见 screens-map.js 的 mapCap 第 3 个参数）：
 * 标题照旧**居中**（那是这一屏的名字，居中才是它的样子）。
 * 键本身现在悬浮在右上角（见 .btn.exit 那一处的悬浮规矩），不在这条流里；
 * 这里只把标题右边让出同样一段，让居中那行字不至于钻到键底下。
 */
.map-cap.has-exit { padding-right: 90px; }

/* 地点牌子：一片钉在城堡上的羊皮标签，各带各的倾角与高低 */
.place {
  --tilt: 0deg;
  --dy: 0px;
  position: relative;
  font-family: var(--font-body);
  text-align: left;
  background:
    radial-gradient(80% 60% at 14% 4%, rgba(255, 255, 255, 0.5), transparent 62%),
    linear-gradient(180deg, #fbf4e4, #eddfc0);
  border: 1px solid rgba(64, 48, 31, 0.44);
  border-radius: var(--r);
  padding: var(--sp-2) var(--sp-4);
  min-width: 6.4em;
  cursor: pointer;
  transform: rotate(var(--tilt)) translateY(var(--dy));
  box-shadow: 0 3px 0 rgba(50, 37, 24, 0.4), 0 12px 26px rgba(0, 0, 0, 0.5);
  transition: transform 0.16s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.16s ease, border-color 0.16s ease;
}
/* 针脚：让牌子看起来是钉上去的 */
.place::after {
  content: '';
  position: absolute;
  top: 5px;
  right: 6px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: rgba(140, 47, 47, 0.75);
  box-shadow: inset 0 -1px 1px rgba(0, 0, 0, 0.4);
}
.place:hover {
  border-color: var(--red);
  transform: rotate(var(--tilt)) translateY(calc(var(--dy) - 3px));
  box-shadow: 0 6px 0 rgba(50, 37, 24, 0.34), 0 16px 28px rgba(0, 0, 0, 0.55);
}
.place:active { transform: rotate(var(--tilt)) translateY(var(--dy)) scale(0.97); }

.place-name { display: block; font-size: var(--fs-m); color: var(--ink); line-height: 1.4; }
.place-sub {
  display: block;
  font-family: var(--font-hand);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  white-space: nowrap;
}

/*
 * 学院专属的那一处：用学院色包边，针脚也换成学院色。
 * 四个学院各有各的一处，永远钉在同一格——于是「我学院那扇门」
 * 变成地图上一个固定的位置，一眼就找得到。
 */
.place.house {
  border-color: var(--house, var(--red));
  border-width: 2px;
  box-shadow: 0 3px 0 rgba(50, 37, 24, 0.4), 0 12px 26px rgba(0, 0, 0, 0.5),
              inset 0 0 0 1px rgba(255, 250, 240, 0.5);
}
.place.house::after { background: var(--house, var(--red)); }
.place.house:hover {
  border-color: var(--house, var(--red));
  box-shadow: 0 6px 0 rgba(50, 37, 24, 0.34), 0 16px 30px rgba(0, 0, 0, 0.55),
              inset 0 0 0 1px rgba(255, 250, 240, 0.6);
}

/*
 * 地点自己带颜色的那一类（眼下只有翻倒巷：深蓝）。
 * 颜色由 JS 从 data/locations.js 的 tint 挂进 --tint，这里只管「涂上之后
 * 字要反白、针脚要压得住」——深蓝底上再写墨字就糊了。
 */
.place.tint {
  background:
    radial-gradient(80% 60% at 14% 4%, rgba(255, 255, 255, 0.16), transparent 62%),
    linear-gradient(180deg, var(--tint, #1d2b4d), #121a2e);
  border-color: rgba(224, 178, 100, 0.5);
  box-shadow: 0 3px 0 rgba(8, 12, 24, 0.6), 0 12px 26px rgba(0, 0, 0, 0.55);
}
.place.tint .place-name { color: #f1e7d0; }
.place.tint .place-sub { color: rgba(224, 178, 100, 0.85); }
.place.tint::after { background: var(--cine-gold); }
.place.tint:hover {
  border-color: var(--cine-gold);
  box-shadow: 0 6px 0 rgba(8, 12, 24, 0.55), 0 16px 28px rgba(0, 0, 0, 0.6);
}

/*
 * 入夜的密林：那一格长出一上一下两颗牌（云游商人 / 往更深处走）。
 *
 * 倾角与上下错位挂在**这一层**，两颗子牌各自的 --tilt / --dy 清成零——
 * 不清的话两颗会各自再转一次、再各抬一次，看着像钉歪了。
 * 整块还是占禁林原来那个位置，所以天黑以后这块坡不会跳位。
 */
.place-pair {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  /* --tilt / --dy 由 JS 挂在这一层（沿用禁林自己的倾角与错位） */
  transform: rotate(var(--tilt, 0deg)) translateY(var(--dy, 0px));
}
/* 子牌各自那份清成零：不然两颗会再各转一次、各抬一次，看着像钉歪了 */
.place-pair .place { --tilt: 0deg; --dy: 0px; }

/* 商人那颗：夜里才来的人，给一点灯火气（暖金边 + 稍微压暗的纸），
   和「正经门牌」区分开，但不做成彩色按钮那样抢。 */
.place.pair-merchant {
  border-color: rgba(224, 178, 100, 0.72);
  background:
    radial-gradient(80% 60% at 14% 4%, rgba(255, 255, 255, 0.42), transparent 62%),
    linear-gradient(180deg, #f7e9c8, #e7d0a2);
}
.place.pair-merchant::after { background: var(--cine-gold); }

/* ============================================================
   9b · 教室：六门学科排成一列长条
   ------------------------------------------------------------
   这一层不摆便签——便签是「随手钉上去的」，而课表是「刻在墙上的」。
   半透明深棕底衬在拱廊照片上，一条压一条，从上到下对齐。
   ============================================================ */

.subject-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
}

.subject {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  width: 100%;
  padding: var(--sp-3) var(--sp-4);
  font-family: var(--font-body);
  text-align: left;
  color: var(--cine-text);
  /* 半透明深棕：底下的照片透出来一点点，字仍然压得住 */
  background: rgba(58, 40, 26, 0.7);
  border: 1px solid rgba(224, 178, 100, 0.28);
  border-radius: var(--r);
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.42), inset 0 1px 0 rgba(255, 235, 200, 0.07);
  transition: background 0.16s ease, border-color 0.16s ease, transform 0.16s cubic-bezier(0.16, 1, 0.3, 1);
}
.subject:hover {
  background: rgba(84, 56, 34, 0.82);
  border-color: rgba(224, 178, 100, 0.6);
  transform: translateY(-1px);
}
.subject:active { transform: translateY(0) scale(0.99); }
.subject .s-zh { font-size: var(--fs-m); letter-spacing: 0.1em; }
.subject .s-en {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  letter-spacing: 0.06em;
  color: var(--cine-dim);
}
/* 今天就是这一堂：一条金边 + 右边两个字。不抢，但一眼看得见 */
.subject.today {
  border-color: rgba(224, 178, 100, 0.68);
  background: rgba(74, 50, 30, 0.78);
}
.subject .s-today {
  flex: none;
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  color: var(--cine-gold);
}

@media (max-width: 560px) {
  .place { min-width: 5.2em; padding: var(--sp-2) var(--sp-3); }
  .place-name { font-size: var(--fs-s); }
  .place-sub { font-size: 9px; }
  .map-row { gap: var(--sp-2); }
  .map-cap .mc-zh { font-size: var(--fs-l); }
  .subject { padding: var(--sp-2) var(--sp-3); gap: var(--sp-2); }
  .subject .s-zh { font-size: var(--fs-s); }
  .subject .s-en { font-size: var(--fs-xs); }
}

/* ============================================================
   9c · 屋子里那一摞牌子（各间屋子共用：医疗翼、教室、走廊、海格门口……）
   ------------------------------------------------------------
   从前 .room-map 是**一张坐标纸**：每块牌子自己报一个百分比角落，于是
   【校园公告板】钉在顶上（y 0）、那扇星空门钉在底下（y 71），中间隔着十万八千里
   ——创作者 2026-10-01 报的就是这个：「全图都有这个毛病，黑湖也是」。
   现在改成**一摞**：从上往下排、居中、缝 10px。挨近了，而且**结构上不可能互相压住**
   （字多到折行的那块自己长高，下面那块跟着往下让；从前两份 y 撞上就是叠在一起）。
   宽度也统一由这一层给（.room-map > *）：一排牌子一样宽才看得齐，
   所以 JS 那边不再一块牌子写一个 x / y / w。
   ============================================================ */

.room-map {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin-top: var(--sp-1);
  padding: var(--sp-4) 0 var(--sp-5);
}
.room-map > * {
  width: min(78%, 22rem);
  min-width: 0;
}

/*
 * 公共休息室那两颗并排的牌子（ui/screens-map.js 的 restBtn）：
 * 上「在休息室沙发上躺一会」，下「回寝室睡觉」。
 *
 * 各占一行、上下叠着，不是左右并排——两颗都带一行小字（+N 精力 与那句提醒），
 * 左右并排在窄屏上会挤成两坨。flex-wrap 是给牌子内部用的：
 * 那行小字（.rb-sub）要自己占满一行，不 wrap 就会被挤在主标题那一行里。
 */
.rest-pair {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  align-items: center;
}
.rest-pair .rest-one {
  width: 100%;
  flex-wrap: wrap;
}
/*
 * 天黑了那颗「沙发上躺一会儿」按不动（小歇是白天的事）。
 * 灰掉之外还要**把 hover 那两下也按住**——不然鼠标一放上去它又亮起来，
 * 玩家会以为是自己点错了。
 */
.rest-pair .rest-one:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}
.rest-pair .rest-one:disabled:hover {
  background: rgba(58, 40, 26, 0.72);
  border-color: rgba(224, 178, 100, 0.3);
  transform: none;
}
/* 灰掉那颗的小字用同一种压暗的色，别再用金字——那会让它看着像还能按 */
.rest-pair .rest-one:disabled .rb-tag,
.rest-pair .rest-one:disabled .rb-sub { color: var(--cine-dim); }

/*
 * 「回寝室睡觉」那颗是**夜色调**（深蓝），木头棕的那一排里一眼分得出来：
 * 它是唯一一颗按下去会「关掉今天」的牌子。两颗并排摆着，差别就得看得见。
 */
.room-btn.rest-sleep {
  background: rgba(32, 38, 62, 0.74);
  border-color: rgba(150, 170, 220, 0.32);
}
.room-btn.rest-sleep:hover {
  background: rgba(44, 52, 84, 0.86);
  border-color: rgba(176, 194, 238, 0.62);
}
.room-btn.rest-sleep .rb-sub { color: rgba(196, 210, 244, 0.86); }

/*
 * 公共休息室里「进入宿舍区」那颗小牌（ui/screens-map.js 的 screenCommonRoom）：
 * 摆在两颗休息牌下面、隔开一档、整体小一号——它是换地方，不是屋里歇一下。
 */
.dorm-entry {
  display: flex;
  justify-content: center;
  margin-top: var(--sp-5);
}
.dorm-entry .room-btn {
  max-width: 15rem;
  padding: calc(var(--sp-1) + 2px) var(--sp-4);
}
.dorm-entry .room-btn .rb-zh { font-size: var(--fs-xs); letter-spacing: 0.1em; }
.dorm-entry .room-btn .rb-en { font-size: 9px; }

.room-btn {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  font-family: var(--font-body);
  text-align: left;
  color: var(--cine-text);
  background: rgba(58, 40, 26, 0.72);
  border: 1px solid rgba(224, 178, 100, 0.3);
  border-radius: var(--r);
  cursor: pointer;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.48), inset 0 1px 0 rgba(255, 235, 200, 0.07);
  transition: background 0.16s ease, border-color 0.16s ease, transform 0.16s cubic-bezier(0.16, 1, 0.3, 1);
}
.room-btn:hover {
  background: rgba(84, 56, 34, 0.84);
  border-color: rgba(224, 178, 100, 0.62);
  transform: translateY(-2px);
}
.room-btn:active { transform: translateY(0) scale(0.98); }
.room-btn:disabled { opacity: 0.45; cursor: not-allowed; transform: none; }
.room-btn .rb-zh { font-size: var(--fs-m); letter-spacing: 0.1em; white-space: nowrap; }
.room-btn .rb-en {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  color: var(--cine-dim);
}
/* 右下角那个状态小标：本周已治过 / 还差几点 / 今天做过了 */
.room-btn .rb-tag {
  flex: none;
  font-size: var(--fs-xs);
  letter-spacing: 0.14em;
  color: var(--cine-gold);
}

/* 里面那些东西的状态（种植箱报的是「2生长·1成熟」） */
.room-btn .rb-sub {
  display: block;
  flex: 1 1 100%;
  margin-top: 1px;
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  color: var(--cine-gold);
}

/*
 * 大一号的那块牌子（温室里的种植箱）。
 * 它比旁边的【上课】更高、字大一号——因为它报的是那几格土现在什么样，
 * 不只是一个动作。一行字的主按钮规矩在这里破一次，是有理由的。
 */
.room-btn.big {
  flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4) var(--sp-4);
}
.room-btn.big .rb-zh { font-size: var(--fs-l); letter-spacing: 0.14em; }
.room-btn.big .rb-en { flex: none; font-size: var(--fs-xs); }

/*
 * 种植箱那一块是**深绿半透明**的。
 * 屋里别的牌子是棕色木头（老屋子的味儿），只有它是土里的东西——
 * 一眼就分得出「那块是地」和「那块是事」。
 * 写在 .big 之后：两条规则一样具体，靠先后顺序定胜负。
 */
.room-btn.plot {
  background: rgba(30, 56, 34, 0.6);
  border-color: rgba(158, 200, 146, 0.3);
}
.room-btn.plot:hover {
  background: rgba(44, 78, 48, 0.74);
  border-color: rgba(158, 200, 146, 0.6);
}
.room-btn.plot .rb-sub { color: #b6d6a4; }

/*
 * 探索牌子（花盆 / 草丛 / 食品柜 / 浴缸 / 露台 / 猫头鹰屋）。
 * 它下面那行写的是「−5 精力」——是**代价**，不是种植箱那种「两格熟了」的状态。
 * 所以压暗一档：两处都用金字，玩家就分不出哪个是提醒、哪个是收费。
 * 带伤干活时这行会自己变成 −10（数由 engine/health.js 算，牌子不骗人）。
 */
.room-btn.forage .rb-sub { color: var(--cine-dim); }

@media (max-width: 560px) {
  /* 窄屏上「Ask for Treatment」这类长英文名先让位给中文名，别让胶囊鼓起来 */
  .room-btn .rb-en { display: none; }
  .room-btn { padding: var(--sp-2) var(--sp-3); }
  .room-btn .rb-zh { font-size: var(--fs-s); }
  /* 一摞本来就没有「坐标纸」这回事，窄屏上只是把那一排让宽一点、更好点 */
  .room-map > * { width: min(88%, 24rem); }
}

/* ============================================================
   9b · 种植箱：六格土（下面三格要花钱修）
   ============================================================ */

/*
 * 一格土 = 一个竖条；六个竖条分两排摆着。
 *
 * 不是羊皮纸清单：它们更像屋里那三个木头箱子，摆在温室的玻璃底下，
 * 所以跟屋里别的牌子同一身皮（半透明深色 + 暖白字）。
 * 整块就是一个按钮——空格点它挑种子，熟了那格点它摘下来。
 */
.bed-row {
  display: flex;
  gap: var(--sp-3);
  margin: 0 0 var(--sp-4);
}
/* 家里田地最后一行不满三格时的空位：只撑宽度，不画东西 */
.bed-spacer { flex: 1 1 0; min-width: 0; }

/* 家里田地的自适应格盘：格数越多列越多、格子越小；36 格不摆植物图 */
.bed-grid {
  display: grid;
  grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr));
  gap: 8px;
  margin: 0 0 var(--sp-4);
}
.bed-grid .bed {
  min-height: 0;
  padding: 8px 4px 9px;
  border-bottom-width: 2px;
}
.bed-grid .bed-no { font-size: 9px; letter-spacing: 0.1em; }
.bed-grid .bed-art { font-size: 20px; line-height: 1.2; }
.bed-grid .bed-art-img { height: 44px; }
.bed-grid .bed-name { font-size: var(--fs-xs); }
.bed-grid .bed-state { font-size: 9px; }
.bed-grid .bed-act { font-size: 9px; }
.bed-grid.cols-3 .bed { min-height: 148px; }
.bed-grid.cols-3 .bed-art-img { height: 56px; }
.bed-grid.cols-5 .bed { min-height: 112px; }
.bed-grid.cols-5 .bed-art-img { height: 40px; }
.bed-grid.cols-8 .bed { min-height: 88px; padding: 6px 3px 7px; }
.bed-grid.cols-8 .bed-art-img { height: 28px; }
.bed-grid.cols-8 .bed-name { font-size: 10px; }
.bed-grid.no-art .bed { min-height: 56px; padding: 6px 3px 7px; justify-content: center; }
.bed-grid.no-art .bed-art { display: none; }
.bed-grid.no-art .bed-name { font-size: 10px; }
.bed-grid.no-art .bed-state,
.bed-grid.no-art .bed-act { font-size: 8px; }
.bed-grid.no-art .bed-bonus { font-size: 8px; padding: 0 4px; }

/* 种子条和催熟钮并排：田格再多，催熟也不会沉到最底下 */
.bed-tools { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.bed-tools .seed-strip { flex: 1 1 220px; margin: 0; }

/* 田地上方：左边那行英文地名，右边两颗整地钮（全部播种 / 全部收获） */
.plots-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.plots-head .place-hint { margin: 0; }
.plots-bulk { display: flex; align-items: center; gap: 8px; }

.bed {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-height: 186px;
  padding: var(--sp-3) var(--sp-2) var(--sp-4);
  font-family: var(--font-body);
  text-align: center;
  color: var(--cine-text);
  background:
    linear-gradient(180deg, rgba(232, 240, 224, 0.07), transparent 40%),
    rgba(20, 32, 22, 0.56);
  border: 1px solid rgba(158, 200, 146, 0.24);
  border-bottom-width: 3px;
  border-radius: var(--r);
  cursor: pointer;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.42), inset 0 1px 0 rgba(220, 240, 214, 0.06);
  transition: background 0.16s ease, border-color 0.16s ease, transform 0.16s cubic-bezier(0.16, 1, 0.3, 1);
}
.bed:hover {
  background: rgba(38, 60, 40, 0.72);
  border-color: rgba(182, 214, 164, 0.54);
  transform: translateY(-2px);
}
.bed:active { transform: translateY(0) scale(0.98); }

/* 熟了的那一格镶一道金边：三格里该对谁动手，一眼看得出 */
.bed.ripe { border-color: rgba(224, 178, 100, 0.66); }
/* 空的那一格：虚线，看着就是个「还能种」的位置 */
.bed.empty { border-style: dashed; }

.bed-no {
  font-family: var(--font-hand);
  font-size: 10px;
  letter-spacing: 0.22em;
  color: var(--cine-dim);
}
.bed-art { font-size: 30px; line-height: 1.5; }
/*
 * 草药那批现在是有图的（HERB_ART）——图按高度定尺寸、宽度跟着比例走。
 * 那些是细长的立幅标本，给一条高度、宽度自然收窄，撑不破这一格；
 * max-width 留着是为了将来补进来一张方构图的东西也不至于顶出去。
 */
.bed-art-img {
  display: block;
  height: 62px;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  filter: drop-shadow(0 3px 4px rgba(0, 0, 0, 0.4));
}
.bed.empty .bed-art { font-size: 24px; color: rgba(224, 178, 100, 0.6); }
.bed-name { font-size: var(--fs-s); letter-spacing: 0.04em; }
.bed-state { font-size: var(--fs-xs); color: var(--cine-dim); }
.bed.ripe .bed-state { color: var(--cine-gold); }
/* 催过的那一格：一个小角标，写着「多几份」——买来的东西得看得见 */
.bed-bonus {
  font-family: var(--font-hand);
  font-size: 10px;
  letter-spacing: 0.06em;
  padding: 0 7px 1px;
  color: #3a2c14;
  background: rgba(224, 178, 100, 0.85);
  border-radius: 999px;
}
/* 最下面那行是「点它会怎样」 */
.bed-act {
  margin-top: auto;
  padding-top: var(--sp-2);
  font-size: var(--fs-xs);
  letter-spacing: 0.12em;
  color: var(--cine-gold);
}

/*
 * 三格下面那颗【快速成熟】：小、靠右、不抢眼。
 * 它不是这一天该做的事，是「等不及了」时才找的那个出口——
 * 所以做成一颗窄按钮，而不是又一枚跟格子一样宽的大按键。
 */
.bed-foot {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--sp-3);
}
.btn.small {
  display: inline-block;
  width: auto;
  padding: 5px 14px 6px;
  font-size: var(--fs-s);
  letter-spacing: 0.08em;
  box-shadow: 0 2px 0 rgba(50, 37, 24, 0.34), 0 6px 14px rgba(0, 0, 0, 0.34);
}

/*
 * 田地上那四颗动作钮（全部播种 / 全部收获 / 制种机 / 快速成熟）：
 * 纸色按钮压在这张深绿田地上太跳，换一身和界面同族的深绿——
 * 底色照 .sheet.green / .seed-tile 那一族（半透明深绿 + 暖白字 + 同色描边），
 * 再叠一层会呼吸的发光 + 往上飘的发光孢子（纯 CSS，不引图）。
 * 排在 .btn.small / .btn:hover 之后：同一批类名，靠源码顺序赢下底色与投影。
 */
.btn.plots-btn {
  position: relative;
  overflow: hidden;
  color: #eef6e6;
  background:
    radial-gradient(80% 60% at 14% 4%, rgba(200, 240, 190, 0.14), transparent 62%),
    linear-gradient(180deg, rgba(44, 68, 46, 0.96), rgba(24, 40, 28, 0.97));
  border: 1px solid rgba(158, 200, 146, 0.5);
  box-shadow: 0 2px 0 rgba(16, 28, 18, 0.5), 0 6px 16px rgba(0, 0, 0, 0.4), 0 0 12px rgba(140, 220, 150, 0.35);
  animation: plotsGlow 2.6s ease-in-out infinite;
}
.btn.plots-btn:hover {
  border-color: rgba(190, 240, 175, 0.85);
  box-shadow: 0 4px 0 rgba(16, 28, 18, 0.5), 0 10px 22px rgba(0, 0, 0, 0.5), 0 0 18px rgba(160, 240, 170, 0.6);
}
.btn.plots-btn:disabled { animation: none; opacity: 0.42; }
/* 往上飘的发光孢子：三小簇错开上升、到顶淡出（overflow:hidden 把它们裁在钮里） */
.btn.plots-btn::after {
  content: '';
  position: absolute; inset: 0; pointer-events: none;
  background-image:
    radial-gradient(circle, rgba(196, 255, 196, 0.95) 0 1.5px, transparent 2.4px),
    radial-gradient(circle, rgba(150, 240, 170, 0.8) 0 1px, transparent 1.8px),
    radial-gradient(circle, rgba(214, 255, 200, 0.85) 0 1.2px, transparent 2px);
  background-repeat: no-repeat;
  background-size: 40px 100%, 26px 100%, 54px 100%;
  animation: plotsSpores 3.2s linear infinite;
}
@keyframes plotsGlow {
  0%, 100% { box-shadow: 0 2px 0 rgba(16, 28, 18, 0.5), 0 6px 16px rgba(0, 0, 0, 0.4), 0 0 9px rgba(140, 220, 150, 0.28); }
  50% { box-shadow: 0 2px 0 rgba(16, 28, 18, 0.5), 0 6px 16px rgba(0, 0, 0, 0.4), 0 0 18px rgba(160, 240, 170, 0.6); }
}
@keyframes plotsSpores {
  0% { background-position: 10px 118%, 24px 128%, 34px 122%; opacity: 0; }
  18% { opacity: 0.9; }
  100% { background-position: 10px -18%, 24px -26%, 34px -20%; opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .btn.plots-btn { animation: none; }
  .btn.plots-btn::after { display: none; }
}

/* ---------- 小面板里那一行行「买什么」（回一口 / 快速成熟共用） ---------- */

.pw-hint {
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-s);
  color: var(--ink-soft);
}
.pw-row {
  padding: var(--sp-2) 0;
  border-top: 1px dashed rgba(64, 48, 31, 0.28);
}
.pw-row:first-of-type { border-top: 0; }
.pw-head {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
}
.pw-name {
  flex: 1;
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: 0.03em;
  color: var(--ink);
}
.pw-val {
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  color: var(--ink-soft);
}
.pw-acts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: 6px;
}
/* 成交按钮在一行里并排，不占满宽——这一张纸本来就小 */
.pw-acts .btn {
  display: inline-block;
  width: auto;
  flex: 1 1 auto;
  padding: 5px 12px 6px;
  font-size: var(--fs-s);
  letter-spacing: 0.06em;
  text-align: center;
  box-shadow: 0 2px 0 rgba(50, 37, 24, 0.34), 0 5px 12px rgba(0, 0, 0, 0.3);
}
/*
 * 「看广告」那一颗：它管的是**这张纸上列出来的每一格**，不是一格。
 *
 * 所以它占满一整行、和上面那些按格的小按钮拉开一道虚线——
 * 摆在一格里、和「花 5 星光币」长得一样的话，玩家会以为广告也只催那一格。
 */
.pw-acts.pw-all {
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px dashed rgba(64, 48, 31, 0.28);
}
.pw-acts.pw-all .btn {
  flex: 1 1 100%;
  padding: 8px 12px 9px;
}

/*
 * 一段催肥道具（两样肥各一段，2026-09-28 解禁后新加的）：
 * 虚线拉开、按钮占满一行，底下那行小字写「要几份 · 你有几份」。
 * 和上面 `.pw-acts.pw-all` 同一副骨架——它管的是这张纸上列出来的**每一格**，
 * 不是某一格，长得像那些按格的小按钮就会被当成只催一格。
 */
.pw-act {
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px dashed rgba(64, 48, 31, 0.28);
}
.pw-act .btn {
  display: block;
  width: 100%;
  padding: 8px 12px 9px;
  font-size: var(--fs-s);
  letter-spacing: 0.06em;
  text-align: center;
}
.pw-act .tiny { display: block; margin-top: 5px; }

.pw-note {
  margin-top: 6px;
  font-size: var(--fs-xs);
  color: var(--ink-soft);
}
/* 满了的那一行整行压暗：它现在只是「状态」，不是一件可买的东西 */
.pw-row.is-full .pw-name,
.pw-row.is-full .pw-val { opacity: 0.6; }

/* ---------- 回一口那一张纸上的一格：手上能补这一样的东西 ---------- */

/*
 * 一行一件：图标 + 名字 / 效果 + 手上几份 + 一颗「用掉」。
 * 排法照背包那一路的紧凑路子（创作者定的：短行，别摆成大格子）——
 * 这一张纸本来就窄，一屏多看两行比格子好看要紧。
 */
/* 喂食那张纸上的两档小标题（回饱食度 / 回血量）：一行极小的字，不抢读数 */
.rf-cap {
  margin: var(--sp-3) 0 0;
  font-size: var(--fs-xs);
  letter-spacing: 0.2em;
  color: var(--ink-soft);
}
/* 血量那一行的读数走套印红（和详情纸上那根红血条一味） */
.pw-head.beast-hp .pw-val { color: var(--red); }
.rf-list { margin-top: var(--sp-1); }
.rf-item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) 0;
  border-top: 1px dashed rgba(64, 48, 31, 0.28);
}
.rf-item:first-child { border-top: 0; }
/*
 * 图标那一个框。**必须是 flex，不能是 grid**：
 * 背包详情里那六个字（.item-art）用的就是「定宽定高 + flex 居中 + 图 max-*:100%」，
 * 那条 max-height 有确定的父高可算。换成 grid 之后隐式那一行是 auto 高的，
 * 百分比没有可算的基准，浏览器就当没写——于是 148×563 那张药水瓶按宽高比撑到 122px 高，
 * 从 32px 的格子里溢出去，压上下两行（创作者：「这个药水爆了」）。
 */
.rf-art {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  font-size: 20px;
  line-height: 1;
}
.rf-art img { max-width: 100%; max-height: 100%; object-fit: contain; }
.rf-tx { flex: 1; min-width: 0; }
.rf-name {
  font-size: var(--fs-s);
  font-weight: 700;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rf-use { margin-top: 1px; font-size: var(--fs-xs); color: var(--ink-soft); }
.rf-qty {
  flex: none;
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  color: var(--ink-soft);
}
/* 成交那颗在这一行里只占自己那一小截（.btn 默认的身板是「占满一整行」） */
.rf-item > .btn {
  flex: none;
  width: auto;
  padding: 5px 12px 6px;
  font-size: var(--fs-s);
  letter-spacing: 0.06em;
  text-align: center;
  box-shadow: 0 2px 0 rgba(50, 37, 24, 0.34), 0 5px 12px rgba(0, 0, 0, 0.3);
}

@media (max-width: 560px) {
  /* 窄屏上三格并排要挤一挤：竖条窄一点、字小一号 */
  .bed-row { gap: var(--sp-2); }
  .bed { min-height: 164px; padding: var(--sp-2) var(--sp-1) var(--sp-3); }
  .bed-art { font-size: 24px; }
  .bed-name { font-size: var(--fs-xs); }
  .bed-act { font-size: 10px; letter-spacing: 0.06em; }
}

/* ---------- 温室那套皮（深绿半透明） ---------- */

/*
 * 用在种植箱里挑种子那一张（openSheet 的 skin: 'green'）。
 * 挑一样按进土里，不该摊一张奶油色的羊皮纸——那看着像在书桌上填表格。
 * 字一律走暖白：底子深了墨色就看不见，和屋里那些牌子同一套处理。
 */
.sheet.green {
  background:
    radial-gradient(80% 50% at 14% 4%, rgba(232, 240, 224, 0.1), transparent 62%),
    linear-gradient(180deg, rgba(38, 58, 40, 0.94), rgba(22, 36, 26, 0.96));
  border: 1px solid rgba(158, 200, 146, 0.34);
  box-shadow: 0 28px 70px rgba(0, 0, 0, 0.7), inset 0 1px 0 rgba(220, 240, 214, 0.08);
  --fg: var(--cine-text);
  --fg-dim: var(--cine-dim);
  --fg-accent: var(--cine-gold);
  color: var(--cine-text);
}
.sheet.green .sheet-head {
  border-bottom-color: rgba(158, 200, 146, 0.42);
  box-shadow: 0 1px 0 rgba(158, 200, 146, 0.16);
}
.sheet.green .sheet-head h2 { color: #eef6e6; }
.sheet.green .tiny { color: #b6d6a4; }

/* ---------- 钓鱼出岔子那套皮（暗红） ---------- */

/*
 * 用在钓鱼「出了点岔子」那一张（openSheet 的 skin: 'red'，见 ui/fishing.js 的 showMishap）。
 *
 * 勾到手、晒中暑、被拽了个趔趄、底下有东西看了你一眼——这一张是**掉了血**的。
 * 摊一张奶油色的纸来说这件事太轻了，抬眼得像那么回事，所以换一身暗红：
 * 和深绿那套同一个路子（半透明深底 + 暖白的字 + 一道同色的描边），
 * 只是把绿换成血——比正红暗一档，是"疼过一下"，不是警示牌。
 *
 * 字色照旧走 cine 那三档：底子深了，墨色（--ink / --ink-soft）根本看不见。
 */
.sheet.red {
  background:
    radial-gradient(80% 50% at 14% 4%, rgba(255, 224, 212, 0.1), transparent 62%),
    linear-gradient(180deg, rgba(74, 26, 22, 0.95), rgba(42, 13, 11, 0.97));
  border: 1px solid rgba(206, 112, 90, 0.36);
  box-shadow: 0 28px 70px rgba(0, 0, 0, 0.72), inset 0 1px 0 rgba(255, 214, 198, 0.08);
  --fg: var(--cine-text);
  --fg-dim: var(--cine-dim);
  --fg-accent: var(--cine-gold);
  color: var(--cine-text);
}
.sheet.red .sheet-head {
  border-bottom-color: rgba(206, 112, 90, 0.44);
  box-shadow: 0 1px 0 rgba(206, 112, 90, 0.18);
}
.sheet.red .sheet-head h2 { color: #f7ded3; }
.sheet.red .tiny { color: #dcab9c; }
/* 健康那一行是这张纸上唯一要盯的数——给它一档血红，别混在灰字里 */
.sheet.red .small.muted { color: #e8a794; }

/*
 * 种子方格：和背包里那一排格子同一套排法（自动铺开的网格、方块、居中），
 * 只是底色换成土绿——两边看着是「同一类东西」，玩家不用重新学怎么点。
 */
.seed-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  gap: var(--sp-2);
}

.seed-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  min-height: 108px;
  padding: var(--sp-2) 5px var(--sp-3);
  font-family: var(--font-body);
  text-align: center;
  color: var(--cine-text);
  background: rgba(30, 48, 32, 0.68);
  border: 1px solid rgba(158, 200, 146, 0.26);
  border-bottom-width: 3px;
  border-radius: var(--r);
  cursor: pointer;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.34), inset 0 1px 0 rgba(220, 240, 214, 0.06);
  transition: background 0.14s ease, border-color 0.14s ease, transform 0.14s cubic-bezier(0.16, 1, 0.3, 1);
}
.seed-tile:hover {
  background: rgba(48, 76, 50, 0.8);
  border-color: rgba(182, 214, 164, 0.58);
  transform: translateY(-2px);
}
.seed-tile:active { transform: translateY(0) scale(0.98); }
/*
 * 【一键播种】那张纸：点一下种子格是**选中**，不是马上种（底下【播种】才落地）。
 * 选中态要比 hover 更实一档，不然看不出"我挑了哪一颗"：底色抬亮、描边换亮绿、
 * 外面再套一圈光，右上角点一颗小圆当记号。
 */
.seed-tile { position: relative; }
.seed-tile.on {
  background: rgba(66, 106, 64, 0.94);
  border-color: rgba(206, 236, 186, 0.92);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(220, 240, 214, 0.14), 0 0 0 2px rgba(178, 214, 164, 0.34);
  transform: translateY(-2px);
}
.seed-tile.on::after {
  content: '';
  position: absolute;
  top: 6px;
  right: 6px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #d6eeb8;
  box-shadow: 0 0 8px rgba(190, 236, 160, 0.85);
}

.seed-tile .st-art { font-size: 26px; line-height: 1.4; }
/* 同 .bed-art-img：种子现在还是「一小包籽」那种 emoji，但哪天配了图也得摆得下 */
.seed-tile .st-art-img {
  display: block;
  height: 38px;
  width: auto;
  max-width: 100%;
  object-fit: contain;
}
.seed-tile .st-name {
  font-weight: 700;
  line-height: 1.25;
  /* 名字长的（曼德拉草籽）自己缩一号，不撑开格子 */
  font-size: clamp(12px, 3.4vw, 14px);
  overflow-wrap: break-word;
}
/* 种下去长成什么、要几夜——和背包格子里那行小字同一个位置 */
.seed-tile .st-info {
  font-family: var(--font-hand);
  font-size: 10px;
  line-height: 1.3;
  color: #b6d6a4;
}
.seed-tile .st-n {
  margin-top: 3px;
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  color: var(--cine-gold);
}

/*
 * 「手里的种子」那条小账。
 * 跟三格土、挑种子那张同属温室那一套：深绿半透明、暖白字，
 * 所以整屏看上去是一件事（都在土里），而不是「照片 + 一张纸」拼起来的。
 */
.seed-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-3);
  padding: var(--sp-2) var(--sp-3) var(--sp-3);
  color: var(--cine-text);
  background: rgba(20, 34, 22, 0.6);
  border: 1px solid rgba(158, 200, 146, 0.24);
  border-radius: var(--r);
}
.seed-strip .ss-label {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.22em;
  color: var(--cine-dim);
}
.seed-strip .ss-item { display: inline-flex; align-items: baseline; gap: 4px; }
.seed-strip .ss-name { font-size: var(--fs-s); }
.seed-strip .ss-n {
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  color: var(--cine-gold);
}
.seed-strip .ss-empty { font-size: var(--fs-s); color: var(--cine-dim); }

/* ============================================================
   10 · 开场：录取通知书飘入
   ============================================================ */

.title-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
  padding-top: var(--sp-5);
}

/* 游戏名：压在礼堂上方的一行金字 */
.game-title { text-align: center; animation: rise 0.9s 0.15s cubic-bezier(0.16, 1, 0.3, 1) both; }
.game-title .zh {
  font-size: var(--fs-2xl);
  letter-spacing: 0.12em;
  color: #fff7e8;
  text-shadow: 0 2px 24px rgba(0, 0, 0, 0.95), 0 0 40px rgba(224, 178, 100, 0.35);
  line-height: 1.3;
}
/* 第二行是同一条名字的后半句：收小一号，字距放松一点 */
.game-title .zh-sub {
  font-size: var(--fs-xl);
  letter-spacing: 0.16em;
  margin-top: 2px;
}
.game-title .en {
  margin-top: 6px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.34em;
  color: var(--cine-gold);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.9);
}
.game-title .rule {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
  color: var(--cine-gold);
  opacity: 0.8;
}
.game-title .rule::before,
.game-title .rule::after {
  content: '';
  width: 46px;
  height: 1px;
  background: linear-gradient(90deg, transparent, currentColor);
}
.game-title .rule::after { background: linear-gradient(90deg, currentColor, transparent); }
.game-title .rule i { font-style: normal; font-size: 10px; letter-spacing: 0.2em; }

.title-note {
  font-size: var(--fs-s);
  line-height: 1.9;
  color: var(--cine-dim);
  margin: 0;
  text-align: center;
  max-width: 32rem;
}

/* 一封信：比别的纸更亮、会飞进来、落定后微微一歪 */
.letter {
  position: relative;
  width: 100%;
  max-width: 540px;
  max-height: min(58dvh, 620px);
  overflow-y: auto;
  overscroll-behavior: contain;
  background:
    radial-gradient(80% 50% at 15% 4%, rgba(255, 255, 255, 0.7), transparent 62%),
    radial-gradient(120% 90% at 50% 105%, rgba(140, 47, 47, 0.07), transparent 70%),
    linear-gradient(180deg, #fdf7ea, #f0e3c6);
  border: 1px solid var(--paper-3);
  border-bottom-color: rgba(64, 48, 31, 0.45);
  border-radius: var(--r);
  box-shadow: 0 20px 46px rgba(0, 0, 0, 0.62), 0 2px 0 var(--paper-3);
  padding: var(--sp-5) var(--sp-5) var(--sp-6);
  color: var(--ink);
  text-shadow: none;
  /* 先飞进来落定，然后一直极轻微地飘着——「飘过来」这件事得留在后劲里 */
  animation:
    letterFly 1.5s cubic-bezier(0.2, 0.86, 0.22, 1) both,
    letterDrift 7.5s 1.6s ease-in-out infinite alternate;
}
@media (prefers-reduced-motion: reduce) {
  .letter { animation: rise 0.5s ease both; }
}

/* 信纸的红框 */
.letter::before {
  content: '';
  position: absolute;
  inset: 8px;
  border: 1px solid rgba(140, 47, 47, 0.22);
  pointer-events: none;
}

.crest { position: relative; display: flex; justify-content: center; margin-bottom: var(--sp-3); }
.crest svg { width: 74px; height: 86px; }

.letter-head {
  position: relative;
  text-align: center;
  padding-bottom: var(--sp-3);
  margin-bottom: var(--sp-4);
  border-bottom: 1px solid var(--paper-3);
}
.letter-school {
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  letter-spacing: 0.18em;
  color: var(--red);
  text-transform: uppercase;
  line-height: 1.6;
}
.letter-motto {
  margin-top: var(--sp-2);
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.14em;
  color: var(--ink-soft);
}

.letter-body { position: relative; }
.letter-body p { margin: 0 0 var(--sp-4); }
.letter-salutation { font-size: var(--fs-m); }

/* 信有两页：翻页只换信里的内容，信纸本身不重来 */
.letter-page[hidden] { display: none; }
/* 动效只在真正翻页时播（开场那一下不能跟着晃，那时信还在飞） */
.letter-page.turning { animation: pageTurn 0.34s cubic-bezier(0.16, 1, 0.3, 1) both; }

/* 附页是张表，比正文长，给它多一点高度 */
.letter.page-two { max-height: min(70dvh, 700px); }

/* 附页的抬头：一行小字，说明这是随信附上的那张表 */
.letter-attach {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.24em;
  color: var(--red);
  text-transform: uppercase;
  padding-bottom: var(--sp-3);
  margin-bottom: var(--sp-4);
  border-bottom: 1px solid var(--paper-3);
}

/* 页码：信脚的一行小字 + 两个小点 */
.letter-pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  margin-top: var(--sp-5);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--paper-3);
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  color: var(--ink-soft);
}
.letter-pager .dots { display: flex; gap: 5px; }
.letter-pager .dots i {
  width: 6px; height: 6px;
  border: 1px solid rgba(140, 47, 47, 0.45);
  border-radius: 50%;
}
.letter-pager .dots i.on { background: var(--red); border-color: var(--red); }

/* 姓名：写在信里的一条线上 */
/*
 * 通知书上的那个名字。
 *
 * 从前这儿是个输入框（名字在信上现填）；现在名字在**登记表**第 1 页就定了，
 * 通知书只把它印出来——所以下面那几条 input 的样式撤了，只留下墨迹本身。
 */
.name-slot {
  display: inline-block;
  min-width: 6em;
  font-family: var(--font-hand);
  font-size: var(--fs-l);
  color: var(--red);
  text-align: center;
  letter-spacing: 0.06em;
}
.name-slot.empty { color: var(--ink-soft); font-size: var(--fs-s); letter-spacing: 0.1em; }

.letter-sign { margin-top: var(--sp-5); padding-right: 56px; text-align: right; position: relative; }
.letter-sign .who { font-family: var(--font-hand); font-size: var(--fs-l); color: var(--ink); }
.letter-sign .title { font-size: var(--fs-xs); color: var(--ink-soft); letter-spacing: 0.1em; }

/* 火漆 */
.seal {
  position: absolute;
  right: 0;
  bottom: -18px;
  width: 52px; height: 52px;
  border-radius: 50%;
  background: radial-gradient(circle at 36% 32%, #a53a3a, var(--red) 58%, #6d2222);
  box-shadow: inset 0 -2px 6px rgba(0, 0, 0, 0.35), 0 3px 8px rgba(64, 48, 31, 0.3);
  display: grid;
  place-items: center;
  font-family: var(--font-hand);
  font-size: 19px;
  color: #f6e3d8;
  transform: rotate(-11deg);
}

/* ---------- 属性分配（信里的一张表） ---------- */
.attr-table { position: relative; }

.attr-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) 0;
  border-bottom: 1px dotted var(--paper-3);
}
.attr-row:last-child { border-bottom: none; }
.attr-name { flex: none; width: 5.4em; }
.attr-name b { font-size: var(--fs-m); }
.attr-info { flex: 1; min-width: 0; }
.attr-info .blurb { font-size: var(--fs-s); color: var(--ink); }
.attr-info .power { font-size: var(--fs-xs); color: var(--ink-soft); line-height: 1.5; }

.roll-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-2);
}
.roll-row .grow { flex: 1; min-width: 0; }
.roll-row .btn { width: auto; flex: none; padding: var(--sp-2) var(--sp-3); font-size: var(--fs-s); }

.base-note {
  display: block;
  margin-top: 4px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  color: var(--ink-soft);
}

.stepper { display: flex; align-items: center; gap: 4px; flex: none; }
.stepper button {
  width: 30px; height: 30px;
  display: grid; place-items: center;
  font-size: var(--fs-m);
  font-family: var(--font-body);
  line-height: 1;
  color: var(--ink);
  background: var(--paper-2);
  border: 1px solid var(--paper-3);
  border-radius: var(--r);
  cursor: pointer;
  transition: transform 0.12s cubic-bezier(0.16, 1, 0.3, 1), background 0.12s ease;
}
.stepper button:hover:not(:disabled) { background: var(--bright); border-color: var(--ink-soft); }
.stepper button:active:not(:disabled) { transform: scale(0.92); }
.stepper button:disabled { opacity: 0.3; cursor: not-allowed; }
.stepper .val {
  min-width: 1.6em;
  text-align: center;
  font-family: var(--font-hand);
  font-size: var(--fs-l);
}

.points-left {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  background: rgba(140, 47, 47, 0.06);
  border: 1px solid rgba(140, 47, 47, 0.22);
  border-radius: var(--r);
  margin-bottom: var(--sp-3);
}
.points-left .n { font-family: var(--font-hand); font-size: var(--fs-xl); color: var(--red); }
.points-left.ready { border-color: rgba(85, 101, 74, 0.4); background: rgba(85, 101, 74, 0.08); }
.points-left.ready .n { color: var(--leaf); }

/* ============================================================
   11 · 飘字 / 提示 / 粒子
   ============================================================ */

.floaters { position: fixed; inset: 0; pointer-events: none; z-index: 60; }
.floater {
  position: absolute;
  font-family: var(--font-hand);
  font-size: var(--fs-l);
  color: var(--cine-red);
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.9), 0 1px 0 rgba(255, 255, 255, 0.35);
  animation: floatup 1.1s cubic-bezier(0.16, 1, 0.3, 1) both;
  white-space: nowrap;
}
.floater.good { color: #a8c08c; }
/* 主线里时间走过去了一天：金字，和好感/风头那两档分开——它是日历在动，不是得失 */
.floater.time { color: var(--cine-gold); }
/* 掉血那一笔：跟血条同一个深红。纸上那道套印红压在照片上不够沉，会飘 */
.floater.bad { color: var(--bar-hp); }

/* 提示挂在顶栏下面：对话框占着下半屏，别互相挤 */
.toast {
  position: fixed;
  left: 50%;
  top: calc(max(var(--hud-h, 0px), env(safe-area-inset-top)) + var(--sp-3));
  transform: translateX(-50%);
  max-width: min(90vw, 420px);
  background: rgba(18, 13, 9, 0.94);
  color: var(--cine-text);
  border: 1px solid var(--glass-line);
  font-size: var(--fs-s);
  line-height: 1.6;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.55);
  z-index: 70;
  animation: toastIn 0.24s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.toast[hidden] { display: none; }

.specks { position: fixed; inset: 0; pointer-events: none; z-index: 55; }

/* 「他在想」：等待回话时的三个墨水点 */
.vn-busy {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  color: var(--cine-dim);
}
.vn-busy[hidden] { display: none; }
.vn-busy .tx { display: inline-block; }
.vn-busy .dots { display: inline-flex; gap: 4px; }
.vn-busy i {
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--cine-dim);
  animation: blot 1s ease-in-out infinite;
}
.vn-busy i:nth-child(2) { animation-delay: 0.15s; }
.vn-busy i:nth-child(3) { animation-delay: 0.3s; }

/* ============================================================
   12 · 动效
   ============================================================ */

/* 翻一页：像把纸揭起来换一面 */
@keyframes pageTurn {
  from { opacity: 0; transform: translateY(8px) rotate(-0.25deg); }
  to   { opacity: 1; transform: none; }
}

@keyframes rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes sheetIn {
  from { opacity: 0; transform: translateY(14px) scale(0.985); }
  to { opacity: 1; transform: none; }
}
@keyframes boxGrow {
  from { transform: translateY(10px); }
  to { transform: none; }
}
@keyframes lineIn {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}
@keyframes choiceIn {
  from { opacity: 0; transform: rotate(var(--tilt)) translateY(12px); }
  to { opacity: 1; transform: rotate(var(--tilt)) translateY(0); }
}
@keyframes toastIn {
  from { opacity: 0; transform: translate(-50%, -8px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}
@keyframes floatup {
  0% { opacity: 0; transform: translateY(6px) scale(0.85); }
  22% { opacity: 1; transform: translateY(-2px) scale(1.1); }
  100% { opacity: 0; transform: translateY(-42px) scale(1); }
}
@keyframes blot { 0%, 100% { opacity: 0.25; transform: translateY(0); } 50% { opacity: 1; transform: translateY(-3px); } }
@keyframes hintPulse { 0%, 100% { opacity: 0.45; } 50% { opacity: 1; } }

/* 落定之后的余韵：极小的上下浮动 + 一点点倾角变化 */
@keyframes letterDrift {
  from { transform: translate3d(0, 0, 0) rotate(-0.5deg); }
  to   { transform: translate3d(0, -6px, 0) rotate(0.35deg); }
}

/* 录取通知书：从画面右上角飞进来，落定前微微一晃 */
@keyframes letterFly {
  0%   { opacity: 0; transform: translate3d(64vw, -78vh, 0) rotate(30deg) scale(0.5); }
  52%  { opacity: 1; transform: translate3d(-1.6%, 1.6%, 0) rotate(-3.2deg) scale(1.04); }
  68%  { transform: translate3d(0, -0.8%, 0) rotate(-0.2deg) scale(1); }
  82%  { transform: translate3d(0, 0.3%, 0) rotate(-0.9deg) scale(1.005); }
  100% { transform: translate3d(0, 0, 0) rotate(-0.5deg) scale(1); }
}

/* 分级屏震：强度由 --shake 传入（0.5 是小失误，1 是炸锅/被撞见） */
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(calc(-5px * var(--shake, 1))) rotate(calc(-0.25deg * var(--shake, 1))); }
  40% { transform: translateX(calc(4px * var(--shake, 1))) rotate(calc(0.2deg * var(--shake, 1))); }
  60% { transform: translateX(calc(-3px * var(--shake, 1))); }
  80% { transform: translateX(calc(2px * var(--shake, 1))); }
}
.shake { animation: shake 0.36s ease both; }

@keyframes punch { 0% { transform: scale(1); } 40% { transform: scale(1.045); } 100% { transform: scale(1); } }
.punch { animation: punch 0.24s cubic-bezier(0.16, 1, 0.3, 1) both; }

/* ============================================================
   13 · 移动端
   ============================================================ */

@media (max-width: 560px) {
  :root { --fs-2xl: 30px; --fs-xl: 23px; }
  .app { padding: 0 var(--sp-3); }
  .hud { margin: 0 calc(var(--sp-3) * -1); padding-left: var(--sp-3); padding-right: var(--sp-3); }
  .tray { margin: 0 calc(var(--sp-3) * -1); padding-left: var(--sp-3); padding-right: var(--sp-3); }
  .tray .btn { flex: 1 1 100%; min-width: 0; }
  .letter { padding: var(--sp-4) var(--sp-4) var(--sp-5); max-height: 54dvh; }
  .title-wrap { padding-top: var(--sp-4); gap: var(--sp-3); }
  .attr-row { gap: var(--sp-2); }
  .attr-name { width: 4.6em; }
  .attr-info .power { display: none; }
  .letter-sign { padding-right: 0; text-align: left; }
  .seal { right: auto; left: 0; bottom: -20px; width: 46px; height: 46px; font-size: 17px; }
}

/* ============================================================
   开场那封信：压紧一点
   目标只有一个——第二页那张评定表不用滚就能看全。
   ============================================================ */

.letter {
  padding: var(--sp-5) var(--sp-5) var(--sp-5);
  max-height: min(78dvh, 800px);
}
.letter-head { padding-bottom: var(--sp-2); margin-bottom: var(--sp-3); }
.letter .crest { margin-bottom: var(--sp-2); }
.letter .crest svg { width: 58px; height: 68px; }
.letter-school { font-size: var(--fs-xs); line-height: 1.5; }
.letter-motto { margin-top: 3px; }

/* 正文段落之间的空档收一半，整封信读起来是一整块，不是散开的几段 */
.letter-body p { margin: 0 0 var(--sp-3); }
.letter-salutation { margin-bottom: var(--sp-2); }
.letter-sign { margin-top: var(--sp-4); }

/* 附页那一行小标 */
.letter-attach { padding-bottom: var(--sp-2); margin-bottom: var(--sp-3); }

/* 掷点那一行 + 剩余点数条，都压薄 */
.roll-row { margin-bottom: var(--sp-1); }
.points-left { padding: var(--sp-2) var(--sp-3); margin-bottom: var(--sp-2); }
.points-left .n { font-size: var(--fs-l); }

/* 属性行：说明与效果并成一行小字，整行压到两行高 */
.attr-row { padding: var(--sp-2) 0; gap: var(--sp-2); }
.attr-name { width: 4.4em; }
.attr-name b { font-size: var(--fs-s); }
.attr-info .blurb { font-size: var(--fs-xs); line-height: 1.5; }
.attr-info .power { font-size: 11px; line-height: 1.45; display: block; }
.attr-info .base-note { margin-top: 1px; font-size: 11px; }
.base-note { font-size: 11px; }
.stepper button { width: 26px; height: 26px; }
.stepper .val { min-width: 1.4em; font-size: var(--fs-m); }

.letter-pager { margin-top: var(--sp-3); padding-top: var(--sp-2); }

/* ============================================================
   数值变化要看得清
   加一点 / 重掷时数字会强调一下——0.7 秒，够读完，不拖沓。
   ============================================================ */

@keyframes valBump {
  0%   { transform: scale(1); color: var(--red); }
  18%  { transform: scale(1.28); color: var(--red); }
  60%  { transform: scale(1.1); color: var(--red); }
  100% { transform: scale(1); color: var(--ink); }
}
.stepper .val { display: inline-block; }
.stepper .val.bumped { animation: valBump 0.7s cubic-bezier(0.16, 1, 0.3, 1) both; }

/* 按钮按下去的那一下也放慢些，别一闪就没 */
.punch { animation-duration: 0.36s; }

@media (prefers-reduced-motion: reduce) {
  .stepper .val.bumped { animation: none; color: var(--red); }
}

/* 附页（评定表）比正文长：给它更大的高度上限，争取一屏看完不用滚 */
.letter.page-two {
  max-height: min(88dvh, 900px);
  padding-bottom: var(--sp-4);
}

@media (max-width: 560px) {
  .letter { padding: var(--sp-4) var(--sp-4); max-height: min(84dvh, 860px); }
  .letter.page-two { max-height: min(88dvh, 900px); }
  .attr-row { padding: 6px 0; }
  .attr-name { width: 4em; }
}


/* ============================================================
   图书馆的书
   一排摆整齐，书影只有一个小图标那么大；
   书名压在画面上，给足对比度——看得见才算数。
   书本身不上纸，只有「阅读」那枚按钮是羊皮纸。
   ============================================================ */

.shelf {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--sp-3) var(--sp-2);
  align-items: stretch;
  justify-items: center;
  margin-top: var(--sp-2);
}

.book {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  text-align: center;
  width: 100%;
  padding: 0;
  background: none;
  border: none;
  box-shadow: none;
  height: 100%;
}

/* 书影：一个很小的固定台面，图在里面等比缩，绝不外溢、不歪 */
.book-figure {
  height: 38px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 6px;
}
.book-figure img {
  max-height: 38px;
  max-width: 100%;
  width: auto;
  height: auto;
  display: block;
  object-fit: contain;
  transform: none;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.55));
}

/* 书名：亮字 + 厚影，压在照片上也读得清楚 */
.book-title {
  font-size: var(--fs-m);
  line-height: 1.35;
  color: #fffdf6;
  text-shadow:
    0 0 3px rgba(0, 0, 0, 1),
    0 1px 2px rgba(0, 0, 0, 1),
    0 2px 10px rgba(0, 0, 0, 0.95);
}
.book-title-en {
  margin-top: 1px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.03em;
  line-height: 1.35;
  color: var(--cine-gold);
  text-shadow: 0 0 3px rgba(0, 0, 0, 1), 0 1px 2px rgba(0, 0, 0, 1);
}
.book-meta {
  margin: 3px 0 var(--sp-2);
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  color: var(--cine-dim);
  text-shadow: 0 0 3px rgba(0, 0, 0, 1), 0 1px 2px rgba(0, 0, 0, 1);
}

/* 「阅读」：全卡唯一的一枚羊皮纸，小方块按钮 */
.book .btn {
  margin-top: auto;
  width: auto;
  min-width: 4.4em;
  text-align: center;
  padding: 5px var(--sp-3);
  font-size: var(--fs-s);
}

@media (max-width: 560px) {
  .shelf { grid-template-columns: repeat(3, 1fr); gap: var(--sp-2) var(--sp-1); }
  .book-title { font-size: var(--fs-s); }
  .book-title-en { font-size: 10px; }
}

/* ============================================================
   对话区顶上的状态栏
   只说三件事：你是谁、什么时候、在哪儿。不报演的是哪一幕。
   ============================================================ */

.vn-status { min-width: 0; }
.vn-status .row1 {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  flex-wrap: wrap;
}
.vn-status .nm {
  font-size: var(--fs-xl);
  font-weight: 700;
  line-height: 1.25;
  color: #fff7e8;
  letter-spacing: 0.04em;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.95), 0 2px 18px rgba(0, 0, 0, 0.8);
}
/* 日期跟着名字排在同一行 */
.vn-status .dt {
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  letter-spacing: 0.06em;
  color: var(--cine-gold);
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.95);
}
/* 第二行：几年级 · 学院 */
.vn-status .row2 {
  margin-top: 2px;
  font-size: var(--fs-s);
  letter-spacing: 0.14em;
  color: var(--cine-text);
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.95), 0 0 12px rgba(0, 0, 0, 0.85);
}
/* 第三行：地点，居中 */
.vn-status .row3 {
  margin-top: 6px;
  text-align: center;
  font-size: var(--fs-s);
  letter-spacing: 0.22em;
  color: #fff6e4;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.95), 0 2px 14px rgba(0, 0, 0, 0.8);
}

/*
 * 顶栏里的学院。
 * flex + wrap：学院名后面挂着「回公共休息室」那颗按钮，窄屏上两个词未必排得下——
 * 排不下就让按钮自己掉到下一行，别去挤右边那行钱和两根条（那些更要紧）。
 */
.hud-house {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 5px;
  margin-top: 2px;
  font-size: var(--fs-xs);
  letter-spacing: 0.22em;
  line-height: 1.4;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.95), 0 0 14px rgba(0, 0, 0, 0.9);
}

/*
 * 「回公共休息室」——就贴在学院名后面的一颗小按钮。
 *
 * 细：字号比学院名再小一号、只有一行那么高，看着像学院那行长出来的一部分，
 * 不是状态栏里多出来的一颗大按钮。
 * 颜色不写任何具体色值：字与边都走 currentColor，学院色从上面那行继承，
 * 四家各自一种，将来调色只动 data/houses.js。
 *
 * 底色必须是浅的：学院色取的都是深调（压羊皮纸才看得清那套），
 * 直接落在夜景照片上就等于把深字放进暗背景，所以垫一层纸色，
 * 顺带把继承下来的那圈暗影关掉——纸色块上留暗影会脏。
 */
.hud-room {
  display: inline-block;
  padding: 0 5px;
  font-family: var(--font-hand);
  font-size: 10px;
  letter-spacing: 0.06em;
  line-height: 1.6;
  vertical-align: 1px;
  white-space: nowrap;
  color: inherit;
  background: rgba(255, 246, 228, 0.9);
  border: 1px solid currentColor;
  border-radius: 2px;
  text-shadow: none;
  cursor: pointer;
  transition: background 0.12s ease, transform 0.12s cubic-bezier(0.16, 1, 0.3, 1);
}
.hud-room:hover { background: #fffaee; }
.hud-room:active { transform: scale(0.96); }

/* ---------- 分院那四个选项：整张纸用学院色包边 ---------- */
.vn-choice.house {
  border: 2px solid var(--house, var(--red));
  box-shadow:
    0 3px 0 var(--house, rgba(50, 37, 24, 0.42)),
    0 12px 26px rgba(0, 0, 0, 0.52),
    inset 0 0 0 1px rgba(255, 255, 255, 0.35);
}
.vn-choice.house:hover {
  box-shadow:
    0 6px 0 var(--house, rgba(50, 37, 24, 0.34)),
    0 16px 30px rgba(0, 0, 0, 0.55),
    inset 0 0 0 1px rgba(255, 255, 255, 0.35);
}
/* 中文名和英文名排在同一行 */
.vn-choice .lbl .en {
  margin-left: 8px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  color: var(--house, var(--ink-soft));
  opacity: 0.95;
}
.vn-choice.house .num {
  color: var(--house, var(--red));
  border-color: var(--house, rgba(140, 47, 47, 0.42));
}
.vn-choice.house .lbl { color: var(--house, var(--ink)); font-weight: 700; }

/* ============================================================
   14 · 两个门牌：手抄本 / 背包
   ------------------------------------------------------------
   「图在上，中文在中，最小号英文在下」——它们不是普通动作按钮，
   是挂在画面上的两块牌子，所以要一眼认得出是哪个。
   其他按钮仍然一律只有一行字。

   不垫羊皮纸：透明底、无边框，图直接从照片上浮出来。
   摆在纸上时字取墨色；摆在照片上（.float）字取暖白并加暗影。
   ============================================================ */

.btn.paper-entry {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  width: auto;
  min-width: 0;
  flex: 0 0 auto;
  padding: 4px var(--sp-2) 5px;
  text-align: center;
  line-height: 1.25;
}
/*
 * 透明底：不垫羊皮纸，图直接从底下的画面浮出来。
 * 但要放过 .ghost —— 对话层那排小按钮用的是墨玻璃底（不是羊皮纸），
 * 那是为了在照片上读得清，别一起抹掉。
 */
.btn.paper-entry:not(.ghost) {
  background: none;
  border: 0;
  box-shadow: none;
}
/* 底部托盘那条规则给所有按钮设了最小宽度，门牌不适用 */
.tray .btn.paper-entry { min-width: 0; }
.btn.paper-entry:not(.ghost):hover,
.btn.paper-entry:not(.ghost):active {
  background: none;
  border: 0;
  box-shadow: none;
  transform: none;
}

.paper-entry .pe-art {
  display: block;
  width: 34px;
  height: 34px;
  object-fit: contain;
  transition: transform 0.14s cubic-bezier(0.16, 1, 0.3, 1);
}
/* 校徽那种内联 SVG（还没分院时顶替学院徽章） */
.paper-entry .pe-art-vec {
  display: flex;
  align-items: center;
  justify-content: center;
}
.paper-entry .pe-art-vec > span,
.paper-entry .pe-art-vec svg { display: block; width: 100%; height: 100%; }

.paper-entry .pe-label { display: block; }
.paper-entry .pe-zh {
  display: block;
  font-size: var(--fs-s);
  font-weight: 700;
  letter-spacing: 0.12em;
}
.paper-entry .pe-en {
  display: block;
  margin-top: 1px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  opacity: 0.72;
}
.paper-entry:hover .pe-art { transform: translateY(-1px) scale(1.05); }

/*
 * 手抄本最底下那一排（相片本 · 图鉴 · 信件夹，见 ui/screens-meta.js）：
 *   图鉴（.big）放大一档——它是眼下真能翻的那本，摆在正中；
 *   相片本与信件夹（.soon）先锁着：图压暗、蒙一层灰。
 */
.btn.paper-entry.big { padding: 4px var(--sp-3) 6px; }
.btn.paper-entry.big .pe-art { width: 52px; height: 52px; }
.btn.paper-entry.big .pe-zh { font-size: var(--fs-m); }
.btn.paper-entry.soon .pe-art { opacity: 0.45; filter: grayscale(0.85); }
.btn.paper-entry.soon .pe-zh,
.btn.paper-entry.soon .pe-en { opacity: 0.5; }
.btn.paper-entry.soon:hover .pe-art { transform: none; }

/* ---------- 浮在照片上：大图标 + 暖白字带暗影 ---------- */
.btn.paper-entry.float {
  gap: 1px;
  padding: 0 var(--sp-2);
  color: #fdf3e0;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.95), 0 2px 12px rgba(0, 0, 0, 0.8);
}
.btn.paper-entry.float .pe-art {
  width: 74px;
  height: 74px;
  /* 图标底下要压得住照片：一圈柔和的暗影，不是描边 */
  filter: drop-shadow(0 3px 10px rgba(0, 0, 0, 0.75));
}
.btn.paper-entry.float .pe-zh {
  font-size: var(--fs-l);
  letter-spacing: 0.16em;
}
.btn.paper-entry.float .pe-en {
  font-size: var(--fs-s);
  letter-spacing: 0.12em;
  opacity: 0.9;
}
.btn.paper-entry.float:hover .pe-art {
  transform: translateY(-2px) scale(1.06);
  filter: drop-shadow(0 5px 14px rgba(0, 0, 0, 0.8));
}

/* ---------- 这一行门牌：左「手抄本」、右「背包」，中间可能挂一幅画 ---------- */
.entry-row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-4);
  width: 100%;
  padding: 0 var(--sp-2) var(--sp-2);
}
/* 在底部托盘里时整行占满，把主按钮挤到下一行去 */
.tray .entry-row { flex: 1 1 100%; }

/*
 * 这一行现在**四颗一样重**：门 / 通讯 / 背包 / 手抄本（创作者 2026-09-29 定的顺序）。
 *
 * 从前门摆在正中，所以用过一段网格来压中线；门挪到最左之后那条规矩就不需要了——
 * 回到普通的 flex（space-between），四颗均匀铺开、下沿对齐（align-items: flex-end）。
 * 谁要是再想给某颗单独加权，先想清楚「整齐」这件事：这一排读起来该是一条带子。
 */

/* 门也走同一副尺码（-2px 只作极轻的强调）：这一排要的是整齐，不是主次 */
.btn.paper-entry.gate .pe-art {
  width: 74px;
  height: 74px;
  filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.8));
}
.btn.paper-entry.gate .pe-zh { font-size: var(--fs-m); }
.btn.paper-entry.gate .pe-en { font-size: 10px; }
.btn.paper-entry.gate:hover .pe-art {
  transform: translateY(-3px) scale(1.05);
  filter: drop-shadow(0 7px 18px rgba(0, 0, 0, 0.85));
}

/* 对话层那排小按钮：图和字并排，英文收掉（位置太挤） */
.btn.paper-entry.compact {
  flex-direction: row;
  align-items: center;
  gap: 6px;
  padding: 4px var(--sp-3) 4px 9px;
}
.btn.paper-entry.compact .pe-art,
.btn.paper-entry.compact .pe-art-vec { width: 20px; height: 20px; }
.btn.paper-entry.compact .pe-art-vec > span,
.btn.paper-entry.compact .pe-art-vec svg { width: 20px; height: 20px; }
.btn.paper-entry.compact .pe-art { filter: none; }
.btn.paper-entry.compact .pe-zh { font-size: var(--fs-xs); font-weight: 400; letter-spacing: 0.08em; }

/* ============================================================
   15 · 背包
   ============================================================ */

/*
 * 成绩单：一门课一行。
 * 左边是课名，右边是那个字母，字母后面缀着它的英文原话。
 * 字母用红套印——一张成绩单上最该被一眼看到的就是它。
 */
.grade-mark {
  flex: none;
  min-width: 1.6em;
  text-align: center;
  font-family: var(--font-hand);
  font-size: var(--fs-l);
  line-height: 1.1;
  letter-spacing: 0.04em;
  color: var(--red);
}
/* 难看的那两档压暗一点：D 和 T 不该和 O 一样亮 */
.grade-mark.low { color: var(--ink-soft); }
.grade-word {
  flex: none;
  max-width: 46%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  color: var(--ink-soft);
}

/* 钱袋：翻开包第一眼看见的那一行，三档各是各的颜色 */
.bag-purse {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  margin: 0 0 var(--sp-3);
  padding-bottom: var(--sp-3);
  border-bottom: 1px dashed rgba(64, 48, 31, 0.26);
}
.bag-purse .bp-label {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.24em;
  color: var(--ink-soft);
}

/* 顶上一排标签：点一下换一类，默认「全部」 */
.bag-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 var(--sp-4);
  padding-bottom: var(--sp-3);
  border-bottom: 1px dashed rgba(64, 48, 31, 0.26);
}
.tag {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  padding: 4px 10px 5px;
  font-family: var(--font-body);
  font-size: var(--fs-s);
  color: var(--ink-soft);
  background: rgba(255, 251, 240, 0.5);
  border: 1px solid rgba(64, 48, 31, 0.28);
  border-radius: var(--r);
  cursor: pointer;
  transition: border-color 0.14s ease, color 0.14s ease, background 0.14s ease;
}
.tag .tag-zh { letter-spacing: 0.1em; }
.tag .tag-en {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  opacity: 0.66;
}
.tag .tag-n {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  color: var(--red);
  opacity: 0.85;
}
.tag:hover { border-color: var(--red); color: var(--ink); }
.tag.on {
  color: var(--ink);
  background: linear-gradient(180deg, #fbf4e4, #f0e2c6);
  border-color: var(--red);
  box-shadow: inset 0 0 0 1px rgba(140, 47, 47, 0.14), 0 2px 6px rgba(0, 0, 0, 0.16);
}
.tag.on .tag-zh { font-weight: 700; color: var(--red); }

/*
 * 中英并排的一行（.bag-name）原先是手抄本里那张「配方本」清单在用。
 * 配方本被图鉴取代之后没人引用了，这一段也就跟着删掉——
 * 需要双语并排的地方走 .item-head / .lt-zh+.lt-en 那两套。
 */
/* ============================================================
   16 · 手抄本：题花与中英双语的小标题
   ============================================================ */

.ledger-top {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0 0 var(--sp-4);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid rgba(64, 48, 31, 0.22);
  /*
   * 抬头是一张纸的最上面一行，它底下那几行（装备木牌、钱袋、标签）只要有一个
   * 往外溢一点点，先被吃掉的准是这一行——随身坩埚那颗小图标就栽过一次
   * （见 .wand-turn 那段）。这里把它抬到上面一层：从今往后，谁溢出谁自己收着，
   * 抬头永远点得到。
   */
  position: relative;
  z-index: 1;
}
/* 图纸不裁切：给定高度、宽度跟着比例走，压在纸上像夹着的一张图 */
.ledger-art {
  flex: 0 0 auto;
  height: 58px;
  width: auto;
  max-width: 46%;
  object-fit: contain;
  transform: rotate(-1.4deg);
  filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.3));
}
.ledger-top-tx { flex: 1 1 auto; min-width: 0; }
/*
 * 校徽是内联 SVG（还没分院时用它顶替学院徽章）。
 * 不能照抄上面那条 width:auto —— 内联 SVG 没有固有宽度，会被撑成 300×150。
 * 这里按 74:86 的比例把方子钉死，怎么都不会满出去。
 */
.ledger-art-vec {
  display: block;
  width: 50px;
  height: 58px;
}
.ledger-art-vec > span,
.ledger-art-vec svg { display: block; width: 100%; height: 100%; }
.lt-zh {
  font-size: var(--fs-l);
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--red);
}
.lt-en {
  margin-top: 1px;
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  letter-spacing: 0.1em;
  color: var(--fg-dim);
}

/* 带英文副标注的小标题 */
.chapter .ch-en {
  flex: 0 0 auto;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  opacity: 0.55;
}

/* ---------- 窄屏：门牌收小一号，别把画面下半截吃掉 ---------- */
@media (max-width: 560px) {
  .btn.paper-entry.float .pe-art { width: 56px; height: 56px; }
  .btn.paper-entry.float .pe-zh { font-size: var(--fs-m); letter-spacing: 0.12em; }
  .btn.paper-entry.float .pe-en { font-size: var(--fs-xs); }
  .entry-row { padding: 0 0 var(--sp-1); gap: var(--sp-3); }
  /* 窄屏那条「托盘按钮占满整行」的规则别把门牌也拉宽 */
  .tray > .entry-row > .btn.paper-entry { flex: 0 0 auto; width: auto; }
  /*
   * 窄屏上门和别的门牌**同一副尺码**：它现在是这一排的第一颗，
   * 单独大一档会把整条带子的上沿顶出一个包（创作者点过「崎岖」这个问题）。
   * 英文也照挂——藏起来那一颗的标签就只剩一行，四颗的下沿会对不齐。
   */
  .btn.paper-entry.gate .pe-art { width: 56px; height: 56px; }
  .btn.paper-entry.gate .pe-zh { font-size: var(--fs-m); }
}

/* ============================================================
   居中摊开的一张纸（手抄本 / 背包）
   ------------------------------------------------------------
   和贴底抽屉的区别有两处，缺一不可：
     1. 位置 —— 挂在画面正中，四边都留白（抽屉是贴着下沿往上长）；
     2. 入场 —— 像一张纸被放下来（轻微缩小 + 淡入），
                不是从画面外升上来（那就是「从地底下爬出来」的观感）。
   ============================================================ */
.panel-layer.centered {
  align-items: center;
  padding: var(--sp-6) var(--sp-4);
}
/* 100% 已经是「去掉四边留白之后」的高度，所以四边一定留得住 */
.panel-layer.centered .panel {
  max-height: min(80vh, 100%);
  animation: paperDrop 0.26s cubic-bezier(0.16, 1, 0.3, 1) both;
}
/* 纸落下来：从略小、略高一点，落到原位 */
@keyframes paperDrop {
  from { opacity: 0; transform: translateY(-10px) scale(0.975); }
  to   { opacity: 1; transform: none; }
}

/* 窄屏：抽屉那套规则会把圆角改成只留上面两个，居中时必须还原 */
@media (max-width: 560px) {
  .panel-layer.centered { padding: var(--sp-5) var(--sp-3); }
  .panel-layer.centered .panel {
    max-width: none;
    max-height: min(82vh, 100%);
    border-radius: var(--r);
  }
}

/* ============================================================
   背包：一格一样东西
   ------------------------------------------------------------
   方块偏长方形（竖的），里头从上到下：图标 / 名字 / 英文 / 数量，
   四行全部居中。名字长的（曼德拉草籽）会缩字号，不让它把格子撑开。
   ============================================================ */
.bag-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(78px, 1fr));
  gap: var(--sp-2);
  padding: 2px;
}

.tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  /* 偏长方形：宽由网格定，高比宽多一点（图标那一条加高之后跟着抬高） */
  min-height: 118px;
  padding: var(--sp-2) 5px var(--sp-3);
  font-family: var(--font-body);
  color: var(--ink);
  background:
    radial-gradient(70% 50% at 50% 4%, rgba(255, 255, 255, 0.5), transparent 65%),
    linear-gradient(180deg, #fbf4e4, #f0e3c7);
  border: 1px solid rgba(64, 48, 31, 0.34);
  border-bottom-color: rgba(64, 48, 31, 0.5);
  border-radius: var(--r);
  cursor: pointer;
  box-shadow: 0 2px 0 rgba(120, 96, 62, 0.42), 0 6px 14px rgba(0, 0, 0, 0.2);
  transition: transform 0.14s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.14s ease, border-color 0.14s ease;
}
.tile:hover {
  border-color: var(--red);
  transform: translateY(-2px);
  box-shadow: 0 4px 0 rgba(120, 96, 62, 0.36), 0 12px 22px rgba(0, 0, 0, 0.26);
}
.tile:active { transform: translateY(1px); box-shadow: 0 1px 0 rgba(120, 96, 62, 0.4); }

/*
 * 图标：emoji 或图片都按同一个方台子摆。
 * 高度留到 46px —— 草药那批图是 1:2 的细长株，太矮就缩成一根线，
 * 宁可格子高一点，也别把标本压没。
 */
.tile-art {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 46px;
  margin-bottom: 3px;
  font-size: 26px;
  line-height: 1;
}
.tile-art-img { max-width: 100%; max-height: 100%; object-fit: contain; }

.tile-zh {
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: 0.02em;
  text-align: center;
  /* 名字长的（曼德拉草籽）自己缩一号，不撑开格子 */
  font-size: clamp(12px, 3.4vw, 14px);
  overflow-wrap: break-word;
}
.tile-en {
  font-family: var(--font-hand);
  font-size: 10px;
  line-height: 1.2;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
  text-align: center;
  /* 一行放不下就截断，方块才整得齐 */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tile-n {
  margin-top: 3px;
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  line-height: 1.2;
  color: var(--red);
}

/*
 * 魔药那一册格子上第三行小字：「这一味从哪儿来」（见 ui/codex.js 的 recipeSource）。
 * 比英文名还小一号、压得更淡 —— 它是"多给的一条线索"，不该跟药名抢。
 * 别的册不带这一行（那几册根本不给 source），格子一个像素都不动。
 */
.tile-src {
  margin-top: 2px;
  font-size: 9.5px;
  line-height: 1.25;
  letter-spacing: 0.02em;
  color: rgba(125, 104, 73, 0.92);
  text-align: center;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/*
 * 装备那一册那句「从哪儿来」比魔药那句长一截（「摩金夫人长袍店 5加隆」要 103 像素，
 * 而格子那一条只有 57 像素宽），上面那条 nowrap 会把它截成"摩金夫人长…"。
 * 这一册允许折两行，并且**先把两行的高度占住**：五格一律两行，不会有一格矮半截。
 * （只挂在这一册上——别的册子的格子一个像素都不动。）
 */
.codex-grid.gear-grid .tile-src {
  white-space: normal;
  min-height: 2.5em;
  line-height: 1.25;
  word-break: break-all;
}
/*
 * 神奇动物那一册：格子上写它出没在哪几处，最多四处名字（「石阵遗迹·沼泽·八眼巨蛛巢穴·独角兽深林」），
 * 一行放不下，所以允许折三行，并且先把三行的高度占住——同一册里格子不会高一块矮一块。
 * （鱼那册不给：它的出没只写「黑湖」「禁林」「黑湖·禁林」，一行够。）
 */
.codex-grid.src-multi .tile-src {
  white-space: normal;
  min-height: 3.75em;
  line-height: 1.25;
  word-break: break-all;
}

/* ============================================================
   送礼：一样东西一行（名字 + 数量）
   ------------------------------------------------------------
   和背包是同一张纸、同一个姿态，但**不放图标**：这一窗是为了
   「一眼找到那样东西」，名字排成一列最好扫，图标反而占掉半格。
   所以这里不套 .bag-grid / .tile，另起一条竖列的行。
   ============================================================ */
.gift-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0 0 var(--sp-3);
  padding-bottom: var(--sp-3);
  border-bottom: 1px dashed rgba(64, 48, 31, 0.26);
}
.gift-head-tx { flex: 1 1 auto; min-width: 0; }
.gift-head .gh-zh {
  font-weight: 700;
  font-size: var(--fs-l);
  letter-spacing: 0.02em;
  color: var(--ink);
}
.gift-head .gh-note {
  margin-top: 2px;
  font-size: var(--fs-s);
  color: var(--ink-soft);
}

/*
 * 「从口袋里挑一样东西」那一列：只有名字与数量，不放图标。
 * 不放图标是创作者定的——这一窗是为了「一眼找到那样东西」，
 * 名字排成一列最好扫；图标占掉半格，反而要多看两眼。
 *
 * 送礼（ui/gift.js）与主线剧情里点【背包】（ui/story-screen.js）共用这一套，
 * 所以类名不叫 gift-*：两件事本质上是同一个动作。
 */
.pick-list { display: flex; flex-direction: column; }
.pick-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  padding: 11px var(--sp-2);
  font: inherit;
  text-align: left;
  color: var(--ink);
  background: none;
  border: none;
  border-bottom: 1px dotted rgba(64, 48, 31, 0.22);
  cursor: pointer;
  transition: background 0.12s ease;
}
.pick-row:last-child { border-bottom: none; }
.pick-row:hover { background: rgba(140, 47, 47, 0.07); }
.pick-row:active { transform: translateY(1px); }
.pick-row .pick-name {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 700;
  overflow-wrap: anywhere;
}
.pick-row .pick-n {
  flex: none;
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  color: var(--red);
}

/* ============================================================
   互动：三行（触摸 / 相处 / 散步）
   ------------------------------------------------------------
   和送礼同一张纸、同一个姿态：一行一样，名字靠左。
   但这里比送礼多两件事：
     · 名字底下那行小字（散步的代价）——所以一行是**上下两层**，不是左右并排；
     · 按过的那一行要**看着就是灰的**。disabled 本身那个灰很难看清，
       玩家会反复去戳；所以整行压暗、去掉 hover、名字划一道线，
       小字换成「今天已经来过了」。
   ============================================================ */
.it-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0 0 var(--sp-3);
  padding-bottom: var(--sp-3);
  border-bottom: 1px dashed rgba(64, 48, 31, 0.26);
}
.it-head-tx { flex: 1 1 auto; min-width: 0; }
.it-head .ih-zh {
  font-weight: 700;
  font-size: var(--fs-l);
  letter-spacing: 0.02em;
  color: var(--ink);
}
.it-head .ih-note {
  margin-top: 2px;
  font-size: var(--fs-s);
  color: var(--ink-soft);
}

.it-list { display: flex; flex-direction: column; }
.it-row {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  width: 100%;
  padding: 12px var(--sp-2);
  font: inherit;
  text-align: left;
  color: var(--ink);
  background: none;
  border: none;
  border-bottom: 1px dotted rgba(64, 48, 31, 0.22);
  cursor: pointer;
}
.it-row:last-child { border-bottom: none; }
.it-row:hover { background: rgba(140, 47, 47, 0.07); }
.it-row:active { transform: translateY(1px); }
.it-row .it-name {
  font-weight: 700;
  font-size: var(--fs-l);
  letter-spacing: 0.04em;
}
.it-row .it-note {
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  line-height: 1.35;
  color: var(--ink-soft);
}

/* 今天来过了：整行压暗，名字划一道，小字换成那一句 */
.it-row.used {
  cursor: default;
  color: var(--ink-soft);
  opacity: 0.62;
}
.it-row.used:hover { background: none; }
.it-row.used:active { transform: none; }
.it-row.used .it-name {
  font-weight: 600;
  text-decoration: line-through;
  text-decoration-color: rgba(64, 48, 31, 0.45);
  text-decoration-thickness: 1px;
}
.it-row.used .it-note { font-style: italic; }

/* ---------- 点开一样东西：详情小纸 ---------- */
.item-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0 0 var(--sp-3);
  padding-bottom: var(--sp-3);
  border-bottom: 1px dashed rgba(64, 48, 31, 0.26);
}
.item-art {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 62px;
  height: 62px;
  font-size: 40px;
  line-height: 1;
  background: rgba(255, 251, 240, 0.6);
  border: 1px solid var(--paper-3);
  border-radius: var(--r);
}
.item-art-img { max-width: 100%; max-height: 100%; object-fit: contain; }
.item-tx { min-width: 0; }
.item-zh {
  font-size: var(--fs-l);
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--red);
}
.item-en {
  margin-top: 2px;
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  letter-spacing: 0.08em;
  color: var(--ink-soft);
}
/* 详情里那行英文副标注：跟在中文后面，小一号 */
.bag-sub-en {
  margin-left: 6px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}

/* 窄屏：格子收窄一档，仍是偏长方形 */
@media (max-width: 560px) {
  .bag-grid { grid-template-columns: repeat(auto-fill, minmax(68px, 1fr)); gap: 6px; }
  .tile { min-height: 104px; padding: 6px 4px 10px; }
  .tile-art { height: 40px; font-size: 22px; }
  .item-art { width: 54px; height: 54px; font-size: 34px; }
  .codex-grid .tile { min-height: 122px; }
  .codex-grid .tile-art { inset: 5px 3px 24px; }
  .codex-art { width: 76px; height: 88px; }
}

/* ============================================================
   17 · 图鉴：五册页签 + 标本格 + 详情
   ------------------------------------------------------------
   壳子跟背包是一套（居中的一张纸 + 方格），差在两处：
     1. 页签换成「册」，锁着的三册灰着、虚线边；
     2. 格子里图是**标本**（铺在格子下半部），字浮在最上层，
        底下垫一条半透明的纸——所以是「图后字前」那种图鉴读法。
   ============================================================ */

/* ---------- 五册页签 ---------- */
.codex-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 var(--sp-3);
  padding-bottom: var(--sp-3);
  border-bottom: 1px dashed rgba(64, 48, 31, 0.26);
}
.cx-tab {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  padding: 4px 10px 5px;
  font-family: var(--font-body);
  font-size: var(--fs-s);
  color: var(--ink-soft);
  background: rgba(255, 251, 240, 0.5);
  border: 1px solid rgba(64, 48, 31, 0.28);
  border-radius: var(--r);
  cursor: pointer;
  transition: border-color 0.14s ease, color 0.14s ease, background 0.14s ease;
}
.cx-tab .cx-zh { letter-spacing: 0.1em; }
.cx-tab .cx-en {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  opacity: 0.66;
}
/* 点亮了几格 / 一共几格 */
.cx-tab .cx-n {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  color: var(--red);
  opacity: 0.85;
}
.cx-tab:hover { border-color: var(--red); color: var(--ink); }
.cx-tab.on {
  color: var(--ink);
  background: linear-gradient(180deg, #fbf4e4, #f0e2c6);
  border-color: var(--red);
  box-shadow: inset 0 0 0 1px rgba(140, 47, 47, 0.14), 0 2px 6px rgba(0, 0, 0, 0.16);
}
.cx-tab.on .cx-zh { font-weight: 700; color: var(--red); }

/* 锁着的三册：照样挂在门上，只是灰的、虚线边——看得见摸不着 */
.cx-tab.locked {
  color: rgba(125, 104, 73, 0.6);
  background: rgba(230, 213, 180, 0.28);
  border-style: dashed;
  border-color: rgba(64, 48, 31, 0.22);
  cursor: not-allowed;
}
.cx-tab.locked:hover {
  color: rgba(125, 104, 73, 0.72);
  border-color: rgba(64, 48, 31, 0.3);
  background: rgba(230, 213, 180, 0.3);
}
.cx-tab .cx-lock {
  font-family: var(--font-hand);
  font-size: 10px;
  letter-spacing: 0.06em;
  padding: 0 5px 1px;
  border: 1px solid rgba(64, 48, 31, 0.24);
  border-radius: 999px;
  color: rgba(90, 72, 48, 0.78);
  background: rgba(213, 189, 146, 0.34);
}

/* ---------- 标本格 ---------- */
.codex-grid .tile,
.codex-grid .tile.dark {
  position: relative;
  /* 字排在最下面，图从上面铺到字底下那点位置 */
  justify-content: flex-end;
  min-height: 136px;
  padding: 6px 5px 7px;
  overflow: hidden;
}
/* 图是标本：贴在格子底部，往上长；下面让出字的那一条 */
.codex-grid .tile-art {
  position: absolute;
  inset: 6px 5px 26px;
  height: auto;
  margin-bottom: 0;
  /* 贴着底摆：根须留在纸上，头不被裁 */
  align-items: flex-end;
}
/* 宽也别超格子：窄屏上细长株最容易顶出去 */
.codex-grid .tile-art img { max-width: 100%; max-height: 100%; }
/* 字在最上层，底下垫一条半透明的纸，压住图也读得清 */
.codex-grid .tile-zh,
.codex-grid .tile-en,
.codex-grid .tile-src {
  position: relative;
  z-index: 2;
  background: rgba(251, 244, 228, 0.76);
  border-radius: 2px;
  padding: 0 4px;
  box-shadow: 0 0 6px rgba(251, 244, 228, 0.7);
}

/* 没点亮的格子：虚线边 + 灰剪影，且没有点击反馈 */
.tile.dark {
  cursor: default;
  border-style: dashed;
  border-color: rgba(64, 48, 31, 0.26);
  box-shadow: none;
  background:
    repeating-linear-gradient(135deg, rgba(120, 96, 62, 0.05) 0 6px, rgba(120, 96, 62, 0) 6px 12px),
    linear-gradient(180deg, #f4ead6, #e9dcbe);
}
.tile.dark:hover { transform: none; border-color: rgba(64, 48, 31, 0.26); box-shadow: none; }
.tile.dark .tile-art img { filter: brightness(0) opacity(0.2); }
.tile.dark .tile-zh,
.tile.dark .tile-en {
  color: var(--ink-soft);
  font-weight: 400;
  opacity: 0.8;
}
/* 只有 emoji、没有真图的那些：不画假剪影（那看着像坏了），就留个问号 */
.tile.blank .tile-mark {
  font-family: var(--font-hand);
  font-size: 30px;
  color: rgba(64, 48, 31, 0.3);
}

/* ---------- 详情纸上的图 ---------- */
.codex-head { align-items: flex-end; }
.codex-art {
  width: 96px;
  height: 108px;
  /* 图贴底摆，名字那条线才不会把根须挤掉 */
  align-items: flex-end;
}
.codex-art-img { max-width: 100%; max-height: 100%; object-fit: contain; }

/* ============================================================
   手抄本 · 温室那几格 / 属性那几个方框
   ============================================================ */

/*
 * 【温室】那一格的底走**淡绿**——土里的事就用土的颜色。
 * 它还是同一张纸（.card 的圆角、厚影、内边距都不动），只是把纸染成浅绿：
 * 在几张米黄的纸中间，这一块一眼就认得出来是「地」。
 * 合着的时候它整块收起来，只剩上面那行标题（见 .fold-head 那一套）。
 */
.card.beds-card {
  background:
    radial-gradient(80% 50% at 14% 4%, rgba(255, 255, 255, 0.6), transparent 62%),
    linear-gradient(180deg, #eef5e4, #d8e7c8);
  border-color: rgba(94, 124, 74, 0.42);
  border-bottom-color: rgba(94, 124, 74, 0.55);
}

/*
 * 属性：六个小方框横着排（五项天赋 + 运气）。
 * 一格一列，名字在上、数在下——扫一眼就把这个人看全了。
 * 窄屏也不折行：折成两行就不是「一眼看全」了，宁可把字收小一档。
 */
.attr-boxes,
.bed-boxes {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 6px;
}
.attr-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  min-width: 0;
  padding: 6px 2px 5px;
  border: 1px solid rgba(64, 48, 31, 0.3);
  border-radius: var(--r);
  background: rgba(255, 255, 255, 0.42);
  text-align: center;
}
.attr-box .ab-name {
  font-size: 11px;
  letter-spacing: 0.04em;
  line-height: 1.25;
  /* 名字留两行的高度：「黑魔法防御」这种五个字的会折成两行，
     留白留匀了，六个数才排在同一条线上 */
  min-height: 2.5em;
  color: var(--ink-soft);
  overflow-wrap: anywhere;
}
.attr-box .ab-val {
  font-family: var(--font-hand);
  font-size: var(--fs-l);
  line-height: 1;
  color: var(--ink);
}
/* 运气那一枚多一行档位小字（顺遂 / 平常 / 背运）——分档用颜色，不用符号 */
.attr-box .ab-tag {
  font-size: 10px;
  letter-spacing: 0.1em;
  line-height: 1.2;
  color: var(--ink-soft);
}
.attr-box.favoured .ab-val,
.attr-box.favoured .ab-tag { color: var(--red); }
.attr-box.jinxed .ab-val,
.attr-box.jinxed .ab-tag { color: var(--leaf); }

/* ============================================================
   手抄本 · 【你的人格】与【记忆】两扇小门里的输入框
   ------------------------------------------------------------
   都摊在纸面上：纸是米黄的，框就顺着纸走——比纸亮一点的底、细棕边，
   聚焦时描一道红（和别的纸面控件一个脾气）。
   ============================================================ */
.profile-input {
  display: block;
  width: 100%;
  padding: var(--sp-2) var(--sp-3);
  font-family: var(--font-body);
  font-size: var(--fs-s);
  line-height: 1.7;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.5);
  border: 1px solid rgba(64, 48, 31, 0.34);
  border-radius: var(--r);
  resize: vertical;
}
.profile-input:focus {
  outline: none;
  border-color: var(--red);
  background: rgba(255, 255, 255, 0.72);
}
.profile-input::placeholder { color: rgba(64, 48, 31, 0.45); }

/* 温室那几格：一格一块土。长着东西的偏白底绿边、能收的描一道金；
   **还塌着的那几格一律灰**——灰是「这儿还没轮到」，绿是「这块土在你手里」，
   两种颜色各自说各自的话，不用再多写一个字。 */
.bed-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  min-width: 0;
  padding: 6px 2px 5px;
  border: 1px solid rgba(94, 124, 74, 0.42);
  border-radius: var(--r);
  background: rgba(255, 255, 255, 0.44);
  text-align: center;
}
.bed-box.shut {
  border-color: rgba(64, 48, 31, 0.22);
  background: rgba(120, 116, 104, 0.18);
}
.bed-box .bb-no {
  font-size: 10px;
  letter-spacing: 0.06em;
  color: rgba(78, 106, 60, 0.95);
}
.bed-box.shut .bb-no { color: #9a948a; }
.bed-box .bb-val {
  font-size: 12px;
  line-height: 1.25;
  /* 和属性那排一样留两行的高度：种子名有长有短，留白了底下的状态才对得齐 */
  min-height: 2.5em;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.bed-box.shut .bb-val { color: #8a8478; }
.bed-box .bb-tag {
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
}
.bed-box.ripe { border-color: rgba(224, 178, 100, 0.85); }
.bed-box.ripe .bb-tag { color: var(--red); }

/*
 * 记忆那一张**大框**（创作者 2026-09-28 改的版式）：所有摘要一条一行装在这一个框里，
 * 底下一颗【存下】。从前是一条一只小框、各带一对按钮——条数一多满屏都是按钮。
 * 框本身就借 .profile-input 那身皮（人格那一栏同一只框），这里只把它撑高、放宽行距。
 */
.memo-box {
  min-height: 12em;
  line-height: 1.85;
  resize: vertical;
}

/* 记忆框底下那行读数（左下角）：当前这一回合要发出去的记忆一共多少字 */
.memo-total {
  margin: 5px 0 0;
  text-align: left;
  font-size: var(--fs-xs);
  font-family: var(--font-hand);
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}

/*
 * 一张纸底下那**一排小按钮**（人格那张纸上就两颗：存下 / 先不改）。
 * .acts 本来是竖着一列、每颗占满一行；这里改成横着并排、只占内容宽——
 * 一颗"存下"不该在纸上占掉一整行。
 */
.acts-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-2);
}
.acts-row .btn { width: auto; }

/*
 * 【更高字数上限】那颗小按钮，以及按下去**就地**摊开的那段警告
 * （说明下面、输入框上面那一格，位置是创作者定的）。
 * 不另开一张纸：纸一换，玩家刚打进去的字就没了。
 */
.cap-slot { margin: 0 0 var(--sp-2); }
.cap-slot .btn.small { padding: 3px 10px 4px; font-size: var(--fs-xs); }
.cap-warn {
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid rgba(140, 47, 47, 0.42);
  border-radius: var(--r);
  background: rgba(140, 47, 47, 0.08);
}
.cap-warn-tx {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-xs);
  line-height: 1.7;
  color: var(--red);
}
.cap-warn-acts { display: flex; gap: var(--sp-2); justify-content: center; }
.cap-warn-acts .btn.small { padding: 3px 10px 4px; font-size: var(--fs-xs); }
/* 已经解除的那一条：一句实话 + 一颗「恢复」 */
.cap-on { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.cap-on-tx { font-size: var(--fs-xs); letter-spacing: 0.04em; color: var(--ink-soft); }
.cap-on .btn.small { padding: 3px 10px 4px; font-size: var(--fs-xs); }

/* ============================================================
   手抄本 · 人物簿：一整本，合着只占一行标题
   ------------------------------------------------------------
   合着：标题行 + 人数 + 一个小箭头，什么都不占。
   点标题：把所有人一起摊出来。
   摊开之后一人一行，一行给全：头像 / 名字与学院 / 十格好感 / 数值 + 一句状态。
   ============================================================ */

/* 把分区标题本身做成可按的 */
.fold-head {
  width: 100%;
  padding: 0;
  font-family: var(--font-body);
  background: none;
  border: 0;
  cursor: pointer;
  text-align: left;
  transition: color 0.14s ease;
}
.fold-head .ch-zh { transition: color 0.14s ease; }
.fold-head:hover .ch-zh { color: var(--red); }
.fold-head:focus-visible { outline: 1px dashed var(--red); outline-offset: 3px; }
.fold-note {
  flex: none;
  letter-spacing: 0.16em;
  opacity: 0.7;
}
/* 右边那个小箭头：合着朝下、摊开朝上 */
.rel-caret {
  flex: none;
  width: 0;
  height: 0;
  margin-left: 3px;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid var(--ink-soft);
  transition: transform 0.18s ease;
}
.fold-head.open .rel-caret { transform: rotate(180deg); }
.fold-body { animation: relOpen 0.2s ease both; }
.fold-body[hidden] { display: none; }

/* 一行一个人 */
.rel { border-bottom: 1px dotted rgba(64, 48, 31, 0.22); }
.rel:last-child { border-bottom: 0; }

.rel-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  padding: var(--sp-2) 2px;
  font-family: var(--font-body);
  color: var(--ink);
  text-align: left;
}
/* 全本摊开时，行本身不再可点：本子已经开着了 */
.rel-head.static { cursor: default; }
.rel-id { flex: 1; min-width: 0; }
.rel-name {
  font-size: var(--fs-m);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rel-right {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
/* 数值用红笔手写体——和精力、金加隆那里一套读法 */
.rel-v {
  min-width: 2ch;
  text-align: right;
  font-family: var(--font-hand);
  font-size: var(--fs-m);
  line-height: 1;
  color: var(--red);
}

@keyframes relOpen {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}
.rel-tag {
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  color: var(--fg-dim);
}
.rel-stage {
  margin: 0 2px 6px;
  font-size: var(--fs-s);
  color: var(--fg);
}
.rel:last-child .rel-stage { margin-bottom: var(--sp-2); }

/* ── 夜间模式：时段进入 evening 后，舞台、地图、地点牌与状态栏同步变暗 ── */
body.night-mode .stage-img { filter: brightness(.52) saturate(.72) hue-rotate(6deg); }
body.night-mode .stage-scrim { background: linear-gradient(180deg, rgba(4, 7, 14, .78), rgba(5, 7, 13, .36) 35%, rgba(3, 5, 10, .84) 100%), radial-gradient(90% 70% at 50% 18%, rgba(50, 67, 111, .12), transparent 72%); }
body.night-mode .hud { background: linear-gradient(180deg, rgba(3, 6, 12, .96), rgba(5, 8, 16, .82) 70%, rgba(5, 8, 16, 0) 100%); }
body.night-mode .hud::after { background: linear-gradient(90deg, transparent, rgba(128, 151, 194, .46) 18%, rgba(128, 151, 194, .46) 82%, transparent); }
body.night-mode .hud-day { color: #dce5f6; text-shadow: 0 0 14px rgba(124, 157, 216, .26); }
body.night-mode .hud-week, body.night-mode .stat-row .tiny { color: rgba(181, 196, 222, .78); }
body.night-mode .hud-slot { color: #e4eaf4; background: linear-gradient(180deg, #596d96, #293750); border: 1px solid rgba(177, 202, 239, .48); box-shadow: 0 0 9px rgba(89, 125, 188, .28); }
body.night-mode .hud-mail, body.night-mode .hud-room, body.night-mode .hud-gear { filter: saturate(.72) brightness(.9); }
body.night-mode .bar i { background: rgba(9, 14, 25, .78); border-color: rgba(177, 195, 222, .32); }
body.night-mode .bar.stam i.on { background: #a99b61; border-color: #d4c27a; }
body.night-mode .bar.hp i.on { background: #9b626d; border-color: #c98a96; }
body.night-mode .tray { background: linear-gradient(0deg, rgba(3, 6, 12, .98) 42%, rgba(4, 7, 14, .86) 72%, rgba(4, 7, 14, 0) 100%); }
body.night-mode .btn.day { color: #c5d2e8; background: linear-gradient(180deg, rgba(24, 35, 57, .92), rgba(9, 15, 28, .94)); border-color: rgba(126, 159, 216, .52); box-shadow: inset 0 1px 0 rgba(203, 221, 250, .1), 0 4px 14px rgba(0,0,0,.54); }
body.night-mode .btn.day:hover { color: #edf4ff; background: linear-gradient(180deg, rgba(37, 54, 84, .96), rgba(13, 22, 40, .98)); border-color: rgba(170, 202, 248, .8); }
body.night-mode .view::before { background: radial-gradient(120% 72% at 50% 42%, rgba(6, 10, 22, .64), rgba(6, 10, 22, .28) 52%, rgba(6, 10, 22, .1) 100%); }
body.night-mode .place { color: #d8e1ef; background: linear-gradient(180deg, rgba(24, 35, 56, .88), rgba(9, 14, 25, .9)); border-color: rgba(119, 149, 195, .44); box-shadow: 0 5px 15px rgba(0,0,0,.5), inset 0 1px 0 rgba(196, 216, 246, .08); }
body.night-mode .place-name { color: #dce7f7; text-shadow: 0 1px 4px rgba(0,0,0,.9); }
body.night-mode .place-sub { color: rgba(173, 193, 222, .78); }
body.night-mode .place::after { background: #8da9d4; box-shadow: 0 0 8px rgba(110, 153, 218, .4); }
body.night-mode .place:hover { background: linear-gradient(180deg, rgba(40, 57, 88, .94), rgba(13, 22, 40, .96)); border-color: rgba(175, 207, 249, .72); }
body.night-mode .room-btn { filter: brightness(.78) saturate(.8); }
body.night-mode .map-top, body.night-mode .room-map { filter: brightness(.78) saturate(.78); }
body.night-mode .wmap-btn, body.night-mode .wm-door { filter: brightness(.72) saturate(.8); }

/* 日程面板：压缩成清晰的标题、账目、操作三段；夜间随全局变成冷暗色。 */
.sheet.day-schedule, body.night-mode .panel.day-schedule { background: linear-gradient(180deg, #f5ead1, #e5d1aa); }
.sheet.day-schedule .sheet-head, .panel.day-schedule .panel-head { padding-bottom: 9px; border-bottom: 1px solid rgba(120, 91, 49, .3); }
.sheet.day-schedule .sheet-head h2, .panel.day-schedule .panel-title .pt-zh { color: #6b4422; letter-spacing: .08em; }
.sheet.day-schedule .acts-grid, .panel.day-schedule .acts-grid { gap: 9px; margin-top: 7px; }
.sheet.day-schedule .acts-grid > .btn, .panel.day-schedule .acts-grid > .btn { min-height: 4.6em; border-radius: 7px; background: linear-gradient(180deg, rgba(255,248,229,.92), rgba(230,210,170,.92)); box-shadow: 0 3px 9px rgba(70,48,25,.22), inset 0 1px 0 rgba(255,255,255,.52); }
.sheet.day-schedule .acts > .btn, .panel.day-schedule .acts > .btn { border-radius: 7px; background: linear-gradient(180deg, rgba(255,248,229,.94), rgba(232,214,176,.94)); box-shadow: 0 3px 9px rgba(70,48,25,.22), inset 0 1px 0 rgba(255,255,255,.52); }
.sheet.day-schedule .acts-grid > .btn:hover, .panel.day-schedule .acts-grid > .btn:hover { transform: translateY(-2px); border-color: rgba(144, 91, 41, .72); }
body.night-mode .sheet.day-schedule, body.night-mode .panel.day-schedule { background: radial-gradient(90% 30% at 50% 0%, rgba(84, 111, 163, .2), transparent 70%), linear-gradient(180deg, #1c263a, #0d1423 92%); border-color: rgba(126, 158, 211, .56); box-shadow: inset 0 0 35px rgba(0,0,0,.52), 0 20px 54px rgba(0,0,0,.68); color: #d9e4f3; }
body.night-mode .sheet.day-schedule .sheet-head, body.night-mode .panel.day-schedule .panel-head { border-color: rgba(135, 166, 216, .3); }
body.night-mode .sheet.day-schedule .sheet-head h2, body.night-mode .panel.day-schedule .panel-title .pt-zh { color: #dce9fb; text-shadow: 0 0 12px rgba(128, 167, 231, .2); }
body.night-mode .sheet.day-schedule .acts-grid > .btn, body.night-mode .panel.day-schedule .acts-grid > .btn { color: #d8e4f4; background: linear-gradient(180deg, rgba(35, 51, 80, .96), rgba(14, 23, 40, .98)); border-color: rgba(125, 159, 216, .48); box-shadow: inset 0 1px 0 rgba(205,224,250,.08), 0 4px 12px rgba(0,0,0,.48); }
body.night-mode .sheet.day-schedule .acts > .btn, body.night-mode .panel.day-schedule .acts > .btn { color: #dbe6f6; background: linear-gradient(180deg, rgba(36, 53, 84, .96), rgba(13, 22, 40, .98)); border-color: rgba(125, 159, 216, .5); border-left-color: rgba(150, 176, 224, .82); box-shadow: inset 0 1px 0 rgba(205,224,250,.08), 0 4px 12px rgba(0,0,0,.5); }
body.night-mode .sheet.day-schedule .acts > .btn:hover, body.night-mode .panel.day-schedule .acts > .btn:hover { color: #f1f6ff; background: linear-gradient(180deg, rgba(52, 74, 112, .98), rgba(17, 29, 51, .98)); border-color: rgba(181, 211, 252, .78); }
body.night-mode .sheet.day-schedule .acts-grid > .btn:hover, body.night-mode .panel.day-schedule .acts-grid > .btn:hover { color: #f1f6ff; background: linear-gradient(180deg, rgba(51, 72, 108, .98), rgba(17, 29, 51, .98)); border-color: rgba(181, 211, 252, .76); }
body.night-mode .sheet.day-schedule .small, body.night-mode .sheet.day-schedule .tiny, body.night-mode .sheet.day-schedule .muted, body.night-mode .panel.day-schedule .small, body.night-mode .panel.day-schedule .tiny, body.night-mode .panel.day-schedule .muted { color: rgba(190, 207, 232, .76); }
body.night-mode .sheet.day-schedule .night-note, body.night-mode .panel.day-schedule .night-note { background: rgba(91, 50, 70, .2); border-color: rgba(190, 112, 139, .58); }
@media (max-width: 560px) { body.night-mode .place { box-shadow: 0 4px 12px rgba(0,0,0,.46); } }

   ------------------------------------------------------------
   旧那套「三选一猜火候 + 品质 + 管制等级」全部作废：
   坩埚搬进魔药课教室（一块灰底大牌子），一锅药变成
   切药 → 下锅 → 点火那一下的时机判定（见 engine/brew.js、ui/bench.js）。
   ============================================================ */

/* ---------- 教室里那块【坩埚】牌：灰底 ---------- */
/* 金属的灰，和种植箱那块深绿分开——它是这间屋子的正事，要一眼认得出 */
.room-btn.steel {
  background: linear-gradient(180deg, rgba(60, 64, 68, 0.78), rgba(38, 41, 44, 0.84));
  border-color: rgba(189, 169, 138, 0.34);
}
.room-btn.steel:hover {
  background: linear-gradient(180deg, rgba(80, 85, 90, 0.86), rgba(48, 52, 56, 0.9));
  border-color: rgba(224, 178, 100, 0.6);
}
.room-btn.steel .rb-sub { color: var(--cine-dim); }

/*
 * 厨房那口锅（学校厨房大锅）：**学院色**的一版（创作者 2026-09-28 定的）。
 *
 * 色号不写在这儿——由 JS 把 data/houses.js 里那个唯一的麦金挂进 `--house`
 * （见 ui/screens-map.js 摆那块牌子的那一段）。这里只管「涂上学院色之后，
 * 字要看得清、悬停要更亮」：压一层上浅下深的薄幕，底就是那支学院色本身。
 * 兜底那个 #8a6a1c 只在变量没送到时生效，正常看到的永远是 data 里那支。
 */
.room-btn.house-pot {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.14), rgba(0, 0, 0, 0.24)),
    var(--house, #8a6a1c);
  border-color: rgba(240, 214, 140, 0.5);
}
.room-btn.house-pot:hover {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.22), rgba(0, 0, 0, 0.14)),
    var(--house, #8a6a1c);
  border-color: rgba(248, 226, 160, 0.78);
}
.room-btn.house-pot .rb-sub { color: rgba(246, 232, 186, 0.92); }

/* ---------- 海格门口那张【小摊】：灰黄色金边的矮长框 ---------- */
/*
 * 它和大煮锅、教室坩埚那种「走进另一个界面」的大灰牌**不一样**（创作者定的，2026-09-27）：
 * 门口这一摊只是顺手买点种子，用矮一条的框就够——原来它也是一块同样大的钢灰牌，
 * 压在【兽栏】正下方，两块叠在一起过（创作者点过这个问题）。
 *
 * 长相：晒旧的**灰黄**底 + 一圈**描金**边，字是深棕——像一块挂在门边的旧木牌。
 * 因为压的是亮黄底，字不能再用 cine 那套浅色（那在纸面上会糊掉），改成深棕。
 * flex-wrap 是为了让那行小字自己占第二行：两块并排挤在一行会把字压没。
 */
.room-btn.market {
  flex-wrap: wrap;
  padding: var(--sp-2) var(--sp-3);
  background: linear-gradient(180deg, rgba(198, 186, 142, 0.86), rgba(152, 140, 102, 0.9));
  border: 1px solid rgba(224, 178, 100, 0.75);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.42), inset 0 1px 0 rgba(255, 246, 214, 0.28);
}
.room-btn.market:hover {
  background: linear-gradient(180deg, rgba(218, 206, 158, 0.94), rgba(170, 157, 116, 0.94));
  border-color: rgba(242, 208, 142, 0.92);
}
.room-btn.market .rb-zh {
  font-size: var(--fs-s);
  letter-spacing: 0.08em;
  color: #2e2415;
}
.room-btn.market .rb-en {
  flex: none;
  font-size: 11px;
  color: rgba(58, 46, 28, 0.7);
}
.room-btn.market .rb-sub {
  margin-top: 1px;
  color: rgba(58, 46, 28, 0.7);
}

/* ---------- 魔咒学教室墙角那个【木人】：一块方牌 ---------- */
/*
 * 它是这一摞里**唯一一块方牌**（其余都是横条）——创作者 2026-10-14 要的
 * 「黄棕色的方形按钮」：像墙角立着个木桩，不是墙上的又一条。
 * 宽度得盖过 `.room-map > *` 那条（那条是给横条定的），所以选择器要写到 .room-btn.dummy。
 */
.room-map > .room-btn.dummy {
  width: 104px;
  height: 104px;
  padding: var(--sp-2);
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  text-align: center;
  color: #2c1f0e;
  background:
    radial-gradient(80% 60% at 30% 10%, rgba(255, 240, 200, 0.45), transparent 62%),
    linear-gradient(180deg, #c99447, #7d5624 92%);
  border: 1px solid rgba(255, 232, 170, 0.6);
  border-radius: 6px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 246, 214, 0.35);
}
.room-btn.dummy:hover {
  transform: translateY(-2px);
  border-color: rgba(255, 244, 208, 0.92);
  box-shadow: 0 9px 20px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 246, 214, 0.45), 0 0 16px rgba(201, 148, 71, 0.35);
}
.room-btn.dummy .dummy-ico { font-size: 30px; line-height: 1; filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.5)); }
.room-btn.dummy .rb-zh {
  font-size: var(--fs-m);
  letter-spacing: 0.16em;
  color: #2c1f0e;
  text-shadow: none;
}
/*
 * 那行小字（「训练魔咒」）。它是**浅木底**上的一行字，不能走 .rb-sub 那个暗底金色
 * （那行金压在黄棕木上几乎看不清），跟着 .rb-zh 用深棕、再小一档。
 */
.room-btn.dummy .rb-sub {
  font-size: 10px;
  letter-spacing: 0.1em;
  color: rgba(44, 31, 14, 0.82);
  text-shadow: none;
}

/* ---------- 炼药 / 烹饪那两张单子：一条方子一条细条子 ---------- */
/*
 * 2026-09-28 改版（创作者定的）：一行从「一张羊皮纸 + 右边一颗方按钮」
 * 换成**一条细条子**——方子多了要翻得动，一张纸占掉三行高，十道菜翻半天。
 * 条子上一共三样：名字（左）、材料串（灰、长了自己截断）、缺几样（右端小标）。
 * 点条子开详情纸，两颗动作按钮（自己上手 / 快速）都在那张纸上。
 */
/* ── 配方选择卡：煮锅与炼金页面专属，不影响随身锅和其他列表 ── */
.view:has(.cookpot-scene),
.view:has(.brewery-scene) { position: relative; isolation: isolate; }
.view:has(.cookpot-scene)::before,
.view:has(.brewery-scene)::before {
  content: ''; position: absolute; inset: -18px -24px; z-index: -2; pointer-events: none;
  background: radial-gradient(40% 26% at 50% 12%, rgba(216, 167, 78, .17), transparent 72%), linear-gradient(180deg, rgba(29, 23, 17, .52), rgba(13, 12, 12, .76));
  box-shadow: inset 0 0 78px rgba(0,0,0,.58);
}
.view:has(.cookpot-scene) .cookpot-scene,
.view:has(.brewery-scene) .brewery-scene {
  position: relative; margin: 0 -6px var(--sp-3); padding: 10px 16px 14px; text-align: center;
  background: linear-gradient(180deg, rgba(61, 44, 27, .72), rgba(24, 20, 17, .76));
  border-top: 1px solid rgba(223, 177, 91, .4); border-bottom: 1px solid rgba(223, 177, 91, .25);
  box-shadow: 0 12px 22px rgba(0,0,0,.32), inset 0 1px 0 rgba(255,240,190,.07);
}
.view:has(.brewery-scene) .brewery-scene { background: linear-gradient(180deg, rgba(38, 47, 52, .78), rgba(18, 23, 25, .82)); border-color: rgba(161, 185, 192, .34); }
.view:has(.cookpot-scene) .cookpot-scene .name,
.view:has(.brewery-scene) .brewery-scene .name { color: #f2d59b; text-shadow: 0 0 15px rgba(224, 172, 74, .28), 0 2px 3px rgba(0,0,0,.86); }
.view:has(.brewery-scene) .brewery-scene .name { color: #d7e2e0; text-shadow: 0 0 15px rgba(147, 191, 195, .2), 0 2px 3px rgba(0,0,0,.86); }
.view:has(.cookpot-scene) .cookpot-scene .name::after,
.view:has(.brewery-scene) .brewery-scene .name::after { width: min(72%, 280px); height: 2px; margin-top: 8px; background: linear-gradient(90deg, transparent, #b57e38 18%, #ebc67b 50%, #b57e38 82%, transparent); opacity: .88; }
.view:has(.brewery-scene) .brewery-scene .name::after { background: linear-gradient(90deg, transparent, #7c9b9c 18%, #d4e1dd 50%, #7c9b9c 82%, transparent); }
/* ---------- 炼药 / 烹饪那两张单子：一方块一副方子（2026-10-19 新版式） ----------
 * 方块上只有图和名字；材料够的亮着，不够的灰着（虚线框、图去色）——
 * **灰的照样点得进去**，缺哪几味写在详情纸上（构造器在 ui/craft-tile.js，三处共用）。
 */
/* 底色：随身锅那边是纸色（和背包同一族） */
.craft-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 10px; margin: 0 auto; max-width: 760px; }
.craft-tile {
  position: relative; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 4px;
  aspect-ratio: 1 / 1; padding: 8px 6px 6px; overflow: hidden; cursor: pointer;
  font-family: var(--font-body); text-align: center; color: var(--ink);
  background: radial-gradient(80% 60% at 22% 6%, rgba(255, 255, 255, 0.42), transparent 62%), linear-gradient(180deg, #fbf4e4, #eddfc0);
  border: 1px solid rgba(64, 48, 31, 0.44); border-radius: 8px;
  box-shadow: 0 2px 0 rgba(50, 37, 24, 0.34), 0 5px 12px rgba(0, 0, 0, 0.26);
  transition: transform 0.16s ease, border-color 0.16s ease, box-shadow 0.16s ease, opacity 0.16s ease;
}
.craft-tile:hover { transform: translateY(-3px); border-color: var(--red); box-shadow: 0 3px 0 rgba(50, 37, 24, 0.3), 0 10px 20px rgba(0, 0, 0, 0.32); }
.craft-tile-art { flex: 1 1 auto; width: 100%; min-height: 0; display: flex; align-items: center; justify-content: center; }
.craft-tile-img { max-width: 100%; max-height: 100%; object-fit: contain; filter: drop-shadow(0 5px 7px rgba(0, 0, 0, 0.34)); }
.craft-tile-emoji { font-size: 30px; line-height: 1.2; }
.craft-tile-name { font-size: var(--fs-s); letter-spacing: 0.06em; line-height: 1.3; }
/* 灰的那一档：虚线框 + 图去色；**不禁用**，点得进去看缺什么 */
.craft-tile.is-off { opacity: 0.52; border-style: dashed; }
.craft-tile.is-off .craft-tile-art { filter: grayscale(0.85); }
.craft-tile.is-off:hover { opacity: 0.74; }

/* 煮锅 / 坩埚那两屏：方块换成和那两张场面同族的深色木牌 */
.view:has(.cookpot-scene) .craft-tile,
.view:has(.brewery-scene) .craft-tile {
  color: #f2e2c2;
  background: radial-gradient(90% 44% at 50% 0%, rgba(224, 178, 86, .15), transparent 72%), linear-gradient(180deg, rgba(55, 42, 28, .96), rgba(22, 18, 15, .98));
  border-color: rgba(196, 153, 77, .46);
  box-shadow: inset 0 1px 0 rgba(255, 237, 183, .08), 0 7px 15px rgba(0, 0, 0, .42);
}
.view:has(.brewery-scene) .craft-tile {
  background: radial-gradient(90% 44% at 50% 0%, rgba(144, 188, 191, .13), transparent 72%), linear-gradient(180deg, rgba(39, 49, 52, .98), rgba(17, 22, 24, .98));
  border-color: rgba(143, 177, 181, .48);
}
.view:has(.cookpot-scene) .craft-tile::before,
.view:has(.brewery-scene) .craft-tile::before { content: ''; position: absolute; inset: 5px; border: 1px solid rgba(255, 224, 151, .08); border-radius: 5px; pointer-events: none; }
.view:has(.brewery-scene) .craft-tile::before { border-color: rgba(203, 225, 221, .09); }
.view:has(.cookpot-scene) .craft-tile:hover,
.view:has(.brewery-scene) .craft-tile:hover { transform: translateY(-3px); border-color: rgba(239, 197, 112, .9); box-shadow: inset 0 1px 0 rgba(255, 240, 193, .14), 0 11px 22px rgba(0, 0, 0, .5), 0 0 14px rgba(211, 161, 72, .15); }
.view:has(.brewery-scene) .craft-tile:hover { border-color: rgba(190, 225, 222, .9); box-shadow: inset 0 1px 0 rgba(224, 242, 238, .14), 0 11px 22px rgba(0, 0, 0, .5), 0 0 14px rgba(139, 192, 193, .13); }
.view:has(.cookpot-scene) .craft-tile-name { color: #f3d48f; }
.view:has(.brewery-scene) .craft-tile-name { color: #d4e4df; }
.view:has(.cookpot-scene) .craft-tile.is-off:hover,
.view:has(.cookpot-scene) .craft-tile.is-off,
.view:has(.brewery-scene) .craft-tile.is-off { background: radial-gradient(90% 44% at 50% 0%, rgba(120, 100, 70, .1), transparent 72%), linear-gradient(180deg, rgba(48, 40, 32, .96), rgba(20, 18, 16, .98)); }

@media (max-width: 620px) {
  .craft-grid { grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 8px; }
  .craft-tile-name { font-size: var(--fs-xs); }
}
@media (prefers-reduced-motion: reduce) {
  .craft-tile { transition: none; }
}

/* ---------- 快速制作那张纸上的「做几份」那一栏 ---------- */
/*
 * 2026-10-14 加的：批量不另开一栏，收进【快速烹饪】/【快速炼药】里——
 * 点那颗按钮先问份数：加减用工程里那套货格步进器（.mkt-step，别处买货也用它），
 * 旁边再给一颗【最大】= 把袋里的料一次做完。
 */
.quick-qty {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: var(--sp-2) 0 4px;
}
.quick-qty .mkt-step { flex: none; margin-top: 0; }
.btn.quick-max {
  flex: none;
  width: auto;
  padding: 5px 16px 6px;
  font-size: var(--fs-s);
  letter-spacing: 0.1em;
  color: #f3e3bd;
  background: linear-gradient(180deg, rgba(56, 41, 26, 0.94), rgba(26, 18, 12, 0.96));
  border: 1px solid rgba(224, 178, 100, 0.55);
  border-radius: 5px;
  box-shadow: inset 0 1px 0 rgba(255, 240, 200, 0.12), 0 3px 10px rgba(0, 0, 0, 0.4);
}
.btn.quick-max:hover { border-color: rgba(246, 210, 138, 0.92); box-shadow: 0 0 14px rgba(224, 178, 100, 0.28), 0 4px 12px rgba(0, 0, 0, 0.45); }
.quick-cap { display: block; margin-bottom: var(--sp-2); font-size: var(--fs-xs); line-height: 1.6; color: var(--ink-soft); }

/*
 * 随身锅那一屏的折叠栏头（ui/portable.js）。
 * 走「贴在木板上的一条标签」那一路：和屋里的长条牌同族，但更扁、更轻，
 * 因为它下面紧跟的就是列表，抢视线就本末倒置了。
 */
.pot-fold {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  width: 100%;
  margin: var(--sp-3) 0 var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  font-family: var(--font-body);
  text-align: left;
  color: var(--cine-text);
  background: linear-gradient(180deg, rgba(58, 46, 34, 0.82), rgba(38, 30, 22, 0.78));
  border: 1px solid rgba(214, 186, 130, 0.28);
  border-radius: var(--r);
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.42);
  cursor: pointer;
  transition: border-color 0.16s ease, filter 0.16s ease, transform 0.12s ease;
}
.pot-fold:hover { border-color: var(--red); filter: brightness(1.1); }
.pot-fold:active { transform: translateY(1px); }
.pot-fold .pf-arrow {
  flex: none;
  font-size: var(--fs-s);
  color: #d9bd8a;
  /* 箭头单独一行高，别把整条基线拽偏 */
  line-height: 1;
}
.pot-fold .pf-zh {
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: 0.12em;
}
.pot-fold .pf-en {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--fs-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cine-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 这一栏有几样。折上之后内容就看不见了，这个数是唯一的交代 */
.pot-fold .pf-count {
  flex: none;
  min-width: 22px;
  padding: 0 6px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  line-height: 20px;
  text-align: center;
  color: #2b2415;
  background: #d9bd8a;
  border-radius: 999px;
}
@media (max-width: 420px) {
  .pot-fold .pf-en { display: none; }
}

/* 详情里缺的那一味整行标红（「要 N，只有 M」那句跟着红） */
.row.lack .grow,
.row.lack .val { color: var(--red); }
.row.lack .val { font-weight: 700; }

/* ---------- 操作台：深褐色那一屏 ---------- */
.bench {
  padding: var(--sp-4) var(--sp-4) var(--sp-5);
  border: 1px solid rgba(224, 178, 100, 0.22);
  border-radius: var(--r);
  color: var(--cine-text);
  /* 木台与炭火气：上头亮一点，底下压到近黑 */
  background:
    radial-gradient(110% 70% at 50% 0%, rgba(132, 90, 48, 0.34), transparent 62%),
    linear-gradient(175deg, #2b1e15 0%, #211610 56%, #180f0a 100%);
  box-shadow: inset 0 1px 0 rgba(255, 220, 170, 0.08), 0 12px 28px rgba(0, 0, 0, 0.4);
}

.bench-head {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  padding-bottom: var(--sp-2);
  border-bottom: 1px dashed rgba(224, 178, 100, 0.24);
}
.bh-name {
  font-size: var(--fs-l);
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--cine-gold);
}
/* 头顶那一排小点 = 这一锅要处理几味；进锅的亮起来 */
.bh-steps { flex: 1 1 auto; display: flex; gap: 5px; }
.bs-dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  border: 1px solid rgba(224, 178, 100, 0.4);
  transition: background 0.2s ease, border-color 0.2s ease;
}
.bs-dot.on { background: var(--cine-gold); border-color: var(--cine-gold); }

/* 材料开锅就扣了，所以这颗按钮写的是「废掉这锅」，不是「取消」 */
.bench-quit {
  flex: none;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  letter-spacing: 0.1em;
  padding: 3px 10px 4px;
  color: rgba(189, 169, 138, 0.85);
  background: rgba(255, 247, 232, 0.06);
  border: 1px solid rgba(189, 169, 138, 0.28);
  border-radius: 999px;
  cursor: pointer;
  transition: color 0.14s ease, border-color 0.14s ease;
}
.bench-quit:hover { color: var(--cine-red); border-color: rgba(212, 105, 95, 0.6); }

/*
 * 台面：一整个横屏，东西按创作者那张参考图摆——
 * 锅在左（约四分之一宽）、砧板在正中（最大的一块）、刀竖在砧板右边、
 * 草药袋挂在右上角并且**探出台面边界**（overflow 把它裁掉一半，只露一点）。
 * 所有摆位都用百分比，所以台面缩到多宽，比例都和那张图一致。
 */
.bench-stage {
  position: relative;
  overflow: hidden;
  aspect-ratio: 83 / 47;
  /* 矮屏上别把面板撑出滚动条：台面自己缩，但比例一定守住——
     物件全是按百分比摆的，比例一变摆位就全歪了 */
  width: min(100%, calc(58vh * 83 / 47));
  margin: var(--sp-4) auto 0;
  border: 1px solid rgba(224, 178, 100, 0.2);
  border-radius: var(--r);
  /* 参考图那块灰褐台面，压暗一档免得抢药名的亮 */
  background:
    radial-gradient(120% 90% at 20% 0%, rgba(255, 236, 208, 0.1), transparent 55%),
    linear-gradient(168deg, #74645d 0%, #6a5a54 46%, #5b4b46 100%);
  box-shadow: inset 0 1px 0 rgba(255, 240, 220, 0.14), inset 0 -22px 40px rgba(0, 0, 0, 0.3);
}

/* 草药袋：挂在右上角，探出边界，点住拖出一根 */
.pouch {
  position: absolute;
  /* 创作者要的是「袋子超出界面，只露出一点」：往上、往右各探出去一大截，
     被台面的边界裁掉半只——像挂在墙上伸进来，不是摆在桌上。
     裁得比参考图更狠一点，但留 enough 袋口和叶子让人认得出、点得到。 */
  top: -14%; right: -9%;
  width: 26%;
  cursor: grab;
  touch-action: none;
  transition: transform 0.16s cubic-bezier(0.16, 1, 0.3, 1), filter 0.16s ease;
}
.pouch:hover { transform: translateY(-2px) rotate(-3deg); filter: drop-shadow(0 6px 12px rgba(0, 0, 0, 0.6)); }
.pouch:active { cursor: grabbing; transform: scale(0.96); }
.pouch-img { display: block; width: 100%; }
/* 提示那颗「拖」字落在台面里——袋子本身探出去了，字不能跟着探出去 */
.pouch-hint {
  position: absolute;
  left: 2%; bottom: 4%;
  font-family: var(--font-hand);
  font-size: 10px;
  letter-spacing: 0.1em;
  padding: 0 6px;
  color: rgba(20, 13, 8, 0.82);
  background: rgba(224, 178, 100, 0.82);
  border-radius: 999px;
}

/* 砧板：台面正中，最大的一块（中间那格虚线就是「材料虚线」） */
.board {
  position: absolute;
  left: 34.5%; top: 15%;
  width: 43%;
  cursor: pointer;
  touch-action: none;
}
.board-img { display: block; width: 100%; }
.board-slot {
  position: absolute;
  left: 50%; top: 46%;
  transform: translate(-50%, -50%);
  width: 74%; height: 70%;
  display: flex; align-items: flex-end; justify-content: center;
}
/* 空的时候那条虚线才露出来（放上东西就不该再提示「这里」） */
.board-slot::after {
  content: '放草药';
  position: absolute;
  inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.14em;
  color: rgba(244, 233, 212, 0.5);
  border: 1px dashed rgba(244, 233, 212, 0.34);
  border-radius: 4px;
  transition: opacity 0.2s ease;
}
.board:not([data-state="empty"]) .board-slot::after { opacity: 0; }
/*
 * 那句提示按台面摆的那一套换词：熬药切的是药材、做菜切的是食材。
 * 两种说法写在 CSS 里、由 .bench 上的 data-kit 切（挂在 ui/bench.js），
 * 不在 JS 里改 DOM——同一句话的两种措辞不值得多一条渲染路径。
 * ⚠️ ::after 的 content 只能在这里换：改 JS 那边是改不动的。
 */
.bench[data-kit="cook"] .board-slot::after { content: '放上食材'; }
.board-slot .herb {
  position: relative;
  max-width: 100%; max-height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.55));
}
/* 药末那一张：矮矮一摊，视觉上就该和整株不一样 */
.board-slot .herb.pile { transform: translateY(2px) scale(0.92); }
/* 切满了：给砧板图本身描一圈光（矩形描边会框到图的透明边上，难看） */
.board[data-state="done"] .board-img { filter: drop-shadow(0 0 7px rgba(224, 178, 100, 0.6)); }
/* 刀数刻度：让玩家知道还要几下，而不是对着一张不动的图猜 */
.board-dots {
  position: absolute;
  left: 0; right: 0; bottom: -16px;
  display: flex; justify-content: center; gap: 5px;
}
.board-dots i {
  width: 7px; height: 7px;
  border-radius: 50%;
  border: 1px solid rgba(244, 233, 212, 0.45);
}
.board-dots i.on { background: var(--cine-gold); border-color: var(--cine-gold); }

/*
 * 刀：竖着放在砧板右缘上（参考图就是这个位置）。
 * 点一下把它拿起来——拿起来之后原位置只剩一点影子，那把活的刀在 .knife-hand 里跟手走。
 */
.knife {
  position: absolute;
  /* 横向按中心定位：素材的刀是细长一张，按左边缘摆的话，图宽一变就会跑出砧板外 */
  left: 71%; top: 9%;
  height: 58%;
  transform: translateX(-50%);
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  /* 按住刀要能拖着走，不能被浏览器当成滚页面手势抢走 */
  touch-action: none;
  opacity: 0.92;
  transition: opacity 0.18s ease, filter 0.18s ease;
}
.knife-img { display: block; height: 100%; width: auto; }
.knife:hover { filter: drop-shadow(0 6px 14px rgba(0, 0, 0, 0.6)); }
/* 拿起来了：台面上这把就该淡下去，不然看着像有两把刀 */
.knife[data-up="yes"] { opacity: 0.12; filter: none; }

/*
 * 五步常驻，当前那一步亮起来。
 * 文案不随配方变（见 ui/bench.js 的 STEPS），所以这里排成一列固定的字：
 * 没轮到的压暗、轮到的上色——玩家扫一眼就知道手该往哪儿放。
 */
.bench-steps {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 0 var(--sp-3);
}
.bs-line {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: var(--fs-xs);
  letter-spacing: 0.05em;
  line-height: 1.8;
  color: rgba(189, 169, 138, 0.5);
  transition: color 0.2s ease;
}
.bs-line .bs-n {
  flex: none;
  width: 13px;
  font-family: var(--font-hand);
  text-align: right;
  opacity: 0.65;
}
.bs-line.on { color: var(--cine-gold); }
.bs-line.on .bs-n { opacity: 1; }

/*
 * 锅：台面左边那口。做菜与熬药的锅不一样（见 ui/bench.js 的 KITS），
 * 但摆位是同一处——两套玩法共用这张台面。
 *
 * 锅里那潭东西**分两种做法**：
 *   熬药 —— 水画在锅图里：空锅 → 换个色的锅 → 炸了换黑的那张，只换素材图，
 *           不画任何自制的圆圈或色块（创作者的资材里已经有变好色的六张锅了）。
 *   做菜 —— 锅是锅、汤水是汤水**两层**（见下面的 .soup-img）：锅图从头到尾不动，
 *           浮上来那潭按做的哪道菜定（data/assets.js 的 soupForDish）。
 */
.pot { position: absolute; left: 3.5%; top: 10%; width: 27%; }
.pot-img { display: block; width: 100%; }
/* 换图那一下轻轻晃一下，看着像水面被搅动，而不是图片闪了一下 */
.pot-img.ripple { animation: potRipple 0.5s ease; }
@keyframes potRipple {
  0% { transform: scale(1); filter: brightness(1); }
  30% { transform: scale(1.025); filter: brightness(1.28); }
  100% { transform: scale(1); filter: brightness(1); }
}

/*
 * 锅里那潭汤水——做菜那两层里的上面一层（熬药永远用不着它，它一直 hidden）。
 *
 * 下面这两个数是**量出来的**，不是估的（锅图 941×810、汤水图 675×667）：
 *   · 锅口（那圈空口子）占锅图宽的约 69%，质心在 (51.9%, 52.4%)——
 *     锅图不是正俯视，口子略偏右下，直接居中会往左上偏出去一点；
 *   · 汤水的不透明部分几乎填满自己的图（约 98%），所以按 67% 缩下去，
 *     汤面圆约当锅图宽的 66%，比锅口小一圈、正好落进内壁里。
 * 实测：宽 67% + 圆心 (52%,52%) 时，压到锅沿的像素只有 0.66%，
 * 而盖住了锅口面积的 93.7%（再放大到 70% 能盖到 99%，但会压 3% 的锅沿，不值）。
 * 改这两个数之前先跑 .agents/fit-soup2.py 重量一遍——别凭眼睛调。
 *
 * ⚠️ 这里**不许写 display** —— [hidden] 靠浏览器默认的 display:none 生效，
 *    在这条规则里一写 display，就把「空锅时把汤水藏起来」覆盖掉了。
 */
.soup-img {
  position: absolute;
  left: 52%; top: 52%;
  width: 67%;
  transform: translate(-50%, -50%);
  pointer-events: none;
}
/* 汤水层有自己的 ripple：它靠 translate 居中，套锅那条（只写 scale）会把它挪偏 */
.soup-img.ripple { animation: soupRipple 0.5s ease; }
@keyframes soupRipple {
  0% { transform: translate(-50%, -50%) scale(1); filter: brightness(1); }
  30% { transform: translate(-50%, -50%) scale(1.025); filter: brightness(1.28); }
  100% { transform: translate(-50%, -50%) scale(1); filter: brightness(1); }
}
/*
 * 炸了锅（做菜那边没有那口黑锅的素材）：把锅和锅里那潭汤一起压黑。
 * 用滤镜，不是往锅里画东西——创作者的口径是「不许再往锅里画圆圈/色块」。
 * 锅本身也要压：一份料都没下就废掉时，汤水那层是藏着的，只压汤水等于毫无反应。
 */
.soup-img.burnt,
.pot-img.burnt { filter: grayscale(0.8) brightness(0.32) contrast(1.3) sepia(0.35); }

/* 台面底下那一栏：提示 + 点火 + 时机条 */
.bench-ctl {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  margin-top: var(--sp-3);
}

/* 点火：暗橙红 */
.ignite {
  font-family: var(--font-body);
  font-size: var(--fs-m);
  letter-spacing: 0.2em;
  padding: 10px 28px 11px;
  color: #ffe9cc;
  background: linear-gradient(180deg, #8d3b13, #5a220a);
  border: 1px solid rgba(255, 150, 70, 0.45);
  border-radius: var(--r);
  box-shadow: inset 0 1px 0 rgba(255, 210, 160, 0.25), 0 0 18px rgba(190, 80, 20, 0.26);
  cursor: pointer;
  transition: transform 0.14s ease, box-shadow 0.14s ease;
}
.ignite:hover:not(:disabled) { transform: translateY(-1px); box-shadow: inset 0 1px 0 rgba(255, 210, 160, 0.25), 0 0 26px rgba(220, 100, 25, 0.42); }
.ignite:active:not(:disabled) { transform: translateY(1px); }
.ignite:disabled {
  color: rgba(189, 169, 138, 0.45);
  background: #322520;
  border-color: rgba(189, 169, 138, 0.2);
  box-shadow: none;
  cursor: not-allowed;
}

/* 时机条：一条 + 一段橙色命中区 + 来回跑的光标 */
.gauge { width: 100%; margin-top: var(--sp-2); }
.gauge[hidden] { display: none; }
.gauge-bar {
  position: relative;
  height: 28px;
  border-radius: 999px;
  background: rgba(8, 5, 3, 0.8);
  border: 1px solid rgba(224, 178, 100, 0.28);
  cursor: pointer;
  overflow: hidden;
  /* 命中亮一下、跑偏抖一下——手感全在这两帧 */
  transition: border-color 0.12s ease;
}
.gauge-window {
  position: absolute;
  top: 0; bottom: 0;
  /* 宽与左移由 JS 写（配方的 timing + 天赋加成）。宽 0 = 真没有窗口。 */
  background: linear-gradient(180deg, rgba(255, 152, 62, 0.92), rgba(206, 84, 22, 0.9));
  box-shadow: 0 0 16px rgba(255, 140, 50, 0.5), inset 0 0 0 1px rgba(255, 200, 150, 0.3);
}
.gauge-cursor {
  position: absolute;
  top: -2px; bottom: -2px;
  width: 3px;
  margin-left: -1.5px;
  background: #fff6e4;
  box-shadow: 0 0 12px rgba(255, 246, 228, 0.9);
}
.gauge[data-hit="yes"] .gauge-bar { border-color: rgba(168, 220, 150, 0.9); box-shadow: 0 0 22px rgba(140, 200, 130, 0.4); }
.gauge[data-hit="no"] .gauge-bar { animation: gaugeMiss 0.2s ease; border-color: rgba(212, 105, 95, 0.9); }
@keyframes gaugeMiss {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  75% { transform: translateX(5px); }
}
.gauge-note {
  margin-top: 7px;
  font-size: var(--fs-xs);
  letter-spacing: 0.05em;
  line-height: 1.7;
  text-align: center;
  color: var(--cine-dim);
}

/* 拖拽时跟着指针的那一张：ghost，永远不吃点击 */
.bench-drag {
  position: fixed;
  left: 0; top: 0;
  z-index: 82;
  pointer-events: none;
  transform: translate(-50%, -50%);
}
.bench-drag[hidden] { display: none; }
.bench-drag .drag-img {
  display: block;
  width: 66px;
  filter: drop-shadow(0 9px 16px rgba(0, 0, 0, 0.7));
}
/* 没放对：让它掉下去。这一下不能省——没有失败反馈的拖拽，
   玩家分不清自己是放下了还是没放下。 */
.bench-drag.fall { animation: dragFall 0.32s ease-in forwards; }
@keyframes dragFall {
  from { transform: translate(-50%, -50%) rotate(0deg); opacity: 1; }
  to   { transform: translate(-42%, 46px) rotate(16deg); opacity: 0; }
}

/*
 * 手里那把刀：跟着指针走，刀尖在指针上方 KNIFE_TIP 那么多（判定按那个点算，
 * 所以 JS 读的是这个元素的实际高度——窄屏把它调小，判定会自己跟上）。
 * 永远不吃点击，不然它会挡住底下砧板与锅的落点判定。
 */
.knife-hand {
  position: fixed;
  left: 0; top: 0;
  z-index: 84;
  /* 刀跟着手走，手是握在刀身中下部的——所以锚点在 66% 那里，
     JS 判定的刀尖位置按这个比例算（窄屏刀变小，判定自己跟着缩） */
  height: clamp(92px, 15vw, 152px);
  pointer-events: none;
  transform: translate(-50%, -66%) rotate(-3deg);
  filter: drop-shadow(0 12px 20px rgba(0, 0, 0, 0.72));
}
.knife-hand[hidden] { display: none; }
.knife-hand .hand-img { display: block; height: 100%; width: auto; }
/* 落刀那一下的短促下压 */
.knife-hand.chop { animation: knifeChop 0.17s ease; }
/* 角度只偏 3 度：偏太多，刀尖就会横向跑离指针那条线，判定会「看着切到却没算」 */
@keyframes knifeChop {
  0% { transform: translate(-50%, -66%) rotate(-3deg); }
  45% { transform: translate(-50%, -61%) rotate(5deg); }
  100% { transform: translate(-50%, -66%) rotate(-3deg); }
}

/* 被刀碰到那一下：草药抖一抖，才知道这一刀真落在它身上了 */
.board-slot .herb.shake { animation: herbShake 0.2s ease; }
@keyframes herbShake {
  0%, 100% { transform: translate(0, 0) rotate(0deg); }
  30% { transform: translate(-3px, 2px) rotate(-2.5deg); }
  65% { transform: translate(2px, -1px) rotate(1.5deg); }
}

/* 窄屏：台面还是那一整块（比例靠 aspect-ratio 自己缩），只收一收内边距和手里的刀 */
@media (max-width: 560px) {
  .bench { padding: var(--sp-3) var(--sp-3) var(--sp-4); }
  .bench-stage { width: min(100%, calc(52vh * 83 / 47)); }
  .bench-drag .drag-img { width: 54px; }
}

@media (prefers-reduced-motion: reduce) {
  .gauge[data-hit="no"] .gauge-bar { animation: none; }
  .bench-drag.fall { animation-duration: 0.01ms; }
  .pouch, .knife { transition-duration: 0.01ms; }
}

/* ============================================================
   10 · 密林云游商人的摊子
   ------------------------------------------------------------
   一张木板摆两件事：他带来的（4 格 + 下面那一条金色特卖位）、
   他想换走的（4 格）。两栏的格子同一套列定义、等宽等大。
   格子是纸，压在夜色的照片上——所以走墨色那一族，
   跟背包、配方纸同一个身份，不再发明第三种卡片。
   ============================================================ */

/*
 * 买入这一栏是「四格 + 下面一条特卖位」，竖着排。
 *
 * 以前特卖位站在四格右边、和四格抢同一行的宽度（它 116px + 间距 12px），
 * 于是买入的格子比卖出窄一截——两栏并排看就是左边缩进去一块。
 * 挪到下面之后，两个 .mkt-grid 都是满宽、同一套列定义，格子必然等大。
 */
.mkt-sell { display: block; }
.mkt-grid {
  display: grid;
  /* 四格两列：一行三格会让窄屏的字挤成竖排 */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-2);
  width: 100%;
}

/* 一格：图 / 名 / 价 / 步进器 / 成交，从上到下 */
.mkt-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: var(--sp-2) var(--sp-2) var(--sp-3);
  text-align: center;
  font-family: var(--font-body);
  color: var(--ink);
  background:
    radial-gradient(80% 60% at 12% 4%, rgba(255, 255, 255, 0.5), transparent 62%),
    linear-gradient(180deg, #fbf4e4, #eddfc0);
  border: 1px solid rgba(64, 48, 31, 0.44);
  border-radius: var(--r);
  box-shadow: 0 2px 0 rgba(50, 37, 24, 0.34), 0 8px 18px rgba(0, 0, 0, 0.34);
}
/* 手上没货的那一格：纸压暗一档，一眼看出「这格现在做不了事」 */
.mkt-cell.short { opacity: 0.72; }

.mkt-item { display: flex; flex-direction: column; align-items: center; gap: 2px; }
/*
 * 物品图那个小框。
 *
 * 框缩到 38px：原先 46px 配这一格的留白，图看起来比字还抢眼，
 * 这几样东西是「摆着的货」，名字和价钱才是要读的。
 *
 * `overflow: hidden` 是**硬保证**，不是装饰：图哪怕因为什么原因比框大
 * （素材本身留白多、emoji 字形比字号高、以后换了更大的图），
 * 也只会在框里被裁掉，绝不会顶到卡片外面去。
 * 靠 object-fit 一个属性不够——那只在「图确实小于框」时才管用。
 */
.mkt-art {
  width: 38px;
  height: 38px;
  flex: none;
  display: grid;
  place-items: center;
  overflow: hidden;
  font-size: 24px;
  line-height: 1;
}
.mkt-art-img {
  display: block;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}
.mkt-name {
  font-size: var(--fs-s);
  font-weight: 700;
  letter-spacing: 0.03em;
  line-height: 1.35;
  color: var(--ink);
}
.mkt-price {
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}
.mkt-price.dim-tx { opacity: 0.6; }
.mkt-have { font-size: var(--fs-xs); color: var(--ink-soft); }

/*
 * 格子里那行小字（对角巷那几家店的规矩：「今天还剩 2 个」「4 天熟 → 缬草根×2」）。
 *
 * 商人那两栏没有这一行——他的货是随机的，没有「还剩几个」可说。
 * 收在 .mkt-have 下面一号、行高紧一点：它是注释，不是主角。
 */
.mkt-note {
  font-size: var(--fs-xs);
  line-height: 1.5;
  color: var(--ink-soft);
  opacity: 0.85;
}
.mkt-only {
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  color: var(--red);
}
.mkt-body { display: flex; flex-direction: column; align-items: center; gap: 2px; }

/*
 * 特卖位：横着躺在四格下面那一条，金色。
 * 没刷出来（那 10% 没中）就整条灰掉——创作者要的就是「看得见这周没有」，
 * 而不是少摆一格让人以为界面坏了。
 *
 * 横排（牌子 / 图与名 / 价钱与按钮靠右）是因为它现在占满一整行宽，
 * 再按竖着居中去排，中间会空出一大片，看着像漏了东西。
 */
.mkt-special {
  flex: none;
  width: 100%;
  margin-top: var(--sp-2);
  flex-direction: row;
  align-items: center;
  justify-content: flex-start;
  gap: var(--sp-3);
  text-align: left;
  padding: var(--sp-2) var(--sp-3);
  border-color: rgba(224, 178, 100, 0.78);
  border-width: 2px;
  background:
    radial-gradient(90% 60% at 20% 0%, rgba(255, 248, 224, 0.75), transparent 60%),
    linear-gradient(180deg, #f6e3b4, #e2c380);
  box-shadow: 0 2px 0 rgba(90, 62, 20, 0.4), 0 0 18px rgba(224, 178, 100, 0.34),
              inset 0 1px 0 rgba(255, 250, 235, 0.6);
}
/* 价钱那一组顶到最右边：这一条从左到右读下来是「特卖 → 是什么 → 多少钱、买走」 */
.mkt-special .mkt-body { margin-left: auto; align-items: flex-end; }
.mkt-special .mkt-flag { flex: none; }
.mkt-flag {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.18em;
  padding: 0 8px 1px;
  color: #4a3410;
  background: rgba(255, 246, 220, 0.7);
  border-radius: 999px;
}
/* 灰态：金边褪成灰边，光晕撤掉，字压暗——但格子还站在原地 */
.mkt-special.dim {
  border-color: rgba(64, 48, 31, 0.3);
  background: linear-gradient(180deg, #e8e0d0, #d8cfbd);
  box-shadow: 0 2px 0 rgba(50, 37, 24, 0.24);
  filter: saturate(0.5);
}
.mkt-special.dim .mkt-flag { color: var(--ink-soft); background: rgba(255, 255, 255, 0.4); }
.mkt-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: var(--sp-3) 0;
}
.mkt-empty-mark { font-size: 24px; line-height: 1; color: rgba(64, 48, 31, 0.34); }
.mkt-empty-tx { font-size: var(--fs-xs); letter-spacing: 0.04em; color: var(--ink-soft); }

/* 快速加减：▼▼ −10 / ▼ −1 / 数 / ▲ +1 / ▲▲ +10 */
.mkt-step {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  margin-top: 2px;
}
.ms-b {
  width: 22px;
  height: 22px;
  padding: 0;
  font-family: var(--font-body);
  font-size: 9px;
  line-height: 1;
  color: var(--ink-soft);
  background: rgba(255, 250, 238, 0.66);
  border: 1px solid rgba(64, 48, 31, 0.32);
  border-radius: var(--r);
  cursor: pointer;
  transition: color 0.12s ease, background 0.12s ease, transform 0.12s ease;
}
.ms-b:hover { color: var(--red); background: var(--bright); }
.ms-b:active { transform: scale(0.9); }
.mkt-n {
  min-width: 34px;
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* 成交那颗。和别处的按钮区分开：小、贴格子里，不是一整行 */
.mkt-go {
  margin-top: 3px;
  padding: 3px 12px 4px;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  letter-spacing: 0.1em;
  color: #f7ecd8;
  background: linear-gradient(180deg, #6f5a3a, #52412a);
  border: 1px solid rgba(64, 48, 31, 0.5);
  border-radius: var(--r);
  cursor: pointer;
  transition: transform 0.12s ease, filter 0.12s ease;
}
.mkt-go:hover:not(:disabled) { transform: translateY(-1px); filter: brightness(1.14); }
.mkt-go:active:not(:disabled) { transform: translateY(1px); }
.mkt-go:disabled {
  color: rgba(64, 48, 31, 0.45);
  background: rgba(255, 250, 238, 0.5);
  cursor: not-allowed;
}

/* 他那句话。压在照片上，走亮字那一族 */
.mkt-line {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-m);
  line-height: 1.8;
  color: var(--cine-text);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.95), 0 2px 16px rgba(0, 0, 0, 0.7);
}
.mkt-want { margin-bottom: var(--sp-3); }

/* 底下那一栏：那颗【刷新货单】要小，不该和成交按钮抢分量 */
.mkt-foot {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
}
.mkt-foot .btn { flex: none; width: auto; font-size: var(--fs-s); padding: 6px 14px 7px; }
/* ── 云游商人：林边隐秘摊位（只作用于 .merchant-scene 所在页面） ── */
.view:has(.merchant-scene) {
  position: relative;
  isolation: isolate;
}
.view:has(.merchant-scene)::before {
  content: '';
  position: absolute;
  inset: -18px -24px;
  z-index: -2;
  pointer-events: none;
  background:
    radial-gradient(32% 24% at 50% 18%, rgba(164, 131, 70, 0.16), transparent 72%),
    radial-gradient(48% 42% at 18% 70%, rgba(37, 83, 63, 0.18), transparent 72%),
    radial-gradient(44% 40% at 84% 78%, rgba(50, 31, 82, 0.22), transparent 74%),
    linear-gradient(180deg, rgba(7, 12, 13, 0.62), rgba(12, 8, 17, 0.74));
  box-shadow: inset 0 0 90px rgba(0, 0, 0, 0.66);
}
.view:has(.merchant-scene)::after {
  content: '';
  position: absolute;
  inset: -10px -18px;
  z-index: -1;
  pointer-events: none;
  opacity: 0.5;
  background:
    radial-gradient(circle at 12% 36%, rgba(190, 213, 172, 0.7) 0 1px, transparent 2px),
    radial-gradient(circle at 86% 28%, rgba(210, 183, 116, 0.68) 0 1px, transparent 2px),
    radial-gradient(circle at 74% 82%, rgba(147, 179, 201, 0.56) 0 1px, transparent 2px);
  background-size: 97px 131px, 149px 173px, 181px 157px;
  animation: merchantDust 12s linear infinite alternate;
}
@keyframes merchantDust { from { translate: 0 4px; opacity: 0.32; } to { translate: 0 -7px; opacity: 0.58; } }

.view:has(.merchant-scene) .merchant-scene {
  position: relative;
  padding: 10px 16px 14px;
  margin: 0 -6px var(--sp-3);
  text-align: center;
  background:
    linear-gradient(90deg, transparent, rgba(18, 13, 20, 0.78) 13%, rgba(28, 19, 23, 0.9) 50%, rgba(18, 13, 20, 0.78) 87%, transparent),
    radial-gradient(60% 100% at 50% 0%, rgba(187, 150, 76, 0.15), transparent 76%);
  border-top: 1px solid rgba(202, 166, 91, 0.26);
  border-bottom: 1px solid rgba(202, 166, 91, 0.32);
  box-shadow: 0 12px 22px rgba(0, 0, 0, 0.34), inset 0 1px 0 rgba(255, 238, 180, 0.06);
}
.view:has(.merchant-scene) .merchant-scene::before,
.view:has(.merchant-scene) .merchant-scene::after {
  position: absolute;
  top: 50%;
  width: 10px;
  height: 10px;
  content: '✦';
  color: rgba(219, 187, 111, 0.76);
  font-size: 10px;
  line-height: 10px;
  text-shadow: 0 0 10px rgba(219, 187, 111, 0.62);
  translate: 0 -50%;
}
.view:has(.merchant-scene) .merchant-scene::before { left: 4px; }
.view:has(.merchant-scene) .merchant-scene::after { right: 4px; }
.view:has(.merchant-scene) .merchant-scene .label {
  color: rgba(190, 165, 120, 0.84);
  letter-spacing: 0.2em;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9);
}
.view:has(.merchant-scene) .merchant-scene .name {
  color: #f3dfb2;
  letter-spacing: 0.16em;
  text-shadow: 0 0 16px rgba(207, 157, 72, 0.34), 0 2px 3px rgba(0, 0, 0, 0.88);
}
.view:has(.merchant-scene) .merchant-scene .name::after {
  width: min(72%, 280px);
  height: 1px;
  margin-top: 8px;
  background: linear-gradient(90deg, transparent, rgba(213, 176, 91, 0.85) 18%, rgba(213, 176, 91, 0.85) 82%, transparent);
  box-shadow: 0 0 8px rgba(213, 176, 91, 0.26);
  opacity: 1;
}
.view:has(.merchant-scene) .merchant-scene .note {
  max-width: 34rem;
  margin: 7px auto 0;
  color: rgba(224, 211, 180, 0.76);
  font-size: var(--fs-xs);
  line-height: 1.75;
}
.view:has(.merchant-scene) > .chapter {
  position: relative;
  margin-top: var(--sp-4);
  color: #d8bd83;
  letter-spacing: 0.12em;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9);
}
.view:has(.merchant-scene) > .chapter::before {
  content: '⌁';
  margin-right: 7px;
  color: rgba(127, 170, 143, 0.8);
}
.view:has(.merchant-scene) > .chapter::after {
  content: '';
  display: block;
  height: 1px;
  margin-top: 5px;
  background: linear-gradient(90deg, rgba(203, 164, 83, 0.5), transparent 82%);
}
.view:has(.merchant-scene) .mkt-sell,
.view:has(.merchant-scene) .mkt-want {
  position: relative;
  padding: 10px;
  border: 1px solid rgba(185, 148, 81, 0.26);
  border-radius: 8px;
  background:
    repeating-linear-gradient(90deg, rgba(255, 231, 170, 0.018) 0 1px, transparent 1px 13px),
    linear-gradient(180deg, rgba(22, 17, 19, 0.72), rgba(10, 9, 14, 0.72));
  box-shadow: inset 0 0 26px rgba(0, 0, 0, 0.52), 0 10px 24px rgba(0, 0, 0, 0.24);
}
.view:has(.merchant-scene) .mkt-cell {
  color: #e8dbc0;
  background:
    radial-gradient(90% 40% at 50% 0%, rgba(184, 147, 79, 0.13), transparent 72%),
    linear-gradient(180deg, rgba(45, 35, 31, 0.92), rgba(18, 15, 20, 0.96));
  border-color: rgba(185, 148, 81, 0.38);
  border-radius: 5px;
  box-shadow: inset 0 1px 0 rgba(255, 230, 177, 0.08), 0 5px 13px rgba(0, 0, 0, 0.44);
  transition: transform 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
}
.view:has(.merchant-scene) .mkt-cell:hover {
  transform: translateY(-3px);
  border-color: rgba(224, 190, 116, 0.78);
  box-shadow: inset 0 1px 0 rgba(255, 237, 190, 0.14), 0 9px 18px rgba(0, 0, 0, 0.52), 0 0 14px rgba(172, 130, 62, 0.16);
}
.view:has(.merchant-scene) .mkt-art {
  width: 54px;
  height: 54px;
  border: 1px solid rgba(198, 163, 93, 0.3);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(165, 133, 79, 0.18), rgba(6, 7, 11, 0.56) 72%);
  box-shadow: inset 0 0 13px rgba(0, 0, 0, 0.52), 0 0 12px rgba(143, 114, 61, 0.12);
}
.view:has(.merchant-scene) .mkt-name { color: #f1dfb7; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9); }
.view:has(.merchant-scene) .mkt-price { color: #d7bb7e; }
.view:has(.merchant-scene) .mkt-have { color: rgba(203, 193, 169, 0.7); }
.view:has(.merchant-scene) .mkt-step { margin-top: 5px; }
.view:has(.merchant-scene) .ms-b {
  color: #cbb47e;
  background: rgba(12, 10, 14, 0.7);
  border-color: rgba(183, 145, 74, 0.42);
}
.view:has(.merchant-scene) .ms-b:hover { color: #f3ddb0; background: rgba(105, 73, 35, 0.54); }
.view:has(.merchant-scene) .mkt-n { color: #f1dfb7; }
.view:has(.merchant-scene) .mkt-go {
  color: #f2dfb8;
  background: linear-gradient(180deg, rgba(90, 63, 35, 0.92), rgba(35, 26, 24, 0.98));
  border-color: rgba(213, 174, 94, 0.52);
  box-shadow: inset 0 1px 0 rgba(255, 233, 181, 0.12), 0 2px 7px rgba(0, 0, 0, 0.42);
}
.view:has(.merchant-scene) .mkt-go:hover:not(:disabled) { filter: brightness(1.2); box-shadow: 0 0 11px rgba(205, 164, 83, 0.24); }
.view:has(.merchant-scene) .mkt-special {
  border-color: rgba(220, 176, 86, 0.72);
  background:
    radial-gradient(50% 100% at 12% 50%, rgba(190, 145, 57, 0.22), transparent 72%),
    linear-gradient(180deg, rgba(69, 48, 27, 0.96), rgba(29, 21, 20, 0.98));
  box-shadow: inset 0 0 18px rgba(206, 160, 70, 0.12), 0 0 18px rgba(184, 132, 45, 0.16);
}
.view:has(.merchant-scene) .mkt-special .mkt-flag {
  color: #f5dfaa;
  background: rgba(105, 72, 31, 0.78);
  border: 1px solid rgba(226, 183, 96, 0.54);
}
.view:has(.merchant-scene) .mkt-line {
  color: rgba(224, 211, 180, 0.84);
  padding: 8px 12px;
  border-left: 2px solid rgba(185, 148, 81, 0.52);
  background: linear-gradient(90deg, rgba(34, 25, 26, 0.62), transparent);
}
.view:has(.merchant-scene) .mkt-foot {
  margin-top: var(--sp-4);
  padding-top: var(--sp-3);
  border-top: 1px solid rgba(185, 148, 81, 0.26);
}
.view:has(.merchant-scene) .mkt-foot .btn {
  color: #f0d8a5;
  background: rgba(22, 16, 20, 0.82);
  border-color: rgba(201, 163, 87, 0.58);
  box-shadow: inset 0 1px 0 rgba(255, 233, 181, 0.1), 0 3px 10px rgba(0, 0, 0, 0.42);
}
.view:has(.merchant-scene) .mkt-week { color: rgba(202, 188, 157, 0.72); }
@media (max-width: 560px) {
  .view:has(.merchant-scene) .merchant-scene { margin-left: -2px; margin-right: -2px; padding-left: 10px; padding-right: 10px; }
  .view:has(.merchant-scene) .mkt-sell,
  .view:has(.merchant-scene) .mkt-want { padding: 7px; gap: 7px; }
}
@media (prefers-reduced-motion: reduce) {
  .view:has(.merchant-scene)::after { animation: none; }
  .view:has(.merchant-scene) .mkt-cell { transition: none; }
}

.mkt-week {
  flex: 1 1 180px;
  font-size: var(--fs-xs);
  line-height: 1.7;
  color: var(--cine-dim);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
}

/* ── 海格小摊：温暖、粗粝、带一点神奇生物气息 ── */
.view:has(.stall-scene) { position: relative; isolation: isolate; }
.view:has(.stall-scene)::before {
  content: ''; position: absolute; inset: -18px -24px; z-index: -2; pointer-events: none;
  background: radial-gradient(38% 28% at 50% 16%, rgba(224, 171, 82, 0.22), transparent 70%), radial-gradient(42% 45% at 16% 70%, rgba(101, 137, 76, 0.2), transparent 72%), linear-gradient(180deg, rgba(61, 38, 23, 0.55), rgba(20, 14, 11, 0.78));
  box-shadow: inset 0 0 72px rgba(0, 0, 0, 0.6);
}
.view:has(.stall-scene) .stall-scene { position: relative; margin: 0 -6px var(--sp-3); padding: 10px 16px 14px; text-align: center; background: linear-gradient(180deg, rgba(63, 43, 27, 0.74), rgba(28, 20, 15, 0.7)); border-top: 1px solid rgba(224, 178, 100, 0.42); border-bottom: 1px solid rgba(224, 178, 100, 0.28); box-shadow: 0 12px 22px rgba(0,0,0,.34), inset 0 1px 0 rgba(255,240,190,.08); }
.view:has(.stall-scene) .stall-scene .label { color: rgba(223, 192, 137, 0.86); letter-spacing: .18em; }
.view:has(.stall-scene) .stall-scene .name { color: #f5d99e; text-shadow: 0 0 15px rgba(231, 171, 73, .3), 0 2px 3px rgba(0,0,0,.8); }
.view:has(.stall-scene) .stall-scene .name::after { width: min(72%, 250px); height: 2px; margin-top: 8px; background: linear-gradient(90deg, transparent, #bd8540 18%, #e8c078 50%, #bd8540 82%, transparent); opacity: .9; }
.view:has(.stall-scene) > .chapter { color: #e2ba74; text-shadow: 0 1px 3px rgba(0,0,0,.9); }
.view:has(.stall-scene) > .chapter::before { content: '♣'; color: #91a86f; margin-right: 7px; }
.view:has(.stall-scene) .mkt-grid { padding: 9px; border: 1px solid rgba(180, 128, 65, .28); border-radius: 8px; background: repeating-linear-gradient(90deg, rgba(255,229,168,.018) 0 1px, transparent 1px 12px), linear-gradient(180deg, rgba(55,36,22,.6), rgba(24,18,14,.72)); box-shadow: inset 0 0 24px rgba(0,0,0,.4); }
.view:has(.stall-scene) .mkt-cell { background: radial-gradient(80% 38% at 50% 0%, rgba(208,160,73,.16), transparent 72%), linear-gradient(180deg, #5a422b, #2a2019); border-color: rgba(221, 176, 93, .42); border-radius: 6px; color: #f2dfbc; box-shadow: inset 0 1px 0 rgba(255,237,183,.1), 0 6px 14px rgba(0,0,0,.42); transition: transform .16s ease, border-color .16s ease, box-shadow .16s ease; }
.view:has(.stall-scene) .mkt-cell:hover { transform: translateY(-3px) rotate(-.3deg); border-color: rgba(246, 208, 126, .82); box-shadow: 0 10px 18px rgba(0,0,0,.5), 0 0 13px rgba(212,163,77,.18); }
.view:has(.stall-scene) .mkt-art { width: 56px; height: 56px; border-radius: 14px 18px 12px 17px; background: radial-gradient(circle, rgba(228,190,112,.18), rgba(21,16,12,.5) 72%); border: 1px solid rgba(235,194,108,.34); box-shadow: inset 0 0 12px rgba(0,0,0,.5), 0 0 10px rgba(213,164,75,.12); }
.view:has(.stall-scene) .mkt-name { color: #ffe7b1; text-shadow: 0 1px 3px rgba(0,0,0,.9); }
.view:has(.stall-scene) .mkt-price { color: #f1c979; }
.view:has(.stall-scene) .mkt-have, .view:has(.stall-scene) .mkt-note { color: rgba(229,211,172,.72); }
.view:has(.stall-scene) .ms-b { color: #e8c984; background: rgba(24,17,12,.66); border-color: rgba(220,170,81,.44); }
.view:has(.stall-scene) .ms-b:hover { background: rgba(131,82,37,.58); color: #fff0c8; }
.view:has(.stall-scene) .mkt-n { color: #ffe6ab; }
.view:has(.stall-scene) .mkt-go { color: #ffecc1; background: linear-gradient(180deg, #8a6035, #4d3220); border-color: rgba(237,195,111,.58); box-shadow: inset 0 1px 0 rgba(255,242,199,.14), 0 3px 8px rgba(0,0,0,.42); }
.view:has(.stall-scene) .mkt-go:hover:not(:disabled) { filter: brightness(1.16); box-shadow: 0 0 12px rgba(233,181,83,.25); }
.view:has(.stall-scene) .mkt-line { color: rgba(238,218,179,.84); padding: 7px 11px; border-left: 2px solid rgba(205,157,76,.58); background: linear-gradient(90deg, rgba(77,49,26,.42), transparent); }
.view:has(.stall-scene) .mkt-cell.short { filter: saturate(.55); opacity: .58; }
.view:has(.stall-scene) .mkt-week { color: rgba(225, 204, 162, .74); }
@media (max-width: 560px) { .view:has(.stall-scene) .stall-scene { margin-left: -2px; margin-right: -2px; padding-left: 10px; padding-right: 10px; } .view:has(.stall-scene) .mkt-grid { padding: 6px; gap: 7px; } }
@media (prefers-reduced-motion: reduce) { .view:has(.stall-scene) .mkt-cell { transition: none; } }

/* 云游商人的主题定义在上一段；这里保留公共货单布局。 */
   ------------------------------------------------------------
   创作者定的长相：**黑半透明 + 紫边**。结构一点没改（还是 .mkt-cell 那一副，
   零件借 ui/shopgrid.js 的 cellHead），只盖颜色——四家店同一副骨架这件事不该破。
   ============================================================ */

/* 巷子里那块牌子（别处的牌子是棕木，只有他是这一身） */
.room-btn.blackmkt {
  background:
    radial-gradient(70% 60% at 16% 6%, rgba(168, 132, 255, 0.16), transparent 62%),
    linear-gradient(180deg, rgba(22, 17, 38, 0.86), rgba(9, 7, 18, 0.9));
  border-color: rgba(158, 120, 232, 0.52);
  color: #e6dcff;
}
.room-btn.blackmkt:hover {
  background: linear-gradient(180deg, rgba(38, 28, 66, 0.92), rgba(16, 12, 30, 0.94));
  border-color: rgba(190, 158, 255, 0.78);
}
.room-btn.blackmkt .rb-zh { color: #f0e8ff; }
.room-btn.blackmkt .rb-sub { color: #b7a2e6; }

/* 摊子那一屏 */
.dark-shop { margin-top: var(--sp-3); }
.dark-shop .mkt-cell {
  background:
    radial-gradient(70% 55% at 14% 5%, rgba(168, 132, 255, 0.14), transparent 62%),
    linear-gradient(180deg, rgba(22, 17, 38, 0.84), rgba(9, 7, 18, 0.88));
  border-color: rgba(158, 120, 232, 0.5);
  color: #e6dcff;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(190, 160, 255, 0.12);
}
.dark-shop .mkt-name { color: #f0e8ff; }
.dark-shop .mkt-price { color: #cbb6f5; }
.dark-shop .mkt-only { color: #b7a2e6; }
/* 卖掉了：留在原位、压暗一档（位置不变，玩家才知道这里本来有过什么） */
.dark-shop .mkt-cell.sold { opacity: 0.5; }
.dark-shop .mkt-go {
  color: #f2e9ff;
  background: linear-gradient(180deg, #4a3577, #2f2150);
  border-color: rgba(158, 120, 232, 0.55);
}
.dark-shop .mkt-go:disabled {
  color: rgba(230, 220, 255, 0.4);
  background: rgba(30, 24, 48, 0.6);
}
/* 底下那一句：这一周的货就三样，下周他自己换 */
.dark-note {
  margin-top: var(--sp-2);
  font-size: var(--fs-xs);
  line-height: 1.7;
  letter-spacing: 0.04em;
  text-align: center;
  color: #a896cf;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
}
/*
 * 斗篷内侧那几张配方上面，他压低嗓子说的那一句（原话在 data/dark.js）。
 * 比上面那行说明亮一档、也大一号——它是台词，不是注脚。
 */
.dark-shop .dark-lead {
  margin: var(--sp-4) 0 var(--sp-2);
  font-size: var(--fs-m);
  line-height: 1.8;
  color: #e6dcff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
}

/* ============================================================
   16c · 古灵阁：白色 + 香槟金（妖精的贵族银行）
   ------------------------------------------------------------
   创作者定的调子：高贵 / 典雅 / 纯净，白色大理石 + 细金边 + 柔和暖影。
   刻意不要的：现代手机银行、科技金融、赛博朋克、廉价金渐变、发光特效。
   所以这里没有 box-shadow 发光、没有动画，装饰只有四样：
   顶上一道细金线、一枚金徽、两个对角金括号、一层很淡的大理石纹。
   钱本身还是那一套（纳特一个单位，见 data/currency.js）——这里只有皮。
   ============================================================ */

.gb-card {
  position: relative;
  width: min(94vw, 26rem);
  margin: var(--sp-3) auto 0;
  padding: var(--sp-4) var(--sp-4) var(--sp-4);
  /* 白大理石：两层很淡的斜纹 + 一层象牙白底，最后才是纯色底（顺序不能反） */
  background:
    repeating-linear-gradient(112deg, rgba(255, 255, 255, 0.5) 0 2px, transparent 2px 9px),
    radial-gradient(120% 70% at 50% -10%, rgba(216, 189, 133, 0.22), transparent 60%),
    linear-gradient(180deg, #fdfaf3 0%, #f6efe1 100%);
  border: 1px solid rgba(184, 147, 63, 0.55);
  border-radius: var(--r);
  color: #2b2620;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.9) inset,
    0 14px 34px rgba(60, 44, 16, 0.22),
    0 2px 0 rgba(184, 147, 63, 0.28);
}
/* 两个对角的金括号（左上 / 右下）：细、短，只压住角，不描整条边 */
.gb-card::before,
.gb-card::after {
  content: '';
  position: absolute;
  width: 16px;
  height: 16px;
  border: 1px solid rgba(184, 147, 63, 0.8);
  pointer-events: none;
}
.gb-card::before { top: 5px; left: 5px; border-right: 0; border-bottom: 0; }
.gb-card::after { bottom: 5px; right: 5px; border-left: 0; border-top: 0; }

/* 顶上一道细金线（中间亮、两头没有） */
.gb-rule {
  height: 1px;
  margin-bottom: var(--sp-3);
  background: linear-gradient(90deg, transparent, rgba(184, 147, 63, 0.85) 20%, rgba(184, 147, 63, 0.85) 80%, transparent);
}

.gb-top {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
/* 金徽：一圈金边 + 一圈细金环，中间一个衬线 G（不是印章动画） */
.gb-seal {
  flex: none;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  font-family: var(--font-body);
  font-size: 17px;
  letter-spacing: 0;
  color: #8a6a22;
  background: radial-gradient(70% 70% at 35% 25%, #fffaf0, #e8d7ae);
  border: 1px solid rgba(184, 147, 63, 0.85);
  border-radius: 50%;
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.7), inset 0 -2px 4px rgba(138, 106, 34, 0.22);
}
.gb-title { flex: 1; min-width: 0; }
.gb-zh {
  font-size: var(--fs-l);
  letter-spacing: 0.3em;
  line-height: 1.25;
  color: #241f18;
}
.gb-en {
  font-size: 9px;
  letter-spacing: 0.3em;
  color: rgba(138, 106, 34, 0.9);
}
.gb-sub {
  margin-top: 2px;
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  color: #5c5346;
}
.gb-no {
  flex: none;
  align-self: flex-start;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  color: rgba(138, 106, 34, 0.95);
}

.gb-cap,
.gb-log-cap {
  margin-top: var(--sp-4);
  font-size: var(--fs-xs);
  letter-spacing: 0.24em;
  color: #6c6153;
}
.gb-log-cap { margin-bottom: var(--sp-2); }

/* 余额那一行：大号衬线数字 + 小一号档位词 */
.gb-balance {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 2px 10px;
  padding: 2px 0 0;
}
.gb-b-part { display: inline-flex; align-items: baseline; gap: 3px; }
.gb-b-n {
  font-size: clamp(28px, 8.4vw, 42px);
  line-height: 1.05;
  letter-spacing: 0.02em;
  color: #1f1a13;
  font-variant-numeric: lining-nums tabular-nums;
}
.gb-b-u { font-size: var(--fs-xs); letter-spacing: 0.14em; color: #6c6153; }

.gb-pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-2);
  margin-top: var(--sp-3);
}
.gb-cell {
  padding: var(--sp-2) var(--sp-2) var(--sp-2) var(--sp-3);
  border-left: 1px solid rgba(184, 147, 63, 0.5);
}
.gb-k { font-size: var(--fs-xs); letter-spacing: 0.1em; color: #6c6153; }
.gb-v {
  margin-top: 1px;
  font-size: var(--fs-m);
  letter-spacing: 0.02em;
  color: #241f18;
  font-variant-numeric: tabular-nums;
}
.gb-v.gold { color: #8a6a22; }

.gb-acts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-2);
  margin-top: var(--sp-4);
}

/*
 * 两颗主按钮：象牙白 + 金边 + 深字。hover 只是边框亮一档、底变暖、一点点金色光泽
 *
 * ⚠️ 后面那两条选择器不是凑数的：**凡穿古灵阁这身皮的地方，里面每一颗键都是这一颗**。
 * 大厅那张卡（.gb-card）与每一张 .gringotts 纸（租赁 / 开店 / 登记 / 今日收摊…）里的按钮
 * 原来还是通用那张米黄纸 + 左边一道红条，摆在这块大理石上就是两块白斑
 * （创作者 2026-10-19 为这件事点到第四次，原话「不允许在一个深绿色的样式里莫名其妙出现浅色按钮」）。
 * 挂在皮肤上而不是挂在那几张纸的调用处 = 以后新加的古灵阁纸自动穿这身。
 */
.btn.gb-btn,
.gb-card .btn,
.sheet.gringotts .btn {
  width: auto;
  padding: 9px 12px 10px;
  font-family: var(--font-body);
  font-size: var(--fs-s);
  letter-spacing: 0.18em;
  color: #2b2620;
  background: linear-gradient(180deg, #fffdf7, #f0e6d1);
  border: 1px solid rgba(184, 147, 63, 0.75);
  border-radius: var(--r);
  box-shadow: 0 2px 0 rgba(138, 106, 34, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.95);
  transition: background 0.16s ease, border-color 0.16s ease, transform 0.12s ease;
}
.btn.gb-btn:hover,
.gb-card .btn:hover,
.sheet.gringotts .btn:hover {
  background: linear-gradient(180deg, #fffef9, #e9dcbf);
  border-color: rgba(206, 168, 82, 0.95);
  transform: translateY(-1px);
}
.btn.gb-btn:active,
.gb-card .btn:active,
.sheet.gringotts .btn:active { transform: translateY(1px); box-shadow: 0 1px 0 rgba(138, 106, 34, 0.2); }
.btn.gb-btn:disabled,
.gb-card .btn:disabled,
.sheet.gringotts .btn:disabled { opacity: 0.5; transform: none; box-shadow: 0 2px 0 rgba(138, 106, 34, 0.14), inset 0 1px 0 rgba(255, 255, 255, 0.9); }

.gb-dates {
  margin-top: var(--sp-4);
  padding-top: var(--sp-2);
  border-top: 1px solid rgba(184, 147, 63, 0.35);
  font-size: var(--fs-xs);
  line-height: 1.9;
  letter-spacing: 0.06em;
  color: #5c5346;
}

/* 流水：整块自己滚（最多存 40 条，见 data/bank.js） */
.gb-log {
  max-height: 210px;
  overflow-y: auto;
  padding-right: 2px;
}
.gb-day {
  margin: var(--sp-2) 0 2px;
  font-size: 9px;
  letter-spacing: 0.22em;
  color: rgba(138, 106, 34, 0.95);
}
.gb-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: 4px 0;
  border-bottom: 1px solid rgba(184, 147, 63, 0.18);
  font-size: var(--fs-s);
}
.gb-kind { letter-spacing: 0.12em; color: #4a4238; }
.gb-kind.interest { color: #8a6a22; }
.gb-amt { font-variant-numeric: tabular-nums; }
.gb-amt.in { color: #3f6b45; }
.gb-amt.out { color: #8a3b34; }
.gb-empty {
  padding: var(--sp-2) 0;
  font-size: var(--fs-xs);
  line-height: 1.7;
  color: #6c6153;
}

/* ── 存 / 取那两张面板里的零件（压在羊皮纸那张纸上） ── */
.gb-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: 3px 0;
  font-size: var(--fs-s);
  color: var(--ink-soft);
}
.gb-line .gb-v { color: var(--ink); }
.gb-field {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: var(--sp-2) 0;
}
.gb-input {
  flex: 1;
  min-width: 0;
  padding: 7px 10px;
  font-family: var(--font-body);
  font-size: var(--fs-l);
  letter-spacing: 0.06em;
  color: #241f18;
  background: #fffdf7;
  border: 1px solid rgba(184, 147, 63, 0.7);
  border-radius: var(--r);
  box-shadow: inset 0 1px 3px rgba(120, 95, 45, 0.16);
}
.gb-input:focus { outline: none; border-color: rgba(206, 168, 82, 1); }
.gb-unit { font-size: var(--fs-xs); letter-spacing: 0.14em; color: var(--ink-soft); }
.gb-all {
  padding: 3px 10px 4px;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  letter-spacing: 0.1em;
  color: #2b2620;
  background: linear-gradient(180deg, #fffdf7, #f0e6d1);
  border: 1px solid rgba(184, 147, 63, 0.6);
  border-radius: var(--r);
  cursor: pointer;
}
.gb-all:hover { border-color: rgba(206, 168, 82, 0.95); }
.gb-live {
  margin-top: var(--sp-2);
  font-size: var(--fs-xs);
  line-height: 1.7;
  letter-spacing: 0.04em;
  color: #8a6a22;
}

/*
 * 存 / 取那两张面板：也穿古灵阁这身（openSheet 的 skin: 'gringotts'，见 ui/bank.js）。
 * 金库那一屏是白的，点开的两张纸却是羊皮卷 + 红描边——同一件事换了一间屋子，
 * 所以这一身从卡片上抄下来：同一层大理石纹、同一条金边、同一族字色。
 * 顶栏那道**红色**的粗下划线换成金细线（红是全游戏「危险 / 花钱」的颜色，
 * 存钱不是）。那枚叉本来就是一块米色小方牌，压在象牙白上正合适，不动它。
 */
.sheet.gringotts {
  background-image:
    repeating-linear-gradient(112deg, rgba(255, 255, 255, 0.5) 0 2px, transparent 2px 9px),
    radial-gradient(120% 70% at 50% -10%, rgba(216, 189, 133, 0.22), transparent 60%),
    linear-gradient(180deg, #fdfaf3 0%, #f6efe1 100%);
  background-size: auto, auto, auto;
  border: 1px solid rgba(184, 147, 63, 0.6);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.9);
  --fg: #241f18;
  --fg-dim: #6c6153;
  --fg-accent: #8a6a22;
  color: #241f18;
}
.sheet.gringotts .sheet-head {
  border-bottom: 1px solid rgba(184, 147, 63, 0.6);
  box-shadow: none;
}
.sheet.gringotts .sheet-head h2 {
  letter-spacing: 0.2em;
  color: #241f18;
}
.sheet.gringotts .muted,
.sheet.gringotts .tiny { color: #6c6153; }
.sheet.gringotts .gb-line { border-bottom: 1px solid rgba(184, 147, 63, 0.18); }

/* 窄屏：两列的（利率/收益、两颗按钮）落成两行，余额那行本来就会 wrap */
@media (max-width: 380px) {
  .gb-pair,
  .gb-acts { grid-template-columns: minmax(0, 1fr); }
  .gb-zh { letter-spacing: 0.2em; }
}

/*
 * 出售那扇小窗摆的是**背包那种方块**（.bag-grid / .tile，见 ui/screens-alley.js
 * 的 openSellPotion）——它本来就是一扇背包样子的窗户，方块不必另配一套皮。
 */

@media (prefers-reduced-motion: reduce) {
  .ms-b, .mkt-go { transition-duration: 0.01ms; }
}

/* ============================================================
   24 · 对角巷：一条街、九家门脸，以及那口随身小锅
   ============================================================ */

/*
 * 街上那一行说明（压在照片上，所以取暖白那一族，不用纸上的墨色）。
 * 和城堡那几层地图的 .place-hint 是同一类东西，只是这一屏没有屋子，
 * 那句话该贴在牌子**上面**，不是压在画面底下。
 */
.alley-note {
  margin: 0 0 var(--sp-2);
  padding: 0 var(--sp-4);
  text-align: center;
  font-size: var(--fs-xs);
  line-height: 1.7;
  color: var(--cine-dim);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.92);
}

/*
 * 店名太长的那几块牌子（弗洛林·福斯特克冰淇淋店 / 奥利凡德魔杖店 / 摩金夫人长袍店）。
 * 便签是并排钉的，一块顶出两行就会把整排推歪——所以名字缩一档、副名跟着缩，
 * 并且容得下换行（不换行的话 nowrap 会把牌子撑破屏幕）。
 */
.place.small {
  padding: var(--sp-2) var(--sp-3);
  min-width: 5.2em;
  max-width: 11.5em;
}
.place.small .place-name,
.place-name.sm { font-size: var(--fs-s); letter-spacing: 0.02em; }
.place.small .place-sub {
  font-size: 8.5px;
  white-space: normal;
  letter-spacing: 0.03em;
}

/*
 * 「灰掉、按住的一扇门」这一种样式整段撤了（原来是 .place.is-off / .place:disabled，
 * 给帕特奇那口锅买过之后的坩埚店用）。
 *
 * 撤的理由是创作者提的那个问题：买完东西回头一看，那家店灰了、点不动，
 * 玩家以为是自己把它弄坏了。**店不会因为你买过就关门**——
 * 现在门照样推得开，进去是那只空了的柜台（见 ui/screens-alley.js 的 renderCauldronEmpty）。
 *
 * 别把它加回来：地图上的便签（城堡那三层、这条街）现在没有一处该是 disabled 的。
 * 「这件事现在做不成」的说法有的是别的（门口摊一张纸、柜台空着、货格上写「今天买满了」），
 * 唯独不该是一扇推不开的门。
 */

/* 还没开张的那三家：一张纸、一枚「敬请期待」的红印。 */
.soon-card {
  position: relative;
  text-align: center;
  padding: var(--sp-5) var(--sp-4) var(--sp-4);
}
.soon-tag {
  display: inline-block;
  margin-bottom: var(--sp-3);
  padding: 3px 14px 4px;
  border: 1.5px solid var(--red);
  border-radius: var(--r);
  color: var(--red);
  font-size: var(--fs-s);
  letter-spacing: 0.32em;
  text-indent: 0.32em;   /* 最后一个字别贴着边 */
  transform: rotate(-1.6deg);
}
/*
 * 那枚印里放中英一对字时（装备那一屏就是「敬请期待 / Coming Soon」）：
 * 外面的字距与缩进让给里面两个 span 各自管，不然中文那四个字会被顶偏。
 */
.soon-tag.has-duo { letter-spacing: 0; text-indent: 0; }
.soon-tag.has-duo .duo-zh { letter-spacing: 0.32em; }
.soon-tag.has-duo .duo-en { letter-spacing: 0.08em; opacity: 0.8; }

/* ============================================================
   25b · 装备：背包里那一行，以及门后那一屏
   ============================================================ */

/*
 * 背包里单开的那一行（创作者指的位置：学院那一行下面）。
 *
 * 左边是栏目名「装备 / Equipment」，右边横躺着一根魔杖，整行点得动。
 * 它是**一块钉在纸上的窄木牌**，不是一条普通的按钮：
 * 纸色底、细边、下沿压一道实边，看着和上面那枚院徽是同一张纸上的东西。
 */
.bag-gear {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  margin: 0 0 var(--sp-3);
  padding: 6px var(--sp-3);
  font-family: var(--font-body);
  color: var(--ink);
  text-align: left;
  background: rgba(255, 251, 240, 0.5);
  border: 1px solid rgba(64, 48, 31, 0.28);
  border-bottom-color: rgba(64, 48, 31, 0.42);
  border-radius: var(--r);
  cursor: pointer;
  transition: border-color 0.14s ease, background 0.14s ease, transform 0.14s ease;
}
.bag-gear:hover { border-color: var(--red); background: rgba(255, 251, 240, 0.78); }
.bag-gear:active { transform: translateY(1px); }
.bag-gear-label { flex: none; font-size: var(--fs-m); font-weight: 700; }
.bag-gear-label .duo-en { font-weight: 400; }
/*
 * 木牌左下角那行小字：你挑的那根杖叫什么（木 · 芯）。
 * 没挑过就没有这一行——那张牌上只留「装备 Equipment」。
 */
.bag-gear-wandname {
  display: block;
  margin-top: 1px;
  font-size: var(--fs-xs);
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}

/*
 * 横躺的那根魔杖。
 *
 * ⚠️ 素材是**竖着的**（杖柄在上，比例差不多 1:7），这里要它横过来，所以整张图转 90°。
 *    麻烦在于**旋转不改布局**：转完那根杖该占多长，浏览器不会自己算，
 *    得由盒子先说好「转之前它是多大一块」。硬写一个长度（比如 240px）在窄屏上
 *    会撑出这张纸，所以这里让长度**自己长出来**：
 *
 *      窄道（.bag-gear-wand）固定 34px 高 —— 这是杖的「厚」
 *      方盒（.wand-turn）宽 = 窄道剩下的宽，高 = 宽（正方，绝对定位，不占版面）
 *      图（.bag-gear-art）厚 34 × 高 100%（= 方盒的高 = 窄道宽），再转 90°
 *
 *    于是：杖的长 = 窄道剩多宽，而 contain 又保证它缩在这个框里、
 *    最厚不超过 34px（按 1:7 长过头就反过来按厚度封顶）。
 *    换句话说，**这两个数是一对：厚 × 长**，别把 34px 单独改掉。
 */
.bag-gear-wand {
  position: relative;
  flex: 1 1 auto;
  height: 34px;
  min-width: 0;
}
/*
 * 正方幽灵盒：要的就是「高 = 宽」这个数，好让里面那张图拿到手。
 *
 * ⚠️ `pointer-events: none` **不是可选的**，删了会出一个很难查的怪事：
 *    这个盒子的高 = 它自己的宽（一整行的宽度，两三百 px），比那条窄木牌
 *    （约 48px 高）高出四五倍。`top: 50%` + `translateY(-50%)` 之后它上下**各溢出
 *    约 (宽−34)/2 ≈ 90~130px**，正好压住上面那一行抬头（学院徽章 + 随身坩埚）。
 *    它是 `.bag-gear`（一颗 button）的后代，所以那一片的悬停与点击**全归装备**：
 *    看着那行无缘无故亮起来，抬手去点锅，打开的是装备。
 *    透明不等于不存在——它只是一层用来量尺寸的框，不该接任何点击，
 *    所以这里关掉它的指针事件，让点哪儿都穿过去（魔杖本身在木牌范围内，
 *    点魔杖仍然由 `.bag-gear` 收到，那条路不受影响）。
 */
.wand-turn {
  position: absolute;
  left: 0;
  top: 50%;
  width: 100%;
  aspect-ratio: 1 / 1;
  transform: translateY(-50%);
  pointer-events: none;
}
/*
 * 木牌自己裁一下：把那个溢出盒子的**视觉**也收在木牌里面。
 * 上一条管点击，这一条管「别让透明的一层飘到下面钱袋与标签行上」——
 * 魔杖是居中画的（厚 34px，木牌加内边距约 48px 高），裁不到它。
 */
.bag-gear { overflow: hidden; }
.bag-gear-art {
  position: absolute;
  top: 50%;
  left: 50%;
  /* 34px = 转完之后的「厚」；高 100% = 转完之后的「长」 */
  width: 34px;
  height: 100%;
  object-fit: contain;
  /* 转的是「柄朝右」：rotate(90deg) 顺时针，图顶那一段（杖柄）转到右边来 */
  transform: translate(-50%, -50%) rotate(90deg);
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.3));
  /*
   * 轻轻漂一下（创作者 2026-10-14 要的：和装备面板那根杖同一种"悬着"的感觉）。
   * ⚠️ 幅度**很小**（±2px）：这是背包里的一行，不是舞台上的主角；
   *    keyframes 里必须把上面那句 translate + rotate 一起写全——不然动画一跑，
   *    杖就跳回竖着的原样（transform 会整条被覆盖）。
   */
  animation: bagWandFloat 4.6s ease-in-out infinite;
}
@keyframes bagWandFloat {
  0%, 100% { transform: translate(-50%, calc(-50% - 2px)) rotate(90deg); }
  50%      { transform: translate(-50%, calc(-50% + 2px)) rotate(90deg); }
}
@media (prefers-reduced-motion: reduce) {
  .bag-gear-art { animation: none; }
}


/*
 * 背包抬头右边那颗小坩埚。
 *
 * 三条硬要求：小、不许捅出这张纸的边、右边还得留出位置给以后要加的功能。
 * 所以：不抢中间那行字的宽度（flex 不 grow）、整颗收到抬头那条分隔线以内、
 * 图标按**高度**定尺寸（图是透明底的方锅，宽度跟着比例走才不会拉变形）。
 */
.ledger-pot {
  flex: 0 0 auto;
  margin-left: auto;          /* 靠右边缘站 */
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  min-width: 0;
  padding: 3px 4px 2px;
  background: none;
  border: 0;
  border-radius: var(--r);
  cursor: pointer;
  opacity: 0.86;
  transition: transform 0.14s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.14s ease, background 0.14s ease;
}
.ledger-pot:hover {
  opacity: 1;
  background: rgba(140, 47, 47, 0.09);
  transform: translateY(-1px);
}
.ledger-pot:active { transform: translateY(1px) scale(0.97); }
.ledger-pot-art {
  display: block;
  height: 30px;
  width: auto;
  max-width: 44px;            /* 再宽的图也压不出这一格 */
  object-fit: contain;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.28));
}
.ledger-pot-tx {
  font-family: var(--font-hand);
  font-size: 9.5px;
  line-height: 1.2;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
  white-space: nowrap;
}
/* 窄屏：抬头那一行本来就挤，字让掉，只留锅 */
@media (max-width: 420px) {
  .ledger-pot-tx { display: none; }
  .ledger-pot-art { height: 26px; }
}

/*
 * 点不动的那一项（咿啦猫头鹰商店那张羊皮纸）。
 *
 * 它照样摊在框上方，只是按下去没有反应——「有这条路，但现在走不通」。
 * 序号留着：不然下面那一项会从 2 变成 1，键盘党按 1 会选错。
 */
.vn-choice.off {
  opacity: 0.44;
  cursor: not-allowed;
  border-style: dashed;
  filter: saturate(0.4);
}
.vn-choice.off:hover {
  transform: rotate(var(--tilt));
  border-color: rgba(64, 48, 31, 0.44);
}
.vn-choice.off .lbl { color: var(--ink-soft); }

/* ============================================================
   25 · 种植箱下面那三格塌过的土
   ============================================================ */

/*
 * 第二排：上面三格是开学就有的，下面那三格塌着。
 * 中间那道虚线是「这是一排新东西」的界线——不是同一条土。
 */
.bed-row-shut {
  margin-top: 0;
  padding-top: var(--sp-3);
  border-top: 1px dashed rgba(158, 200, 146, 0.22);
}

/*
 * 塌着的那一格。
 *
 * 压暗、虚线边、锁——但不能压到看不见：玩家得看得出「这儿本来该有三格土」，
 * 才知道有东西可以修。所以它不是 `display:none`，也不是灰到糊掉。
 */
.bed.locked {
  border-style: dashed;
  border-color: rgba(158, 200, 146, 0.18);
  background:
    linear-gradient(180deg, rgba(232, 240, 224, 0.03), transparent 40%),
    rgba(14, 20, 15, 0.5);
  color: var(--cine-dim);
  opacity: 0.74;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.36);
}
.bed.locked:hover {
  background: rgba(26, 36, 26, 0.62);
  border-color: rgba(158, 200, 146, 0.3);
  transform: translateY(-1px);
}
.bed.locked .bed-art { font-size: 24px; opacity: 0.55; }
.bed.locked .bed-act { color: var(--cine-dim); }

/*
 * 轮得到的那一格：比后面两格亮一档。
 * 土是一格一格往上翻的，三格长得一模一样的话，玩家会挨个点过去试哪一格能修。
 */
.bed.locked.next {
  opacity: 0.94;
  border-color: rgba(224, 178, 100, 0.42);
}
.bed.locked.next .bed-state { color: var(--cine-gold); }
.bed.locked.next .bed-act { color: var(--cine-gold); }
.bed.locked.next:hover { transform: translateY(-2px); }

/* ============================================================
   25b · 兽栏（海格小屋后面那两栏）
   ------------------------------------------------------------
   一栏一张面板，**收着**是窄窄一条、摊开占满一整行（点抬头那一条展开 / 收起）：
     抬头  整条就是开关：徽章 + 栏名 + 英文名 + 这一栏的现状 + 折角箭头；
     摊开  上排（栏舍画框 + 产出卡）、下排（干草槽 / 窝·桶 两格）、底上买按钮。
   两栏同时摊开会挤（创作者点过这个问题），所以默认都收着，一次摊一栏看。
   图是 assets.js 的 PEN_BARN_ART / PEN_BIN_ART / HAY_TROUGH_ART 那五张，
   同一副暖灯笼光的暗棕木底插画，压在暗卡片上不串味。
   ============================================================ */
.pen-wrap {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
  gap: var(--sp-4);
  align-items: start;
}

/* ── 兽栏视觉重制：木栏、草地与两种栏舍的独立气质 ── */
.view:has(.pasture-scene) { position: relative; isolation: isolate; }
.view:has(.pasture-scene)::before {
  content: ''; position: absolute; inset: -22px -26px; z-index: -2; pointer-events: none;
  background:
    radial-gradient(38% 24% at 50% 12%, rgba(224, 180, 100, .2), transparent 72%),
    radial-gradient(45% 48% at 15% 72%, rgba(108, 143, 76, .22), transparent 74%),
    linear-gradient(180deg, rgba(39, 31, 21, .48), rgba(12, 17, 13, .78));
  box-shadow: inset 0 0 88px rgba(0,0,0,.58);
}
.view:has(.pasture-scene) .pasture-scene {
  position: relative; margin: 0 -6px var(--sp-3); padding: 10px 16px 14px; text-align: center;
  background: linear-gradient(180deg, rgba(65, 48, 29, .7), rgba(25, 24, 18, .72));
  border-top: 1px solid rgba(221, 177, 95, .42); border-bottom: 1px solid rgba(160, 143, 86, .28);
  box-shadow: 0 12px 22px rgba(0,0,0,.34), inset 0 1px 0 rgba(255,242,193,.08);
}
.view:has(.pasture-scene) .pasture-scene .label { color: rgba(213, 190, 139, .86); letter-spacing: .2em; }
.view:has(.pasture-scene) .pasture-scene .name { color: #f3d89f; text-shadow: 0 0 16px rgba(226, 174, 77, .3), 0 2px 3px rgba(0,0,0,.86); }
.view:has(.pasture-scene) .pasture-scene .name::after { width: min(72%, 280px); height: 2px; margin-top: 8px; background: linear-gradient(90deg, transparent, #aa8244 18%, #e9c67e 50%, #aa8244 82%, transparent); opacity: .9; }
.view:has(.pasture-scene) .pen-wrap { gap: 14px; }
.view:has(.pasture-scene) .pen {
  position: relative; overflow: hidden; padding: 9px;
  border: 1px solid rgba(190, 151, 82, .44); border-radius: 9px;
  background: repeating-linear-gradient(90deg, rgba(255, 225, 159, .018) 0 1px, transparent 1px 13px), linear-gradient(180deg, rgba(48, 40, 27, .9), rgba(17, 22, 16, .92));
  box-shadow: inset 0 1px 0 rgba(255,239,190,.08), 0 9px 22px rgba(0,0,0,.46);
}
.view:has(.pasture-scene) .pen::before {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 4px; pointer-events: none;
  background: repeating-linear-gradient(90deg, rgba(176, 126, 65, .7) 0 24px, rgba(91, 67, 38, .7) 24px 48px);
}
.view:has(.pasture-scene) .pen-head-in { position: relative; z-index: 1; }
.view:has(.pasture-scene) .btn.pen-fold { background: rgba(23, 28, 19, .68); border-color: rgba(193, 157, 88, .36); }
.view:has(.pasture-scene) .btn.pen-fold:hover { background: rgba(76, 66, 36, .5); border-color: rgba(232, 194, 113, .72); }
.view:has(.pasture-scene) .pen-title { color: #f2d59b; }
.view:has(.pasture-scene) .pen-en { color: rgba(200, 187, 148, .68); }
.view:has(.pasture-scene) .pen-glance { color: rgba(211, 199, 164, .75); }
.view:has(.pasture-scene) .pen-main { gap: 10px; }
.view:has(.pasture-scene) .pen-art { border-color: rgba(194, 157, 87, .46); border-radius: 6px; box-shadow: inset 0 0 28px rgba(0,0,0,.68), 0 4px 12px rgba(0,0,0,.35); }
.view:has(.pasture-scene) .pen-art::after { content: ''; position: absolute; inset: 0; pointer-events: none; background: linear-gradient(112deg, transparent 28%, rgba(255,239,183,.13) 48%, transparent 65%); opacity: .62; }
.view:has(.pasture-scene) .pen-herd { background: linear-gradient(rgba(12,10,5,0), rgba(12,10,5,.9)); }
.view:has(.pasture-scene) .pen-slot { filter: drop-shadow(0 0 5px rgba(239, 195, 102, .36)); }
.view:has(.pasture-scene) .pen-slot:not(.on) { border-color: rgba(222, 202, 155, .42); }
.view:has(.pasture-scene) .pen-yield,
.view:has(.pasture-scene) .pen-box { border-color: rgba(187, 150, 81, .32); border-radius: 6px; background: linear-gradient(180deg, rgba(76, 61, 37, .56), rgba(22, 26, 19, .72)); box-shadow: inset 0 1px 0 rgba(255,238,182,.06); }
.view:has(.pasture-scene) .pen-y-name,
.view:has(.pasture-scene) .pen-box-title { color: #eed49b; }
.view:has(.pasture-scene) .pen-y-every,
.view:has(.pasture-scene) .pen-box-state { color: rgba(210, 198, 163, .72); }
.view:has(.pasture-scene) .pen-y-next { border-color: rgba(193, 157, 88, .3); color: #daba78; }
.view:has(.pasture-scene) .pen-box-art { border-color: rgba(191, 150, 79, .36); background: #191a12; }
.view:has(.pasture-scene) .hay-slot { background: rgba(205, 193, 143, .08); border-color: rgba(222, 202, 155, .3); }
.view:has(.pasture-scene) .hay-slot.on { background: linear-gradient(180deg, #b8c96c, #637b3b); border-color: #d8df91; box-shadow: 0 0 7px rgba(169, 201, 92, .28), inset 0 -4px 7px rgba(0,0,0,.3); }
.view:has(.pasture-scene) .bin-chip { color: #e5c477; background: rgba(186, 143, 70, .16); border-color: rgba(218, 173, 89, .46); }
.view:has(.pasture-scene) .pen-buy { border-color: rgba(193, 157, 88, .28); }
.view:has(.pasture-scene) .pen-buy-left { color: rgba(211, 195, 155, .72); }
.view:has(.pasture-scene) .pen-buy .btn,
.view:has(.pasture-scene) .pen-box .btn { color: #f3dca8; background: linear-gradient(180deg, rgba(105, 74, 39, .94), rgba(43, 31, 21, .98)); border-color: rgba(222, 178, 91, .56); box-shadow: inset 0 1px 0 rgba(255,239,184,.12), 0 3px 8px rgba(0,0,0,.4); }
.view:has(.pasture-scene) .pen.is-shut { background: rgba(20, 24, 18, .6); border-color: rgba(166, 143, 91, .3); }
.view:has(.pasture-scene) .pen.is-shut .pen-title { color: rgba(220, 199, 151, .68); }
.view:has(.pasture-scene) .pen.is-shut .pen-shut-line { color: rgba(197, 190, 166, .62); }
/* 鸡栏：偏暖的干草金；牛栏：偏深的苔绿。两栏不再只是换个 emoji。 */
.view:has(.pasture-scene) .pen:nth-child(1) { --pen-accent: #d8ad61; }
.view:has(.pasture-scene) .pen:nth-child(2) { --pen-accent: #9bb17b; border-color: rgba(142, 169, 113, .48); background: repeating-linear-gradient(90deg, rgba(185, 213, 153, .018) 0 1px, transparent 1px 13px), linear-gradient(180deg, rgba(35, 51, 31, .9), rgba(14, 22, 16, .94)); }
.view:has(.pasture-scene) .pen:nth-child(2)::before { background: repeating-linear-gradient(90deg, rgba(116, 151, 91, .75) 0 24px, rgba(57, 87, 53, .75) 24px 48px); }
.view:has(.pasture-scene) .pen:nth-child(2) .pen-title,
.view:has(.pasture-scene) .pen:nth-child(2) .pen-box-title { color: #c4d69c; }
.view:has(.pasture-scene) .pen:nth-child(2) .pen-y-next { color: #b7ca8c; }
@media (max-width: 560px) { .view:has(.pasture-scene) .pasture-scene { margin-left: -2px; margin-right: -2px; padding-left: 10px; padding-right: 10px; } .view:has(.pasture-scene) .pen-main { grid-template-columns: 1fr; } }
@media (prefers-reduced-motion: reduce) { .view:has(.pasture-scene) .pen-art::after { display: none; } .view:has(.pasture-scene) .pen, .view:has(.pasture-scene) .pen-art { transition: none; } }

/*
 * 家里农庄的鸡栏牛栏：面板/交互**照搬**海格屋后那座兽栏（同一份 DOM、同一套尺寸），
 * 只有颜色从深绿换成深棕（创作者 2026-10-18 点的：UI 一样，绿改棕）。
 */
.view:has(.pasture-scene.home-pasture)::before {
  background:
    radial-gradient(38% 24% at 50% 12%, rgba(224, 180, 100, .2), transparent 72%),
    radial-gradient(45% 48% at 15% 72%, rgba(148, 108, 60, .18), transparent 74%),
    linear-gradient(180deg, rgba(39, 31, 21, .48), rgba(17, 12, 8, .78));
}
.view:has(.pasture-scene.home-pasture) .btn.pen-fold {
  background: rgba(40, 28, 16, .72);
  border-color: rgba(193, 157, 88, .36);
}
.view:has(.pasture-scene.home-pasture) .btn.pen-fold:hover {
  background: rgba(92, 64, 36, .55);
  border-color: rgba(232, 194, 113, .72);
}
.view:has(.pasture-scene.home-pasture) .pen {
  background:
    repeating-linear-gradient(90deg, rgba(255, 225, 159, .018) 0 1px, transparent 1px 13px),
    linear-gradient(180deg, rgba(48, 40, 27, .9), rgba(24, 16, 9, .94));
}
.view:has(.pasture-scene.home-pasture) .pen-yield,
.view:has(.pasture-scene.home-pasture) .pen-box {
  background: linear-gradient(180deg, rgba(76, 58, 35, .56), rgba(28, 19, 11, .74));
}
.view:has(.pasture-scene.home-pasture) .pen-box-art { background: #17120c; }
.view:has(.pasture-scene.home-pasture) .pen.is-shut {
  background: rgba(28, 20, 12, .62);
  border-color: rgba(166, 143, 91, .3);
}
.view:has(.pasture-scene.home-pasture) .pen:nth-child(2) {
  --pen-accent: #c49a5b;
  border-color: rgba(190, 151, 82, .48);
  background:
    repeating-linear-gradient(90deg, rgba(255, 225, 159, .018) 0 1px, transparent 1px 13px),
    linear-gradient(180deg, rgba(55, 40, 24, .9), rgba(22, 15, 9, .94));
}
.view:has(.pasture-scene.home-pasture) .pen:nth-child(2)::before {
  background: repeating-linear-gradient(90deg, rgba(176, 126, 65, .75) 0 24px, rgba(91, 67, 38, .75) 24px 48px);
}
.view:has(.pasture-scene.home-pasture) .pen:nth-child(2) .pen-title,
.view:has(.pasture-scene.home-pasture) .pen:nth-child(2) .pen-box-title { color: #eed49b; }
.view:has(.pasture-scene.home-pasture) .pen:nth-child(2) .pen-y-next { color: #daba78; }
.view:has(.pasture-scene.home-pasture) .hay-slot.on {
  background: linear-gradient(180deg, #d8b36a, #8a5b2c);
  border-color: #f0d19a;
  box-shadow: 0 0 7px rgba(224, 178, 100, .28), inset 0 -4px 7px rgba(0, 0, 0, .3);
}


.pen {
  padding: var(--sp-2);
  border: 1px solid rgba(224, 178, 100, 0.28);
  border-radius: var(--r);
  background: rgba(14, 20, 15, 0.58);
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.45);
}
/*
 * 摊开的那一栏占满整行——它里面有画框、有产出卡、有两格槽，
 * 半行宽放不下（挤成一团正是要修的那个毛病）。
 */
.pen.is-open { grid-column: 1 / -1; }
.pen.is-open .pen-head-in { margin-bottom: var(--sp-2); }

/* ── 抬头（整条是开关） ── */
.pen-head-in {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--sp-2);
  width: 100%;
  min-width: 0;
}
.pen-icon {
  flex: none;
  font-size: var(--fs-l);
  line-height: 1;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.6));
}
.pen-title {
  flex: none;
  font-family: var(--font-hand);
  font-size: var(--fs-l);
  letter-spacing: 0.06em;
  color: var(--cine-text);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.95);
}
.pen-en {
  flex: none;
  font-size: var(--fs-xs);
  letter-spacing: 0.14em;
  color: var(--cine-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/*
 * 收着时那行现状：**自己占一整行**（basis 100%），左对齐压在徽章那一行下面。
 * 不让它跟栏名挤在同一行——那行字有长有短，挤着就会折得七零八落、箭头也跟着飘。
 */
.pen-glance {
  flex: 1 1 100%;
  min-width: 0;
  margin-top: 2px;
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  color: var(--cine-dim);
  overflow-wrap: anywhere;
}
/* 槽空着、栏里又养着东西：「今天白养着」，最该刺眼的一句 */
.pen-glance.warn { color: #e07a5f; }
/* 折角箭头：留在第一行行尾（现状那一行在它后面自己换行） */
.pen-caret { flex: none; margin-left: auto; font-size: var(--fs-s); color: var(--cine-gold); }

/*
 * 那一条抬头按钮：盖掉 .btn 那副羊皮纸身板（它是一整条暗底，不是一颗纸按钮）。
 * 宽度撑满、不左对齐地把内容交回 .pen-head-in 自己排。
 */
.btn.pen-fold {
  width: 100%;
  text-align: left;
  padding: var(--sp-2) var(--sp-3);
  background: rgba(244, 233, 212, 0.06);
  border: 1px solid rgba(224, 178, 100, 0.26);
  border-radius: var(--r);
  box-shadow: none;
}
.btn.pen-fold:hover {
  background: rgba(244, 233, 212, 0.12);
  border-color: rgba(224, 178, 100, 0.5);
  transform: none;
  box-shadow: none;
}
.btn.pen-fold:active { transform: scale(0.995); box-shadow: none; }
/* 塌着那一栏的抬头不是按钮，但长得一样（只是没有折角箭头） */
.pen-head-in.is-static { padding: var(--sp-2) var(--sp-3); opacity: 0.85; }

/* ── 上排：栏舍画框 + 产出卡 ── */
.pen-main {
  display: grid;
  grid-template-columns: 3fr 2fr;
  gap: var(--sp-2);
  align-items: stretch;
}
.pen-art {
  position: relative;
  overflow: hidden;
  border: 1px solid rgba(224, 178, 100, 0.35);
  border-radius: var(--r);
  background: #17110b;
  box-shadow: inset 0 0 22px rgba(0, 0, 0, 0.65);
}
.pen-art-img {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 150px;
  object-fit: cover;
}
/* 牲口槽一排：养着的亮成徽章、没买的留虚线空槽——还装得下几只一眼数出来 */
.pen-herd {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  justify-content: center;
  gap: 3px;
  padding: 14px 6px 5px;
  background: linear-gradient(rgba(10, 8, 4, 0), rgba(10, 8, 4, 0.82));
}
.pen-slot {
  width: 19px;
  height: 19px;
  font-size: 14px;
  line-height: 19px;
  text-align: center;
}
.pen-slot:not(.on) {
  border: 1px dashed rgba(244, 233, 212, 0.38);
  border-radius: 50%;
  opacity: 0.55;
}

.pen-yield {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  padding: var(--sp-2);
  border: 1px solid rgba(224, 178, 100, 0.22);
  border-radius: var(--r);
  background: rgba(244, 233, 212, 0.06);
}
.pen-y-row { display: flex; align-items: center; gap: 6px; }
/* 产出的小图：真图按 20px 画，emoji 占位照字号 */
.pen-y-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 20px;
  font-size: 17px;
  line-height: 1;
}
.pen-y-icon-img { width: 20px; height: 20px; object-fit: contain; }
.pen-y-name {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--fs-s);
  color: var(--cine-text);
}
.pen-y-every {
  flex: none;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  color: var(--cine-dim);
}
.pen-y-next {
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: 6px;
  padding-top: var(--sp-2);
  border-top: 1px dotted rgba(224, 178, 100, 0.22);
  font-size: var(--fs-xs);
  color: var(--cine-dim);
}
/* 产不出（槽空 / 栏空）：这块牌要刺眼一点 */
.pen-y-next.warn { color: #e07a5f; }
.pen-y-clock { font-size: 13px; line-height: 1; }

/* ── 下排：干草槽 / 窝·桶 两格 ── */
.pen-boxes {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
}
.pen-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: var(--sp-2) var(--sp-2) var(--sp-3);
  border: 1px solid rgba(224, 178, 100, 0.22);
  border-radius: var(--r);
  background: rgba(244, 233, 212, 0.06);
  text-align: center;
}
.pen-box-title {
  font-size: var(--fs-s);
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--cine-text);
}
.pen-box-art {
  width: 100%;
  overflow: hidden;
  border: 1px solid rgba(224, 178, 100, 0.25);
  border-radius: var(--r);
  background: #17110b;
}
.pen-box-art img {
  display: block;
  width: 100%;
  height: 86px;
  object-fit: cover;
}
/* 干草一天一格：亮着 = 槽里还能撑几天（一共 HAY_DAYS 格） */
.hay-slots { display: flex; justify-content: center; gap: 5px; }
.hay-slot {
  width: 15px;
  height: 15px;
  border: 1px solid rgba(244, 233, 212, 0.3);
  border-radius: 3px;
  background: rgba(244, 233, 212, 0.08);
}
.hay-slot.on {
  background: #7d9b4f;
  border-color: #a3c46d;
  box-shadow: inset 0 -4px 7px rgba(0, 0, 0, 0.28);
}
.pen-box-state {
  font-size: var(--fs-xs);
  line-height: 1.6;
  color: var(--cine-dim);
  overflow-wrap: anywhere;
}
/* 槽空着、栏里又养着东西：「今天白养着」，最该刺眼的一句 */
.pen-box.warn .pen-box-state { color: #e07a5f; }
/* 攒着东西：chips 亮一档——这是玩家来这一趟的理由 */
.bin-chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
}
.bin-chip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 9px;
  border: 1px solid rgba(224, 178, 100, 0.42);
  border-radius: 999px;
  background: rgba(224, 178, 100, 0.13);
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  color: var(--cine-gold);
}
.bin-chip-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  line-height: 1;
}
.bin-chip-icon-img { width: 16px; height: 16px; object-fit: contain; }

/* ── 底上：买 ── */
.pen-buy {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
  padding-top: var(--sp-2);
  border-top: 1px dotted rgba(224, 178, 100, 0.2);
}
.pen-buy-left {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  color: var(--cine-dim);
}

/* 「满了」这类没法按的说明（摆在那儿，但不抢戏） */
.pen-full {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  color: var(--cine-dim);
}

/*
 * 塌着的那一栏：同一副面板骨架，虚线压暗——但不能压到看不见：
 * 玩家得看出「这里本来该有一栏」，才知道有东西可以请人清
 * （和塌着的那三格土同一处理）。
 */
.pen.is-shut {
  border-style: dashed;
  border-color: rgba(224, 178, 100, 0.24);
  background: rgba(14, 20, 15, 0.42);
}
.pen.is-shut .pen-head { opacity: 0.72; }
.pen-shut-line {
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-s);
  line-height: 1.8;
  color: var(--cine-dim);
}
.pen-foot { display: flex; justify-content: flex-start; }

/* ============================================================
   26 · （空）丽痕那三摞书
   ------------------------------------------------------------
   这一栏原先是「一行一摞 + 左边一张小图」（.row.row-art / .row-thumb）。
   三摞书改成货格摆法之后那两个类没人在挂了，整段删掉——
   留着一段没人挂的类，等于给下一个人留一个「这还有人用吗」的问号。
   ============================================================ */

/* ============================================================
   27 · 寝室门牌，以及「他个人的界面」
   ============================================================ */

/*
 * 宿舍区里那扇门。
 *
 * 一块深灰的长方牌，最前头压着这个人的**学院院徽**，牌子颜色跟着院徽走
 * （四个学院的深灰，见 data/houses.js 的 HOUSE_PLATE）。
 * 院徽是这块牌子上最该被认出来的东西，所以它压在最上面、
 * 而且比牌子高出一头——参考图里就是它先入眼，名字跟在后面。
 */
.dorm-doors {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-3);
  margin-top: var(--sp-2);
  padding-left: 14px;          /* 给院徽探出去的那半个身位留地方 */
}
.dorm-doors-label {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.24em;
  color: var(--cine-dim);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.92);
}

.dorm-door {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  min-width: 232px;
  padding: 13px 26px 14px 62px;
  font-family: var(--font-body);
  text-align: left;
  color: #f4e9d4;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.08), transparent 46%),
    var(--plate, #4a4a44);
  border: 1px solid rgba(0, 0, 0, 0.42);
  border-radius: 2px;
  cursor: pointer;
  box-shadow: 0 3px 0 rgba(20, 14, 9, 0.5), 0 12px 26px rgba(0, 0, 0, 0.5),
              inset 0 1px 0 rgba(255, 255, 255, 0.1);
  transition: transform 0.16s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.16s ease, filter 0.16s ease;
}
.dorm-door:hover {
  filter: brightness(1.12);
  transform: translateY(-2px);
  box-shadow: 0 5px 0 rgba(20, 14, 9, 0.44), 0 16px 30px rgba(0, 0, 0, 0.55),
              inset 0 1px 0 rgba(255, 255, 255, 0.12);
}
.dorm-door:active { transform: translateY(1px) scale(0.99); }

/*
 * 院徽：压在牌子上面，左右各探出去一点。
 * 用 absolute + 负位移，让它跨在牌子的左边缘上（参考图就是这个样子）。
 */
.dd-crest {
  position: absolute;
  left: -20px;
  top: 50%;
  z-index: 2;
  height: 158%;
  width: auto;
  transform: translateY(-50%);
  filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.6));
  pointer-events: none;
}
.dd-name {
  font-size: var(--fs-l);
  letter-spacing: 0.1em;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}
/* 好感还不够时那句实话：小、靠右、不抢名字 */
.dd-hint {
  margin-left: auto;
  padding-left: var(--sp-3);
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  color: rgba(244, 233, 212, 0.66);
  white-space: nowrap;
}

/*
 * 27a-1b · 韦斯莱双胞胎那块门牌（ui/twins.js）
 *
 * 和寝室门口那块**同一副样子**（复用 .dorm-door 的全套：院徽探出半个身位、
 * 名字跟在后面、牌子颜色跟着院徽走），只多一道**金边**——
 * 他们不是住在这层的人，是来做生意的，那道金边就是这一点区别。
 * 创作者的原话：「按钮样式和人物寝室样式一样（狮院徽章+框，但是带一个金边）」。
 *
 * 所以这里**只写「多出来的那一道」**，base 那一套一个字都不重抄：
 * 重抄一份，等于把「寝室门牌改了、这块忘了改」的风险翻一倍。
 */
.dorm-door.gold-edge {
  /* 外一圈金 + 里头再压一道更暗的金线：一块鎏边的招牌，不是一颗发光的按钮 */
  border-color: rgba(224, 178, 100, 0.92);
  box-shadow:
    0 0 0 1px rgba(224, 178, 100, 0.5),
    0 3px 0 rgba(20, 14, 9, 0.5),
    0 12px 26px rgba(0, 0, 0, 0.5),
    inset 0 0 0 1px rgba(224, 178, 100, 0.4),
    inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
.dorm-door.gold-edge:hover {
  border-color: var(--cine-gold);
  box-shadow:
    0 0 0 1px rgba(224, 178, 100, 0.8),
    0 5px 0 rgba(20, 14, 9, 0.44),
    0 16px 30px rgba(0, 0, 0, 0.55),
    inset 0 0 0 1px rgba(224, 178, 100, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.12);
}
/* 金边那一版角上的小字亮一点：它是价目（什么都收 · 六折），不是在说「你进不去」 */
.dorm-door.gold-edge .dd-hint { color: rgba(233, 205, 152, 0.86); }

/* ─────────────────────────────
   走廊尽头那扇有求必应屋的门（ui/screens-map.js 的 wishDoor）
   ───────────────────────────── */

/*
 * 这扇门现在只是走廊那一摞里的一块（它那一层 .ror-doorwrap 只是包一下，
 * 宽与位置跟着 .room-map > * 走）。原来它钉在照片右下角，和顶上那块【校园公告板】
 * 隔着一整间屋子——创作者报的「离得十万八千里」就是这一对。
 */

/*
 * 门牌本身：**深紫蓝 + 星空 + 细细的金边**（创作者定的长相）。
 *
 * 星空是画出来的、不是图片：底下一层深紫蓝的渐变当夜色，
 * 上面撒几颗不同大小的小白点（radial-gradient，一个圆一颗星）。
 * 用 background 而不是 img，是因为这块牌子要跟着字号一起伸缩——
 * 换成图片就得为每个屏幕宽度备一张。
 * ⚠️ 最后那一行 linear-gradient 是**底色**，必须排在最后：CSS 的
 *    background 层里前面的画在上面，夜色垫在底下，星星才浮得出来。
 */
.ror-door {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  width: 100%;
  padding: var(--sp-3) var(--sp-4);
  font-family: var(--font-body);
  color: #ece8ff;
  background:
    radial-gradient(circle at 14% 24%, rgba(255, 255, 255, 0.92) 0 1px, transparent 1.6px),
    radial-gradient(circle at 78% 18%, rgba(214, 226, 255, 0.8) 0 1px, transparent 1.5px),
    radial-gradient(circle at 43% 62%, rgba(255, 255, 255, 0.75) 0 1px, transparent 1.4px),
    radial-gradient(circle at 88% 72%, rgba(226, 220, 255, 0.7) 0 1px, transparent 1.3px),
    radial-gradient(circle at 26% 84%, rgba(255, 255, 255, 0.6) 0 1px, transparent 1.2px),
    radial-gradient(circle at 62% 34%, rgba(255, 244, 214, 0.85) 0 1.2px, transparent 2px),
    linear-gradient(158deg, #2a1e57 0%, #1a1240 52%, #0d0922 100%);
  /* 细细的金边 */
  border: 1px solid rgba(224, 178, 100, 0.55);
  border-radius: var(--r);
  cursor: pointer;
  box-shadow:
    0 8px 24px rgba(0, 0, 0, 0.55),
    inset 0 0 20px rgba(122, 100, 214, 0.22);
  transition: background-color 0.18s ease, border-color 0.18s ease,
    box-shadow 0.18s ease, transform 0.16s cubic-bezier(0.16, 1, 0.3, 1);
}
.ror-door:hover {
  border-color: rgba(240, 205, 140, 0.9);
  box-shadow:
    0 10px 28px rgba(0, 0, 0, 0.6),
    inset 0 0 26px rgba(140, 118, 232, 0.34);
  transform: translateY(-2px);
}
.ror-door:active { transform: translateY(0) scale(0.985); }

/* 中文名：这一部一贯的宽字距；比暗底上的灰字亮一档，夜色里才读得清 */
.ror-door .ror-zh { font-size: var(--fs-m); letter-spacing: 0.16em; }
/*
 * 英文名：**在中文字下面、小一号**（创作者定的）。
 * 深紫蓝底上不能再用深灰（那是照片上的写法），用一层浅浅的月白。
 */
.ror-door .ror-en {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  color: rgba(206, 200, 246, 0.78);
}
/* 角落里那行小字：说明这门后面是干什么的（星币商城），用金色压轴 */
.ror-door .ror-hint {
  margin-top: 3px;
  font-size: var(--fs-xs);
  letter-spacing: 0.14em;
  color: var(--cine-gold);
}

@media (max-width: 560px) {
  /* 窄屏上这块牌子上的字长，降一档字号（宽度跟着 .room-map > * 走，不在这里另定） */
  .ror-door .ror-zh { font-size: var(--fs-s); letter-spacing: 0.12em; }
}

/*
 * 有求必应屋那一屏（ui/wishroom.js）：屋里那格货架。
 *
 * 配色跟着门牌走——深紫蓝的卡片、细细的金边、亮一档的字。
 * 别改成羊皮纸那套（.card）：那扇门后面是一间夜色里的屋子，
 * 换成米黄纸片就断了。
 */
.ror-shelf { display: flex; flex-direction: column; gap: var(--sp-3); }

.ror-cell {
  position: relative;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid rgba(224, 178, 100, 0.42);
  border-radius: var(--r);
  background: linear-gradient(158deg, rgba(42, 30, 87, 0.74), rgba(13, 9, 34, 0.84));
  box-shadow: inset 0 0 22px rgba(122, 100, 214, 0.18), 0 8px 22px rgba(0, 0, 0, 0.42);
  color: #ece8ff;
}

.ror-cell-head { display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; }
.ror-cell-name { font-size: var(--fs-l); letter-spacing: 0.14em; }
/* 英文名跟在后面：手写体、小一号、压暗一档（这一部一贯的双语写法） */
.ror-cell-en {
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  letter-spacing: 0.06em;
  color: rgba(206, 200, 246, 0.72);
}

.ror-cell-note { margin: 6px 0 0; font-size: var(--fs-s); color: rgba(234, 230, 255, 0.92); }
.ror-cell-line { margin: 4px 0 0; font-size: var(--fs-s); color: rgba(206, 200, 246, 0.78); }
/* 用料那行压到最小：它是「买了之后还得凑什么」，不是卖点 */
.ror-cell-need { margin: 5px 0 0; font-size: var(--fs-xs); color: rgba(206, 200, 246, 0.6); }
/* 价钱用金色——那一行是这一格的重点，和门牌那道金边是同一个意思 */
.ror-cell-price {
  margin: 8px 0 0;
  font-size: var(--fs-s);
  letter-spacing: 0.08em;
  color: var(--cine-gold);
}
/* 问不到价时那句实话：不是「操作失败」，是一条要说清楚的缘由，所以用暖红字 */
.ror-cell-warn { margin: 8px 0 0; font-size: var(--fs-xs); color: #e8b3a2; }

.ror-cell-acts { display: flex; gap: var(--sp-2); flex-wrap: wrap; margin-top: var(--sp-3); }

/*
 * 走廊屋里那一摊落的位置（ui/screens-map.js 的 screenPlace）。
 *
 * 它现在只是那一摞里的一块（.room-map 从上往下排，见 9c），不再钉角落。
 * padding-left 是**给院徽留的地方**：徽章按 .dd-crest 的写法往左探出 20px，
 * 不留这一截就会连狮子带边框一起被屋子的左边缘裁掉。
 */
.corridor-stall {
  padding-left: 22px;
}

/*
 * 这块牌子上的字比寝室那块长得多（「韦斯莱双胞胎的交易」九个字 + 一行英文，
 * 寝室那边最多三个字），所以配色不动、**排布要单独收一次**。
 * 这些只作用在 .corridor-stall 里，寝室门牌一个字都不受影响。
 *
 * 🔴 这里踩过一个坑：**字被院徽压住了**（创作者报的）。
 *    根因是 `.dd-crest` 那条 `height: 158%` —— 它是按**牌子高度**算的，
 *    寝室那块只有一行、没事；这块牌子有两三行，牌子一高，徽章跟着长，
 *    宽度是 `width: auto` 按比例跟着长，于是从左边一直顶到字上。
 *    改法两条一起用，缺一条都还会压：
 *      1. 这里把徽章**定成固定高度**（78px ≈ 寝室那块的实际大小），
 *         行数再怎么变它都不再长；
 *      2. 文字那一侧把左边留足（padding-left: 84px），
 *         即便徽章是正方形的（最宽的情况）也还空着一截。
 *    别把 84px 改小、也别把 height 改回百分比——除非你重新量过一遍。
 */
.corridor-stall .dorm-door {
  min-width: 0;
  max-width: 100%;
  /* 左 84px 让给院徽（见上头那两条改法），右 26px 跟着寝室那套 */
  padding: 12px 26px 13px 84px;
  flex-wrap: wrap;
}

/* 徽章：固定高度、只探出一点点——不再跟着行数长 */
.corridor-stall .dd-crest {
  height: 78px;
  left: -18px;
}

/* 中文名：降一档字号，九个字尽量一行放得下 */
.corridor-stall .dd-name { font-size: var(--fs-m); letter-spacing: 0.08em; }

/*
 * 英文名：跟在中文后面（放得下就并排、放不下自己折行）。
 * 手写体、颜色压暗一档；字号压到比中文小很多——中文是牌子上唯一的主角。
 */
.corridor-stall .dd-en {
  font-family: var(--font-hand);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: rgba(244, 233, 212, 0.66);
}

/*
 * 摊子那一屏（ui/twins.js 的 .twin-shop）：分区标题的英文副标同样压到比中文小很多，
 * 作用域只在这一屏，别处章节的英文不受影响。
 */
.twin-shop .chapter .ch-en { font-size: 9px; }

/*
 * 角上那行小字（什么都收 · 六折）换到自己那一行。
 * `.dd-hint` 带着 `white-space: nowrap`，硬挤在名字那一行里一定顶出屏幕。
 * 它**只有中文**，所以不跟英文名并排。
 */
.corridor-stall .dd-hint {
  flex: 1 1 100%;
  margin-left: 0;
  margin-top: 3px;
  padding-left: 0;
}

/* ─────────────────────────────
   27a-3 · 校园公告栏（ui/bulletin.js）
   ───────────────────────────── */
/*
 * 那块**深棕色的板子**：木纹 + 一圈描金边，纸都钉在它上面。
 * 底图是走廊那张照片，板子压暗一档才托得住上面浅色的纸。
 */
.bulletin-board {
  padding: var(--sp-4) var(--sp-3) var(--sp-5);
  border-radius: calc(var(--r) + 6px);
  background:
    repeating-linear-gradient(90deg, rgba(0, 0, 0, 0.06) 0 2px, transparent 2px 7px),
    linear-gradient(180deg, #4a3120, #2e1e10);
  border: 1px solid rgba(224, 178, 100, 0.3);
  box-shadow: inset 0 2px 18px rgba(0, 0, 0, 0.5), 0 10px 26px rgba(0, 0, 0, 0.45);
}
.bl-title {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-4);
}
.bl-title-zh { font-family: var(--font-hand); font-size: var(--fs-l); letter-spacing: 0.2em; color: #f0d9a6; }
.bl-title-en { font-family: var(--font-hand); font-size: var(--fs-xs); letter-spacing: 0.2em; color: rgba(224, 178, 100, 0.7); }

/* 纸们错落着钉：flex 换行 + 每档一点上下错开与歪角 */
.bl-papers {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: center;
  gap: var(--sp-3);
}

/* 一张钉着的纸：顶上那枚图钉是 ::before */
.bl-paper {
  position: relative;
  width: 15rem;
  padding: var(--sp-3) var(--sp-3) var(--sp-4);
  color: var(--ink);
  border-radius: 3px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.45);
}
.bl-paper::before {
  content: '';
  position: absolute;
  left: 50%;
  top: -6px;
  width: 12px;
  height: 12px;
  margin-left: -6px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #f6d9a0, #b07a2c 60%, #6d4413);
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.5);
}
/* 羊皮纸那两种的底图由 JS 铺（路径只在 assets.js 登记），这里只管铺满不重复 */
.bl-paper.pic { background-size: 100% 100%; background-repeat: no-repeat; border: none; }
/* 大小不一：正方 / 长方 / 便签三档 */
.bl-paper.sq { width: 13rem; min-height: 13rem; }
.bl-paper.wide { width: 18rem; min-height: 10rem; }
.bl-paper.note {
  width: 14rem;
  background:
    radial-gradient(80% 50% at 14% 4%, rgba(255, 255, 255, 0.55), transparent 62%),
    linear-gradient(180deg, #fbf4e4, #eee1c4);
  border: 1px solid rgba(64, 48, 31, 0.38);
}
/* 错落：三档歪角 + 三档上下错开（按序号取模，重画不跳） */
.bl-paper.tilt-a { transform: rotate(-1.6deg); }
.bl-paper.tilt-b { transform: rotate(1.1deg); }
.bl-paper.tilt-c { transform: rotate(-0.5deg); }
.bl-paper.drop-a { margin-top: 0; }
.bl-paper.drop-b { margin-top: var(--sp-4); }
.bl-paper.drop-c { margin-top: var(--sp-2); }

.bl-from {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  color: var(--red);
  margin-bottom: var(--sp-2);
}
.bl-wants { display: flex; flex-direction: column; gap: var(--sp-2); margin-bottom: var(--sp-2); }
.bl-want { display: flex; align-items: center; gap: var(--sp-2); }
.bl-art {
  width: 44px;
  height: 44px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.5);
  border-radius: 4px;
  overflow: hidden;
}
.bl-art-img { width: 100%; height: 100%; object-fit: contain; }
.bl-name { font-size: var(--fs-m); font-weight: 700; }
.bl-qty { font-size: var(--fs-xs); color: var(--ink-soft); }
.bl-qty.short { color: var(--red); }
.bl-note { font-size: var(--fs-s); line-height: 1.7; margin: 0 0 var(--sp-2); }
.bl-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); }
.bl-foot .btn { width: auto; flex: none; }
.bl-pay { font-family: var(--font-hand); font-size: var(--fs-s); font-weight: 700; color: #8a5a12; }
.bl-done { font-size: var(--fs-xs); color: var(--ink-soft); }
.bl-paper.done { opacity: 0.62; filter: saturate(0.7); }
.bl-paper.empty { width: 16rem; }

/* 窄屏：纸一律占满一行，错落只留歪角不留左右错开 */
@media (max-width: 560px) {
  .bl-paper, .bl-paper.sq, .bl-paper.wide, .bl-paper.note, .bl-paper.empty { width: 100%; min-height: 0; }
}

/* ─────────────────────────────
   27a-2 · 随机邂逅：地图上那一行名牌（ui/meetup.js）
   ───────────────────────────── */

/*
 * 他今天在外面时，那一层地图底下多出这么一行。
 *
 * 名牌本身就是 .dorm-door——创作者点名的：地图上这一块和宿舍区那一块
 * 要认得出是同一样东西。这里只把尺寸收小一档（地图上牌子本来就挤），
 * 再把「在黑湖边」那行小字压成金色：一眼扫过去，先认出是谁，再看出他在哪儿。
 *
 * 摆位：永远在**所有地点牌下面**，自成一格、上面隔一条细线。
 * 地图上牌子多是常态，插进牌堆里迟早互相压（见 ui/meetup.js 顶上那段）。
 */
.meet-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  /* 一条细到几乎看不见的分隔线：不是框，只是把这一行从上面那堆牌里分出来 */
  border-top: 1px solid rgba(255, 255, 255, 0.09);
  /* 院徽要往牌子外面探出去半个身位，这一行不能裁 */
  overflow: visible;
}
.meet-row-label {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.24em;
  color: var(--cine-gold);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.92), 0 0 12px rgba(0, 0, 0, 0.8);
}
.meet-plates {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-3);
}
/* 名牌比寝室门牌再小一号（创作者看过地图后点的）：这里是「今天碰得见」，不是一整扇门 */
.meet-door {
  min-width: 0;
  padding: 8px 15px 9px 38px;
}
.meet-door .dd-name { font-size: var(--fs-s); }
/* 角上写的是他今天在哪儿（「在黑湖边」），不是「还不太熟」——所以用金色 */
.meet-door .dd-hint {
  font-size: 11px;
  color: var(--cine-gold);
  opacity: 0.9;
}

/*
 * 「他就在这儿」——钉在他待的那一处**屋子里面**的那一块。
 *
 * 和地图上那一行是同一套零件（.meet-row-label + .meet-plates + .dorm-door），
 * 只是这一处不在地图上：他屋里没有地点牌可挤，所以**不要那条分隔线**，
 * 也不用躲谁——它就在抬头正下方，进屋第一眼看见的就是它。
 */
.meet-here {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-3);
  /* 院徽要往牌子外面探出去半个身位，这一块不能裁 */
  overflow: visible;
}

/*
 * 指路小条那一排：横着摆，装不下才换行。
 * 同时有三个人在外面的时候（周三中午就是这样），竖着堆会把这半屏撑得比地点牌还高。
 */
.meet-chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-2);
}

/*
 * 别人在别处时那条指路（「黑湖边有人 · 去看看」）。
 * 它是一条**线索**，不是一块名牌：不带院徽、不抢眼，但点得动——
 * 点一下就走到那一层，人还得自己过去点他。
 */
.meet-chip {
  padding: 5px 14px 6px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.12em;
  color: var(--cine-dim);
  background: rgba(12, 9, 6, 0.42);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 999px;
  cursor: pointer;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9);
  transition: color 0.16s ease, border-color 0.16s ease, background 0.16s ease;
}
.meet-chip:hover {
  color: var(--cine-gold);
  border-color: rgba(212, 175, 106, 0.42);
  background: rgba(20, 15, 9, 0.56);
}
.meet-chip:active { transform: translateY(1px); }

/* ─────────────────────────────
   27b · 他个人的界面（ui/screens-char.js）
   ───────────────────────────── */

/*
 * 一屏四样东西：左边的框与人、右边那一叠纸、底下那块名牌与好感条。
 *
 * 层叠顺序是创作者定的，别改：
 *   好感条 ＞ 名牌 ＞ 立绘 ＞ 画框
 * 也就是「人名压在画上、好感压在名字上」——一眼扫下去，先看见他是谁、
 * 你们到哪一步了，然后才是那张脸。
 */
.cr {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  /*
   * 顶部这一条：**把整屏往下挪**，因为立绘的头会从画框顶上探出去。
   *
   * 探出去多少不是随便一个数：立绘比画框大（见 .cr-portrait 的 --pz），
   * 绕底边放大时**往上长**，实测探出画框上沿约画框高的 14%（画框 560px 时约 78px）。
   * 而画框上沿离状态栏底边只有 24px（.view 的 sp-2 加这一行原来的 sp-4），
   * 于是头顶正好顶进状态栏——创作者点名的就是这个。
   *
   * 为什么按 **vh** 给：探出去的那一截是跟着画框大小走的，画框又跟着屏高走
   * （见 .cr-stage 的 34vh 那一道闸），所以按同样的比例补，小屏大屏都对得上：
   *   1086px 高的窗口 → 87px（差不多就是创作者说的「2 厘米」）
   *    700px 高的窗口 → 56px（那边画框也小，探出去只剩 51px）
   * 上限 104px 是给很高的窗口兜的：超过之后画框已经不再长，补再多只是白留一道空。
   */
  padding: clamp(16px, 8vh, 104px) 0 68px;
}

/* ── 左边的框与人 ── */

/*
 * 画框那一格。比例照创作者画的红框量：长宽比约 0.65（13/20）。
 *
 * ⚠️ 三个数是一组，别单独改某一个：
 *    · `50%`  —— 上限由右栏定：那四张纸片还要站得下人，格子再宽就把它们挤扁了。
 *    · `34vh` —— 高度那一道闸。宽度定了，高度就由长宽比推出来（× 1/0.65），
 *                 所以 34vh 正好换来「最多占到屏高 52%」。矮屏（横屏笔记本、
 *                 带地址栏的手机）上靠它兜底，否则框子会顶出画面。
 *                 **这是「再大一点 / 再小一点」时该动的那个数**：它一收，
 *                 框与人一起小（从前是 39vh，创作者说太大了，收到这里）。
 *    · `13/20` —— 长宽比。**只写在 aspect-ratio 这一处**：宽度那一行里别再塞
 *                 一个别的项，否则比例会被悄悄改掉。
 *
 * `align-self: center`：这一格在那一行里**竖直居中**，而不是顶对齐。
 * 创作者的原话是「名牌、畅聊按钮可以压在画框和立绘上，所以往下挪一点没关系」——
 * 占位放宽之后，让框在「上沿到名牌之间」居中，比死贴顶上更稳：
 * 框缩小时它的下沿会自然落到名牌上沿那里，而不是在两者之间空出一条缝。
 * 想让它再低一档、真正压到名牌上，把 center 换成 flex-end 就行（一个词的改动）。
 *
 * ⚠️ **代价（已知、且是故意的）**：框那张素材是 `object-fit: fill` 铺满这一格的，
 *    长宽比从 3/4 变成 13/20，等于把它纵向拉长约 15%，金边与角花会跟着变细长。
 *    要让它回到「不拉长」只有两条路：把 13/20 往回挪（0.72 上下几乎看不出拉长，
 *    但框就矮一截），或者把右栏那叠纸收窄、腾宽度按原比例放大（会挤纸）。
 */
.cr-stage {
  position: relative;
  flex: 0 0 auto;
  align-self: center;
  width: min(50%, 34vh, 420px);
  aspect-ratio: 13 / 20;
  /* 立绘的头要探出框沿，所以这一格不该裁掉溢出的部分 */
  overflow: visible;
}
/* 框在最底下（z 1）：它是一圈装饰，人物压着它 */
.cr-frame {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: fill;
  filter: drop-shadow(0 10px 24px rgba(0, 0, 0, 0.55));
}
/*
 * 圆框那张素材的四角是白的（不是透的），直接摆会出现一块白底。
 * 拿一个椭圆的蒙版把四角裁掉——只留框子本身那一圈。
 */
.cr-stage[data-shape="oval"] .cr-frame {
  -webkit-mask-image: radial-gradient(ellipse 51% 51% at 50% 50%, #000 0 99%, transparent 100%);
  mask-image: radial-gradient(ellipse 51% 51% at 50% 50%, #000 0 99%, transparent 100%);
}
/*
 * 框里面那层衬底（z 2）：盖住素材中间那片白。
 * 有了它，人物才像「装在框里」；没有它，人物是贴在一张白纸上。
 * 颜色取深棕的绒面，和两圈框的金边都是一族；形状跟着框走（方框配方衬、圆框配椭圆）。
 */
.cr-matte {
  position: absolute;
  inset: 9% 8% 10%;
  z-index: 2;
  background: linear-gradient(180deg, #2b2119, #1a130d);
  border-radius: 2px;
  box-shadow: inset 0 0 22px rgba(0, 0, 0, 0.75);
}
.cr-stage[data-shape="oval"] .cr-matte { border-radius: 50%; inset: 10% 9% 11%; }

/* 立绘（z 3）：压在框和衬底上，底边对齐、头顶探出框沿 */
.cr-portrait {
  position: absolute;
  left: 3%;
  right: 3%;
  bottom: 2%;
  z-index: 3;
  width: 94%;
  height: 104%;
  object-fit: contain;
  /* 人物的脚要落在框的下沿上，所以图往底边贴 */
  object-position: bottom center;
  /*
   * 这个人要放大多少（--pz，按人给，见 data/charroom.js 的 PORTRAIT_SCALE）。
   * 绕**底边中点**放：脚照旧踩在框的下沿上，人往上涨，头就探出框沿了。
   *
   * ⚠️ 这个数**默认就不是 1**（创作者定的：人要比画框略大一点）：
   *    四张立绘各自在画布上留了一圈白，照 `contain` 原样摆出来，
   *    人总是缩在框子里、头顶离框沿还有一截。乘上 1.1 左右之后，
   *    脚还踩在框底，头顶刚好探出上沿——「人压着框」这件事才真成立。
   *    倍数**只用在这一个变量上**（不靠改 width/height，那会连带把长宽比也改掉：
   *    图是 contain 的，只给高度不给宽度，实际画出来多大要看图自己的比例）。
   */
  transform: scale(var(--pz, 1.12));
  transform-origin: bottom center;
  filter: drop-shadow(0 6px 14px rgba(0, 0, 0, 0.5));
  pointer-events: none;
}

/* ── 右边那一叠纸 ── */

.cr-menu {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  /* 四张之间的间隙：从 12px 收到 8px。它们是一叠随手贴的纸，不该像四个按钮那样疏 */
  gap: var(--sp-2);
  padding-right: var(--sp-2);
}

/*
 * 一张纸（便签）。
 *
 * 宽度按 --w 收（越靠下越宽），每张自己带一点倾角——参考图里它们像随手摊在桌上
 * 的一叠，而不是四个并排的按钮。钉子那一小点在左上角。
 *
 * ⚠️ **「便签感」靠三个数撑着，改字号时三个要一起动**：
 *    · `padding` 薄（9/10 px）——便签是薄薄一张，留白一多就变成按钮；
 *    · 中文字号小一档（见 .crc-label）+ 字距收到 0.08em——字挤在一起才像手写的；
 *    · 阴影轻（见下）——`0 3px 0` 那种硬厚边是立体按钮的做法，不是纸。
 * 创作者的原话是「太大了…没有便签的感觉了」，压的就是这三样。
 */
.cr-card {
  position: relative;
  width: calc(var(--w, 1) * 100%);
  max-width: 264px;
  padding: 9px 14px 10px;
  font-family: var(--font-body);
  text-align: center;
  color: var(--ink);
  background:
    radial-gradient(80% 60% at 14% 4%, rgba(255, 255, 255, 0.5), transparent 62%),
    linear-gradient(180deg, #fbf4e4, #eddfc0);
  border: 1px solid rgba(64, 48, 31, 0.44);
  border-radius: var(--r);
  cursor: pointer;
  /* 比从前薄一档：硬边从 3px 收到 2px，外扩的投影也收小 */
  box-shadow: 0 2px 0 rgba(50, 37, 24, 0.36), 0 8px 18px rgba(0, 0, 0, 0.42);
  transition: transform 0.16s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.16s ease, border-color 0.16s ease;
}
/* 每一张歪一点点，四张不重样 */
.cr-card:nth-child(1) { transform: rotate(-0.8deg); }
.cr-card:nth-child(2) { transform: rotate(0.9deg); }
.cr-card:nth-child(3) { transform: rotate(-0.5deg); }
.cr-card:nth-child(4) { transform: rotate(0.7deg); }
/* 钉子：和便签牌同一处细节（.place::after） */
.cr-card::before {
  content: '';
  position: absolute;
  top: 6px;
  left: 7px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: rgba(140, 47, 47, 0.7);
  box-shadow: inset 0 -1px 1px rgba(0, 0, 0, 0.4);
}
.cr-card.on:hover {
  border-color: var(--red);
  transform: rotate(0deg) translateY(-2px);
  box-shadow: 0 4px 0 rgba(50, 37, 24, 0.3), 0 12px 22px rgba(0, 0, 0, 0.48);
}
.cr-card.on:active { transform: rotate(0deg) translateY(0) scale(0.98); }

/*
 * 【畅聊】那一张是这一屏的出口，所以它比上面三张都大一号——
 * 参考图里就是这个大小差。它是唯一「按得下去」的那一张。
 * 大一号只体现在**字号与内边距**上（各高一档），不是把它撑成一块牌子。
 */
.cr-card.on { padding: 15px 18px 16px; }
.crc-label {
  display: block;
  font-size: var(--fs-m);
  font-weight: 700;
  /*
   * 字距 0.16em → 0.08em。原来是照「按钮标签」那种疏朗排的，
   * 可便签上的字该是挤在一起的；疏着排，两个字的标签会摊得像四个字。
   */
  letter-spacing: 0.08em;
  /* 字距把最后一个字也推出去了，缩回来一点才居中 */
  text-indent: 0.08em;
  line-height: 1.15;
}
.cr-card.on .crc-label { font-size: var(--fs-l); }
/*
 * 纸片上那行英文（互动 / Interact）。
 *
 * 创作者的口径：**「特别特别小」，它只是中文的附庸**——所以这里不用字号阶
 * （最小那一档 --fs-xs 是 12px，还是太抢眼），直接钉 9px，另加手写体与压暗。
 * 它不该被单独读懂，只该让人扫到「这里还有一行洋文」。
 * 不给 text-indent —— 中文那边是为字距把末字推出去才缩回的，英文不必。
 */
.crc-en {
  display: block;
  font-family: var(--font-hand);
  font-size: 9px;
  letter-spacing: 0.08em;
  line-height: 1.25;
  color: var(--ink-soft);
}
.cr-card.on .crc-en { font-size: 10px; }
.cr-card.soon .crc-en { opacity: 0.8; }

/*
 * 还没做的那三张：摆着，但按不动。
 * 抽掉它们会让这一屏看着像「这个人只能聊天」；压暗摆着才是实话。
 */
.cr-card.soon {
  cursor: not-allowed;
  opacity: 0.62;
  box-shadow: 0 1px 0 rgba(50, 37, 24, 0.26), 0 6px 14px rgba(0, 0, 0, 0.34);
}
.cr-card.soon .crc-label { color: var(--ink-soft); }
/*
 * 【亲密】解锁之后那张便签：名字走**淡淡的粉**（创作者定的）。
 * 只给字色，不给底、不给边框——它和另外三张是同一叠纸，
 * 粉只是「这一张从此点得动了」的一点暗示，不该比【畅聊】那张更抢眼。
 */
.cr-card.pink .crc-label { color: #d98fab; }
.cr-card.pink.on:hover .crc-label { color: #c9769a; }
/*
 * 便签底下那行小字「敬请期待 Coming Soon」。
 * 字号从 --fs-xs（12px）压到 10px、离上一行从 3px 收到 2px：
 * 它是最不重要的那一行，便签上该是最不起眼的一道注脚。
 */
.crc-note {
  display: block;
  margin-top: 2px;
  font-family: var(--font-hand);
  font-size: 10px;
  letter-spacing: 0.08em;
  line-height: 1.25;
  color: var(--ink-soft);
}
/*
 * 那一行里的中英两半：「敬请期待」+「Coming Soon」，并排一行。
 * 英文那半比中文再小半档（同 .crc-en 一条口径：它是附庸）。
 * 中英之间那点间隔由 crn-en 自己的左边距给——多一个分隔符（·）反而像两件东西。
 */
.crc-note .crn-en { margin-left: 5px; font-size: 8.5px; opacity: 0.8; }

/* ── 底下那块名牌 + 好感条 ── */

/*
 * 名牌（z 5）：压在立绘上。
 *
 * bottom 这个数是跟着 .cr 下面那条 padding-bottom（68px）定的：
 * 舞台的下沿就在离底 68px 那里，名牌（高约 44px）摆在 46px 上，
 * 于是它**骑在画框的下沿**上——像参考图那样一条横牌别在画上。
 * 放到 68px 以下就整块掉出画框，变成画面底下多出来的一行字。
 */
.cr-plate {
  position: absolute;
  left: 2%;
  bottom: 46px;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-width: 56%;
  padding: 11px 26px 12px 64px;
  color: #f4e9d4;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.08), transparent 46%),
    var(--plate, #4a4a44);
  border: 1px solid rgba(0, 0, 0, 0.42);
  border-radius: 2px;
  box-shadow: 0 3px 0 rgba(20, 14, 9, 0.5), 0 12px 26px rgba(0, 0, 0, 0.5),
              inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
/* 院徽跨在名牌的左边缘上（和寝室门牌同一处细节） */
.cp-crest {
  position: absolute;
  left: -22px;
  top: 50%;
  z-index: 2;
  height: 168%;
  width: auto;
  transform: translateY(-50%);
  filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.6));
  pointer-events: none;
}
/*
 * 名牌上是**全名**，中英并排（创作者定的）：中文大字、英文小字，中文在上。
 *
 * 中文字距比从前收到 0.1em：牌子上现在写的是「德拉科·马尔福」七个字，
 * 还按走廊里喊简称那样排（0.12em），牌子会一路顶到好感条外面去。
 */
.cp-name {
  display: flex;
  flex-direction: column;
  min-width: 0;
  font-weight: 700;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}
.cp-name .cpn-zh {
  font-size: var(--fs-l);
  /*
   * 字距收到 0.06em（原来是 0.12em）。名牌上现在是「塞德里克·迪戈里」八个字，
   * 名牌是从左边缘一路按内容撑宽的，字距松一格就多压到右边那叠纸上十几像素。
   */
  letter-spacing: 0.06em;
  line-height: 1.18;
}
.cp-name .cpn-en {
  font-family: var(--font-hand);
  /* 比中文小两档：它只是同一个名字的另一半，不是第二行标题 */
  font-size: var(--fs-xs);
  font-weight: 400;
  letter-spacing: 0.1em;
  line-height: 1.15;
  opacity: 0.84;
}

/*
 * 好感条（z 6）：压在名牌上——这是创作者定的最上一层。
 * 它挂在名牌的右下角、往下探出一点，像别在牌子上的一张窄签。
 */
.cp-aff {
  position: absolute;
  right: -8px;
  bottom: -15px;
  z-index: 6;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 4px 12px 5px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  color: var(--ink);
  background: linear-gradient(180deg, #fbf4e4, #eddfc0);
  border: 1px solid rgba(64, 48, 31, 0.4);
  border-radius: 999px;
  box-shadow: 0 2px 0 rgba(50, 37, 24, 0.34), 0 6px 14px rgba(0, 0, 0, 0.42);
  transform: rotate(-1.2deg);
  white-space: nowrap;
}
.cpa-heart { color: var(--red); font-size: 11px; }
/* 十格刻度：点亮的填红，其余留一枚空槽（和手抄本人物簿同一套读法） */
.cpa-bar { display: inline-flex; gap: 2px; }
.cpa-bar i {
  width: 7px;
  height: 7px;
  border-radius: 1px;
  background: rgba(64, 48, 31, 0.2);
  box-shadow: inset 0 0 0 1px rgba(64, 48, 31, 0.28);
}
.cpa-bar i.on { background: var(--red); box-shadow: none; }
.cpa-word { color: var(--ink-soft); letter-spacing: 0.04em; }

/* 窄屏：框与人收窄一点，那一叠纸跟着让位 */
@media (max-width: 480px) {
  /*
   * 窄屏上不再单给 .cr-stage 一个尺寸：上面那条 `min(50%, 34vh, 420px)`
   * 本来就是按百分比与屏高算的，小屏自然就小，钉死一个 px 反而会把
   * 「大屏上放大」这件事一起钉没（190px 那个上限就是这么来的）。
   */
  .cr { gap: var(--sp-3); padding-bottom: 62px; }
  .cr-menu { gap: var(--sp-2); }
  /*
   * 窄屏再收一档。⚠️ 这几条要**比基础规则小**才对：
   * 从前这里的字号是 --fs-m / --fs-l，后来基础规则也降到了同一个数，
   * 这一整段就等于没写——小屏上纸片反而显得比大屏还大。别再让它们相等。
   */
  .cr-card { padding: 8px 11px 9px; }
  .cr-card.on { padding: 11px 13px 12px; }
  .crc-label { font-size: var(--fs-s); }
  .cr-card.on .crc-label { font-size: var(--fs-m); }
  .crc-en { font-size: 8px; letter-spacing: 0.06em; }
  .cr-card.on .crc-en { font-size: 9px; }
  .crc-note { font-size: 9px; }
  .crc-note .crn-en { font-size: 8px; margin-left: 4px; }
  .cr-plate { padding: 9px 18px 10px 52px; min-width: 62%; }
  /* 字号挂到中文那一行上：.cp-name 现在是两行的容器，给它定字号管不到里面 */
  .cp-name .cpn-zh { font-size: var(--fs-m); }
  .cp-name .cpn-en { font-size: var(--fs-xs); letter-spacing: 0.1em; }
  .dd-crest { height: 148%; left: -16px; }
  .dorm-door { min-width: 190px; padding: 11px 18px 12px 52px; }
  .dd-name { font-size: var(--fs-m); }
  /* 地图上那一行邂逅名牌：小屏上再收一档，别把地点牌挤到下面去 */
  .meet-door { min-width: 0; padding: 7px 12px 8px 34px; }
  .meet-row { margin-top: var(--sp-2); padding-top: var(--sp-2); }
}

/* ============================================================
   28 · 禁林
   ------------------------------------------------------------
   一屏两截：上面一颗【深入探索】（整行、字大一号），下面一排
   「你认得的地方」（每处一块窄牌）。**两截共用一副骨架**：
   名字竖着写、代价那一行小字跟在名字右边——一眼看出来是一类事。
   两处也是同一个深绿：这一屏的东西都长在林子这块地面上。
   ============================================================ */

/* 深绿那一族：底子、边框、亮字，两处按钮共用 */
:root {
  --forest-bg:    rgba(18, 42, 26, 0.82);
  --forest-bg-2:  rgba(28, 60, 36, 0.9);
  --forest-line:  rgba(126, 190, 132, 0.3);
  --forest-line-2: rgba(126, 190, 132, 0.6);
  --forest-ink:   #cfe3c8;   /* 名字：浅绿白，压在深绿上才看得清 */
  --forest-dim:   #8fae87;   /* 代价那一行小字 */
}

/*
 * 两颗按钮共用的骨架：一行里先**竖着写名字**，右边那一行小字跟着排。
 * 竖排只在这里处理一次——牌子（.trail）和那颗大的（.btn.forest）共用这副骨架，
 * 它们只是块头与底色不同，看上去就该是「一类事」。
 */
.forest-row {
  display: flex;
  flex-direction: row;
  align-items: stretch;
  gap: 6px;
}
.forest-row .fr-name {
  writing-mode: vertical-rl;
  font-size: var(--fs-m);
  letter-spacing: 0.16em;
  line-height: 1;
}
/* 价钱不是卖点，不该和名字一样大：小一号，压在主标题右边 */
.forest-row .fr-cost {
  align-self: flex-end;
  font-family: var(--font-hand);
  font-size: 10px;
  letter-spacing: 0.04em;
  white-space: nowrap;
  color: var(--forest-dim);
}

/*
 * 木牌上那行小字**也竖着写**（创作者 2026-10-01）：名字是竖的一列，边上钉一行
 * 横着的小字，看着就像贴歪了。
 * 只有【深入探索】那一颗留着横写：它是整行宽的一条，横着那截有地方放，
 * 竖起来反而把那一颗撑高一倍（而且里面混着数字与点号，竖排会把数字躺倒）。
 */
.trail .fr-cost {
  writing-mode: vertical-rl;
}

/*
 * 【深入探索】：整行一条、字大一号——它是一屏的正事。
 * 骨架和下面的牌子完全一样（名字竖排 + 代价在右），只是更大、更亮一档。
 */
.btn.forest {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-3) var(--sp-4);
  color: var(--forest-ink);
  background:
    radial-gradient(80% 60% at 14% 4%, rgba(190, 232, 190, 0.1), transparent 62%),
    linear-gradient(180deg, var(--forest-bg-2), var(--forest-bg));
  border: 1px solid var(--forest-line);
  border-left-width: 1px;          /* 盖掉 .btn 默认那道红色左沿 */
  box-shadow: 0 3px 0 rgba(8, 18, 10, 0.5), 0 10px 24px rgba(0, 0, 0, 0.42);
}
.btn.forest:hover {
  border-color: var(--forest-line-2);
  background:
    radial-gradient(80% 60% at 14% 4%, rgba(200, 240, 200, 0.16), transparent 62%),
    linear-gradient(180deg, rgba(38, 78, 46, 0.94), rgba(24, 52, 32, 0.92));
}
.btn.forest .fr-name { font-size: var(--fs-l); letter-spacing: 0.2em; }
.btn.forest .fr-cost { font-size: var(--fs-xs); }

/*
 * 池子空了（这一片都走遍了）：还是那颗的样子，但压暗、不接点击、也不收钱。
 * 走的是同一个 .btn.forest 骨架，只盖颜色与边框——别让它看着像坏了。
 */
.btn.forest.done {
  color: var(--forest-dim);
  background: rgba(16, 30, 20, 0.6);
  border-style: dashed;
  border-color: rgba(126, 190, 132, 0.22);
  box-shadow: none;
  cursor: default;
}
.btn.forest.done .fr-name { font-size: var(--fs-m); letter-spacing: 0.14em; }
.btn.forest.done:hover { transform: none; border-color: rgba(126, 190, 132, 0.22); }

/* 下半截：认下的地方 */
.forest-part { margin-top: var(--sp-5); }
.forest-cap {
  margin-bottom: var(--sp-2);
  font-size: var(--fs-xs);
  letter-spacing: 0.24em;
  color: var(--forest-dim);
}

/*
 * 那一排木牌：骨架在上面（.forest-row），这里只管块头与底子。
 * 一排窄牌立在林子边上——这是「木牌」的样子，不是菜单行。
 */
/* 原先这一排的摆法（.forest-trails：flex 横排）整块撤了——第 42 节把六处散进了一片地图 */
.trail {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 104px;
  padding: var(--sp-3) 10px;
  cursor: pointer;
  color: var(--forest-ink);
  background:
    linear-gradient(180deg, rgba(190, 232, 190, 0.08), transparent 40%),
    linear-gradient(180deg, var(--forest-bg-2), var(--forest-bg));
  border: 1px solid var(--forest-line);
  border-radius: var(--r);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.46), inset 0 1px 0 rgba(220, 240, 214, 0.08);
  transition: background 0.16s ease, border-color 0.16s ease, transform 0.16s cubic-bezier(0.16, 1, 0.3, 1);
}
.trail:hover {
  border-color: var(--forest-line-2);
  transform: translateY(-2px);
}
.trail:active { transform: translateY(0) scale(0.98); }
/* 认得、但这一处还没有事件可走的那几处：暗一档，代价那行写「还没走通」 */
.trail.quiet { background: linear-gradient(180deg, rgba(28, 60, 36, 0.6), rgba(18, 42, 26, 0.6)); }
.trail.quiet .fr-name { color: #a9c2a2; }

/* 回林边时那一句「刚才认下了什么」 */
.forest-note { color: var(--forest-ink); }

/* 天黑之后那一句：这一格走进去今晚就没得睡了（见 ui/screens-forest.js 的 nightHint） */
.forest-hint { margin-top: var(--sp-2); color: var(--forest-dim); }

/* ═══════════ 主线剧情：一篇文（ui/story-screen.js） ═══════════
 *
 * 这一屏不是对话框，是一张**越写越长的纸**：上面是已经写完的段落，
 * 底下是"我还想做什么"。所以排版按**读文章**来定，不按"点着看的对话"来定：
 *   1. 正文用衬线体、行距放大到 1.9——一千字连着读，行距不够眼睛会累；
 *   2. 行宽收到 34em 左右（中文一行 32～40 字最顺），贴在中间不成一堵墙；
 *   3. 段落之间空一行，首行不缩进（中文长文里缩进加空行会显得散）；
 *   4. 对白单独成段、左边压一道金线——一眼分得出谁在说话，不用读名牌。
 *
 * 配色跟着场景照来（这一屏铺的是礼堂那张照片），所以正文用暖白、金线点缀，
 * 和恋爱对话那一层是一家的。
 */

.doc-head {
  margin-bottom: var(--sp-5);
  text-align: center;
}

/* 抬头下面那两行小字：一行说"更早的收起来了"，一行说"现在在哪儿、什么时候" */
.doc-note,
.doc-where {
  margin: var(--sp-2) 0 0;
  font-size: var(--fs-s);
  color: var(--fg-dim);
}
.doc-where { letter-spacing: 0.08em; }

.doc {
  /* 读文章的那一栏：窄到一屏读得完一行，宽到不至于每行只落五六个字 */
  width: 100%;
  max-width: 34em;
  margin: 0 auto;
  color: var(--cine-text);
  cursor: pointer;   /* 点纸面 = 剩下的字一次全给你（见 skipReveal） */
}

/* 一轮 = 她走的那一步 + 它写的那一篇 */
.doc-turn { margin-bottom: var(--sp-6); }

/* 「你：…」那一行（.doc-act）在 v45 换成了气泡 .doc-b.you，见第 60 节 */

/* 正文段落。行距 1.9 是这一屏最要紧的一个数：一千字连着读 */
.doc-p {
  margin: 0 0 var(--sp-4);
  font-family: var(--font-body);
  font-size: var(--fs-m);
  line-height: 1.9;
  letter-spacing: 0.02em;
  color: #f2e8d6;
  overflow-wrap: anywhere;
}

/* 对白：单独成段，左边一道金线，说话的谁写在小字里 */
.doc-say {
  margin: 0 0 var(--sp-4);
  padding-left: var(--sp-4);
  border-left: 2px solid rgba(224, 178, 100, 0.5);
}
.doc-say .who {
  display: block;
  margin-bottom: 2px;
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  color: var(--cine-gold);
}
.doc-say .line {
  display: block;
  font-family: var(--font-body);
  font-size: var(--fs-m);
  line-height: 1.9;
  color: #fbf3e2;
}

/* 正在进行的这一轮（等 AI / 正在浮现 / 没写出来）：新落的墨微微亮一点 */
.doc-turn.pending .doc-p,
.doc-turn.pending .doc-say { animation: docIn 0.42s ease both; }
@keyframes docIn {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}

/* 没写出来的那一条：她那一步留着（是事实），底下跟一句说明 */
.doc-cut {
  margin: 0;
  font-size: var(--fs-s);
  color: rgba(212, 105, 95, 0.9);
}

/* 等它写的时候那条"墨迹"——它会自己消失，所以做得像纸上洇开的一点墨 */
.doc-ink {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: var(--sp-4) 0;
  color: var(--cine-dim);
}
.doc-ink .tx { font-size: var(--fs-s); letter-spacing: 0.1em; }
.doc-ink .hint { font-size: var(--fs-xs); opacity: 0.7; }
.doc-ink .dots { display: inline-flex; gap: 3px; }
.doc-ink .dots i {
  width: 4px; height: 4px;
  border-radius: 50%;
  background: currentColor;
  animation: inkPulse 1.1s ease-in-out infinite;
}
.doc-ink .dots i:nth-child(2) { animation-delay: 0.16s; }
.doc-ink .dots i:nth-child(3) { animation-delay: 0.32s; }
@keyframes inkPulse {
  0%, 100% { opacity: 0.25; transform: scale(0.8); }
  50%      { opacity: 1; transform: scale(1); }
}

/* ---------- 底下：选项 + 输入框 ---------- */

/* 托盘里这两块都是 div（不是 .btn），拿不到 .tray > .btn 那条"占满一行"，
 * 所以要自己声明——不然它们会按内容收窄，三条选项并排挤成一团。 */
.tray > .doc-compose,
.tray > .doc-tools { flex: 1 1 100%; }

.doc-compose {
  width: 100%;
  max-width: 34em;
  margin: 0 auto;
}
.doc-compose.busy { text-align: center; }
.doc-status {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  text-align: center;
  color: var(--cine-dim);
}

/*
 * 这一屏的托盘上面那道 32px 羽化留白，是给"主按钮"留的气口。
 * 剧情屏托里全是辅助件（三颗小选项、一个框、三颗小工具），
 * 留白只是在正文上再压掉一截——收到 16px，羽化还在，纸面多露出一行字。
 * :has() 认不出来就当这条不存在，退回原来的 32px 也不会难看。
 */
.tray:has(> .doc-tools) { padding-top: var(--sp-4); }

.doc-opts { display: flex; flex-direction: column; gap: 5px; margin-bottom: var(--sp-2); }
/* 点【展开选项】拉出来那一下：从上面滑下来一点（收着时它根本不进 DOM） */
.doc-opts { animation: docOptsIn 0.18s ease both; }
@keyframes docOptsIn {
  from { opacity: 0; transform: translateY(-5px); }
  to   { opacity: 1; transform: none; }
}

/*
 * 一条选项：羊皮纸那条的观感留着（它本来就是"你可以这么说"），
 * 但**不摆序号章**——三条并排在一篇文下面，序号不加信息，
 * 只占掉最左边一格。整条可点，点一下是"抄进框里"，不是"做决定"。
 *
 * 尺寸上刻意压扁：三条只是"接下来能怎么走"，正文才是这一屏的主角。
 * 所以行高收到 1.45、上下只留 7px、投影也只有薄薄一层——
 * 原来那条 12px 内边距 + 10px 外投影的厚卡片，三条叠起来就把纸面顶掉了。
 */
.doc-opt {
  position: relative;
  display: block;
  width: 100%;
  padding: 7px var(--sp-3);
  text-align: left;
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.45;
  color: var(--ink);
  background:
    radial-gradient(80% 60% at 14% 4%, rgba(255, 255, 255, 0.44), transparent 62%),
    linear-gradient(180deg, rgba(251, 244, 228, 0.94), rgba(238, 225, 196, 0.94));
  border: 1px solid rgba(150, 108, 46, 0.5);
  border-radius: var(--r);
  cursor: pointer;
  box-shadow:
    0 1px 0 rgba(120, 96, 62, 0.45),
    0 5px 12px rgba(0, 0, 0, 0.32),
    inset 0 0 0 1px rgba(240, 205, 140, 0.35);
  transition: transform 0.16s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.16s ease;
}
.doc-opt:hover { transform: translateY(-1px); border-color: rgba(190, 140, 60, 0.85); }
.doc-opt:active { transform: translateY(1px) scale(0.995); }
/* 序号只做"键盘 1/2/3 也能选"的提示，所以缩小、压到边上 */
.doc-opt .num {
  display: inline-block;
  min-width: 1.1em;
  margin-right: 5px;
  font-family: var(--font-hand);
  font-size: 11px;
  color: rgba(140, 47, 47, 0.75);
}

.doc-bar { display: flex; gap: var(--sp-2); align-items: flex-end; }
.doc-bar textarea {
  flex: 1;
  resize: none;
  min-height: 44px;
  max-height: 104px;
  font-family: var(--font-body);
  font-size: var(--fs-m);
  line-height: 1.6;
  color: var(--cine-text);
  background: rgba(12, 8, 5, 0.55);
  border: 1px solid rgba(224, 178, 100, 0.26);
  border-radius: var(--r);
  padding: 11px 12px;
}
.doc-bar textarea:focus { outline: none; border-color: rgba(224, 178, 100, 0.6); }
.doc-bar textarea::placeholder { color: rgba(196, 172, 136, 0.68); }
.doc-send {
  flex: none;
  font-family: var(--font-body);
  font-size: var(--fs-m);
  color: #2a1c10;
  background: linear-gradient(180deg, #f0d49a, #dcb872);
  border: 1px solid rgba(224, 178, 100, 0.5);
  border-radius: var(--r);
  padding: 12px var(--sp-4);
  cursor: pointer;
  box-shadow: 0 3px 0 rgba(50, 37, 24, 0.4);
}
.doc-send:hover { border-color: rgba(240, 205, 140, 0.9); }
.doc-send:active { transform: translateY(1px); }

/* 底下那三颗工具（背包 / 展开选项 / 离开）排成一行：**背包靠左、离开靠右、
 * 中间那颗落在正中**（创作者定的；从前三颗挤在中间一堆）。
 *
 * 三条讲究：
 *   1. **必须是一行**：窄屏那条 `.tray .btn { flex: 1 1 100% }`（见"移动端"段）
 *      会让每颗各占一整行，三颗叠三层，正文就被推出屏幕了。这里用更高一档的
 *      选择器 .tray .doc-tools .btn 明确写死 flex: 0 1 auto，把它们按回来。
 *   2. **够小**：它们是辅助按钮（开背包、拉选项、退出），不是主按钮——
 *      字号压到 12px、上下只留 3px、投影收掉，一眼是"三颗小东西"，不跟输入框抢。
 *   3. **不换行**：nowrap + 不给下限，320px 宽也排得下。
 */
.doc-tools {
  display: flex;
  flex-wrap: nowrap;
  justify-content: space-between;
  align-items: center;
  gap: 6px;
  width: 100%;
}
.tray .doc-tools .btn {
  flex: 0 1 auto;
  width: auto;
  min-width: 0;
  padding: 3px 11px 4px;
  font-size: var(--fs-xs);
  line-height: 1.5;
  letter-spacing: 0.04em;
  text-align: center;
  box-shadow: none;
  white-space: nowrap;
}
.tray .doc-tools .btn:hover { transform: none; box-shadow: none; }
.tray .doc-tools .btn:active { transform: translateY(1px) scale(0.98); }

/*
 * 【离开】那一颗要**一眼看得见**（创作者定的）。
 * 它和旁边两颗不一样：背包、展开选项是随手用的，这一颗是"这一程先到这儿"。
 * 所以它比旁边两颗大一号、字更亮，底子是暖色半透明——压在暗场景上也亮得起来；
 * 描边用金色，和这一屏的纸、灯一个调子（不是警示红，是"门在那儿"）。
 */
.tray .doc-tools .btn.exit {
  padding: 6px 16px 7px;
  font-size: var(--fs-s);
  letter-spacing: 0.08em;
  color: #fff4dd;
  background: rgba(150, 58, 46, 0.62);
  border-color: rgba(240, 205, 140, 0.72);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.42);
}
.tray .doc-tools .btn.exit:hover {
  color: #fffdf6;
  background: rgba(178, 72, 56, 0.8);
  border-color: rgba(255, 228, 174, 0.95);
}

/* 「再查一次」那一块：结果未知时才出现（见 ui/story-screen.js 的 lookAgainNode） */
.doc-again {
  width: 100%;
  max-width: 34em;
  margin: var(--sp-5) auto 0;
  padding: var(--sp-4);
  text-align: center;
  background: var(--glass);
  border: 1px solid var(--glass-line);
  border-radius: var(--r);
}
.doc-again p { margin: 0 0 var(--sp-3); }

@media (max-width: 460px) {
  .doc-p, .doc-say .line { font-size: var(--fs-s); line-height: 1.85; }
  .doc { max-width: none; }
}

/* ═══════════ 设置：右上角那颗齿轮，与里面的十格存档（ui/settings.js） ═══════════
 *
 * 齿轮为什么是绝对定位：状态栏是「日期在左、账目在右」两栏，中间没有它的位置，
 * 硬塞成第三个 flex 孩子只会把右边那两根条挤窄（space-between 还会把它俩挤到中间去）。
 * 所以它自己落在右上角，账目那一栏把右边 36px 让出来给它（见下面 .hud-stats 那条）——
 * 不让的话钱和两根条会正好铺到齿轮底下：账目栏是**贴内容底部**的，
 * 而"它顶在哪"随当天那几行字多长而变，撞不撞得上根本算不准。
 * 让开一条固定的位置，就永远撞不上了。
 *
 * 十格那一段是「一行一格」：左边数字章 + 中间一行信息 + 右边几颗小按钮。
 * 一格要报三件事（哪一天、谁的档、什么时候存的），挤成一块两行的信息块
 * 比排成三行更省地方，窄屏上也不会把按钮顶到屏幕外面去。
 */

/*
 * 日期旁边那枚小信封（【信件】的入口，见 ui/shell.js 的 renderHud 与 ui/letters.js）。
 * 它跟在大字日期后面，**不占托盘的地**：托盘不在的屏（文游 / 面板）也靠它进信。
 * 做成不高过那行大字的一小块——它是行内的一枚小图标，不是第三颗门牌。
 * position: relative 是给右上角那颗没读的红点用的（.hud-mail-dot 钉在它角上）。
 */
.hud-mail {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 1px;
  border: 0;
  background: transparent;
  cursor: pointer;
  opacity: 0.85;
  transition: opacity 0.14s ease, transform 0.14s ease;
}
.hud-mail:hover { opacity: 1; transform: translateY(-1px); }
.hud-mail:active { transform: translateY(1px); }
.hud-mail-img { display: block; height: 18px; width: auto; max-width: 24px; object-fit: contain; }
.hud-gear {
  position: absolute;
  /* 顶边跟日期那行大字对齐（状态栏的 padding-top 就是这个数），
   * 齿轮 30px 高、大字那行也差不多 30px——两条并排看着才是一行的东西。 */
  top: calc(env(safe-area-inset-top) + var(--sp-3));
  right: var(--sp-4);
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  padding: 0;
  color: var(--cine-dim);
  background: rgba(16, 11, 8, 0.55);
  border: 1px solid var(--glass-line);
  border-radius: 50%;
  cursor: pointer;
  backdrop-filter: blur(6px);
  transition: color 0.14s ease, border-color 0.14s ease, transform 0.14s cubic-bezier(0.16, 1, 0.3, 1);
}
.hud-gear:hover { color: #fff6e4; border-color: var(--cine-gold); }
.hud-gear:active { transform: scale(0.92); }

/* 给右上角那颗齿轮腾出右边这一条：账目照样右对齐，只是右缘落在齿轮左边。
 * 36px = 齿轮 30 + 中间那条缝，换到的是"什么时候都不会叠在一起"。 */
.hud-stats { padding-right: 36px; }

/* ---------- 设置那一页里的第一节 ---------- */

.set-title {
  margin: 0 0 4px;
  font-size: var(--fs-m);
  color: var(--ink);
  letter-spacing: 0.06em;
}
/*
 * 标题那一行：标题在左、动作在**最靠右**（存档那一节的【导入】就挂这儿）。
 * 底下的间距由这一行出，所以行内的标题自己那个下边距要收掉。
 */
.set-title-row { display: flex; align-items: center; gap: var(--sp-2); margin: 0 0 4px; }
.set-title-row .set-title { flex: 1 1 auto; min-width: 0; margin: 0; }
.set-note {
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-xs);
  line-height: 1.7;
  color: var(--ink-soft);
}

/* ---------- 滑条（声音那一节的音量、历史那一节的记忆条数，共用这一套） ---------- */
.range-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}
/* 滑条本身交给浏览器画，只把颜色对上羊皮纸/朱红这一身 */
.range {
  flex: 1 1 auto;
  min-width: 0;
  accent-color: var(--red);
  cursor: pointer;
}
/* 右边那个百分比：定宽，拖动时数字变了整行不会跟着抖 */
.range-n {
  flex: none;
  min-width: 3.2em;
  text-align: right;
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  color: var(--ink);
}

/*
 * 「当前提示词字符数」那条细长条（创作者 2026-09-28 定的版式）：
 * 它只是个读数，不该占一整张卡——一条矮窄线，字比正文小一档。
 */
.prompt-len {  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  margin-top: 10px;
  padding: 4px 10px 5px;
  font-size: var(--fs-xs);
  color: var(--ink-soft);
  border: 1px solid rgba(64, 48, 31, 0.26);
  border-radius: var(--r);
  background: rgba(255, 255, 255, 0.24);
}
.prompt-len .pl-zh { flex: 1 1 auto; min-width: 0; }
.prompt-len .pl-n {
  flex: none;
  font-family: var(--font-hand);
  color: var(--ink);
}

/* ---------- 设置 · 历史 那一节：两本账的行 ---------- */
.his-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 9px var(--sp-3);
  border-bottom: 1px dashed rgba(64, 48, 31, 0.22);
}
.his-row:last-child { border-bottom: 0; }
.his-tx { flex: 1 1 auto; min-width: 0; }
.his-zh { font-size: var(--fs-m); font-weight: 700; letter-spacing: 0.06em; color: var(--ink); }
.his-note { margin-top: 3px; font-size: var(--fs-xs); color: var(--ink-soft); }

/* ---------- 对话历史那张纸：一个角色一行 ---------- */
.chat-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 9px var(--sp-3);
  border-bottom: 1px dashed rgba(64, 48, 31, 0.22);
}
.chat-row:last-of-type { border-bottom: 0; }
.chat-who { flex: 1 1 auto; min-width: 0; }
.chat-name { font-size: var(--fs-m); font-weight: 700; letter-spacing: 0.06em; color: var(--ink); }
.chat-note { margin-top: 3px; font-size: var(--fs-xs); color: var(--ink-soft); }
/* 「进剧情」那个勾：小方块 + 两个字，靠右挤在一行里 */
.chat-lab {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: var(--fs-xs);
  color: var(--ink-soft);
  cursor: pointer;
}
.chat-pick { accent-color: var(--red); cursor: pointer; }
.set-wait,
.set-warn {
  margin: var(--sp-3) 0;
  padding: var(--sp-3);
  font-size: var(--fs-s);
  text-align: center;
  color: var(--ink-soft);
  background: rgba(213, 189, 146, 0.28);
  border: 1px solid var(--paper-3);
  border-radius: var(--r);
}

.slot-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: 5px;
  padding: 7px var(--sp-2) 7px 6px;
  background: rgba(255, 250, 240, 0.55);
  border: 1px solid var(--paper-3);
  border-radius: var(--r);
  transition: border-color 0.14s ease, background 0.14s ease;
}
/* 有档的那几行给一点纸白：一列看下去，哪几格有东西一眼就分得出来 */
.slot-row.used { background: var(--bright); }
/* 正被问着的那一格：和底下那条确认**连成一块**。
 * 所以要抽掉自己那道下边距、并把下面两个角削平——否则确认条的 border-top: 0
 * 会在接缝处露出一道纸色，看着像两块不相干的東西。 */
.slot-row.asking {
  margin-bottom: 0;
  border-color: var(--red);
  background: var(--bright);
  border-radius: var(--r) var(--r) 0 0;
}

/* 左边那个数字章：第几格。手写体、压暗，只是个记号 */
.slot-no {
  flex: none;
  width: 22px;
  text-align: center;
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  color: var(--ink-soft);
}
.slot-info {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
  line-height: 1.45;
}
/* 信息列的第一行：左边游戏里的日期，右边「几天前存」那点小字。
 * 挤的时候截左边那个（日期不能丢，它是认格子的主要依据），所以两端都留 min-width: 0。 */
.slot-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
  min-width: 0;
}
.slot-when {
  font-size: var(--fs-s);
  color: var(--ink);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.slot-row:not(.used) .slot-when { color: var(--ink-soft); }
.slot-who {
  font-size: var(--fs-xs);
  color: var(--ink-soft);
  /* 名字长的玩家不该把这一行撑到按钮底下 */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 那点小字不参与压缩：宁可左边日期截断，也要让「几天前」始终看得见
 * ——它正是「两格同一天但不同时候存」时唯一分得出来的东西。 */
.slot-age { flex: none; font-size: 11px; color: var(--ink-soft); opacity: 0.8; }

.slot-acts { flex: none; display: flex; gap: 4px; }

/* 一格上的小按钮（存 / 读 / 删）。它们不是主角，压到最小：
 * 一页里要摆十行，每颗按钮都按正常尺寸排，十行就成了一张表格。 */
.set-btn {
  padding: 4px 9px 5px;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  line-height: 1.4;
  color: var(--ink);
  background: linear-gradient(180deg, #fbf4e4, #efe1c4);
  border: 1px solid rgba(64, 48, 31, 0.36);
  border-radius: var(--r);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color 0.14s ease, transform 0.14s cubic-bezier(0.16, 1, 0.3, 1);
}
.set-btn:hover { border-color: var(--red); }
.set-btn:active { transform: translateY(1px); }
.set-btn:disabled { opacity: 0.45; cursor: not-allowed; }
/* 一行里只该有一颗「主」按钮：空档那颗【存到这一格】，或者确认条上那颗 */
.set-btn.primary { background: linear-gradient(180deg, #f0d49a, #dcb872); border-color: rgba(140, 108, 46, 0.7); }
.set-btn.danger { color: var(--red); }

/* 确认条：贴在被问的那一格底下，说清后果 + 两颗按钮（右边那颗永远能反悔） */
.slot-ask {
  /* 贴着上面那一格，中间那道线由那一格自己的下边框出（所以这里 border-top: 0）。
   * 不用负边距去对：负 1px 会把那道分隔线盖掉，接缝反而糊成一团。 */
  margin: 0 0 var(--sp-3);
  padding: var(--sp-3);
  background: rgba(140, 47, 47, 0.06);
  border: 1px solid var(--red);
  border-top: 0;
  border-radius: 0 0 var(--r) var(--r);
}
.slot-ask p {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-xs);
  line-height: 1.6;
  color: var(--ink);
}
.set-acts { display: flex; gap: var(--sp-2); justify-content: flex-end; }

/*
 * 读档列表最底下那颗【重开一局】：红底整行，跟上面十格分出一条线。
 * 它比"读一格"还狠——那一颗是换回去，这一颗是**从头再来**，所以要红、要问一句。
 */
.set-restart {
  margin-top: var(--sp-4);
  padding-top: var(--sp-3);
  border-top: 1px solid rgba(140, 47, 47, 0.28);
}
.set-restart .slot-ask { border-radius: var(--r); border-top: 1px solid var(--red); }
.set-restart-btn {
  display: block;
  width: 100%;
  padding: var(--sp-3);
  font-family: var(--font-body);
  font-size: var(--fs-m);
  letter-spacing: 0.12em;
  color: #fbeceb;
  text-align: center;
  background: linear-gradient(180deg, #8c2f2f, #5e1c1c);
  border: 1px solid rgba(224, 120, 120, 0.5);
  border-radius: var(--r);
  cursor: pointer;
  box-shadow: 0 3px 0 rgba(50, 20, 20, 0.4), 0 10px 22px rgba(0, 0, 0, 0.4);
  transition: background 0.14s ease, border-color 0.14s ease, transform 0.14s ease;
}
.set-restart-btn:hover { background: linear-gradient(180deg, #a33a3a, #6f2222); border-color: var(--red); }
.set-restart-btn:active { transform: translateY(1px); }
.set-restart-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }

/* ---------- 设置顶上那两节：存档 / API设置 ---------- */

.set-tabs {
  display: flex;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
  padding-bottom: 6px;
  border-bottom: 1px solid var(--paper-3);
}
.set-tab {
  padding: 4px 12px 3px;
  font: inherit;
  font-size: var(--fs-xs);
  letter-spacing: 0.14em;
  color: var(--ink-soft);
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  cursor: pointer;
  transition: color 0.14s ease, border-color 0.14s ease;
}
.set-tab:hover { color: var(--ink); }
/* 选中那一节：红套印下划线。这是全工程唯一的强调色，别换成金色去抢戏 */
.set-tab.on { color: var(--red); border-bottom-color: var(--red); }

/* 一行一个字段：标签压在输入框上面。
 * 标签在上而不是左：地址和模型名都长，左边挤一个标签会把值压成看不全的半截。 */
.api-field { margin: 0 0 var(--sp-2); }
.api-label {
  display: block;
  margin-bottom: 3px;
  font-size: var(--fs-xs);
  letter-spacing: 0.1em;
  color: var(--ink-soft);
}
.api-input {
  display: block;
  box-sizing: border-box;
  width: 100%;
  padding: 6px 8px;
  font: inherit;
  font-size: var(--fs-xs);
  color: var(--ink);
  background: rgba(255, 250, 240, 0.72);
  border: 1px solid var(--paper-3);
  border-radius: var(--r);
}
.api-input:focus { outline: none; border-color: var(--red); background: var(--bright); }
.api-input::placeholder { color: var(--paper-3); }

/* Key 那一行：输入框吃掉剩余宽度，「显 / 藏」那颗小的贴右边 */
.api-key-row { display: flex; align-items: flex-end; gap: var(--sp-2); }
.api-key-row .api-field { flex: 1; min-width: 0; }
.api-peek {
  margin-bottom: 2px;
  padding: 6px 9px;
  font: inherit;
  font-size: var(--fs-xs);
  color: var(--ink-soft);
  background: none;
  border: 1px solid var(--paper-3);
  border-radius: var(--r);
  cursor: pointer;
}
.api-peek:hover { color: var(--ink); border-color: var(--red); }

/* 在用哪一条：一行装下拉 + 两颗小按钮。整行只有一粒字那么高，不抢编辑表的地方 */
.api-pick {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-2);
}
.api-pick-label {
  flex-shrink: 0;
  /* 定宽：场合那三行里「面对面」比别的多一个字，不钉住就把三个下拉框挤成阶梯 */
  min-width: 3.6em;
  font-size: var(--fs-xs);
  letter-spacing: 0.14em;
  color: var(--ink-soft);
}
.api-select {
  flex: 1;
  min-width: 0;
  padding: 5px 7px;
  font: inherit;
  font-size: var(--fs-s);
  color: var(--ink);
  background: var(--bright);
  border: 1px solid var(--paper-3);
  border-radius: var(--r);
  /* 名字长了要截断，不能把整行撑开 */
  text-overflow: ellipsis;
  white-space: nowrap;
  overflow: hidden;
}
.api-select:focus { outline: none; border-color: var(--red); }

/* 三个场合各走哪一条：一小块，紧贴在「默认」那一行底下 */
.api-slots { margin: 0 0 var(--sp-3); }
.api-slots-head {
  margin: 0 0 6px;
  font-size: var(--fs-xs);
  letter-spacing: 0.14em;
  color: var(--ink-soft);
}

/* 导出 / 导入那张纸上的大框文本 */
.api-io-box {
  width: 100%;
  min-height: 180px;
  padding: 8px 10px;
  box-sizing: border-box;
  font: inherit;
  font-size: var(--fs-xs);
  line-height: 1.5;
  color: var(--ink);
  background: var(--bright);
  border: 1px solid var(--paper-3);
  border-radius: var(--r);
  resize: vertical;
  /* 一串 base64 似的东西不许把纸横向撑开 */
  word-break: break-all;
}
.api-io-box:focus { outline: none; border-color: var(--red); }
.api-io-box::placeholder { color: var(--paper-3); }

/* 导出前那句 Key 警告：整页唯一一处必须先读一段再按一颗的地方，给它点分量 */
.api-warn {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-m);
  font-weight: 700;
  color: var(--red);
}

/* 刚说过的一句话（通了 / 没通 / 切到哪条了） */
/*
 * 设置页里那几行"刚说过的话"（API 那一节的动静、存档那一节的导入导出）。
 * 两节共用这三个颜色类——同一种口气的话，在两张纸上不该是两个颜色。
 * 一律走小字：那是**回执**，不是标题，不该比它下面那张编辑表还抢眼。
 */
.set-off { font-size: var(--fs-xs); color: var(--ink-soft); }
.set-ok { font-size: var(--fs-xs); color: var(--leaf); }
.set-bad { font-size: var(--fs-xs); color: var(--red); }

/* 编辑表：和上面那列之间给一条缝，别看成一坨 */
.api-edit {
  margin: 0 0 var(--sp-3);
  padding-top: var(--sp-2);
  border-top: 1px solid var(--paper-3);
}

/* 窄屏：三颗按钮要留在原地不换行，所以信息那一段自己收窄——副行压一号字、
 * 长了就省略号截断（玩家名加上学院加上钱，本来也不是非读完不可的那一行）。
 * 按钮也收一号：一列十行，每行多占 4px 就是白白多滚半屏。 */
@media (max-width: 460px) {
  .slot-info .slot-who { font-size: 11px; }
  .set-btn { padding: 4px 7px 5px; }
}

/* 窄屏的状态栏左右内边距收到 sp-3（见"移动端"那一段），齿轮跟着收 */
@media (max-width: 560px) {
  .hud-gear { right: var(--sp-3); }
}

/* ============================================================
   开局 · 入学登记表（三页，像便签一样一页页翻）
   ------------------------------------------------------------
   第 1 页 姓名 / 性别 / 血统、第 2 页 掷属性、第 3 页 选魔杖。
   纸是米黄的便签（.setup-note），上头那一小块是抬头，底下是小圆点翻页。
   界面在 ui/setup.js。
   ============================================================ */
.setup { max-width: 46rem; margin: 0 auto; }

.setup-cap { text-align: center; margin: 0 0 var(--sp-3); }
.setup-cap .sc-zh {
  font-family: var(--font-hand);
  font-size: var(--fs-xl);
  letter-spacing: 0.14em;
  color: var(--cine-text);
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.9);
}
.setup-cap .sc-en {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.3em;
  color: var(--cine-gold);
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.9);
}
.setup-cap .sc-note { margin-top: 2px; font-size: var(--fs-xs); color: var(--cine-dim); }

/* 那张便签：和信纸同一副身板（米黄、细棕边、厚影），翻开时轻轻落下来 */
.setup-note {
  padding: var(--sp-4);
  color: var(--ink);
  background:
    radial-gradient(80% 50% at 14% 4%, rgba(255, 255, 255, 0.55), transparent 62%),
    radial-gradient(120% 90% at 52% 112%, rgba(140, 47, 47, 0.07), transparent 72%),
    linear-gradient(180deg, #fbf4e4, #eee1c4);
  border: 1px solid rgba(64, 48, 31, 0.38);
  border-bottom-color: rgba(64, 48, 31, 0.52);
  border-radius: var(--r);
  box-shadow: var(--sh-paper);
  animation: setupIn 0.28s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes setupIn {
  from { opacity: 0; transform: translateY(10px) rotate(-0.4deg); }
  to   { opacity: 1; transform: none; }
}
/* 屏内重画（点选项 / 翻页 / 加减属性）不重播入场动画，否则每点一下整张纸闪一遍 */
.setup-note.still { animation: none; }

/*
 * 第 3 页（选魔杖）整张便签换成**透光的暗色磨砂**：米黄的纸不透光、也不神秘，
 * 这一页要让礼堂的底图从框后面糊着透出来、压暗一档，配一点金边与内圈微光。
 * 底变暗了，纸里那些深色字就得翻成浅色（见下面 .glass 里那一组覆盖）。
 */
.setup-note.glass {
  background: rgba(14, 10, 20, 0.42);
  border: 1px solid rgba(224, 178, 100, 0.3);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.55), inset 0 0 46px rgba(224, 178, 100, 0.07);
  backdrop-filter: blur(10px) saturate(0.92) brightness(0.82);
  -webkit-backdrop-filter: blur(10px) saturate(0.92) brightness(0.82);
}
/* 底变暗 → 这一页里原本写在米黄纸上的深色字全部翻成浅色 / 金字 */
.setup-note.glass .setup-hint { color: rgba(244, 233, 212, 0.82); }
.setup-note.glass .wp-title { color: rgba(224, 178, 100, 0.9); }
.setup-note.glass .wp-note { color: rgba(244, 233, 212, 0.72); }
.setup-note.glass .wand-count { color: rgba(244, 233, 212, 0.85); }
.setup-note.glass .wand-arrow {
  color: #f0e6d2;
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(224, 178, 100, 0.42);
}
.setup-note.glass .wand-arrow:hover { background: rgba(255, 255, 255, 0.2); border-color: var(--cine-gold); }
/* 暗底上那排木/芯小按钮抬亮一档，金边才还看得清 */
.setup-note.glass .wp-chip { background: linear-gradient(180deg, rgba(64, 46, 74, 0.9), rgba(36, 26, 44, 0.9)); }
.setup-pager { display: flex; justify-content: center; gap: 7px; margin-top: var(--sp-3); }
.setup-pager i {
  width: 8px; height: 8px;
  border: 1px solid rgba(244, 233, 212, 0.45);
  border-radius: 50%;
  background: rgba(244, 233, 212, 0.18);
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.setup-pager i:hover { transform: scale(1.25); }
.setup-pager i.on { background: var(--cine-gold); border-color: var(--cine-gold); }

.setup-page { min-height: 16rem; }
.setup-hint { margin: 0 0 var(--sp-3); font-size: var(--fs-s); line-height: 1.75; color: var(--ink-soft); }
.setup-field { margin-bottom: var(--sp-4); }
.sf-label {
  margin-bottom: 5px;
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  color: var(--ink-soft);
}
.setup-name {
  width: 100%;
  padding: var(--sp-2) var(--sp-3);
  font-family: var(--font-hand);
  font-size: var(--fs-l);
  color: var(--red);
  background: rgba(255, 255, 255, 0.5);
  border: 1px solid rgba(64, 48, 31, 0.34);
  border-radius: var(--r);
  letter-spacing: 0.06em;
}
.setup-name:focus { outline: none; border-color: var(--red); background: rgba(255, 255, 255, 0.74); }
.setup-name::placeholder { color: var(--paper-3); letter-spacing: 0.18em; }

.setup-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.setup-chip {
  flex: 1 1 7rem;
  min-width: 0;
  padding: var(--sp-2) var(--sp-3);
  font-family: var(--font-body);
  color: var(--ink);
  text-align: center;
  background: rgba(255, 255, 255, 0.42);
  border: 1px solid rgba(64, 48, 31, 0.3);
  border-radius: var(--r);
  cursor: pointer;
  transition: background 0.14s ease, border-color 0.14s ease, transform 0.14s ease;
}
.setup-chip:hover { border-color: var(--red); transform: translateY(-1px); }
.setup-chip.on {
  background: rgba(255, 255, 255, 0.77);
  border-color: var(--red);
  border-left-width: 4px;
  cursor: default;
}
.setup-chip .chip-zh { display: block; font-size: var(--fs-m); font-weight: 700; letter-spacing: 0.06em; }
.setup-chip .chip-note { display: block; margin-top: 3px; font-size: var(--fs-xs); line-height: 1.5; color: var(--ink-soft); }
.setup-foot { margin: var(--sp-2) 0 0; font-size: var(--fs-xs); line-height: 1.7; color: var(--ink-soft); }

/* ---------- 第 3 页：选魔杖 ---------- */
/*
 * 磨砂框里再嵌一道浅槽，把「轮播 + 计数 + 箭头」这一块圈出来当展示位。
 * 外层 .setup-note.glass 已经磨过了，这里不再叠第二层 backdrop-filter（叠了会糊）。
 */
.wand-panel {
  padding: var(--sp-2) var(--sp-2) var(--sp-3);
  margin: 0 0 var(--sp-3);
  border-radius: calc(var(--r) + 4px);
  background: rgba(0, 0, 0, 0.22);
  border: 1px solid rgba(224, 178, 100, 0.18);
  box-shadow: inset 0 2px 14px rgba(0, 0, 0, 0.35);
}

/*
 * 一排横着摆的魔杖：最前面那根最大，后面的往两边缩、慢慢让出画面（变换在 ui/setup.js 里算）。
 * 拖动换一根 —— 所以这一块要 touch-action: none（不然手机上会先滚页面）。
 * 高度按屏幕给：最前面那根要占到约三分之一屏高，太小了不像「挑一根杖」。
 */
.wand-stage {
  position: relative;
  height: clamp(300px, 42dvh, 540px);
  margin: 0 0 var(--sp-2);
  touch-action: none;
  cursor: grab;
}
.wand-stage:active { cursor: grabbing; }
/*
 * 最前面那根背后一圈**淡淡的光晕**：从中心往外化开的暖金，压在魔杖底下、
 * 浮在磨砂底上——「这根是被挑中的」不用说话也看得出来。
 */
.wand-stage::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(70%, 420px);
  height: min(88%, 420px);
  transform: translate(-50%, -50%);
  background: radial-gradient(closest-side, rgba(255, 214, 140, 0.3), rgba(255, 214, 140, 0.1) 48%, transparent 74%);
  filter: blur(8px);
  pointer-events: none;
  z-index: 1;
}
.wand-card {
  position: absolute;
  left: 50%;
  top: 50%;
  transform-origin: 50% 60%;
  transition: transform 0.38s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.38s ease;
  will-change: transform;
}
.wand-card.ghost { pointer-events: none; }
.wand-card img {
  display: block;
  height: clamp(240px, 34dvh, 460px);
  width: auto;
  -webkit-user-drag: none;
  user-select: none;
  filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.5));
}
.wand-card.front img {
  filter: drop-shadow(0 10px 20px rgba(0, 0, 0, 0.55)) drop-shadow(0 0 14px rgba(255, 216, 148, 0.55)) drop-shadow(0 0 34px rgba(255, 200, 110, 0.3));
}

/* 最前面那根周围浮的那几点光：慢慢亮起来，又淡下去。
   z-index 要压过魔杖（杖那些卡片的 z-index 是 50 往下数），光点才浮在杖前面。 */
.wand-glow { position: absolute; inset: 0; pointer-events: none; z-index: 60; }
.wand-glow i {
  position: absolute;
  width: var(--s, 5px);
  height: var(--s, 5px);
  margin: calc(var(--s, 5px) / -2);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 242, 208, 0.95), rgba(255, 205, 120, 0));
  animation: wandMote 3.6s ease-in-out infinite;
  animation-delay: var(--d, 0s);
  opacity: 0;
}
@keyframes wandMote {
  0%, 100% { opacity: 0; transform: translateY(8px) scale(0.6); }
  30%      { opacity: 0.95; }
  70%      { opacity: 0.3; transform: translateY(-14px) scale(1.15); }
}

/* 计数 + 两侧箭头：划不动的玩家靠这两颗小箭头一根根跳 */
.wand-counter {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
}
.wand-count {
  min-width: 4em;
  text-align: center;
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  letter-spacing: 0.16em;
  color: var(--ink-soft);
}
.wand-arrow {
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font-size: 22px;
  line-height: 1;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.5);
  border: 1px solid rgba(64, 48, 31, 0.3);
  border-radius: 50%;
  cursor: pointer;
  transition: background 0.14s ease, border-color 0.14s ease, transform 0.14s ease;
}
.wand-arrow:hover { border-color: var(--red); background: rgba(255, 255, 255, 0.72); transform: scale(1.08); }
.wand-arrow:active { transform: scale(0.96); }
.wand-arrow:disabled { opacity: 0.32; cursor: not-allowed; transform: none; }

/* 底下左右两块：魔杖木 / 杖芯 */
.wand-picks { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
.wp-col { min-width: 0; }
.wp-title {
  margin-bottom: 5px;
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  text-align: center;
  color: var(--ink-soft);
}
.wp-chips { display: flex; flex-wrap: wrap; gap: 4px; justify-content: center; }
/*
 * 木 / 芯的小按钮走**深色魔法款**：暗紫黑的底、一道金边，选中时金边点亮、
 * 外圈泛一点金光——和上面玻璃柜里那排杖是同一套「夜里的手艺」语言。
 */
.wp-chip {
  padding: 5px 10px 6px;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  color: #efe4cd;
  background: linear-gradient(180deg, rgba(44, 32, 52, 0.94), rgba(26, 19, 32, 0.94));
  border: 1px solid rgba(224, 178, 100, 0.34);
  border-radius: var(--r);
  cursor: pointer;
  text-align: center;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08), 0 2px 6px rgba(0, 0, 0, 0.28);
  transition: background 0.14s ease, border-color 0.14s ease, box-shadow 0.14s ease;
}
.wp-chip:hover { border-color: rgba(224, 178, 100, 0.72); }
.wp-chip.on {
  background: linear-gradient(180deg, rgba(78, 54, 88, 0.96), rgba(44, 30, 52, 0.96));
  border-color: var(--cine-gold);
  box-shadow: 0 0 10px rgba(224, 178, 100, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.12);
}
.wp-chip .wp-zh { display: block; }
.wp-chip .wp-en {
  display: block;
  margin-top: 1px;
  font-family: var(--font-hand);
  font-size: 9px;
  color: rgba(224, 178, 100, 0.72);
}
.wp-note {
  min-height: 2.8em;
  margin-top: 6px;
  font-size: var(--fs-xs);
  line-height: 1.5;
  text-align: center;
  color: var(--ink-soft);
}

/*
 * 那颗【选择这根】：做短、居中、深棕底 + 金边——一枚压在手抄本上的火漆印章，
 * 不是从前那条占满一行的纸按钮。primary 那道红左边在这里要盖掉。
 */
.wand-confirm { display: flex; justify-content: center; margin-top: var(--sp-4); }
.wand-confirm .btn {
  width: auto;
  min-width: 0;
  padding: var(--sp-2) var(--sp-5);
  font-size: var(--fs-m);
  text-align: center;
  letter-spacing: 0.2em;
  color: #f3e3c0;
  background: linear-gradient(180deg, #4a2f1b, #31200f);
  border: 1px solid var(--cine-gold);
  border-left-width: 1px;
  box-shadow: 0 0 0 1px rgba(224, 178, 100, 0.22), 0 6px 16px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.12);
}
.wand-confirm .btn:hover {
  border-color: #f0d9a6;
  box-shadow: 0 0 0 1px rgba(224, 178, 100, 0.4), 0 8px 20px rgba(0, 0, 0, 0.46), inset 0 1px 0 rgba(255, 255, 255, 0.16);
}

/* 窄屏：两列改上下、魔杖缩一号 */
@media (max-width: 560px) {
  .wand-stage { height: clamp(240px, 36dvh, 340px); }
  .wand-card img { height: clamp(200px, 30dvh, 280px); }
  .wand-picks { grid-template-columns: 1fr; gap: var(--sp-2); }
  .wand-confirm .btn { width: auto; }
}

/* ─────────────────────────────
   钓鱼（ui/fishing.js）+ 钓具店（ui/screens-alley.js）
   ───────────────────────────── */
/*
 * 钓鱼那一屏：中间一片水，结果写在下面；**右下角一个小格**放鱼竿
 * （鱼竿不进背包，只在这儿切换）。
 */
.fish-stage { position: relative; margin-top: var(--sp-2); min-height: 42vh; }
.fish-empty { text-align: center; color: var(--cine-dim); padding: 18vh 0; }
/*
 * 刚钓上来那一条：从 `left/top`（每抛一竿换一处，见 ui/fishing.js）冒出来，
 * 亮一下，再慢慢浮起来化掉——**不常驻**（动画放完 animationend 就把自己收掉）。
 */
.fish-catch {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  pointer-events: none;
  animation: catchFloat 2.4s ease forwards;
}
@keyframes catchFloat {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.92); }
  12%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  52%  { opacity: 1; transform: translate(-50%, -54%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -64%) scale(0.98); }
}
/* 晕动症的玩家：别飘，短一点、直接淡掉 */
@media (prefers-reduced-motion: reduce) {
  .fish-catch { animation: catchFade 1.2s ease forwards; }
  @keyframes catchFade {
    0% { opacity: 0; } 20% { opacity: 1; } 100% { opacity: 0; }
  }
}
.fc-art { font-size: 44px; line-height: 1; }
/* 钓上来的那条现在有真图了（鱼约 477×270、杂物方一些）：按高收，别把水面撑爆 */
.fc-art-img { display: block; height: 44px; width: auto; max-width: 88px; object-fit: contain; }
.fc-name { font-size: var(--fs-l); font-weight: 700; color: var(--cine-text); }
.fc-sub { font-size: var(--fs-xs); color: var(--cine-gold); margin-top: 2px; }

/*
 * 「你钓起了个大家伙！」那张纸上的立绘窗（ui/fishing.js 的 askCapture）。
 * 和战斗里那套「立绘舞台」同一个思路：**一块固定的透明安全区**，
 * 全身图按原比例摆进来（contain，不压扁、不裁头），超出的部分由 overflow 收掉。
 * 窗本身无边框、无底色——玩家看到的只是那只动物站在纸上。
 */
.fish-big {
  position: relative;
  width: min(58%, 190px);
  aspect-ratio: 1;
  margin: 0 auto var(--sp-2);
  overflow: hidden;
  pointer-events: none;
}
.fish-big .beast-art {
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: 0;
}
.fb-name {
  margin: 0 0 var(--sp-2);
  text-align: center;
  font-size: var(--fs-l);
  letter-spacing: 0.14em;
  color: var(--ink);
}
.fb-q { font-size: 54px; color: var(--ink-soft); opacity: 0.6; }

.fish-rod-chip {
  position: absolute;
  right: 3%;
  bottom: 2%;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: var(--r);
  background: rgba(46, 30, 18, 0.72);
  border: 1px solid rgba(224, 178, 100, 0.35);
  color: #e6cf9a;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  cursor: pointer;
  backdrop-filter: blur(6px);
  transition: border-color 0.14s ease, background 0.14s ease;
}
.fish-rod-chip:hover { border-color: var(--cine-gold); background: rgba(58, 40, 26, 0.82); }
.fish-rod-art { font-size: 18px; line-height: 1; }
/* 鱼竿图是细长的一根（约 291×813）：这颗小格里按高收，宽度跟着走 */
img.fish-rod-art { height: 26px; width: auto; max-width: 26px; object-fit: contain; }

/*
 * 抛竿那一颗：屏幕正中偏下，泡在水里。
 * 居中靠**外面这层 wrapper**（left:50% + translateX），按钮自己那两态才不会被这条顶掉。
 * 长相照上面那根鱼竿的小格（.fish-rod-chip）来：同一套暗棕底 + 金细边 + 毛玻璃，
 * 只把字排成两行——动作名一行，代价一行小字。
 */
.fish-cast-wrap {
  position: absolute;
  left: 50%;
  /* 抬高一截，给正下方那颗「你不会受伤」的小气泡留位置（创作者 2026-10-17 点的位置） */
  bottom: 8%;
  transform: translateX(-50%);
  z-index: 2;
}
.fish-cast-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 6px 16px;
  border-radius: var(--r);
  background: rgba(46, 30, 18, 0.72);
  border: 1px solid rgba(224, 178, 100, 0.35);
  color: #e6cf9a;
  font-family: var(--font-body);
  cursor: pointer;
  backdrop-filter: blur(6px);
  transition: border-color 0.14s ease, background 0.14s ease;
}
.fish-cast-chip:hover { border-color: var(--cine-gold); background: rgba(58, 40, 26, 0.82); }
.fish-cast-chip:active { background: rgba(36, 23, 14, 0.88); }
.fcc-name { font-size: var(--fs-s); font-weight: 700; letter-spacing: 0.08em; }
.fcc-sub { font-size: var(--fs-xs); line-height: 1.2; color: var(--cine-gold); }

/* 换竿那一扇小窗：竿**一把把竖着**排（每块是竖长的方块，像一根根立着的竿） */
.rod-picks {
  display: flex;
  flex-direction: row;
  align-items: stretch;
  gap: var(--sp-2);
  overflow-x: auto;
  padding-bottom: 6px;
}
.rod-pick {
  flex: 0 0 auto;
  width: 84px;
  height: 220px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: var(--sp-2) 4px;
  border-radius: var(--r);
  background: rgba(255, 255, 255, 0.42);
  border: 1px solid rgba(64, 48, 31, 0.3);
  cursor: pointer;
}
.rod-pick.on { border-color: var(--red); background: rgba(255, 255, 255, 0.78); }
.rod-pick .rp-art {
  font-size: 22px;
  line-height: 1;
  /* 这根竿的图是竖长的一条（约 291×813）：给它一块固定的竖位，名字与说明照旧在下面 */
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 104px;
}
.rod-pick .rp-art-img { max-width: 100%; max-height: 100%; object-fit: contain; }
/* 名字**竖着写**（writing-mode 竖排）——一眼就是一根立起来的竿 */
.rod-pick .rp-zh {
  writing-mode: vertical-rl;
  text-orientation: upright;
  letter-spacing: 0.14em;
  font-size: var(--fs-s);
  font-weight: 700;
  color: var(--ink);
}
.rod-pick .rp-note { font-size: 9px; line-height: 1.35; text-align: center; color: var(--ink-soft); }
.rod-pick .rp-on { font-size: var(--fs-xs); font-weight: 700; color: var(--red); }

/* ─────────────────────────────
   马人商人（ui/centaur.js）：以物换物
   ───────────────────────────── */
/* 就一个框：他说的话 + 那颗按钮 */
.centaur-box {
  margin: var(--sp-3) auto 0;
  max-width: 30rem;
  padding: var(--sp-4);
  border-radius: var(--r);
  background: rgba(20, 16, 26, 0.42);
  border: 1px solid rgba(224, 178, 100, 0.28);
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.42), inset 0 1px 0 rgba(255, 255, 255, 0.05);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.centaur-box .ct-say { margin: 0 0 var(--sp-2); font-size: var(--fs-s); line-height: 1.85; color: var(--cine-text); }
.centaur-box .btn { margin-top: var(--sp-3); }

/* 摊开的手上东西：**只有名字与数量**（不给看能换到什么） */
.ct-list { display: flex; flex-direction: column; gap: 6px; max-height: 52vh; overflow-y: auto; }
.ct-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r);
  background: rgba(255, 255, 255, 0.42);
  border: 1px solid rgba(64, 48, 31, 0.3);
  cursor: pointer;
}
.ct-item:hover { border-color: var(--red); background: rgba(255, 255, 255, 0.66); }
.ct-item .ci-name { font-size: var(--fs-s); color: var(--ink); }
.ct-item .ci-qty { font-family: var(--font-hand); font-size: var(--fs-s); color: var(--ink-soft); }

/* 换到的：一列（那时已经进背包了，这张纸只是给看） */
.ct-got { display: flex; flex-direction: column; gap: 6px; }
.ct-get {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 6px 0;
  border-bottom: 1px solid rgba(64, 48, 31, 0.14);
}
.ct-get .cg-art { font-size: 20px; line-height: 1; }
.ct-get .cg-name { flex: 1; font-size: var(--fs-s); }
.ct-get .cg-qty { font-family: var(--font-hand); color: var(--ink-soft); }

/* 深色 + 绿边那颗（马人框里的【拿一样吃的来换】/【收下】）：
   和下面那张深绿弹页同一身语言——林子里的事，别用奶油色纸按钮 */
.btn.deep {
  color: #eef6e6;
  background: linear-gradient(180deg, rgba(38, 58, 40, 0.94), rgba(22, 36, 26, 0.96));
  border: 1px solid rgba(158, 200, 146, 0.55);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(220, 240, 214, 0.1);
}
.btn.deep:hover {
  border-color: #cfe3c2;
  background: linear-gradient(180deg, rgba(50, 74, 52, 0.96), rgba(28, 44, 32, 0.96));
}

/*
 * 深绿那几张纸（一键播种 / 挑种子 / 温室 / 马人…）里的钮**全走深绿那一身**：
 * 纸本身是深绿的，底下却浮着一排米黄纸片，看着像从别的窗口剪贴过来的
 * （2026-10-19 创作者：「按钮全统一深色 ui」）。色值与 .btn.deep 同一组，
 * 这里只是让"站在这张纸上"就够了，不用每处都记得传 kind: 'deep'。
 * ⚠️ 只圈 .sheet.green：米黄纸（默认皮）、家那套深棕皮、图鉴的浅纸都不动。
 */
.sheet.green .btn {
  color: #eef6e6;
  background: linear-gradient(180deg, rgba(38, 58, 40, 0.94), rgba(22, 36, 26, 0.96));
  border-color: rgba(158, 200, 146, 0.55);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(220, 240, 214, 0.1);
}
.sheet.green .btn:hover {
  border-color: #cfe3c2;
  background: linear-gradient(180deg, rgba(50, 74, 52, 0.96), rgba(28, 44, 32, 0.96));
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(220, 240, 214, 0.14);
  transform: translateY(-1px);
}
.sheet.green .btn:active { transform: translateY(1px); }
.sheet.green .btn:disabled { opacity: 0.45; transform: none; }

/* 深绿弹页（skin: 'green'）里那两张纸（马人）：行也走深绿，别留一排奶油色方块 */
.sheet.green .ct-item {
  background: rgba(240, 246, 236, 0.1);
  border-color: rgba(158, 200, 146, 0.34);
}
.sheet.green .ct-item:hover { background: rgba(240, 246, 236, 0.18); border-color: #cfe3c2; }
.sheet.green .ct-item .ci-name { color: var(--cine-text); }
.sheet.green .ct-item .ci-qty { color: #b6d6a4; }
.sheet.green .ct-get { border-bottom-color: rgba(158, 200, 146, 0.22); }
.sheet.green .ct-get .cg-name { color: var(--cine-text); }
.sheet.green .ct-get .cg-qty { color: #b6d6a4; }

/* ============================================================
   18 · 通讯（猫头鹰）：一条条气泡的聊天屏
   ------------------------------------------------------------
   和文游对话框（.vn）是**两层不同的东西**，各占各的 host：
     .vn      面对面：旁白 + 台词 + 立绘，一次出一整段
     .owl     猫头鹰：一条条短消息，自己的靠右、对面的靠左带小像
   创作者 2026-09-29 定的：这两条线不能混着用。
   ============================================================ */
.owl-host { position: fixed; inset: 0; z-index: 46; pointer-events: none; }
.owl-host:empty { display: none; }

/*
 * 聊天时页面那一层让开（和 body.vn-live 那一段同一个理由）：
 * 这一层是铺满整屏的，底下那屏的牌子会从边上露出来，
 * 看着就是「开着聊天，地图的按钮还在」。用 visibility 不用 display：
 * 留着占位，页面高度不塌，合上时滚动位置不会跳。
 * class 由聊天屏自己挂 / 摘（ui/owl.js）。
 */
body.owl-live .view,
body.owl-live .tray { visibility: hidden; }

.owl {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  pointer-events: auto;
  /* 底图还看得见一点（半透明 + 虚化），但重心全在这一屏的字上 */
  background: rgba(9, 7, 5, 0.92);
  /* 玩家自己那张背景图是**内联**铺上来的（ui/owl.js 的 paintBg）：
     两层——压暗的渐变 + 图本身；没设过时内联是空的，走上面这层纯色 */
  background-size: cover, cover;
  background-position: center, center;
  background-repeat: no-repeat, no-repeat;
  backdrop-filter: blur(10px);
  padding-left: max(var(--sp-4), env(safe-area-inset-left));
  padding-right: max(var(--sp-4), env(safe-area-inset-right));
  padding-bottom: env(safe-area-inset-bottom);
  --fg: var(--cine-text);
  --fg-dim: var(--cine-dim);
  --fg-accent: var(--cine-gold);
  animation: fade 0.18s ease both;
}

/* ---------- 顶上那一行：返回 + 这是谁 + 设置 ---------- */
.owl-head {
  position: relative;
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: calc(var(--sp-3) + env(safe-area-inset-top)) 0 var(--sp-3);
  border-bottom: 1px solid rgba(224, 178, 100, 0.22);
}
.owl-back {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-family: var(--font-body);
  font-size: var(--fs-s);
  color: #e6cf9a;
  background: none;
  border: 1px solid rgba(224, 178, 100, 0.3);
  border-radius: var(--r);
  padding: 6px 10px;
  cursor: pointer;
  transition: border-color 0.14s ease, color 0.14s ease;
}
.owl-back:hover { border-color: var(--cine-gold); color: #fbf0d2; }
.owl-back .ob-arrow { font-size: var(--fs-l); line-height: 1; margin-top: -2px; }
/* 右边留一颗同样宽的空位，名字才是真的居中（不是被返回键挤偏）。
   齿轮是**绝对定位**在这一格上的，所以它不参与这行 flex，位置不会把名字带偏。 */
.owl-head::after { content: ''; flex: none; width: 58px; }
.owl-gear {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  padding: 0;
  color: var(--cine-dim);
  background: rgba(16, 11, 8, 0.55);
  border: 1px solid var(--glass-line);
  border-radius: 50%;
  cursor: pointer;
  transition: color 0.14s ease, border-color 0.14s ease, transform 0.14s ease;
}
.owl-gear:hover { color: #fff6e4; border-color: var(--cine-gold); }
.owl-gear:active { transform: translateY(-50%) scale(0.92); }
.owl-who { flex: 1 1 auto; min-width: 0; text-align: center; }
.owl-who .ow-zh {
  font-size: var(--fs-l);
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--cine-text);
}
.owl-who .ow-en {
  margin-top: 1px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  color: var(--cine-dim);
}

/* ---------- 中间那一栏气泡 ---------- */
.owl-thread {
  flex: 1 1 auto;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: var(--sp-4) 0;
  overscroll-behavior: contain;
}
.owl-line { display: flex; align-items: flex-end; gap: 8px; }
.owl-line.me { justify-content: flex-end; }
.owl-face { flex: none; }
/* 小像本身就带描金画框、自己是透明底，缩到 34px 宽还认得出是谁 */
.owl-face .who-face.art { border: 0; }

/*
 * 气泡。
 * 尾巴那一角（自己那颗右下 / 对面那颗左下）**不圆**——这是即时通讯软件
 * 一眼就能认出来的那个记号；四面一样圆就成了一排圆角方块。
 */
.owl-bubble {
  max-width: 76%;
  padding: 9px 13px;
  border-radius: 12px;
  font-size: var(--fs-m);
  line-height: 1.55;
  letter-spacing: 0.01em;
  white-space: pre-wrap;
  overflow-wrap: break-word;
}
.owl-line.them .owl-bubble {
  background: rgba(52, 35, 21, 0.92);
  border: 1px solid rgba(224, 178, 100, 0.26);
  color: var(--cine-text);
  border-bottom-left-radius: 3px;
}
.owl-line.me .owl-bubble {
  background: rgba(140, 47, 47, 0.88);
  border: 1px solid rgba(224, 178, 100, 0.3);
  color: #fdf3e3;
  border-bottom-right-radius: 3px;
}
/* 「他在打字」那一排三点（.dots 那一套在 .vn-busy 里也用过） */
.owl-bubble .dots { display: inline-flex; gap: 4px; padding: 2px 0; }
.owl-bubble .dots i {
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--cine-dim);
  animation: blot 1s ease-in-out infinite;
}
.owl-bubble .dots i:nth-child(2) { animation-delay: 0.15s; }
.owl-bubble .dots i:nth-child(3) { animation-delay: 0.3s; }
/*
 * 红包气泡（2026-10-30）：AI 在通讯里发 `{红包：…}` 时，那条气泡换成这个红封套。
 * 没拆的是颗按钮（金边 + 「拆红包」），拆过就压暗、写「已拆开」——
 * 钱在拆的那一下入账（engine/red-envelope.js），重画多少次都不会再入。
 */
.owl-bubble.envelope {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 168px;
  padding: 10px 14px;
  background: linear-gradient(160deg, #a8322a, #7d1f1a 70%);
  border: 1px solid rgba(240, 200, 120, 0.55);
  border-bottom-left-radius: 3px;
  color: #ffe9c9;
  text-align: left;
  cursor: pointer;
}
.owl-bubble.envelope .env-ico { font-size: 22px; line-height: 1; }
.owl-bubble.envelope .env-tx { display: flex; flex-direction: column; gap: 2px; }
.owl-bubble.envelope .env-amt { font-size: var(--fs-m); font-weight: 700; letter-spacing: 0.02em; }
.owl-bubble.envelope .env-note { font-size: var(--fs-s); opacity: 0.9; }
.owl-bubble.envelope .env-state { font-size: var(--fs-xs); opacity: 0.75; }
.owl-bubble.envelope.opened {
  cursor: default;
  filter: saturate(0.55) brightness(0.82);
}
.owl-bubble.envelope:not(.opened):hover { border-color: rgba(255, 226, 160, 0.9); }
/*
 * 顶上那条系统提示（猫头鹰把联系方式送到了，见 ui/owl.js 的 noticeNode）：
 * 居中、压暗、圆角小药丸——和聊天软件里那句「你已添加了 XXX」是同一个位置感。
 * 它不在 owlHist 里，所以永远排在最上面，不会被当成一条消息读回去。
 */
.owl-notice {
  align-self: center;
  max-width: 88%;
  margin: 2px 0 12px;
  padding: 5px 13px;
  text-align: center;
  font-size: var(--fs-xs);
  line-height: 1.5;
  color: var(--cine-dim);
  background: rgba(255, 247, 232, 0.07);
  border-radius: 999px;
}

/* ---------- 底下那条输入栏 ---------- */
.owl-bar {
  flex: none;
  display: flex;
  align-items: flex-end;
  gap: var(--sp-2);
  padding: var(--sp-3) 0 var(--sp-3);
  border-top: 1px solid rgba(224, 178, 100, 0.18);
}
.owl-input {
  flex: 1 1 auto;
  resize: none;
  min-height: 44px;
  max-height: 120px;
  font-family: var(--font-body);
  font-size: var(--fs-m);
  line-height: 1.6;
  color: var(--cine-text);
  background: rgba(255, 247, 232, 0.06);
  border: 1px solid var(--glass-line);
  border-radius: var(--r);
  padding: 10px 12px;
}
.owl-input:focus { outline: none; border-color: var(--glass-line-2); background: rgba(255, 247, 232, 0.1); }
.owl-input::placeholder { color: rgba(189, 169, 138, 0.75); }
.owl-send {
  flex: none;
  font-family: var(--font-body);
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: 0.06em;
  color: #fdf3e3;
  background: linear-gradient(180deg, #a63a3a, #7e2626);
  border: 1px solid rgba(224, 178, 100, 0.42);
  border-radius: var(--r);
  padding: 12px var(--sp-4);
  cursor: pointer;
  box-shadow: 0 3px 0 rgba(50, 20, 20, 0.5);
  transition: transform 0.14s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.14s ease;
}
.owl-send:hover { border-color: var(--cine-gold); }
.owl-send:active { transform: translateY(1px); }
.owl-send:disabled { opacity: 0.45; cursor: not-allowed; transform: none; }

@media (max-width: 560px) {
  .owl-head::after { width: 48px; }
  .owl-bubble { max-width: 84%; }
  .owl-thread { padding: var(--sp-3) 0; }
}

/*
 * 通讯记录那张纸（【设置 · 历史 · 通讯历史】/ 抬头齿轮里的【历史】点开）：
 * 一张大框，玩家自己改——版式走 .profile-input / .memo-box 那一套
 * （和【对话历史】里那个人那本逐格对齐），这里不再另配样式。
 */

/* 齿轮里那张纸上的背景预览（【换一张】/【撤掉这张】上面那一条）：
   按高裁切成一条横幅，看得见换的是哪张图就够 */
.owl-bg-prev {
  margin: 0 0 10px;
  border: 1px solid rgba(64, 48, 31, 0.22);
  border-radius: var(--r);
  overflow: hidden;
}
.owl-bg-prev img { display: block; width: 100%; height: 118px; object-fit: cover; }

/*
 * 自由对话里那颗【不聊了】：**暗红底 + 红边**（创作者 2026-09-29 定的）。
 * 它和另外三颗（手抄本 / 背包 / 设置）并排挂在对话框上沿——
 * 那三颗是「随手点一下」，这一颗是**走**，得在一排里一眼认得出来。
 * 尺寸照 .btn.ghost 那一套（同一个托盘里，宽窄要齐），只换皮。
 */
.btn.quit {
  width: auto;
  font-size: var(--fs-s);
  padding: var(--sp-2) var(--sp-3);
  color: #f7ded9;
  background: linear-gradient(180deg, rgba(94, 28, 28, 0.92), rgba(58, 16, 16, 0.95));
  border: 1px solid rgba(198, 74, 74, 0.9);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(6px);
}
.btn.quit:hover {
  color: #fff2ee;
  border-color: #e37a7a;
  background: linear-gradient(180deg, rgba(118, 36, 36, 0.95), rgba(74, 22, 22, 0.96));
}

/*
 * 细长一条、字居中、暗棕底（创作者 2026-09-29 看截图后定的口径）。
 *
 * 谁在用：三张历史纸底下的【存下】，以及长期记忆那一块里的【总结】。
 * 和旁边那种「按内容宽的小方块」不是一路——那几颗是随手点一下的动作，
 * 这几颗是**这一张纸上要落下的那一笔**，长得就该像一条落款。
 *
 * ⚠️ 从前点掉过一次「大长条按钮」（见上面 .acts 那段注释），说的是
 *    「字靠左、整条横贯纸面」那种；这里是**字居中**的一条，不是那一种。
 *    要是不想让它占满整张纸的宽，把 width 改成一个定值就行。
 */
.btn.bar {
  width: 100%;
  display: block;
  text-align: center;
  padding: 10px var(--sp-3);
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: 0.24em;
  /* 字距是给中文留的气口，末尾那一格会顶出去——缩回来，看着才是居中的 */
  text-indent: 0.24em;
  color: #f4e6cd;
  background: linear-gradient(180deg, #5c4733 0%, #3a2b1e 100%);
  border: 1px solid rgba(64, 48, 31, 0.9);
  border-radius: var(--r);
  box-shadow: 0 3px 0 rgba(28, 20, 12, 0.5), 0 8px 18px rgba(0, 0, 0, 0.28);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
.btn.bar:hover {
  color: #fff6e4;
  background: linear-gradient(180deg, #6d5540 0%, #46341f 100%);
  border-color: #8a6f52;
}
.btn.bar:active { transform: translateY(1px); box-shadow: 0 1px 0 rgba(28, 20, 12, 0.5); }
.btn.bar:disabled { opacity: 0.5; }

/*
 * 他在敲字的时候，抬头那一行整个换成「对方正在输入…」
 * （ui/owl.js 的 setTyping）：名字让位给状态，所以压暗一档、不抢眼；
 * 英文那一行收起来——两个字的身份换成一整句状态，并排读不通了。
 */
.owl-who.typing .ow-zh {
  font-size: var(--fs-m);
  font-weight: 400;
  letter-spacing: 0.1em;
  color: #cdb489;
}
.owl-who.typing .ow-en { display: none; }

/*
 * 弹页底下那条按钮栏默认「按内容宽」（见上面 .sheet > .acts 那段注释）。
 * 细长那几颗（【存下】【总结】）是**这一张纸上要落下的那一笔**，得走满整张纸——
 * 那条规则的特异性比 .btn.bar 高，所以这里照它的写法再压一条，不然只有 72px 宽。
 */
.sheet > .acts > .btn.bar,
.panel-acts > .btn.bar { width: 100%; flex: 1 1 100%; }

/*
 * 登记表第 2 页【主角设定】那张大框（见 ui/setup.js 的 pagePersona）。
 * 它长在**便签纸**上（不是弹页里），所以底色要跟着纸走：微透的白，
 * 让纸的纹理透上来一点，看着还是写在纸上、不是贴了块塑料。
 */
.setup-persona {
  width: 100%;
  min-height: 9.5em;
  resize: vertical;
  font-family: var(--font-body);
  font-size: var(--fs-s);
  line-height: 1.7;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.42);
  border: 1px solid rgba(64, 48, 31, 0.3);
  border-radius: var(--r);
  padding: 10px 12px;
}
.setup-persona:focus { outline: none; border-color: var(--red); background: rgba(255, 255, 255, 0.62); }
.setup-persona::placeholder { color: var(--ink-soft); opacity: 0.72; }
.sp-len {
  margin-top: 6px;
  text-align: right;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  color: var(--ink-soft);
}

/* ============================================================
   19 · 开始页（开机那一张，见 ui/start.js）
   ------------------------------------------------------------
   黑底 + 紫框 + 金点慢慢往上飘 + HOGWARTS。
   创作者 2026-09-29 定的：网页一打开（包括刷新）先看到这一屏。
   整屏只有两件事可做：入学 / 查看存档。
   ============================================================ */
.start {
  position: fixed;
  inset: 0;
  z-index: 30;
  display: grid;
  /*
   * ⚠️ 这一列**必须夹住**（minmax(0, 1fr)）：不夹的话，格子里的 `.start-frame` 是
   *    `width: min(560px, 100%)`，容器在算这一列有多宽时那个百分比是"不定的"，
   *    于是按内容的最大宽度撑（比容器还宽）——框就从右边溢出、右边一截被吃掉，
   *    看起来整块往右偏。夹住之后列宽 = 容器内容宽，place-items 才真的把它居中。
   */
  grid-template-columns: minmax(0, 1fr);
  place-items: center;
  padding: var(--sp-5) var(--sp-4);
  /* 极暗的底，带一点点紫——像礼堂熄灯之后那种黑，不是纯灰 */
  background:
    radial-gradient(120% 90% at 50% 108%, rgba(74, 42, 110, 0.34), transparent 62%),
    radial-gradient(90% 70% at 50% -10%, rgba(88, 54, 128, 0.20), transparent 60%),
    #07060a;
  overflow: hidden;
}

/* ---------- 那一圈紫框 ---------- */
.start-frame {
  position: relative;
  z-index: 2;
  width: min(560px, 100%);
  padding: clamp(28px, 7vh, 56px) clamp(22px, 6vw, 48px) clamp(24px, 5vh, 40px);
  text-align: center;
  border: 1px solid rgba(158, 116, 214, 0.55);
  border-radius: 6px;
  /* 框里再套一圈暗紫：像礼堂门上的双线描金，不发光刺眼 */
  box-shadow:
    inset 0 0 0 1px rgba(84, 52, 132, 0.55),
    inset 0 0 0 9px rgba(12, 9, 20, 0.72),
    inset 0 0 0 10px rgba(158, 116, 214, 0.22),
    0 24px 70px rgba(0, 0, 0, 0.7);
  background: linear-gradient(180deg, rgba(20, 15, 30, 0.72), rgba(10, 8, 16, 0.86));
  backdrop-filter: blur(2px);
}
/* 四角那四枚小菱形：紫框的"描金"（纯装饰，不参与布局） */
.start-frame::before,
.start-frame::after {
  content: '';
  position: absolute;
  width: 7px;
  height: 7px;
  background: rgba(198, 160, 78, 0.85);
  transform: rotate(45deg);
}
.start-frame::before { left: 14px; top: 14px; }
.start-frame::after { right: 14px; bottom: 14px; }

.start-mark {
  font-size: 22px;
  line-height: 1;
  color: rgba(198, 160, 78, 0.9);
  text-shadow: 0 0 14px rgba(198, 160, 78, 0.5);
}

/* ---------- HOGWARTS ---------- */
.start-title {
  margin: 10px 0 6px;
  /* 这套字里那个 IM Fell English 正是'老学院'那一路（工程里已有的字体，不另引） */
  font-family: var(--font-hand, Georgia, 'Times New Roman', serif);
  font-size: clamp(38px, 12vw, 66px);
  font-weight: 400;
  letter-spacing: 0.18em;
  /* 末尾那一个字母的间距会把标题顶偏左，缩回来才真居中 */
  text-indent: 0.18em;
  line-height: 1.1;
  background: linear-gradient(180deg, #f0e3c2 0%, #c9a44f 48%, #8d6f2c 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 2px 10px rgba(158, 116, 214, 0.45));
}
.start-sub {
  margin-bottom: clamp(26px, 6vh, 44px);
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  letter-spacing: 0.18em;
  color: rgba(178, 156, 214, 0.85);
}

/* ---------- 两颗按钮：黑底、紫边、中文大英文极小 ---------- */
.start-btns { display: flex; flex-direction: column; gap: var(--sp-3); }
.start-btn {
  display: block;
  width: 100%;
  padding: 13px var(--sp-4) 11px;
  font-family: var(--font-body);
  color: #efe6ff;
  background: linear-gradient(180deg, rgba(24, 18, 38, 0.95), rgba(12, 9, 20, 0.98));
  border: 1px solid rgba(158, 116, 214, 0.6);
  border-radius: var(--r);
  cursor: pointer;
  transition: border-color 0.16s ease, box-shadow 0.16s ease, transform 0.14s cubic-bezier(0.16, 1, 0.3, 1);
}
.start-btn .sb-zh {
  display: block;
  font-size: var(--fs-l);
  font-weight: 700;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
}
.start-btn .sb-en {
  display: block;
  margin-top: 3px;
  font-family: var(--font-hand);
  font-size: 9px;
  letter-spacing: 0.34em;
  text-indent: 0.34em;
  color: rgba(178, 156, 214, 0.8);
}
.start-btn:hover {
  border-color: #b98cf0;
  box-shadow: 0 0 0 1px rgba(185, 140, 240, 0.35), 0 10px 26px rgba(0, 0, 0, 0.6);
  transform: translateY(-1px);
}
.start-btn:active { transform: translateY(1px); }
.start-btn.primary { border-color: rgba(198, 160, 78, 0.75); }
.start-btn.primary .sb-zh { color: #f6e7bf; }
.start-btn.primary:hover { border-color: #e0bb63; box-shadow: 0 0 0 1px rgba(224, 187, 99, 0.35), 0 10px 26px rgba(0, 0, 0, 0.6); }

.start-foot {
  margin-top: clamp(22px, 5vh, 36px);
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.14em;
  color: rgba(140, 124, 168, 0.7);
}

/* ---------- 金色光点：从下面慢慢飘上去 ---------- */
.start-motes { position: absolute; inset: 0; z-index: 1; pointer-events: none; overflow: hidden; }
.start-motes i {
  position: absolute;
  bottom: -8px;
  width: var(--s, 3px);
  height: var(--s, 3px);
  border-radius: 50%;
  background: radial-gradient(circle, #ffe9a8 0%, #d9ab48 55%, rgba(217, 171, 72, 0) 72%);
  opacity: 0;
  animation: moteRise var(--t, 16s) linear var(--d, 0s) infinite;
  will-change: transform, opacity;
}
@keyframes moteRise {
  0%   { opacity: 0; transform: translateY(0) translateX(0) scale(0.7); }
  8%   { opacity: var(--o, 0.5); }
  50%  { transform: translateY(-48vh) translateX(9px) scale(1); }
  82%  { opacity: var(--o, 0.5); }
  100% { opacity: 0; transform: translateY(-96vh) translateX(-6px) scale(0.85); }
}
@media (prefers-reduced-motion: reduce) {
  .start-motes i { animation: none; opacity: 0.22; }
  .start-motes i:nth-child(-n+8) { animation: moteBlink 6s ease-in-out infinite; }
  @keyframes moteBlink { 0%,100% { opacity: 0.12; } 50% { opacity: 0.3; } }
}

/* 【自由剧情 · 设置】里那一格：关掉「读取游戏内信息」之后才摆的日期框 */
.sg-box {
  margin-top: 10px;
  padding: 10px 12px;
  border: 1px solid rgba(64, 48, 31, 0.22);
  border-radius: var(--r);
  background: rgba(255, 255, 255, 0.3);
}
.sg-label { margin-bottom: 6px; font-size: var(--fs-s); color: var(--ink-soft); }
.sg-input {
  width: 100%;
  font-family: var(--font-body);
  font-size: var(--fs-m);
  color: var(--ink);
  background: rgba(255, 255, 255, 0.55);
  border: 1px solid rgba(64, 48, 31, 0.3);
  border-radius: var(--r);
  padding: 8px 10px;
}
.sg-input:focus { outline: none; border-color: var(--red); background: rgba(255, 255, 255, 0.7); }

/* ============================================================
   通讯 · 玩家自己传的头像（ui/owl-avatar.js）
   ============================================================ */
/* 自定义头像那一小块：系统那批小像是透明底的立绘，方照片得自己收个边才像头像 */
.who-face.face-img {
  border-radius: 7px;
  overflow: hidden;
  border: 1px solid rgba(224, 178, 100, 0.45);
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.5);
}
.who-face.face-img img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* 齿轮里那张纸上的头像预览：一行居中 */
.owl-face-prev { display: flex; justify-content: center; margin: 0 0 10px; }

/* ---------- 裁剪那一张纸 ---------- */
.ac-wrap { display: flex; justify-content: center; }
/*
 * 取景框：正方、overflow 藏住外头的部分。`touch-action: none` 是必须的——
 * 少了它，手机上拖图会变成滚页面，图纹丝不动。
 */
.ac-stage {
  position: relative;
  width: 260px;
  height: 260px;
  overflow: hidden;
  border: 1px solid rgba(64, 48, 31, 0.45);
  border-radius: var(--r);
  background: repeating-conic-gradient(rgba(64, 48, 31, 0.08) 0% 25%, transparent 0% 50%) 50% / 18px 18px;
  touch-action: none;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
}
.ac-stage:active { cursor: grabbing; }
/* 图按自己的像素尺寸摆，缩放与位移全靠 transform（transform-origin 得钉在左上角） */
.ac-img {
  position: absolute;
  left: 0;
  top: 0;
  transform-origin: 0 0;
  max-width: none;
  pointer-events: none;
  will-change: transform;
}

/* ---------- 开始页那份公告（ui/start.js 的 notice） ---------- */
/*
 * 它盖在整个开始页上：底下一层压暗的幕（点幕不收，只有底下那颗【知道了】能关，
 * 见那份「一张纸一个出口」的规矩），中间一张和开始页同一套的紫框黑底卡片。
 */
.start-notice {
  /* fixed 而不是 absolute：不管挂在哪一层底下，都永远浮在**屏幕**中间。
     手机上那颗【知道了】要是跟着内容沉到屏幕外去，这一层就等于把游戏锁死了。 */
  position: fixed;
  inset: 0;
  z-index: 5;
  /* flex + margin:auto 的居中：框放得下就居中，万一放不下也能整层滚动，
     不像 grid 的 place-items:center 那样会把溢出的上半截悄悄裁掉 */
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: auto;
  padding: var(--sp-4);
  background: rgba(5, 4, 8, 0.74);
  backdrop-filter: blur(3px);
  animation: fade 0.22s ease both;
}
.sn-frame {
  display: flex;
  flex-direction: column;
  width: min(520px, 100%);
  margin: auto;
  /* dvh 那一行盖住上一行：手机的 vh 是连地址栏后面一起算的大视口，
     84vh 居中后底边落在可视区的九成上下，按钮正好被地址栏压住（2026-09-30 报的）。 */
  max-height: min(84vh, 100%);
  max-height: min(84dvh, 100%);
  padding: clamp(20px, 4.5vh, 34px) clamp(18px, 5vw, 34px) clamp(16px, 3.5vh, 26px);
  border: 1px solid rgba(158, 116, 214, 0.55);
  border-radius: 6px;
  box-shadow:
    inset 0 0 0 1px rgba(84, 52, 132, 0.5),
    inset 0 0 0 9px rgba(12, 9, 20, 0.7),
    inset 0 0 0 10px rgba(158, 116, 214, 0.2),
    0 22px 60px rgba(0, 0, 0, 0.72);
  background: linear-gradient(180deg, rgba(20, 15, 30, 0.94), rgba(10, 8, 16, 0.97));
}
.sn-title {
  flex: none;
  margin-bottom: 6px;
  font-family: var(--font-hand);
  font-size: var(--fs-l);
  letter-spacing: 0.24em;
  text-indent: 0.24em;
  color: #f0e3c2;
  text-align: center;
}
/* 正文可滚：这份东西不短，小屏上不能撑破卡片 */
.sn-body { flex: 1 1 auto; overflow-y: auto; padding-right: 4px; overscroll-behavior: contain; }
.sn-body > div + div { margin-top: 12px; }
.sn-lead {
  font-size: var(--fs-m);
  color: #efe6ff;
  text-align: center;
  letter-spacing: 0.06em;
}
.sn-sub {
  font-size: var(--fs-s);
  color: rgba(178, 156, 214, 0.9);
  text-align: center;
  letter-spacing: 0.12em;
}
.sn-p { font-size: var(--fs-s); line-height: 1.75; color: rgba(206, 196, 226, 0.82); }
.sn-p > div + div { margin-top: 2px; }
/* 他圈出来的那两行：加粗 + 提亮（金压紫，和开始页那两颗按钮一个色系） */
.sn-hi {
  padding: 9px 12px;
  border-left: 2px solid rgba(224, 187, 99, 0.85);
  border-radius: 3px;
  background: rgba(224, 187, 99, 0.09);
  font-size: var(--fs-s);
  font-weight: 700;
  line-height: 1.7;
  color: #f6e7bf;
}
.sn-hi > div + div { margin-top: 2px; }
.sn-sign {
  font-family: var(--font-hand);
  font-size: var(--fs-m);
  color: rgba(198, 160, 78, 0.92);
  text-align: center;
  letter-spacing: 0.14em;
}
.sn-act { flex: none; margin-top: 16px; }

/*
 * 公告里那一格联系方式（ui/start.js 的 notice）：
 * 金线金底、居中、字号比正文大一档——玩家找的就是它，得在整张纸上跳出来。
 * 和「高亮那两行」是两种意思：那两行是重点说明，这一格是**要照着做**的一条路。
 */
.sn-contact {
  margin-top: 12px;
  padding: 12px 14px;
  border: 1px solid rgba(198, 160, 78, 0.55);
  border-radius: 4px;
  background: linear-gradient(180deg, rgba(198, 160, 78, 0.14), rgba(198, 160, 78, 0.06));
  text-align: center;
  line-height: 1.8;
}
.sn-contact .sc-lead { font-size: var(--fs-s); color: rgba(214, 204, 232, 0.9); }
.sn-contact .sc-link { font-size: var(--fs-m); font-weight: 700; color: #f6e7bf; }
.sn-contact .sc-link a {
  color: #ffd98a;
  text-decoration: underline;
  text-underline-offset: 3px;
  /* 长网址在窄屏上不许撑破卡片 */
  word-break: break-all;
}
.sn-contact .sc-link a:hover { color: #fff3d0; }

/* ============================================================
   开始页 · 更新公告（ui/start.js 的 showUpdates）
   ------------------------------------------------------------
   和那份作品公告同一个壳（.start-notice / .sn-frame），
   只多两样：一条一条的版本记录，和「最新那版」的那点强调。
   ============================================================ */
/* 更新那一张比免责声明窄一点、好读一行 */
.upd-frame { width: min(480px, 100%); }

.upd-item + .upd-item { margin-top: 16px; }
/* 最新那版：左边一道金线 + 抬头提亮（"略微突出"而不是另一种样式） */
.upd-item.fresh {
  padding-left: 12px;
  border-left: 2px solid rgba(224, 187, 99, 0.75);
}
.upd-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 6px; }
.upd-ver {
  font-family: var(--font-hand);
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: 0.1em;
  color: #f0e3c2;
}
.upd-item.fresh .upd-ver { color: #ffd98a; }
.upd-date {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  color: rgba(178, 156, 214, 0.8);
}
.upd-list { font-size: var(--fs-s); line-height: 1.8; color: rgba(206, 196, 226, 0.86); }
.upd-li + .upd-li { margin-top: 2px; }
/* 一条不带编号的补充（比如"这个开关怎么用"） */
.upd-note {
  margin-top: 6px;
  padding: 7px 10px;
  border-radius: 3px;
  background: rgba(158, 116, 214, 0.12);
  color: rgba(214, 204, 232, 0.9);
  line-height: 1.7;
}

/* 首页第三颗按钮：比【入学】【查看存档】小一号、轻一点（它不"进去"，只是看看） */
.start-btn.mini {
  padding: 9px var(--sp-4) 8px;
  border-color: rgba(158, 116, 214, 0.34);
}
.start-btn.mini .sb-zh {
  font-size: var(--fs-m);
  font-weight: 400;
  letter-spacing: 0.22em;
  text-indent: 0.22em;
  color: rgba(226, 216, 246, 0.9);
}
.start-btn.mini .sb-en { opacity: 0.7; }
.start-btn.mini:hover { border-color: rgba(185, 140, 240, 0.7); }

/* ---------- 21 · 神奇动物手提箱（ui/beasts.js） ----------
 * 整只箱子是一屏全屏层（和通讯屏同一层，压在弹页底下）；
 * 箱身是一张干净底图，标签 / 格子 / 按钮全是按百分比贴上去的零件。
 * 字号走 cqw：箱子随窗口缩放时，贴上去的字跟着箱子一起缩放。
 */
.case-host {
  /* 54：压在背包那层小窗（52）上面、弹页（56）下面——从背包里开箱子时不该被背包盖住 */
  position: fixed; inset: 0; z-index: 54;
  display: grid; place-items: center;
  /* 整屏挡住底下那世界：开着箱子时地图的牌子不该还能点 */
  background: rgba(23, 16, 10, 0.55);
}
.case-host:empty { display: none; }
/*
 * 箱子开着的那阵（body.case-live，ui/beasts.js 挂/摘）：小窗与弹页整层抬到箱子上面——
 * 喂食档是从箱子按出来的，浮在箱子底下等于没弹（创作者 2026-10-01 报的）。
 * 顺序：箱子 54 < 小窗 58 < 弹页 60；吐司(70)与飘字(60，DOM 在后)照旧浮在最上。
 */
body.case-live .panel-layer { z-index: 58; }
body.case-live .overlay { z-index: 60; }
.case {
  position: relative;
  height: min(94vh, 118vw);
  max-width: 96vw;
  aspect-ratio: 2048 / 2493;
  container-type: inline-size;
}
.case > * { position: absolute; }
.case-art { inset: 0; width: 100%; height: 100%; display: block; }
.case-x { top: 1.2%; right: 1.6%; width: 40px; height: 40px; z-index: 3; }
.case-title { left: 7%; top: 3.4%; width: 40%; }

/* 上半两颗入口标签：空白标签纸 + 往上写的黑字 */
.case-tag { background: none; border: 0; padding: 0; cursor: pointer; top: 12%; width: 19%; }
.case-tag.wh { left: 12%; }
.case-tag.breed { left: 36%; }
.case-tag img { width: 100%; display: block; }
.case-tag .ct-box {
  position: absolute; inset: 8% 6% 6%;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2%;
}
.case-tag .ct-zh {
  /* 版式图上那两行黑字只是「这儿要有字」的占位：真字走游戏自己的衬线墨字 */
  font-family: var(--font-body);
  font-weight: 600; font-size: clamp(13px, 4.1cqw, 24px); line-height: 1.32; letter-spacing: 0.16em;
  color: var(--ink); text-align: center;
}
/* 动物仓库摞两行（一行两个字），繁育区一行放下 */
.case-tag.wh .ct-zh { width: 2.5em; }
.case-tag.breed .ct-zh { white-space: nowrap; }
.case-tag .ct-en {
  font-family: var(--font-hand);
  font-size: clamp(7px, 1.6cqw, 11px); letter-spacing: 0.2em; text-indent: 0.2em;
  color: var(--ink-soft); white-space: nowrap;
}
.case-tag:hover img { filter: brightness(1.04); }
.case-tag:active { transform: scale(0.98); }

/* 功能按钮（详情 / 喂食 / 放生）：图就是键面 */
.case-fn { background: none; border: 0; padding: 0; cursor: pointer; display: block; }
.case-fn img { width: 100%; display: block; }
.case-fn:hover img { filter: brightness(1.06); }
.case-fn:active { transform: translateY(1px) scale(0.97); }

/* 空格子那颗框：本身是颗键，点去仓库挑一只 */
.case-slotbtn {
  background: none; border: 0; padding: 0; cursor: pointer; display: block;
  /* 空格子也锁成正方形：这张底图是 485×506（差 4%），不锁的话空的那格比站了动物的矮一截 */
  width: 100%; aspect-ratio: 1; overflow: hidden;
}
.case-slotbtn img { width: 100%; height: 100%; object-fit: cover; display: block; }
.case-slotbtn:hover img { filter: brightness(1.05); }
.case-slotbtn:active { transform: scale(0.98); }

/* 格子里放了动物：正方形羊皮纸卡（和仓库卡同一味底），中间先摆 ?，以后透明底头像铺这儿。
   点它去仓库替换 / 卸下。（圆形那版太丑，创作者 2026-10-01 撤了）
   ⚠️ overflow: hidden 不是装饰：这盒子是 grid 容器，格子里的立绘默认会把**自动最小尺寸**
   顶起来（竖图尤其明显——嗅嗅那张 636×966 能把一格 92 的框撑到 124），
   于是三格高矮不一、还有一截戳到框外面。裁在框里才是这里要的（创作者 2026-10-02）。 */
.case-avatar,
.bb-avatar {
  position: relative;
  width: 100%; aspect-ratio: 1;
  overflow: hidden;
  background: var(--case-sq-parchment) center / 100% 100% no-repeat;
  border: 0; border-radius: 6px;
  box-shadow: 0 3px 9px rgba(30, 20, 8, 0.3);
  display: grid; place-items: center;
  cursor: pointer; padding: 0;
  font-family: var(--font-body); font-size: 8cqw; color: var(--ink-soft);
}
/*
 * 立绘**钉死在框内 88% 那一方**（四周各留 6%）：不靠百分比高——百分比高在这种
 * 由 aspect-ratio 定高的盒子里落不住，图会照自己的比例往下长。
 * 裁哪儿照 data/assets.js 那个物种的 focus（object-position 由界面写进 style）。
 */
.case-avatar .beast-art,
.bb-avatar .beast-art {
  position: absolute;
  top: 6%; left: 6%;
  width: 88%; height: 88%;
}
.case-avatar:hover { filter: brightness(1.04); }
.case-avatar:active { transform: scale(0.98); }
/* 繁育槽里那张不吃 cqw（繁育纸不是容器），字号给固定值 */
.bb-avatar { font-size: 26px; }

/*
 * 一只动物的立绘，摆在格子 / 纸卡 / 相框正中（ui/beasts.js 的 beastArtNode）。
 *
 * 创作者要的是**方块里只看到头**：立绘是一整只动物，所以
 *   · `object-fit: cover` 把装不下的部分裁掉；
 *   · 裁哪儿（`object-position`）由 data/assets.js 里那个物种的 focus 给——
 *     横着侧身站的那几张（独角兽 / 海蛇 / 希拉克鱼）头在左边，竖着站的（嗅嗅 / 凤凰）头在上头，
 *     一个全局值会把一半物种裁成没有头。
 * 留 12% 的空：图缩在羊皮纸框里头，不压着框边。
 *
 * ⚠️ `mix-blend-mode: multiply` 是给**白底**擦出来的：这批立绘是白底（创作者说以后换透明底），
 *    压在羊皮纸上就是一块白方形。等换成透明底那批，这一行删掉就行。
 */
.beast-art {
  width: 88%;
  height: 88%;
  object-fit: cover;
  border-radius: 4px;
  mix-blend-mode: multiply;
  pointer-events: none;
}

/*
 * 详情纸那一格：这是「看这一只」的地方，整只站进来（不裁）。
 * 小格子（仓库卡 / 方格 / 繁育槽 / 孵化卡）走的是下面那张裁好的头像，没有头像的才退回 cover 裁头。
 */
.beast-art.full {
  width: 94%;
  height: 94%;
  object-fit: contain;
}

/*
 * 已经裁好的头像（assets/avatar/，登记在 data/assets.js 的 BEAST_AVATARS，ui/beasts.js 的 beastArtNode）。
 *
 * 这一张本来就是头，所以**不再裁**：cover 会把 0.7 的竖构图在方框里削掉上下两成（耳朵、角就没了），
 * 换成 contain 让整颗头站进来。这批图是透明底，四周不会留出一个白框。
 * 框本身的比例不动（方格是方的、仓库卡那扇窗是方的）——头像各家的裁切高矮不一，
 * 统一塞进同一个窗里才看得齐，所以宁可留一点空也不裁。
 */
.beast-art.av {
  object-fit: contain;
}
/* 野外那一摞圆圈：自己的规则排在后面，这里按三条选择器的权重压回 contain */
.wild-av .beast-art.av {
  object-fit: contain;
}

/*
 * 仓库那张竖纸卡（319×503）：头像是一个**正方形的窗**压在卡的上半，
 * 不是把整张长条卡都拿来当头像位——那样一条横着站的龙会被拉成只剩半截身子。
 * 名字还在卡的下沿（.bf-name），中间那段留白就是纸卡本来的样子。
 */
.beast-face .beast-art {
  width: 84%;
  height: auto;
  aspect-ratio: 1;
  align-self: start;
  margin-top: 6%;
}

/*
 * 头像占满整张纸卡（创作者 2026-10-03 指的图：中间缩成一个方块、四周空着不好看）。
 * 图照自己的比例铺满卡片的内框（左右各让 5% 给那道描边），顶端贴着描边往下长，
 * 长到哪儿算哪儿 —— 这批头像的宽高比不完全一致（0.6～1.0），所以宁可下面留一点空，
 * 也不 cover 二次裁（裁下去龙的角、嗅嗅的耳朵就没了）。
 * 名字那颗牌子本来就浮在纸卡下沿，图铺满后正好压在图上。
 */
.beast-face .beast-art.av,
.bd-face .beast-art.av {
  width: 90%;
  height: auto;
  aspect-ratio: auto;
  align-self: start;
  margin-top: 5%;
}

/*
 * 手提箱那几颗方格（出战格 / 三个生产格 / 孵化卡）：框是画在箱子上的正方形，改不了形状，
 * 所以头像在这里铺满**整格高度**（原来是 88% 那一方，四周都留空），左右那点空是这批头像的
 * 比例（0.7 的竖构图）自带的——再要满就得裁掉上下各一成半，耳朵角会没，所以停在这儿。
 */
.case-avatar .beast-art.av,
.bb-avatar .beast-art.av,
.hs-avatar .beast-art.av {
  top: 0; left: 0;
  width: 100%; height: 100%;
  object-fit: contain;
}

/* 出战格：格子在上，两颗钮在下 */
.case-slot.fight {
  left: 64%; top: 11.5%; width: 21%;
  display: flex; flex-direction: column; align-items: center; gap: 4%;
}
.case-slot.fight .case-fn { width: 76%; }

/* 下半：栏目名一条 + 三个生产格（钮在上、格子在下） */
.case-bar { left: 11%; top: 46.6%; width: 25%; }
.case-bar img { width: 100%; display: block; }
.case-bar span {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 3.1cqw; letter-spacing: 0.18em; color: var(--ink);
}
.case-col { top: 52.4%; width: 21.5%; display: flex; flex-direction: column; align-items: center; gap: 2.5%; }
.case-col.c1 { left: 14%; }
.case-col.c2 { left: 40.5%; }
.case-col.c3 { left: 67%; }
.case-col .case-fn { width: 76%; }

/*
 * 生产格底下那颗小【收获】（创作者 2026-10-02 指的位子：格子正下方）。
 * 小药丸、暗棕底 + 暖金字；右上角那份数 = 这一格攒了几份，没攒下就不挂角标、
 * 也不描金（空的那颗不该催人去按）。字号走 cqw：跟着箱子那一屏一起缩放。
 */
.case-harvest {
  position: relative;
  flex: 0 0 auto;
  width: 62%;
  padding: 1.1% 0 1.4%;
  border: 1px solid rgba(118, 85, 44, 0.75);
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(72, 51, 32, 0.95), rgba(44, 30, 18, 0.96));
  box-shadow: 0 2px 6px rgba(30, 20, 8, 0.35), inset 0 1px 0 rgba(255, 235, 200, 0.12);
  color: #f0dcae;
  font-family: var(--font-body);
  font-size: 2.5cqw;
  letter-spacing: 0.16em;
  text-indent: 0.16em;
  cursor: pointer;
}
.case-harvest:hover { filter: brightness(1.1); }
.case-harvest:active { transform: translateY(1px) scale(0.98); }
.case-harvest.has {
  border-color: rgba(224, 189, 120, 0.9);
  box-shadow: 0 0 10px rgba(224, 189, 120, 0.35), 0 2px 6px rgba(30, 20, 8, 0.35);
}
.case-harvest .ch-n {
  position: absolute;
  right: -3%;
  top: -40%;
  min-width: 3.4cqw;
  padding: 0 0.8cqw;
  border-radius: 999px;
  background: #8c2f2f;
  color: #fff3e2;
  font-size: 2cqw;
  line-height: 3.6cqw;
  text-indent: 0;
  letter-spacing: 0;
}

.case-note { left: 6%; top: 90%; width: 38%; }

/*
 * 按了【收获】之后那张纸：一样一行（图 / 中文名 + 一行极小英文 / 份数）。
 * 行的骨架借 .pick-row 那一味（细行 + 点线分隔），只是这里多了图与份数。
 */
.hv-list { display: flex; flex-direction: column; }
.hv-row {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 8px 2px;
  border-bottom: 1px dotted rgba(64, 48, 31, 0.22);
}
.hv-row:last-child { border-bottom: none; }
.hv-art {
  flex: 0 0 auto; width: 30px; height: 30px;
  display: flex; align-items: center; justify-content: center;
}
.hv-art-img { width: 30px; height: 30px; object-fit: contain; }
.hv-tx { flex: 1 1 auto; min-width: 0; }
.hv-nm { display: block; font-size: var(--fs-s); font-weight: 700; letter-spacing: 0.04em; }
.hv-en {
  display: block; margin-top: 1px;
  font-size: 9px; letter-spacing: 0.04em; color: var(--ink-soft); opacity: 0.72;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.hv-n { flex: 0 0 auto; font-family: var(--font-hand); font-size: var(--fs-m); color: var(--red); }

/* ── 仓库弹窗：换皮 + 三列卡 ── */
.sheet.beast-wh { background-image: var(--case-wh-panel); max-width: 470px; }
.sheet.beast-wh .sheet-head,
.sheet.beast-detail .sheet-head,
.sheet.beast-breed .sheet-head {
  border-bottom: 0; box-shadow: none;
  margin-bottom: 2px; padding-bottom: 0; min-height: 34px;
}
.beast-wh-title { position: relative; width: 62%; margin: 2px 0 14px; }
.beast-wh-title img { width: 100%; display: block; }
.beast-wh-title span {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 21px; font-weight: 700; letter-spacing: 0.34em; text-indent: 0.34em; color: var(--ink);
}
.beast-wh-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px 10px; }
.beast-card { display: flex; flex-direction: column; align-items: center; gap: 6px; border-radius: 8px; }
/* 挑配对那一档：整张卡可点；配不上的变灰点不动；挑中的描一道金边 */
.beast-card.pick { cursor: pointer; }
.beast-card.pick:hover { background: rgba(185, 138, 46, 0.08); }
.beast-card.off { opacity: 0.42; filter: grayscale(0.75); cursor: not-allowed; pointer-events: none; }
.beast-card.sel { outline: 2px solid #b98a2e; outline-offset: 2px; }
.beast-status { align-self: flex-start; width: 78%; display: block; margin: 0 0 -5px 3%; }
/* 繁育中那张牌还没画：同一家族的纸 bar 先拼一颗 */
.beast-status.dom {
  display: flex; align-items: center; justify-content: center; gap: 5px;
  background: #e9ddc7; border: 1px solid rgba(64, 48, 31, 0.26); border-radius: 6px;
  padding: 5px 0 6px; font-size: 12px; letter-spacing: 0.14em; text-indent: 0.14em; color: var(--ink);
}
.beast-status.dom::before { content: ''; width: 13px; height: 13px; border-radius: 50%; background: #5d8a4e; }
.beast-face { width: 100%; aspect-ratio: 319 / 503; background: var(--case-beast-card) center / 100% 100% no-repeat; display: grid; place-items: center; position: relative; }
/* 名字浮在纸卡下沿（创作者 2026-10-01 指的图一那个位置） */
.bf-name {
  position: absolute; left: 8%; right: 8%; bottom: 9%;
  text-align: center; font-size: 12px; letter-spacing: 0.08em; color: var(--ink);
  background: rgba(248, 241, 227, 0.85); border-radius: 4px;
  padding: 1px 4px 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.beast-card .case-fn { width: 90%; }
.beast-card .case-fn + .case-fn { width: 76%; }

/* ── 详情弹窗：横纸 + 三列 ── */
.sheet.beast-detail { background-image: var(--case-detail-panel); max-width: 780px; }
.bd-grid {
  display: grid;
  grid-template-columns: 30% 1fr 30%;
  grid-template-rows: auto auto;
  gap: 2px 22px;
  align-items: start;
}
/* 详情纸左边那一格：和仓库卡同一张底、同一个比例（从前是 10/9 的横框，把竖纸卡拉扁了） */
.bd-face { position: relative; overflow: hidden; grid-column: 1; grid-row: 1; aspect-ratio: 319 / 503; background: var(--case-beast-card) center / 100% 100% no-repeat; display: grid; place-items: center; }
/* 立绘没给之前的占位：相框里一个 ?（创作者定的） */
.bf-q { font-family: var(--font-body); font-size: 30px; color: var(--ink-soft); opacity: 0.7; }
.bf-q.big { font-size: 54px; }
.bd-lv {
  position: absolute; top: 2%; left: 2%;
  background: #9a6bd0; color: #fff;
  font-size: 12px; letter-spacing: 0.08em;
  padding: 2px 8px 3px; border-radius: 4px;
}
.bd-main { grid-column: 2; grid-row: 1; }
/* 名字就地改（创作者 2026-10-01）：平时是字 + 一支羽毛笔，点笔才变输入框 */
.bd-namewrap { display: flex; align-items: center; gap: 8px; }
.bd-name {
  display: inline-block; max-width: 100%;
  background: transparent; border: 0; border-bottom: 1px dashed transparent;
  font-family: var(--font-body); font-size: 26px; font-weight: 700; letter-spacing: 0.1em;
  color: var(--ink); padding: 0 2px 2px;
}
input.bd-name { width: 12ch; border-bottom-color: rgba(64, 48, 31, 0.4); }
.bd-pen {
  flex: none; background: none; border: 0; padding: 2px 4px; cursor: pointer;
  font-size: 16px; line-height: 1; color: var(--ink-soft);
  filter: grayscale(0.2);
}
.bd-pen:hover { transform: translateY(-1px) rotate(-8deg); }
.bd-chips { display: flex; flex-wrap: wrap; gap: 7px; margin: 8px 0 12px; }
/*
 *  chips 按品阶分色（创作者 2026-10-01 二稿：暗色那版撤了，要简约但好看）：
 *  传说暖金 / 史诗淡紫 / 一般羊皮白 / 差冷灰，各配一道同族深一点的细边；
 *  品级芯片跟同色族走（顶级=金、稀有=紫、普通=白），表现型与性格另两味淡色。
 */
.bd-chip {
  position: relative; overflow: hidden;
  padding: 3px 13px 4px; border-radius: 999px;
  background: #f6efe2; border: 1px solid #b7a687;
  color: #4a3a26; font-size: 12px; letter-spacing: 0.14em; text-indent: 0.14em;
  cursor: pointer;              /* 点一下浮出加成小纸片（见 ui/beasts.js 的 showChipPop） */
}
.bd-chip:active { transform: scale(0.96); }
.bd-chip.t-legend, .bd-chip.g3 {
  background: linear-gradient(180deg, #f7dd9a, #ecc469);
  border-color: #b98a2e; color: #5a3c05;
}
.bd-chip.t-epic, .bd-chip.g2 {
  background: linear-gradient(180deg, #e9dbf7, #d6bdee);
  border-color: #8d5fb8; color: #472a68;
}
.bd-chip.t-poor {
  background: #e6e4df; border-color: #9b968c; color: #6b665c;
}
/* 表现型三味色（创作者 2026-10-01 定的）：闪耀贝壳色渐变带粒子、白化白底紫边、原始色灰绿 */
.bd-chip.ph-shine {
  background: linear-gradient(135deg, #fefeff, #e9def6 45%, #cfe2f4);
  border-color: #cbb3e6; color: #5a4a7a;
  box-shadow: inset 0 0 6px rgba(203, 179, 230, 0.35);
}
.bd-chip.ph-shine .spark {
  background: #f3ebff; box-shadow: 0 0 4px 1px rgba(214, 196, 255, 0.9);
}
.bd-chip.ph-albino { background: #fbf9f4; border-color: #9a6bd0; color: #6b4f8f; }
.bd-chip.ph-original { background: #dfe5d8; border-color: #6f7d68; color: #3f4a3a; }
.bd-chip.ps { background: #e6ead9; border-color: #93a07f; color: #3f4a33; }
/* 传说芯片里一点金色粒子往上升（二稿保留的小细节，浅色底上用亮白金光） */
.bd-chip .spark {
  position: absolute; bottom: -3px; width: 2px; height: 2px; border-radius: 50%;
  background: #fffbe8; box-shadow: 0 0 4px 1px rgba(255, 244, 200, 0.9);
  opacity: 0; pointer-events: none;
  animation: sparkUp 2.4s linear infinite;
}
.bd-chip .spark:nth-of-type(1) { left: 18%; animation-delay: 0s; }
.bd-chip .spark:nth-of-type(2) { left: 42%; animation-delay: 0.7s; }
.bd-chip .spark:nth-of-type(3) { left: 66%; animation-delay: 1.3s; }
.bd-chip .spark:nth-of-type(4) { left: 84%; animation-delay: 1.9s; }
@keyframes sparkUp {
  0% { transform: translateY(0); opacity: 0; }
  18% { opacity: 0.95; }
  100% { transform: translateY(-24px); opacity: 0; }
}

/* 点词条 / 性格芯片浮出来的加成小纸片 */
.chip-pop {
  position: fixed; z-index: 80; max-width: 240px;
  background: #f8f1e3; border: 1px solid #b7a687; border-radius: 8px;
  box-shadow: 0 10px 26px rgba(30, 20, 8, 0.35);
  padding: 8px 11px 9px;
  font-size: 12px; color: var(--ink);
  animation: sheetIn 0.16s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.chip-pop .cp-head { display: flex; align-items: baseline; gap: 7px; margin-bottom: 4px; }
.chip-pop .cp-name { font-weight: 700; font-size: 13px; letter-spacing: 0.12em; }
.chip-pop .cp-sub { color: var(--ink-soft); font-size: 11px; letter-spacing: 0.08em; }
.chip-pop .cp-line { padding: 1px 0; letter-spacing: 0.06em; }
.bd-chip.void {
  width: 66px; height: 23px; padding: 0;
  background: rgba(64, 48, 31, 0.07);
  border: 1px dashed rgba(64, 48, 31, 0.34);
}
.bd-st { display: flex; justify-content: space-between; align-items: baseline; padding: 4px 2px; border-bottom: 1px dashed rgba(64, 48, 31, 0.2); }
.bd-st-zh { font-size: 14px; letter-spacing: 0.22em; color: var(--ink); }
.bd-st-v { font-size: 15px; color: var(--ink); }
.bd-side { grid-column: 3; grid-row: 1; padding-top: 4px; }
.bd-side-row { display: flex; justify-content: space-between; align-items: baseline; margin: 5px 0; font-size: 13.5px; color: var(--ink); }
.bd-side-row .bd-v { font-size: 14px; }
.bd-bar { height: 9px; border-radius: 999px; background: rgba(64, 48, 31, 0.16); overflow: hidden; margin: 3px 0 9px; }
.bd-bar i { display: block; height: 100%; background: #7da05d; }
/* 血条：样式同饱食度，只是红的 */
.bd-bar.red i { background: #b04a3f; }
/* 繁育成功那张纸：中间摆蛋（一枚一个，多胞胎并排） */
.bs-eggs { display: flex; justify-content: center; gap: 10px; margin-top: 4px; }
.bs-egg { font-size: 46px; line-height: 1; filter: drop-shadow(0 3px 6px rgba(30, 20, 8, 0.28)); }

/* 破壳那张纸：头像卡（同一味正方形羊皮纸）+ 名字，点它直接看详情 */
.hs-card {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  background: none; border: 0; padding: 0; margin: 0 auto; cursor: pointer;
}
.hs-avatar {
  width: 96px; height: 96px;
  background: var(--case-sq-parchment) center / 100% 100% no-repeat;
  border-radius: 6px; box-shadow: 0 3px 9px rgba(30, 20, 8, 0.3);
  display: grid; place-items: center;
  font-family: var(--font-body); font-size: 34px; color: var(--ink-soft);
}
.hs-card:hover .hs-avatar { filter: brightness(1.04); }
.hs-card:active { transform: scale(0.98); }
.hs-name { font-size: 14px; letter-spacing: 0.1em; color: var(--ink); }

/* 详情纸右下角那颗喂食 */
.bd-feed-slot { margin-left: auto; }
/* 详情纸里那颗【遗忘特质】（2026-10-20）：跟纸同一身浅色，按内容宽、不吃满一行 */
.bd-forget {
  margin-top: 14px;
  padding: 6px 14px 7px;
  font: inherit;
  font-size: 14px;
  letter-spacing: 0.14em;
  color: var(--ink);
  background: linear-gradient(180deg, #fbf4e4, #e9ddc7);
  border: 1px solid rgba(64, 48, 31, 0.42);
  border-radius: 6px;
  box-shadow: 0 2px 0 rgba(50, 37, 24, 0.3), 0 5px 12px rgba(0, 0, 0, 0.18);
  cursor: pointer;
}
.bd-forget:hover { filter: brightness(1.03); }
.bd-forget:active { transform: translateY(1px); }
.bd-words { grid-column: 2 / 4; grid-row: 2; }
.bd-words-t { font-size: 14.5px; font-weight: 700; letter-spacing: 0.14em; color: var(--ink); margin: 10px 0 7px; }

/* ── 繁育弹窗：干花纸 + 两格 + 状态钮 ── */
.sheet.beast-breed { background-image: var(--case-breed-panel); max-width: 660px; }
.sheet.beast-breed .beast-wh-title { width: 38%; margin: 2px 0 6px; }
.bb-slots { display: flex; justify-content: center; gap: 30px; margin: 20px 8px 16px; }
.bb-slot { background: none; border: 0; padding: 0; cursor: pointer; width: 35%; position: relative; }
.bb-slot img { width: 100%; display: block; }
.bb-slot:hover img { filter: brightness(1.05); }
/* 格子里已挑了家长：名字写在格子下沿 */
.bb-who {
  position: absolute; left: 6%; right: 6%; bottom: 22%;
  font-size: 11px; letter-spacing: 0.06em; color: var(--ink);
  background: rgba(248, 241, 227, 0.88); border-radius: 4px;
  padding: 1px 4px 2px; text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bb-state { position: relative; display: block; width: 44%; margin: 0 auto 6px; background: none; border: 0; padding: 0; cursor: pointer; }
.bb-state img { width: 100%; display: block; }
.bb-state span {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 15px; font-weight: 700; letter-spacing: 0.16em; text-indent: 0.16em; color: var(--ink);
}
.bb-state:hover img { filter: brightness(1.05); }
.bb-state:active { transform: scale(0.98); }

/* 背包抬头：手提箱与随身坩埚并排靠右（两颗共用 .ledger-pot-art）
   整组靠右由 .ledger-icons 的 margin-left:auto 负责，组内两颗不再各自 auto——
   不然 flex 会把多余的空间塞到它们中间，看着就是被拉开两半 */
.ledger-icons { margin-left: auto; display: inline-flex; align-items: flex-end; gap: 2px; }
.ledger-icons .ledger-pot { margin-left: 0; }
/*
 * 手提箱那颗要**再大一丢丢**（创作者 2026-10-02）：只放大这一颗的图（30 → 34），
 * 坩埚照旧。两颗的盒子因此差 4px 高——靠上面那行 `align-items: flex-end`
 * 把**标签底边**对齐，一排看着仍是齐的，不会一个高一个低。
 */
.ledger-pot-art.case-icon { height: 34px; max-width: 50px; }
@media (max-width: 420px) {
  .ledger-pot-art.case-icon { height: 30px; }
}

/* ---------- 22 · 钓鱼张力小游戏（ui/fishing-game.js） ----------
 * 深海军蓝的一汪水：JS 管位置与判定，这一节只管长相。
 * 动效一律慢、软；成功淡金、失败下沉，不用红色警告。
 */
.fg {
  position: fixed; inset: 0; z-index: 74;
  display: grid; place-items: center; padding: 16px;
  background: rgba(8, 16, 32, 0.74);
  animation: fgFade 0.24s ease both;
}
@keyframes fgFade { from { opacity: 0; } to { opacity: 1; } }

.fg-panel {
  width: min(88vw, 380px);
  box-sizing: border-box;
  padding: 16px 20px 14px;
  text-align: center;
  border-radius: 14px;
  border: 1px solid rgba(150, 182, 232, 0.34);
  background: linear-gradient(180deg, #12253f, #0a1830);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6), inset 0 0 0 1px rgba(214, 183, 120, 0.14);
  color: #dce8f8;
  cursor: pointer;
  user-select: none;
  touch-action: none;
  animation: fgIn 0.28s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes fgIn { from { opacity: 0; transform: translateY(8px) scale(0.985); } to { opacity: 1; transform: none; } }

.fg-head { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.fg-zh { font-family: var(--font-body); font-size: 21px; font-weight: 600; letter-spacing: 0.34em; text-indent: 0.34em; color: #eaf2ff; }
.fg-en { font-family: var(--font-hand); font-size: 9.5px; letter-spacing: 0.3em; text-indent: 0.3em; color: rgba(160, 190, 230, 0.6); }
.fg-fish { margin: 7px 0 10px; font-size: 12.5px; letter-spacing: 0.08em; color: #bcd4f2; }

/* 那一汪水：轨道 */
.fg-track {
  position: relative;
  height: min(262px, 46vh);
  border-radius: 10px;
  overflow: hidden;
  background:
    radial-gradient(120% 55% at 50% 0%, rgba(120, 170, 230, 0.1), transparent 62%),
    repeating-linear-gradient(180deg, rgba(255, 255, 255, 0.035) 0 2px, transparent 2px 26px),
    linear-gradient(180deg, #0a1a30, #10294a 58%, #0a1a30);
  box-shadow: inset 0 0 26px rgba(0, 0, 0, 0.55);
}
/* 极慢的水波（纯装饰，不影响判定） */
.fg-track::after {
  content: '';
  position: absolute; inset: -20% -10%;
  background: radial-gradient(60% 22% at 50% 50%, rgba(170, 215, 255, 0.07), transparent 70%);
  animation: fgWater 9s ease-in-out infinite;
  pointer-events: none;
}
@keyframes fgWater {
  0%, 100% { transform: translateY(-16%); opacity: 0.5; }
  50% { transform: translateY(16%); opacity: 0.95; }
}

/* 目标区：浅蓝半透明 + 一点光晕，大小固定，只呼吸亮度 */
.fg-zone {
  position: absolute; left: 6%; right: 6%;
  transform: translateY(-50%);
  border-radius: 8px;
  background: linear-gradient(180deg, rgba(126, 190, 255, 0.3), rgba(126, 190, 255, 0.15));
  border: 1px solid rgba(160, 210, 255, 0.5);
  box-shadow: 0 0 18px rgba(120, 190, 255, 0.22), inset 0 0 12px rgba(160, 215, 255, 0.22);
  display: grid; place-items: center;
  animation: fgBreath 3.6s ease-in-out infinite;
}
@keyframes fgBreath {
  0%, 100% { box-shadow: 0 0 14px rgba(120, 190, 255, 0.18), inset 0 0 10px rgba(160, 215, 255, 0.18); }
  50% { box-shadow: 0 0 22px rgba(120, 190, 255, 0.3), inset 0 0 14px rgba(160, 215, 255, 0.28); }
}
.fg-zone-tx { font-size: 10px; letter-spacing: 0.3em; text-indent: 0.3em; color: rgba(232, 245, 255, 0.72); }

/* 控制条：JS 每一帧写 top / height，这里只给样子 */
.fg-bar {
  position: absolute; left: 10%; right: 10%;
  transform: translateY(-50%);
  border-radius: 999px;
  background: linear-gradient(180deg, #eaf4ff, #9cc8f2 46%, #5b9ad6);
  box-shadow: 0 0 14px rgba(150, 205, 255, 0.5), inset 0 -2px 6px rgba(20, 60, 100, 0.45);
}
.fg.hold .fg-bar { box-shadow: 0 0 24px rgba(175, 222, 255, 0.8), inset 0 -2px 6px rgba(20, 60, 100, 0.4); }

/* 一次点击的小水波 */
.fg-ripple {
  position: absolute; left: 50%; bottom: 10%;
  width: 46px; height: 46px; margin-left: -23px;
  border-radius: 50%;
  border: 1px solid rgba(170, 215, 255, 0.65);
  pointer-events: none;
  animation: fgRipple 0.8s ease-out forwards;
}
@keyframes fgRipple {
  from { transform: scale(0.3); opacity: 0.9; }
  to { transform: scale(2.4); opacity: 0; }
}

.fg-label { margin-top: 12px; font-size: 11px; letter-spacing: 0.22em; text-indent: 0.22em; color: rgba(172, 200, 235, 0.82); }
.fg-meter {
  margin-top: 6px; height: 10px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(150, 185, 235, 0.3);
  overflow: hidden;
}
.fg-meter i {
  display: block; height: 100%; width: 0;
  background: linear-gradient(90deg, #6fb6ea, #a8e0ff);
  box-shadow: 0 0 10px rgba(150, 210, 255, 0.55);
  transition: width 0.12s linear;
}
.fg-msg { margin-top: 12px; min-height: 1.5em; font-size: 13px; letter-spacing: 0.1em; color: #e2efff; }
.fg-foot { margin-top: 3px; font-size: 10.5px; letter-spacing: 0.12em; color: rgba(150, 180, 215, 0.6); }

/* 收杆：成功淡金，失败下沉——都不响不炸 */
.fg.win .fg-panel { border-color: rgba(232, 202, 132, 0.6); }
.fg.win .fg-zone { background: linear-gradient(180deg, rgba(240, 214, 150, 0.34), rgba(240, 214, 150, 0.16)); border-color: rgba(235, 208, 145, 0.7); box-shadow: 0 0 26px rgba(235, 205, 140, 0.5); }
.fg.win .fg-bar { background: linear-gradient(180deg, #fff6dd, #e7c883); box-shadow: 0 0 22px rgba(240, 214, 150, 0.7); }
.fg.win .fg-msg { color: #f4e2b4; }
.fg.lose .fg-bar { animation: fgSink 0.9s ease-in forwards; }
@keyframes fgSink { to { transform: translateY(-50%) translateY(92px); opacity: 0.22; } }
.fg.lose .fg-msg { color: #9fb6d2; }

/* 传奇鱼竿一杆起稀有鱼：屏幕中间那一行字，亮一下再淡去 */
.fish-center-note {
  position: fixed; left: 50%; top: 46%;
  transform: translate(-50%, -50%);
  z-index: 72;
  max-width: min(84vw, 520px);
  box-sizing: border-box;
  padding: 10px 18px 11px;
  text-align: center;
  font-family: var(--font-body); font-size: 15px; line-height: 1.75; letter-spacing: 0.06em;
  color: #f3e6c8;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.75);
  background: rgba(20, 14, 8, 0.42);
  border: 1px solid rgba(214, 183, 120, 0.35);
  border-radius: 8px;
  pointer-events: none;
  animation: fishNote 3.8s ease both;
}
@keyframes fishNote {
  0% { opacity: 0; transform: translate(-50%, -46%); }
  14% { opacity: 1; transform: translate(-50%, -50%); }
  72% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -54%); }
}

/* ============================================================
   29 · 野外遭遇：某一处那一排圆头像 + 捕捉那张纸（ui/wild.js）
   ------------------------------------------------------------
   禁林那四处（石阵遗迹 / 沼泽 / 八眼巨蛛巢穴 / 独角兽深林）每天刷五只，
   摆成一排圆头像。边按层级分（创作者 2026-10-02 给的原文）：
   **3 级金边 / 2 级紫边 / 1 级无边**。
   跑掉的与已经收进箱子的那一只**整个撤掉**（他 2026-10-02 看截图后改的口径：
   不留印子、不占位），五只都没了就换一句空态。
   圈底是**深色半透明磨砂**（同一批截图里他点的：羊皮纸底太亮）。
   ============================================================ */

.wild-part { margin-top: var(--sp-3); }
.wild-cap {
  margin-bottom: var(--sp-2);
  text-align: center;
  font-size: var(--fs-xs);
  letter-spacing: 0.24em;
  color: var(--forest-dim, #bda98a);
}
/*
 * 原先这一排的摆法（.wild-row：flex 平均间距横排）整块撤了——
 * 第 41 节把这一处改成一片观察区，五只各站自己的位置（见文件末尾那一节）。
 */
.wild-one {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: var(--font-body);
}

/* 那一圈圆头像：**深色半透明磨砂**（创作者 2026-10-02 看截图后定的——原先那层羊皮纸底
   压在暗照片上太亮，一排像五个灯泡）。磨砂是背后那张照片透过 blur 来的，不是底色画出来的。
   里面的立绘**不再乘底**（这批图本身是透明底，multiply 只会把动物自己压黑）。 */
.wild-av {
  position: relative;
  display: block;
  width: 60px;
  height: 60px;
  border-radius: 50%;
  overflow: hidden;
  background: rgba(15, 11, 8, 0.46);
  backdrop-filter: blur(7px) saturate(0.9);
  -webkit-backdrop-filter: blur(7px) saturate(0.9);
  border: 2px solid rgba(224, 189, 120, 0.26);
  box-shadow: inset 0 1px 0 rgba(255, 235, 200, 0.08), 0 3px 10px rgba(0, 0, 0, 0.5);
}
.wild-av .beast-art {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  mix-blend-mode: normal;
}
/* 还没图的物种：圈里一个浅色问号（不是空白，免得看着像没画出来） */
.wa-q {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  font-size: 20px;
  color: rgba(238, 224, 196, 0.42);
}
/* 3 级：金边 */
.wild-av.g3 {
  border-color: #e0bd78;
  box-shadow: 0 0 0 1px rgba(118, 85, 44, 0.8), 0 0 14px rgba(224, 189, 120, 0.55);
}
/* 2 级：紫边 */
.wild-av.g2 {
  border-color: #a58ac7;
  box-shadow: 0 0 0 1px rgba(70, 52, 96, 0.8), 0 0 12px rgba(165, 138, 199, 0.45);
}
/* 1 级：无边（就是上面那圈默认的暗金细边） */

.wild-nm {
  max-width: 76px;
  font-size: 11px;
  line-height: 1.3;
  letter-spacing: 0.06em;
  color: var(--cine-text);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9);
}
.wild-cost { margin: var(--sp-2) 0 0; text-align: center; }
.wild-none { margin: 2px 0 0; text-align: center; }

/* ── 捕捉那张纸：一张脸 + 一行概率 + 手上那几瓶药 ── */

.cpt-face {
  display: flex;
  justify-content: center;
  height: 168px;
  margin-bottom: var(--sp-2);
}
.cpt-face .beast-art.full { width: auto; height: 100%; max-width: 100%; }
.cpt-name {
  margin: 0 0 var(--sp-1);
  text-align: center;
  font-size: var(--fs-l);
  letter-spacing: 0.14em;
  color: var(--ink);
}
/* 打过一架那一句（创作者 2026-10-02 指的）：名字上面一行套印红，只在这一只真被打趴过时出现 */
.cpt-weary {
  margin: 0 0 2px;
  text-align: center;
  font-size: var(--fs-s);
  letter-spacing: 0.08em;
  color: var(--red);
}
/* 颜色 / 属性词条 / 性格那一条：芯片与详情纸共用同一批函数画的，这儿只是居中排一行 */
.trait-strip { justify-content: center; margin: 4px 0 10px; }
.cpt-pots {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: var(--sp-2);
}
.cpt-pot {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  width: 100%;
  padding: 5px 9px;
  border: 1px solid var(--paper-3);
  border-radius: var(--r);
  background: rgba(255, 250, 240, 0.44);
  font-family: var(--font-body);
  text-align: left;
  cursor: pointer;
}
.cpt-pot.on {
  border-color: rgba(140, 47, 47, 0.65);
  background: rgba(140, 47, 47, 0.09);
  box-shadow: inset 0 0 0 1px rgba(140, 47, 47, 0.22);
}
.cpt-art { flex: 0 0 auto; display: flex; }
.cpt-art img { width: 26px; height: 26px; object-fit: contain; }
.cpt-tx { flex: 1 1 auto; min-width: 0; }
.cpt-nm { display: block; font-size: var(--fs-s); letter-spacing: 0.08em; color: var(--ink); }
.cpt-bonus { display: block; font-size: 11px; letter-spacing: 0.06em; color: var(--ink-soft); }
.cpt-qty { flex: 0 0 auto; font-size: var(--fs-xs); color: var(--ink-soft); }
.cpt-tick { flex: 0 0 auto; font-size: 12px; color: var(--red); }

/* ============================================================
   30 · 遭遇战那一屏（ui/battle.js）
   ------------------------------------------------------------
   一身暗棕 + 古铜金：昏暗的魔法房间里，古老的金色魔法卡正在发光。
   规格里的顺序（第八节）：敌方立绘 → 己方立绘 → 魔力 → 三张手牌 → 结束回合。

   立绘舞台（.bp-stage）是**纯视觉层**：无边框、无底色、无阴影、
   overflow hidden、pointer-events none——玩家看不见这个框，
   只看见动物站在场景里（规格第二、十三节）。图绝不拉伸：
   max-width / max-height 装进舞台（保持原比例），再按每个物种自己的
   scale / x / y 摆（数在 data/battle.js 的 BATTLE_PORTRAITS）。
   层级（规格第八节）：底图 → 立绘 → 特效（.bt-fx）→ 名牌与 HP → 卡牌 → 按钮。
   ============================================================ */

.battle {
  --bt-bg: #211712;
  --bt-panel: #2d2018;
  --bt-panel-dark: #19110d;
  --bt-gold: #c49a55;
  --bt-gold-light: #e0bd78;
  --bt-gold-dark: #76552c;
  --bt-paper: #d5c09a;
  --bt-text: #f1dfbd;
  --bt-danger: #8f4a3d;

  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  width: min(94%, 40rem);
  margin: 0 auto;
  padding: var(--sp-3) var(--sp-3) var(--sp-4);
  border: 1px solid rgba(196, 154, 85, 0.42);
  border-radius: 6px;
  background:
    linear-gradient(180deg, rgba(45, 32, 24, 0.94), rgba(25, 17, 13, 0.96)),
    radial-gradient(120% 80% at 50% 0%, rgba(224, 189, 120, 0.14), rgba(0, 0, 0, 0));
  box-shadow: 0 22px 54px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 235, 200, 0.08);
  color: var(--bt-text);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
}

.bt-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding-bottom: 6px;
  border-bottom: 1px solid rgba(196, 154, 85, 0.28);
}
.bt-turn { font-size: var(--fs-xs); letter-spacing: 0.2em; color: var(--bt-gold); }
.bt-vs {
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  letter-spacing: 0.28em;
  color: var(--bt-paper);
  opacity: 0.85;
}

/* ── 场地：敌方在上、己方在下，中间那一层放飘字 ── */

.bt-arena {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  min-height: min(52vh, 420px);
  padding: var(--sp-2) 0;
}
.bt-side {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  padding: 4px 6px 6px;
  border: 1px solid transparent;
  border-radius: 8px;
  transition: border-color 160ms ease, box-shadow 160ms ease, background 160ms ease;
}
/* 拖着牌经过时，这一边亮一圈暗金（规格第十一节：目标区域出现暗金色魔法光晕） */
.bt-side.drop-hot {
  border-color: rgba(224, 189, 120, 0.7);
  background: radial-gradient(70% 70% at 50% 50%, rgba(224, 189, 120, 0.16), rgba(0, 0, 0, 0));
  box-shadow: 0 0 22px rgba(196, 154, 85, 0.3);
}

/* 立绘舞台：透明安全区，超出的部分裁掉 */
.bp-stage {
  position: relative;
  overflow: hidden;
  pointer-events: none;
  width: min(58%, 260px);
  height: min(24vh, 190px);
}
.enemy-portrait-stage { height: min(25vh, 200px); }
.bp-img {
  position: absolute;
  left: 50%;
  top: 50%;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 8px 18px rgba(0, 0, 0, 0.55));
  transition: transform 220ms ease, opacity 320ms ease;
}
.bp-none {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  font-size: 40px;
  color: rgba(213, 192, 154, 0.5);
}
/* 那一层柔光：治疗 / 护盾 / 暴击都借它发光（平时是灭的） */
.bp-glow {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 260ms ease;
}

/* 立绘调试（DEBUG_BATTLE_PORTRAIT，或 localStorage 的 hw:bp-debug = '1'）：
   把安全区画出来，并把这一只此刻用的三个数写在角上。正式游戏里这一段一个节点都不生成。 */
.bp-stage:has(.bp-dbg) { outline: 1px dashed rgba(224, 189, 120, 0.85); outline-offset: -1px; }
.bp-dbg {
  position: absolute;
  left: 2px;
  bottom: 2px;
  z-index: 3;
  padding: 1px 4px;
  font-size: 10px;
  letter-spacing: 0.04em;
  color: #ffe9b8;
  background: rgba(10, 7, 5, 0.7);
}

/* 名牌 + HP + 五维那三行（在立绘下面，绝不被立绘盖住） */
.bt-plate { position: relative; z-index: 2; display: flex; flex-direction: column; align-items: center; gap: 2px; }
.bt-nm {
  font-size: var(--fs-s);
  letter-spacing: 0.16em;
  color: var(--bt-gold-light);
}
.bt-hprow { display: flex; align-items: center; gap: 7px; }
.bt-hp {
  position: relative;
  width: min(46vw, 190px);
  height: 9px;
  overflow: hidden;
  border: 1px solid rgba(196, 154, 85, 0.65);
  border-radius: 5px;
  background: linear-gradient(180deg, #100b08, #1d1410);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.8);
}
.bt-hp i {
  display: block;
  height: 100%;
  background: linear-gradient(180deg, #c1553f, #7d2a22);
  box-shadow: 0 0 8px rgba(193, 85, 63, 0.45);
  transition: width 300ms ease;
}
/* 低于三成：轻微呼吸（只是视觉，**不加捕捉概率**——规格第十七节点了两遍） */
.bt-hp.low i { animation: btBreath 1.5s ease-in-out infinite; }
@keyframes btBreath {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(1.45); }
}
.bt-hpnum { font-size: 11px; letter-spacing: 0.06em; color: var(--bt-paper); opacity: 0.9; }
.bt-sub { font-size: 10px; letter-spacing: 0.08em; color: rgba(213, 192, 154, 0.62); }

/* 防御中那枚小牌（规格第六节：🛡 防御中，不做 Buff 图标系统） */
.bt-shield {
  position: absolute;
  left: 50%;
  bottom: 4px;
  z-index: 3;
  transform: translateX(-50%);
  padding: 1px 8px 2px;
  font-size: 11px;
  letter-spacing: 0.12em;
  color: #cfe0ea;
  background: rgba(24, 38, 48, 0.8);
  border: 1px solid rgba(150, 190, 210, 0.45);
  border-radius: 10px;
  opacity: 0;
  transition: opacity 200ms ease;
  pointer-events: none;
}
.bt-shield.on { opacity: 1; }

/* ── 立绘上的那几个动作（都只动立绘，不出安全区） ── */

.bp-stage.lunge .bp-img { animation: btLunge 260ms ease; }
@keyframes btLunge {
  0% { translate: 0 0; }
  40% { translate: 0 12px; }
  100% { translate: 0 0; }
}
.mine .bp-stage.lunge .bp-img { animation-name: btLungeUp; }
@keyframes btLungeUp {
  0% { translate: 0 0; }
  40% { translate: 0 -12px; }
  100% { translate: 0 0; }
}
.bp-stage.hurt .bp-img { animation: btHurt 320ms ease; }
@keyframes btHurt {
  0%, 100% { translate: 0 0; }
  20% { translate: -7px 0; }
  45% { translate: 6px 0; }
  70% { translate: -4px 0; }
}
.bp-stage.dodge .bp-img { animation: btDodge 340ms ease; }
@keyframes btDodge {
  0%, 100% { translate: 0 0; opacity: 1; }
  40% { translate: 26px 0; opacity: 0.55; }
}
.bp-stage.crit .bp-glow {
  opacity: 1;
  background: radial-gradient(60% 60% at 50% 45%, rgba(255, 226, 150, 0.55), rgba(255, 200, 90, 0));
}
.bp-stage.healing .bp-glow {
  opacity: 1;
  background: radial-gradient(60% 60% at 50% 55%, rgba(150, 220, 160, 0.4), rgba(120, 200, 140, 0));
}
.bp-stage.shielded .bp-glow {
  opacity: 1;
  background: radial-gradient(62% 62% at 50% 50%, rgba(150, 195, 220, 0.32), rgba(120, 170, 200, 0));
}
/* 倒下：整只轻轻缩一下、淡出去（规格第九节） */
.bp-stage.down .bp-img { transform: translate(-50%, -46%) scale(0.94); opacity: 0; }

/* 特效层：飘起来的伤害数字（在立绘之上、名牌之下） */
.bt-fx { position: absolute; inset: 0; z-index: 4; pointer-events: none; }
.bt-num {
  position: absolute;
  transform: translate(-50%, -50%);
  font-family: var(--font-hand);
  font-size: 22px;
  letter-spacing: 0.06em;
  color: #f6e3b6;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.85);
  animation: btNum 950ms ease-out forwards;
}
.bt-num.dmg { color: #f0cfa4; }
.bt-num.crit { font-size: 30px; color: #ffe6a8; }
.bt-num.crit::after { content: ' CRITICAL'; font-size: 12px; letter-spacing: 0.22em; color: #e0bd78; }
.bt-num.miss { font-size: 17px; letter-spacing: 0.24em; color: #cfd6dd; }
.bt-num.heal { font-size: 21px; color: #b9e3bd; }
.bt-num.shield { font-size: 17px; color: #c6dcea; }
@keyframes btNum {
  0% { opacity: 0; margin-top: 8px; }
  18% { opacity: 1; margin-top: 0; }
  70% { opacity: 1; }
  100% { opacity: 0; margin-top: -26px; }
}

/* 胜负那一下横在场地上的一行字 */
.bt-banner {
  position: absolute;
  left: 50%;
  top: 42%;
  z-index: 6;
  transform: translate(-50%, -50%);
  padding: 8px 22px 10px;
  font-size: var(--fs-l);
  letter-spacing: 0.22em;
  color: #ffe9b8;
  text-align: center;
  white-space: nowrap;
  background: rgba(16, 11, 8, 0.72);
  border-top: 1px solid rgba(224, 189, 120, 0.6);
  border-bottom: 1px solid rgba(224, 189, 120, 0.6);
  animation: btBanner 1.6s ease both;
  pointer-events: none;
}
@keyframes btBanner {
  0% { opacity: 0; transform: translate(-50%, -46%) scale(0.96); }
  18% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  78% { opacity: 1; }
  100% { opacity: 0; }
}

/* ── 战况那一行 ── */

.bt-log {
  min-height: 2.4em;
  padding: 5px 10px 6px;
  font-size: var(--fs-xs);
  line-height: 1.6;
  letter-spacing: 0.06em;
  text-align: center;
  color: rgba(241, 223, 189, 0.86);
  border: 1px solid rgba(196, 154, 85, 0.22);
  border-radius: 4px;
  background: rgba(12, 8, 6, 0.42);
}

/* ── 底下那一条：魔力 / 三张手牌 / 结束回合 ── */

.bt-bar {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  padding-top: var(--sp-2);
  border-top: 1px solid rgba(196, 154, 85, 0.28);
}
.bt-mana { display: flex; align-items: center; gap: 7px; }
.bt-mana-lb { font-size: var(--fs-xs); letter-spacing: 0.24em; color: var(--bt-gold); }
.bt-gem {
  width: 13px;
  height: 13px;
  transform: rotate(45deg);
  border: 1px solid rgba(196, 154, 85, 0.7);
  background: rgba(10, 7, 5, 0.6);
  transition: background 240ms ease, box-shadow 240ms ease;
}
.bt-gem.on {
  background: linear-gradient(135deg, #f0d79a, #b9873f);
  box-shadow: 0 0 9px rgba(224, 189, 120, 0.6);
}

.bt-hand {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  gap: 10px;
  min-height: 138px;
  padding: 6px 4px 0;
}

/* 一张魔法卡：暗底 + 古铜金边 + 一点纸纹（规格第九节） */
.bt-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  width: 92px;
  height: 128px;
  padding: 9px 7px 8px;
  border: 1px solid rgba(196, 154, 85, 0.72);
  border-radius: 9px;
  background:
    linear-gradient(180deg, rgba(60, 43, 31, 0.96), rgba(28, 19, 14, 0.98)),
    repeating-linear-gradient(45deg, rgba(255, 240, 210, 0.03) 0 2px, rgba(0, 0, 0, 0) 2px 5px);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 235, 200, 0.1);
  color: var(--bt-text);
  cursor: grab;
  /* 移动端：拖牌的时候不许带着页面滚（规格第二十三节） */
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease, opacity 180ms ease;
}
/* 卡面上那道符文装饰：一圈暗金细线，不抢名字 */
.bt-card::before {
  content: '';
  position: absolute;
  inset: 4px;
  border: 1px solid rgba(196, 154, 85, 0.24);
  border-radius: 6px;
  pointer-events: none;
}
.bc-cost {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  margin-bottom: 2px;
  font-family: var(--font-hand);
  font-size: 14px;
  color: #1b120c;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #f4dfa8, #b9873f);
  box-shadow: 0 0 8px rgba(224, 189, 120, 0.4);
}
.bc-name { font-size: 14px; letter-spacing: 0.14em; color: #f6e6c2; }
.bc-desc {
  margin-top: auto;
  font-size: 10px;
  line-height: 1.5;
  letter-spacing: 0.04em;
  text-align: center;
  color: rgba(213, 192, 154, 0.82);
}

/* 六种卡各自那一身颜色（只改那道边与那颗费，整体仍是暗棕金体系） */
.bt-card.c-grey  { border-color: rgba(168, 160, 148, 0.6); }
.bt-card.c-grey  .bc-cost { background: radial-gradient(circle at 34% 30%, #d6d0c4, #8b8578); }
.bt-card.c-gold  { border-color: rgba(224, 189, 120, 0.72); }
.bt-card.c-red   { border-color: rgba(178, 92, 74, 0.78); box-shadow: 0 6px 18px rgba(0, 0, 0, 0.6), inset 0 0 22px rgba(143, 74, 61, 0.28); }
.bt-card.c-red   .bc-cost { background: radial-gradient(circle at 34% 30%, #f0c3a4, #9c4a35); }
.bt-card.c-green { border-color: rgba(126, 160, 122, 0.66); }
.bt-card.c-green .bc-cost { background: radial-gradient(circle at 34% 30%, #cfe3c4, #5f7d55); }
.bt-card.c-blue  { border-color: rgba(126, 158, 178, 0.66); }
.bt-card.c-blue  .bc-cost { background: radial-gradient(circle at 34% 30%, #cfe0ea, #4e6d80); }
.bt-card.c-purple { border-color: rgba(154, 128, 178, 0.66); }
.bt-card.c-purple .bc-cost { background: radial-gradient(circle at 34% 30%, #ddcdee, #6a5188); }

/* 悬停：抬起来、金边变亮、影子加重，左右两张让一让（规格第十节，不要霓虹） */
.bt-card:hover {
  transform: translateY(-18px) scale(1.03);
  border-color: rgba(240, 214, 150, 0.95);
  box-shadow: 0 16px 30px rgba(0, 0, 0, 0.66), 0 0 18px rgba(196, 154, 85, 0.28);
  z-index: 3;
}
.bt-card:has(+ .bt-card:hover) { transform: translateX(-9px); }
.bt-card:hover + .bt-card { transform: translateX(9px); }

/* 魔力不足 / 这一回合用过了：压暗、费用转暗红、拖不动（规格第十四节） */
.bt-card.off {
  cursor: not-allowed;
  opacity: 0.52;
  filter: brightness(0.74) saturate(0.7);
}
.bt-card.off .bc-cost { color: #e6c9bd; background: radial-gradient(circle at 34% 30%, #7e4437, #4a2a22); box-shadow: none; }
.bt-card.off:hover { transform: none; box-shadow: 0 6px 16px rgba(0, 0, 0, 0.55); border-color: rgba(196, 154, 85, 0.4); }

/* 拖起来的那张（跟手的复制品）与留在手里的那个空位 */
.bt-card.drag {
  cursor: grabbing;
  transform: scale(1.08);
  box-shadow: 0 22px 40px rgba(0, 0, 0, 0.7), 0 0 26px rgba(196, 154, 85, 0.34);
  border-color: rgba(240, 214, 150, 0.95);
}
.bt-card.lifted { opacity: 0.22; }
/* 松手在空处：弹回来之后轻轻抖一下（牌不消失） */
.bt-card.nope { animation: btNope 260ms ease; }
@keyframes btNope {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  60% { transform: translateX(4px); }
}

/* 底下那两颗（【逃跑】+【结束回合】）：**跟着这一屏一身**——
   暗棕底 + 古铜金边 + 暖金字，不再是全局那副羊皮纸（创作者 2026-10-02 指的：
   「结束回合按键和战斗背景风格统一」）。两颗并排一律等高、同字号。 */
.bt-acts {
  display: flex;
  gap: 10px;
  width: min(100%, 24rem);
}
.bt-acts .btn {
  flex: 1 1 0;
  width: auto;
  min-width: 0;
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-s);
  letter-spacing: 0.14em;
  text-align: center;
  color: var(--bt-gold-light);
  background: linear-gradient(180deg, rgba(58, 41, 28, 0.95), rgba(28, 19, 14, 0.97));
  border: 1px solid rgba(196, 154, 85, 0.58);
  border-radius: 5px;
  box-shadow: 0 3px 0 rgba(10, 7, 5, 0.6), 0 10px 22px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 235, 200, 0.08);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
}
.bt-acts .btn:hover {
  color: #fff2d2;
  border-color: rgba(240, 214, 150, 0.95);
  box-shadow: 0 3px 0 rgba(10, 7, 5, 0.6), 0 12px 26px rgba(0, 0, 0, 0.5), 0 0 16px rgba(196, 154, 85, 0.22), inset 0 1px 0 rgba(255, 235, 200, 0.1);
}
.bt-acts .btn:active { transform: translateY(1px); }
/* 正事那颗（结束回合）：金边重一档，和【逃跑】一眼分得开 */
.bt-acts .btn.bt-go {
  border-color: rgba(224, 189, 120, 0.8);
  background: linear-gradient(180deg, rgba(78, 55, 33, 0.96), rgba(36, 24, 16, 0.97));
}
/* 【逃跑】走全局那副暗红（和【不聊了】同一身），只把尺寸跟着这一排对齐 */
.bt-acts .btn.quit {
  color: #f7ded9;
  background: linear-gradient(180deg, rgba(94, 28, 28, 0.92), rgba(58, 16, 16, 0.95));
  border: 1px solid rgba(198, 74, 74, 0.9);
  box-shadow: 0 3px 0 rgba(10, 7, 5, 0.55), 0 6px 16px rgba(0, 0, 0, 0.45);
}
.bt-acts .btn.quit:hover {
  color: #fff2ee;
  border-color: #e37a7a;
  background: linear-gradient(180deg, rgba(118, 36, 36, 0.95), rgba(74, 22, 22, 0.96));
}

/* ── 窄屏（手机）：舞台矮一点、卡小一点，比例不变（规格第十二节） ── */

@media (max-width: 560px) {
  .battle { width: 98%; padding: var(--sp-2) var(--sp-2) var(--sp-3); }
  .bt-arena { min-height: min(46vh, 340px); }
  .bp-stage { width: min(52%, 200px); height: min(19vh, 150px); }
  .enemy-portrait-stage { height: min(20vh, 158px); }
  .bt-hand { gap: 7px; min-height: 118px; }
  .bt-card { width: 78px; height: 110px; padding: 7px 5px 6px; }
  .bc-name { font-size: 13px; letter-spacing: 0.08em; }
  .bc-desc { font-size: 9px; }
  .bt-num { font-size: 19px; }
  .bt-num.crit { font-size: 25px; }
  .wild-av { width: 52px; height: 52px; }
}

/* ============================================================
   32 · 捕捉那张纸换成战斗那一身（ui/wild.js 的 skin: 'cpt-night'）
   ------------------------------------------------------------
   创作者 2026-10-02：捕捉和战斗是同一件事的两头，别摊一张羊皮纸。
   所以这一张的底、边、字、按钮全部取战斗那一屏的那套数（第 30 段）：
   暗棕渐变 + 顶部一层暖金泛光、古铜金细边、暗金按钮。
   **词条芯片照原样**（浅色按档分色是他 2026-10-01 定的二稿），只垫一圈影子。
   ============================================================ */

.sheet.cpt-night {
  /* 纸上那套色令牌整个翻回暗场景：正文 / 小字 / 强调跟着走，不用一处一处补 */
  --fg: #f1dfbd;
  --fg-dim: rgba(213, 192, 154, 0.78);
  --fg-accent: #e0bd78;
  --fg-danger: #e0a49a;
  --bt-gold: #c49a55;
  --bt-gold-light: #e0bd78;
  --bt-paper: #d5c09a;
  background:
    linear-gradient(180deg, rgba(45, 32, 24, 0.97), rgba(25, 17, 13, 0.98)),
    radial-gradient(120% 80% at 50% 0%, rgba(224, 189, 120, 0.14), rgba(0, 0, 0, 0)) #211712;
  border: 1px solid rgba(196, 154, 85, 0.42);
  border-radius: 6px;
  box-shadow: 0 22px 54px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 235, 200, 0.08);
  color: var(--fg);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
}
/* 抬头那条：2px 墨线 + 红影换成一道暗金细线；标题降一档重（名字才是主角） */
.sheet.cpt-night .sheet-head {
  border-bottom: 1px solid rgba(196, 154, 85, 0.28);
  box-shadow: none;
}
.sheet.cpt-night .sheet-head h2 {
  font-size: var(--fs-m);
  letter-spacing: 0.18em;
  color: var(--bt-gold);
}

/* 立绘：这批图本身是透明底，压在暗底上绝不能再乘底（multiply 会把动物整个吃进背景里） */
.sheet.cpt-night .cpt-face .beast-art {
  mix-blend-mode: normal;
  filter: drop-shadow(0 8px 18px rgba(0, 0, 0, 0.55));
}
.sheet.cpt-night .cf-q {
  font-size: 46px;
  color: rgba(213, 192, 154, 0.5);
}
.sheet.cpt-night .cpt-name { color: var(--bt-gold-light); }
.sheet.cpt-night .cpt-weary { color: var(--fg-danger); }
/* 芯片不动颜色族，只垫一圈暗影子，免得看着像浮在纸上 */
.sheet.cpt-night .bd-chip { box-shadow: 0 1px 5px rgba(0, 0, 0, 0.5); }

/* 那一列药水瓶：浅色纸行 → 暗槽 + 古铜金细边，选中那一行亮一圈暖金 */
.sheet.cpt-night .cpt-pot {
  border: 1px solid rgba(196, 154, 85, 0.3);
  border-radius: 4px;
  background: rgba(12, 8, 6, 0.42);
  color: var(--fg);        /* <button> 自己不带色，不写就是黑字压在暗底上 */
}
.sheet.cpt-night .cpt-pot:hover { border-color: rgba(224, 189, 120, 0.62); }
.sheet.cpt-night .cpt-pot.on {
  border-color: rgba(224, 189, 120, 0.85);
  background: radial-gradient(80% 130% at 50% 0%, rgba(224, 189, 120, 0.16), rgba(0, 0, 0, 0));
  box-shadow: 0 0 12px rgba(196, 154, 85, 0.25), inset 0 0 0 1px rgba(224, 189, 120, 0.2);
}
.sheet.cpt-night .cpt-nm { color: var(--fg); }
.sheet.cpt-night .cpt-bonus,
.sheet.cpt-night .cpt-qty { color: var(--fg-dim); }
.sheet.cpt-night .cpt-tick { color: var(--bt-gold-light); }

/*
 * 底下两颗：和战斗那一排（.bt-acts .btn）同一身。
 * 【捕捉】是正事那颗（金边重一档，同 .bt-go）；【算了】走全局那副暗红（同【逃跑】【不聊了】）。
 */
.sheet.cpt-night .acts > .btn {
  flex: 1 1 0;
  width: auto;
  min-width: 0;
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-s);
  letter-spacing: 0.14em;
  text-align: center;
  color: var(--bt-gold-light);
  background: linear-gradient(180deg, rgba(58, 41, 28, 0.95), rgba(28, 19, 14, 0.97));
  border: 1px solid rgba(196, 154, 85, 0.58);
  border-radius: 5px;
  box-shadow: 0 3px 0 rgba(10, 7, 5, 0.6), 0 10px 22px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 235, 200, 0.08);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
}
.sheet.cpt-night .acts > .btn:hover {
  color: #fff2d2;
  border-color: rgba(240, 214, 150, 0.95);
  box-shadow: 0 3px 0 rgba(10, 7, 5, 0.6), 0 12px 26px rgba(0, 0, 0, 0.5), 0 0 16px rgba(196, 154, 85, 0.22), inset 0 1px 0 rgba(255, 235, 200, 0.1);
  transform: none;
}
.sheet.cpt-night .acts > .btn:active { transform: translateY(1px); }
.sheet.cpt-night .acts > .btn.primary {
  color: #fff2d2;
  border-color: rgba(224, 189, 120, 0.85);
  background: linear-gradient(180deg, rgba(78, 55, 33, 0.96), rgba(36, 24, 16, 0.97));
}
.sheet.cpt-night .acts > .btn:not(.primary) {
  color: #f7ded9;
  background: linear-gradient(180deg, rgba(94, 28, 28, 0.92), rgba(58, 16, 16, 0.95));
  border-color: rgba(198, 74, 74, 0.9);
}
.sheet.cpt-night .acts > .btn:not(.primary):hover {
  color: #fff2ee;
  border-color: #e37a7a;
  background: linear-gradient(180deg, rgba(118, 36, 36, 0.95), rgba(74, 22, 22, 0.96));
}

/* ============================================================
   31 · 自由剧情那一屏：磨砂玻璃、底下那一排、之前章节
   ------------------------------------------------------------
   2026-10-02 创作者要的五件事，样式这一头占前三件：
     1. 进这一屏不摆状态栏（那是 main.js 里 showHud 那一行的事，不在这段）；
     2. 正文后面垫一块磨砂玻璃——底图是实景照片，暖白字直接搁上去读不动；
     3. 底下那一排多了【存】【读】，输入框上面那一行换给了【之前章节】。
   ============================================================ */

/*
 * 磨砂玻璃只垫**正文那一栏**（34em 那一列），不是整屏糊一层：
 * 读的是这一栏字，边上那些景色留着，画面才不像盖了张保鲜膜。
 * blur 的量跟入学登记那三页的 .setup-note.glass 同一档（10px），
 * 但底色偏暖不偏紫——这一屏的纸、灯、金线都是暖的。
 */
.doc {
  padding: var(--sp-5) var(--sp-4) var(--sp-4);
  background: rgba(14, 10, 7, 0.44);
  border: 1px solid var(--glass-line);
  border-radius: var(--r);
  box-shadow: 0 18px 46px rgba(0, 0, 0, 0.5);
  -webkit-backdrop-filter: blur(10px) saturate(0.92);
  backdrop-filter: blur(10px) saturate(0.92);
}

/* 第一次进来、纸上还没字：玻璃不该是一块空的 */
.doc-empty {
  margin: 0;
  text-align: center;
  font-size: var(--fs-s);
  color: var(--cine-dim);
}

/* ---------- 底下那一排：中间那一撮挨着站 ---------- */

/* 【存】【读】【展开选项】是一伙的（都管这一屏的杂事），别让 space-between 把它们拉散 */
.doc-tools-mid {
  display: flex;
  align-items: center;
  gap: 6px;
}

/* ---------- 输入框上面那一行：【之前章节】 + 「少一件」那句 ---------- */

/*
 * 这一行原本站着一句说明书（「点一条 → 那句话写进下面的框里，可以改」），
 * 创作者让删，换成【之前章节】。只有从背包点了东西才会有那行小字，
 * 所以大多数时候这一行只有一颗钮：居左不成立，就用 justify-content: center 让它站正中。
 */
.doc-meta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-2);
}
.doc-meta .doc-status { flex: 1 1 auto; min-width: 0; margin: 0; text-align: left; }
/* 没话可说的时候那一行是个空壳（点过背包才会有「少一件」那句）——不占地方 */
.doc-meta .doc-status:empty { display: none; }
/* 托盘里那颗 min-width: 9.5rem 的主按钮规则会漏到这颗小钮身上，按回辅助件那一档 */
.tray .doc-meta .btn {
  flex: none;
  width: auto;
  min-width: 0;
  padding: 3px 11px 4px;
  font-size: var(--fs-xs);
  line-height: 1.5;
  letter-spacing: 0.04em;
  text-align: center;
  box-shadow: none;
  white-space: nowrap;
}
.tray .doc-meta .btn:hover { transform: none; box-shadow: none; }
.tray .doc-meta .btn:active { transform: translateY(1px) scale(0.98); }

/* ---------- 【之前章节】：一轮一行的目录（小窗里） ---------- */

.hist-list { display: flex; flex-direction: column; }
.hist-row {
  display: block;
  width: 100%;
  padding: 9px var(--sp-2);
  font: inherit;
  text-align: left;
  color: var(--ink);
  background: none;
  border: none;
  border-bottom: 1px dotted rgba(64, 48, 31, 0.22);
  cursor: pointer;
  transition: background 0.12s ease;
}
.hist-row:last-child { border-bottom: none; }
.hist-row:hover { background: rgba(140, 47, 47, 0.07); }
.hist-row:active { transform: translateY(1px); }
/* 一行一轮：左边第几轮，右边它是什么时候跑的（真实时间，不是游戏里的第几天） */
.hist-row .hist-line { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.hist-row .hist-line b { font-size: var(--fs-s); font-weight: 700; }
.hist-row .hist-line i {
  font-style: normal;
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}

/* ---------- 点开一段：原文摊在羊皮纸上 ---------- */
/*
 * .doc-* 那几档原本是为**暗场景**配的暖白字，搬到纸上就是白纸写字。
 * 排版（衬线、行距 1.9、金线、名牌）照原样留着，只把颜色交给墨。
 */
.sheet .hist-when {
  margin: 0 0 var(--sp-4);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  text-align: center;
  color: var(--ink-soft);
}
.sheet .doc-turn { margin-bottom: 0; }
.sheet .doc-p,
.sheet .doc-say .line { color: var(--ink); }
.sheet .doc-say { border-left-color: rgba(150, 108, 46, 0.5); }
.sheet .doc-say .who { color: #8a5a12; }
.sheet .doc-cut { color: var(--red); }

/* ============================================================
   33 · 信件（状态栏那枚小信封进来的一摞羊皮纸）
   账在 engine/letters.js，界面在 ui/letters.js。
   ============================================================ */

/*
 * 信封右上角那颗红点：有几封没读。
 * 只说"有"，不写数字——数字进了那张纸自然看得见，
 * 而 27px 那一行里多一个数字会把日期顶得晃。
 * ⚠️ .hud-mail 那头补了 position: relative（这颗是钉在信封角上的）。
 */
.hud-mail-dot {
  position: absolute;
  top: -1px;
  right: -3px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--red);
  /* 一圈纸色的边：红点直接压在信封图上会糊成一团，描一道就分得开 */
  box-shadow: 0 0 0 1.5px rgba(248, 241, 227, 0.9);
  pointer-events: none;
}

/*
 * 名单。
 *
 * 整张纸本来就是羊皮纸（.sheet 那层 --parchment-wide），所以每一行**不再另铺一层纸**——
 * 一行一张纸就成了现代卡片列表。分隔用一条虚线，和【通讯】通讯录那排同一个手感。
 *
 * 两颗钮是**并排**的（星标一颗、信一颗），不是套在一起：
 * 嵌套 button 是非法 HTML，浏览器会自己拆开，拆坏的是点哪儿算哪儿。
 * 这么摆还有个好处——点星标压根不会冒泡到"打开信"上，不用 stopPropagation。
 */
.ltr-cap {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 2px 0 6px;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  letter-spacing: 0.22em;
  color: var(--ink-soft);
}
.ltr-cap-star { color: #b18a4a; letter-spacing: 0; }
.ltr-cap + .ltr-row { border-top: 1px solid rgba(64, 48, 31, 0.14); }

.ltr-row {
  display: flex;
  align-items: stretch;
  width: 100%;
}
.ltr-row:last-child { border-bottom: 0; }

/* 左滑删除：正文整条跟着 --swipe 左移，删除键从右边滑进来盖住尾部 */
.ltr-swipe { position: relative; overflow: hidden; }
.ltr-swipe .ltr-row {
  position: relative;
  z-index: 1;
  transform: translateX(var(--swipe, 0px));
  transition: transform 0.18s cubic-bezier(0.16, 1, 0.3, 1);
  touch-action: pan-y;
}
.ltr-swipe.dragging .ltr-row,
.ltr-swipe.dragging .ltr-del { transition: none; }
.ltr-del {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  z-index: 2;
  width: 78px;
  display: flex; align-items: center; justify-content: center;
  padding: 0;
  border: 0;
  background: linear-gradient(180deg, #9e3833, #6f2522);
  color: #ffede4;
  font-family: var(--font-body);
  font-size: var(--fs-s);
  letter-spacing: 0.12em;
  cursor: pointer;
  transform: translateX(calc(100% + var(--swipe, 0px)));
  transition: transform 0.18s cubic-bezier(0.16, 1, 0.3, 1), filter 0.14s ease;
}
.ltr-del:hover { filter: brightness(1.12); }
.ltr-cap + .ltr-swipe .ltr-row { border-top: 1px solid rgba(64, 48, 31, 0.14); }

/* 星标那一格：单独一颗小钮，宽度固定，和右边那颗一样高（一排要齐） */
.ltr-star {
  flex: 0 0 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1;
  color: var(--ink-soft);
  background: transparent;
  border: 0;
  border-bottom: 1px dashed rgba(64, 48, 31, 0.22);
  cursor: pointer;
  transition: color 0.14s ease, transform 0.14s ease;
}
.ltr-star:hover { transform: translateY(-1px); }
.ltr-row .ltr-star.on,
.ltr-row .ltr-star[title="取消标记"] { color: #b18a4a; }

/* 一整条能点的信 */
.ltr-main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  width: 100%;
  padding: 9px var(--sp-2) 9px 0;
  text-align: left;
  font-family: var(--font-body);
  background: transparent;
  border: 0;
  border-bottom: 1px dashed rgba(64, 48, 31, 0.22);
  cursor: pointer;
  transition: background 0.14s ease, transform 0.14s ease, box-shadow 0.14s ease;
}
.ltr-main:hover {
  background: rgba(255, 255, 255, 0.3);
  transform: translateY(-1px);
  /* hover 才浮出来的一圈古铜金，淡到几乎只是"这一条活了" */
  box-shadow: inset 0 0 0 1px rgba(177, 138, 74, 0.34);
}
.ltr-row:last-child .ltr-main { border-bottom: 0; }

/*
 * 未读那一颗小墨点。
 * 占位一直在（读过只是把字清空），不然标题会跟着左右跳一格。
 * 未读只靠"墨点 + 标题加粗、颜色深一档"说事——不刷红边，
 * 一列里七八条红边看着像报错，不像一摞信。
 */
.ltr-dot {
  flex: 0 0 12px;
  font-size: 9px;
  line-height: 1;
  color: #8f3f32;
  text-align: center;
}
.ltr-tx { flex: 1 1 auto; min-width: 0; }
.ltr-title {
  display: block;
  font-size: var(--fs-m);
  color: var(--ink-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ltr-from {
  display: block;
  margin-top: 2px;
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  color: var(--ink-soft);
  opacity: 0.8;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ltr-when {
  flex: 0 0 auto;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  color: var(--ink-soft);
  opacity: 0.86;
  white-space: nowrap;
}
.ltr-row.unread .ltr-title { font-weight: 700; color: var(--ink); }

/* 一封都没有：照实说一句，不摆几行假信 */
.ltr-none {
  margin: var(--sp-4) 0 0;
  text-align: center;
  font-size: var(--fs-s);
  color: var(--ink-soft);
}

/*
 * 详情那一层：把一封信从桌上抽出来摊平。
 *
 * 纸还是 .sheet 那一张（不另铺第二层纸），只把它放宽到 720——
 * 正文铺满整屏就不像一封信了，像一篇文章。
 * 入场沿用 .sheet 自己的 sheetIn（0.3s），不引动画库。
 */
.sheet.letter { max-width: 720px; }

.ltr-paper {
  position: relative;
  padding: var(--sp-1) 2px 0;
  /* 两道极淡的折痕：一张对折过的纸摊开会有。压到 5% 透明度，
   * 是"摸着像有"的程度，不是画上去的横线 */
  background: repeating-linear-gradient(
    180deg,
    transparent 0 47%,
    rgba(120, 90, 50, 0.05) 47% 47.4%,
    transparent 47.4% 100%
  );
}
.ltr-paper-star {
  position: absolute;
  top: 0;
  right: 0;
  padding: 2px 4px;
  font-family: var(--font-body);
  font-size: 18px;
  line-height: 1;
  color: var(--ink-soft);
  background: transparent;
  border: 0;
  cursor: pointer;
  transition: color 0.14s ease, transform 0.14s ease;
}
.ltr-paper-star:hover { transform: translateY(-1px); }
.ltr-paper-star.on { color: #b18a4a; }

.ltr-p-title {
  margin: var(--sp-2) 44px var(--sp-1) 0;
  font-family: var(--font-hand);
  font-size: var(--fs-l);
  font-weight: 400;
  letter-spacing: 0.04em;
  text-align: center;
  color: var(--ink);
}
.ltr-p-meta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding-bottom: var(--sp-3);
  margin-bottom: var(--sp-4);
  border-bottom: 1px solid rgba(64, 48, 31, 0.16);
  text-align: center;
}
.ltr-p-from { font-size: var(--fs-s); letter-spacing: 0.08em; color: var(--ink); }
.ltr-p-when {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}

/*
 * 正文：字比原来小一档（创作者 2026-10-03 指的），并且**框在纸上一格里**——
 * 四周描一道细墨线，像羊皮纸上预留出来的书写区，字不再一路顶到纸边。
 * 抬头那三行（标题 / 谁寄的 / 哪天到的）留在框外面：那是信封面上的字，不是信里的话。
 */
.ltr-p-body {
  max-width: 720px;
  /* 底下留一段空白再落款（创作者 2026-10-03 指的）：字不要一路顶到署名 */
  margin: 0 auto var(--sp-5);
  padding: var(--sp-4) var(--sp-4) var(--sp-5);
  border: 1px solid rgba(64, 48, 31, 0.32);
  border-radius: 4px;
  font-size: var(--fs-s);
  line-height: 1.8;
  color: var(--ink);
}
.ltr-p-body p { margin: 0 0 var(--sp-3); }
.ltr-p-body p:last-child { margin-bottom: 0; }
/* 整段括起来的那一句（「（你学会了…）」那一类）：斜体，颜色压半档 */
.ltr-p-body .ltr-em { font-style: italic; color: var(--ink-soft); }
/* 落款那种短行（“—— 海格”）跟着正文走，不另摆一颗钮 */
.ltr-p-none { font-size: var(--fs-s); color: var(--ink-soft); text-align: center; }

/*
 * 成绩单那张纸里的一行一行（ui/letters.js 的 reportBlock）
 * ------------------------------------------------------------------
 * 一行一门：课名在左、等级靠右（字母大一号、英文原话小字跟在后面），
 * 底下一道横线隔开总评。创作者 2026-10-14 点的：要排成一列列的，别堆成一段话。
 * 它只管排，颜色字号全用这一屏现成的那几个（--fs-* / --ink-soft），不另起一套。
 */
.ltr-report { display: flex; flex-direction: column; }
.ltr-rep-cap {
  margin-bottom: 8px; padding-bottom: 6px;
  border-bottom: 1px solid rgba(64, 48, 31, 0.4);
  font-size: var(--fs-xs); letter-spacing: 0.16em; color: var(--ink-soft);
}
.ltr-rep-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 4px 2px; border-bottom: 1px dotted rgba(64, 48, 31, 0.2);
}
.ltr-rep-row.sum {
  margin-top: 6px; padding-top: 8px; border-bottom: 0;
  border-top: 1px solid rgba(64, 48, 31, 0.45);
}
.ltr-rep-name { font-size: var(--fs-s); letter-spacing: 0.04em; }
.ltr-rep-grade { flex: none; display: inline-flex; align-items: baseline; gap: 7px; }
.ltr-rep-grade b { font-size: var(--fs-m); letter-spacing: 0.1em; }
.ltr-rep-grade i { font-style: normal; font-size: var(--fs-xs); color: var(--ink-soft); }
.ltr-rep-note { margin: var(--sp-4) 0 0; }

/*
 * 落款：贴在纸的**右下角**（创作者 2026-10-03 指的）。
 * 它在书写区那道墨线框**外面**——名字是写在纸边的，不是正文里的一句话。
 */
.ltr-p-sign {
  display: flex;
  justify-content: flex-end;
  /* 右边留出书写区那道内边距的宽度：名字要对齐正文那栏的右沿，不是纸的毛边 */
  padding-right: var(--sp-4);
  font-family: var(--font-hand);
  font-size: var(--fs-m);
  letter-spacing: 0.04em;
  color: var(--ink);
}

/*
 * 随信带来的那一样：一行一样，**话在左、图在右**（绿框那个位置）。
 * 外面那道框照正文框的样子来，只是更扁——它是附在信角上的一件东西，不是又一段话。
 */
.ltr-p-attach {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-top: var(--sp-4);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid rgba(64, 48, 31, 0.24);
  border-radius: 4px;
  background: rgba(255, 250, 235, 0.55);
}
.ltr-p-attach + .ltr-p-attach { margin-top: var(--sp-2); }
.ltr-p-attach-tx { font-size: var(--fs-s); line-height: 1.5; color: var(--ink); }
/* 40×40 的小方块配小影子（大投影压在这么小的块上会糊成一团） */
.ltr-p-attach-art {
  flex: none;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  line-height: 1;
  background: rgba(255, 251, 240, 0.72);
  border: 1px solid var(--paper-3);
  border-radius: var(--r);
  box-shadow: 0 2px 0 rgba(50, 37, 24, 0.34), 0 5px 12px rgba(0, 0, 0, 0.3);
}
.ltr-attach-img { max-width: 100%; max-height: 100%; object-fit: contain; }

/*
 * 窄屏：日期从右边挪到标题下面去，不然「12月13日 下午」会把标题挤没。
 * 星标那一格收到 28px，整条还是两颗钮并排。
 */
@media (max-width: 560px) {
  .ltr-main { flex-wrap: wrap; padding-right: 2px; }
  .ltr-star { flex-basis: 28px; font-size: 15px; }
  .ltr-dot { flex-basis: 10px; }
  .ltr-tx { flex-basis: auto; }
  .ltr-when {
    flex: 1 1 100%;
    order: 3;
    padding-left: 22px;
    font-size: 11px;
  }
  .ltr-title { font-size: var(--fs-s); }
  .ltr-paper { padding-top: var(--sp-1); }
  .ltr-p-title { margin-right: 30px; font-size: var(--fs-m); }
  /* 窄屏上那圈留白收一档：框本身要留着，先瘦的是它的内边 */
  .ltr-p-body { padding: var(--sp-3) var(--sp-3) var(--sp-4); }
}


/* ============================================================
   34 · 龙斗场（翻倒巷地下那一层）
   入口那块木牌在 ui/screens-map.js 的 screenPlace，那一屏在 ui/arena.js。
   这一版只有门脸与两块锁着的牌子 —— 战斗 / 节目 / 奖励一概没有，所以这里也没有账。
   全节**一张新图都不用**：木板、铁钉、烛火、石墙、铁栏、锁，全是渐变与伪元素画的。
   ============================================================ */

/* ── 一、巷子里那块木牌（.room-btn 的一身 tone） ── */

/*
 * 创作者要的是「不起眼」：它比同一摞里【黑街商人】矮一档、不写那行小字，
 * 歪着挂（rotate 半度），像钉在墙上很多年了。
 * 木纹 = 一层横纹 repeating-linear-gradient，两头再各压一道暗边假装板子磨旧了。
 */
.room-btn.arena {
  position: relative;
  padding: var(--sp-2) var(--sp-4);
  background:
    linear-gradient(90deg, rgba(0, 0, 0, 0.38), transparent 16%, transparent 84%, rgba(0, 0, 0, 0.38)),
    repeating-linear-gradient(180deg, rgba(60, 37, 20, 0.95) 0 9px, rgba(43, 26, 15, 0.95) 9px 18px);
  border: 1px solid rgba(126, 100, 64, 0.6);
  border-radius: 3px;
  box-shadow: inset 0 1px 0 rgba(226, 178, 100, 0.14), 0 3px 12px rgba(0, 0, 0, 0.52);
  transform: rotate(-0.6deg);
}
/* 两颗铁钉：钉在**上沿两个角**（下沿留给那支蜡烛）。
 * 一颗伪元素铺满整块牌子，上面画两层同一个钉帽、各摆一个角 = 两颗钉子。
 * ⚠️ 要两层图：只写一层图配两个 background-position，第二个位置会被整个丢掉（一层图只吃一个位置）。 */
.room-btn.arena::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    radial-gradient(circle, #9a8f80 0 2.2px, #2a231d 2.4px 3.4px, transparent 3.6px),
    radial-gradient(circle, #9a8f80 0 2.2px, #2a231d 2.4px 3.4px, transparent 3.6px);
  background-repeat: no-repeat, no-repeat;
  background-size: 8px 8px, 8px 8px;
  background-position: 6px 5px, calc(100% - 6px) 5px;
}
/* 右下角那一支蜡烛：一小团暖光，慢慢晃（进这一屏的人不会注意到它，但会记得这儿暗） */
.room-btn.arena::after {
  content: '';
  position: absolute;
  right: 10px;
  bottom: 4px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 205, 122, 0.95), rgba(255, 140, 40, 0.3) 52%, transparent 74%);
  pointer-events: none;
  animation: arenaCandle 2.8s ease-in-out infinite alternate;
}
@keyframes arenaCandle {
  from { opacity: 0.55; transform: scale(0.88); }
  to   { opacity: 1;    transform: scale(1.1); }
}
.room-btn.arena:hover {
  border-color: rgba(224, 178, 100, 0.66);
  background:
    linear-gradient(90deg, rgba(0, 0, 0, 0.3), transparent 16%, transparent 84%, rgba(0, 0, 0, 0.3)),
    repeating-linear-gradient(180deg, rgba(78, 48, 26, 0.96) 0 9px, rgba(56, 34, 19, 0.96) 9px 18px);
  /* 晃一下牌子（±1px 级别），像被人碰了碰 */
  animation: arenaSign 0.42s ease;
}
@keyframes arenaSign {
  0%   { transform: rotate(-0.6deg) translateY(-2px); }
  35%  { transform: rotate(0.7deg) translateY(-3px); }
  70%  { transform: rotate(-1.1deg) translateY(-1px); }
  100% { transform: rotate(-0.6deg) translateY(-2px); }
}
.room-btn.arena:active { transform: rotate(-0.6deg) translateY(0) scale(0.98); }
.room-btn.arena .rb-zh { font-size: var(--fs-s); letter-spacing: 0.16em; color: #e9d9b0; }
.room-btn.arena .rb-en { font-size: 9px; letter-spacing: 0.2em; color: rgba(224, 178, 100, 0.5); }
/* 手一碰，烛光跟着亮一点 */
.room-btn.arena:hover::after { animation-duration: 1.1s; }

/* ── 二、地下那一层：把整片地面压暗，再盖上石墙与铁栏 ── */

/*
 * 开关是 body.arena-live（每次换场面在 main.js 的 render 里重对一次，
 * 不靠某一屏自己负责摘）。它改的是**舞台**，不是那一屏的内容：
 * 顶栏、托盘、飘字都照常亮着，只有脚下那张翻倒巷的照片沉下去了。
 */
body.arena-live .stage-scrim {
  background:
    /* 底下那一圈烛火：暖色从地面漫上来一点 */
    radial-gradient(52% 34% at 50% 86%, rgba(255, 168, 72, 0.13), transparent 72%),
    /* 暗红压在下面（铁锈与旧木），越往上越黑 */
    linear-gradient(180deg,
      rgba(6, 4, 3, 0.93) 0%,
      rgba(10, 7, 5, 0.8) 34%,
      rgba(30, 11, 8, 0.72) 72%,
      rgba(9, 5, 4, 0.94) 100%),
    /* 石墙：横缝一道、竖缝一道，缝只压 2% 的亮度，是「摸着像有」的程度 */
    repeating-linear-gradient(180deg, rgba(255, 255, 255, 0.028) 0 2px, transparent 2px 36px),
    repeating-linear-gradient(90deg, rgba(0, 0, 0, 0.15) 0 2px, transparent 2px 62px);
}
/* 左右两边各一列铁栏：只画在两侧 22% 的范围内，中间整块留给内容，不吃点击 */
body.arena-live .stage::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    repeating-linear-gradient(90deg, rgba(9, 7, 5, 0.7) 0 5px, transparent 5px 42px) left / 22% 100% no-repeat,
    repeating-linear-gradient(90deg, rgba(9, 7, 5, 0.7) 0 5px, transparent 5px 42px) right / 22% 100% no-repeat;
  animation: arenaIn 0.34s ease both;
}
@keyframes arenaIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ── 三、那一屏：两块锁着的牌子 ── */

.arena-cards {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-4);
  margin-top: var(--sp-5);
  animation: arenaCards 0.36s ease both;
}
/* 进场：字比地面晚半拍浮上来，玩家才觉得是「走下来了」，不是画面换了张壁纸 */
@keyframes arenaCards {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
.arena-gate {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: var(--sp-5) var(--sp-3) var(--sp-4);
  font-family: var(--font-body);
  text-align: center;
  color: var(--cine-text);
  border-radius: 5px;
  cursor: pointer;
  transition: filter 0.16s ease, transform 0.16s ease;
}
/*
 * 两块牌子**不是一副模子**（创作者要的：一眼看出是两种玩法）。
 * 每周节目 = 钉在木框上的一张羊皮纸节目单；斗兽场挑战 = 一块铁牌，后头是铁栏。
 */
.arena-gate.playbill {
  background: linear-gradient(180deg, rgba(226, 205, 160, 0.9) 0%, rgba(198, 172, 126, 0.86) 100%);
  border: 6px solid rgba(52, 33, 18, 0.9);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.55), inset 0 0 22px rgba(120, 88, 40, 0.35);
}
.arena-gate.challenge {
  background:
    repeating-linear-gradient(90deg, rgba(10, 8, 6, 0.5) 0 3px, transparent 3px 22px),
    linear-gradient(180deg, rgba(58, 55, 52, 0.94) 0%, rgba(30, 26, 23, 0.94) 100%);
  border: 2px solid rgba(150, 140, 124, 0.5);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(220, 210, 190, 0.14);
}
.arena-gate:hover { transform: translateY(-2px); }
.arena-gate:active { transform: translateY(0) scale(0.99); }

.ag-zh { font-size: var(--fs-l); letter-spacing: 0.14em; }
.ag-en { font-size: 9px; letter-spacing: 0.22em; text-transform: uppercase; }
.ag-sub { font-size: var(--fs-xs); line-height: 1.6; }
.ag-state { font-size: var(--fs-xs); letter-spacing: 0.2em; }
/* 羊皮纸那张是亮底，字要换成墨色；铁牌那张是暗底，走亮字 */
.arena-gate.playbill .ag-zh { color: #3a2a15; text-shadow: none; }
.arena-gate.playbill .ag-en { color: rgba(90, 64, 32, 0.72); text-shadow: none; }
.arena-gate.playbill .ag-sub { color: rgba(72, 52, 28, 0.82); text-shadow: none; }
.arena-gate.playbill .ag-state { color: #7d4a17; text-shadow: none; }
.arena-gate.challenge .ag-zh { color: #f0dfb6; }
.arena-gate.challenge .ag-en { color: rgba(224, 178, 100, 0.55); }
.arena-gate.challenge .ag-sub { color: var(--cine-dim); }
.arena-gate.challenge .ag-state { color: #c9a24a; }

/*
 * 一把锁，纯 CSS：上面一截环（::before 只有三条边、顶上圆角）、下面一块牌体（::after）。
 * 没画钥匙孔——26px 的东西上多一道刻痕只会糊。
 */
.ag-lock {
  position: relative;
  width: 26px;
  height: 32px;
  margin-top: var(--sp-2);
}
.ag-lock::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 0;
  width: 13px;
  height: 13px;
  transform: translateX(-50%);
  border: 3px solid #8a7a5e;
  border-bottom: 0;
  border-radius: 8px 8px 0 0;
}
.ag-lock::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 26px;
  height: 18px;
  transform: translateX(-50%);
  border-radius: 3px;
  background: linear-gradient(180deg, #6f6046 0%, #3b3223 100%);
  box-shadow: inset 0 1px 0 rgba(255, 228, 176, 0.28), 0 2px 6px rgba(0, 0, 0, 0.55);
}
/* 锁着的东西不该看着像能按：整块压暗一档，hover 也只亮一点点 */
.arena-gate.locked { filter: saturate(0.82) brightness(0.9); }
.arena-gate.locked:hover { filter: saturate(0.9) brightness(1.06); }

/* 底下那句：巷子里的人说的话，不是界面在解释规则 */
.arena-foot {
  margin: var(--sp-5) 0 var(--sp-1);
  font-size: var(--fs-xs);
  line-height: 1.8;
  color: var(--cine-dim);
  text-align: center;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.9);
}

/*
 * 窄屏（手机）：两块牌子上下叠着，不横着挤——各自都带三行字，并排会挤成一坨。
 * 560 这一档和全工程别的窄屏判断是同一个数，别在这儿另起一个。
 */
@media (max-width: 560px) {
  .arena-cards { grid-template-columns: 1fr; gap: var(--sp-3); }
  .arena-gate { padding: var(--sp-4) var(--sp-3) var(--sp-3); }
  .arena-gate.playbill { border-width: 5px; }
  .ag-zh { font-size: var(--fs-m); }
}


/* ============================================================
   35 · 钓鱼那一屏的水面（沉浸化改版，ui/fishing.js）
   创作者 2026-10-03 给的规格：这一屏不该是「一张背景图 + 几个按钮」，
   要看着像**人真的坐在水边**。所以这里加的是整个水面层：
   水面 / 反光 / 薄雾 / 萤火 / 两岸的草 / 水底那道鱼影 / 伸出去的竿 / 线上那个漂 / 漂下两圈水波。
   ⚠️ 一张新图都没有：每一样都是渐变、伪元素与 transform 画的（以后换背景图不影响这一层）。
   ⚠️ 这一节只改长相。抛竿的账（一格时间 + 8 精力）、抽鱼的概率、希拉克鱼那一张纸、
      稀有鱼的较劲小游戏，全在 JS 那边一个字没动。
   下面几条会**盖住**第 21 节里钓鱼那几块老规矩（同一批类名，写在后面才赢）。
   ============================================================ */

/* ── 一、那一屏的地面：水占住下半屏 ── */

/*
 * 原来 42vh 只够摆一颗按钮；现在水面是主角，抬到 56vh。
 * 不写 overflow:hidden —— 钓上来的那一条要能跳出水面（跳到抬头那一带也行），
 * 真正需要收边的是 .fish-lake 那一层。
 */
.fish-stage {
  min-height: 56vh;
  margin-top: var(--sp-3);
}

/* 整层装饰：铺满这一屏、不吃点击，谁也点不到它 */
.fish-lake {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}

/*
 * 水面本体。顶上那 20% 是**透明的**：让照片自己的岸与城堡留在上面，
 * 往下才慢慢变成一潭深水——这样这一层是"接在照片上的水"，不是贴了一块蓝布。
 * 两道极慢的横移（一层 26s、一层 41s）：慢到看不见在动，但页面不是死的。
 */
.lake-water {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(140% 62% at 50% 0%, rgba(10, 18, 28, 0) 52%, rgba(10, 18, 28, 0.3) 76%, rgba(6, 11, 18, 0.7) 100%),
    linear-gradient(180deg,
      rgba(8, 12, 18, 0) 0%,
      rgba(8, 12, 18, 0) 26%,
      rgba(9, 16, 24, 0.24) 40%,
      rgba(7, 13, 21, 0.58) 60%,
      rgba(5, 9, 15, 0.8) 80%,
      rgba(3, 6, 10, 0.92) 100%);
}
.lake-water::before,
.lake-water::after {
  content: '';
  position: absolute;
  left: -50%;
  right: -50%;
  top: 34%;
  bottom: 0;
  background:
    repeating-linear-gradient(178deg,
      rgba(150, 186, 214, 0.05) 0 1px,
      transparent 1px 7px,
      rgba(232, 200, 132, 0.035) 7px 8px,
      transparent 8px 21px);
  animation: lakeDriftA 26s linear infinite;
}
.lake-water::after {
  top: 52%;
  opacity: 0.62;
  background:
    repeating-linear-gradient(181deg,
      rgba(190, 214, 232, 0.055) 0 1px,
      transparent 1px 11px,
      rgba(255, 240, 210, 0.03) 11px 12px,
      transparent 12px 29px);
  animation: lakeDriftB 41s linear infinite;
}
@keyframes lakeDriftA {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-7%, 1.5%, 0); }
}
@keyframes lakeDriftB {
  from { transform: translate3d(-6%, 0, 0); }
  to   { transform: translate3d(2%, -1.2%, 0); }
}

/* 水面高光：几道很淡的横抹，颜色压在灰蓝与月光白上（别亮过字） */
.lake-glint {
  position: absolute;
  left: 6%;
  right: 6%;
  top: 40%;
  height: 42%;
  opacity: 0.5;
  background:
    radial-gradient(38% 3% at 24% 12%, rgba(226, 236, 245, 0.16), transparent 70%),
    radial-gradient(30% 2.4% at 68% 34%, rgba(214, 228, 240, 0.13), transparent 70%),
    radial-gradient(44% 2.6% at 42% 62%, rgba(236, 214, 168, 0.1), transparent 72%),
    radial-gradient(26% 2.2% at 82% 82%, rgba(226, 236, 245, 0.1), transparent 70%);
  animation: lakeGlint 17s ease-in-out infinite alternate;
}
@keyframes lakeGlint {
  from { opacity: 0.34; transform: translateX(-1.5%); }
  to   { opacity: 0.62; transform: translateX(1.5%); }
}

/* 薄雾：压在岸与水交接那一带，慢慢横着走。淡到不影响读字 */
.lake-mist {
  position: absolute;
  left: -20%;
  right: -20%;
  top: 20%;
  height: 26%;
  opacity: 0.5;
  filter: blur(14px);
  background:
    radial-gradient(46% 60% at 22% 50%, rgba(206, 220, 232, 0.16), transparent 70%),
    radial-gradient(38% 52% at 62% 44%, rgba(198, 212, 226, 0.13), transparent 72%),
    radial-gradient(30% 46% at 88% 58%, rgba(206, 220, 232, 0.1), transparent 74%);
  animation: lakeMist 34s ease-in-out infinite alternate;
}
@keyframes lakeMist {
  from { transform: translateX(-4%); opacity: 0.36; }
  to   { transform: translateX(4%); opacity: 0.58; }
}

/* 萤火：六七颗，各自身上慢慢亮、慢慢飘（位置与节奏在 JS 里一次算好） */
.lake-fireflies { position: absolute; inset: 0; }
.lake-fireflies .ff {
  position: absolute;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 238, 190, 0.95), rgba(226, 176, 84, 0.5) 55%, transparent 72%);
  box-shadow: 0 0 6px 1px rgba(255, 216, 138, 0.32);
  animation-name: ffFloat;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}
.lake-fireflies .ff.big { width: 4px; height: 4px; }
@keyframes ffFloat {
  0%   { opacity: 0.08; transform: translate3d(0, 0, 0); }
  45%  { opacity: 0.85; transform: translate3d(4px, -7px, 0); }
  100% { opacity: 0.16; transform: translate3d(-5px, 5px, 0); }
}

/* 两岸的草：只当剪影，各占一角，不铺满（一片叶子 = 一个 border-radius 的椭圆） */
.lake-reed { position: absolute; bottom: 0; width: 26%; height: 34%; }
.lake-reed.left { left: -3%; }
.lake-reed.right { right: -4%; transform: scaleX(-1); }
.lake-reed::before,
.lake-reed::after {
  content: '';
  position: absolute;
  bottom: 0;
  width: 9px;
  height: 76%;
  background: linear-gradient(180deg, rgba(16, 22, 16, 0.9), rgba(8, 12, 9, 0.96));
  border-radius: 90% 8% 0 0 / 100% 8% 0 0;
  transform-origin: bottom left;
}
.lake-reed::before { left: 12%; transform: rotate(-9deg); }
.lake-reed::after {
  left: 34%;
  height: 58%;
  transform: rotate(11deg);
  /* 同一片叶再复制两道：box-shadow 不带旋转，所以这三根是一个方向的——够了，别多 */
  box-shadow: 16px 6px 0 -1px rgba(10, 15, 11, 0.92), 34px 12px 0 -2px rgba(10, 15, 11, 0.88);
}

/* ── 二、竿、线、漂、水波、鱼影 ── */

/*
 * 竿、线、漂、影子是**一串父子**（DOM 见 ui/fishing.js 的 lakeLayers）：
 * 竿从屏幕右下伸进来，竿头上吊着一根线，线尾是漂，漂底下的水里压着那道影子。
 *
 * 为什么非要挂成父子、不各摆各的坐标：竿是**斜着**的，竿头落在屏幕哪儿，
 * 取决于这一屏有多少像素宽（% 的长 × cos 角度 = 像素，再换算成 % 的高——两个百分比不互乘）。
 * 摆成四个独立坐标，换台手机、转一次屏就会看见线从漂上头脱开。
 */
.lake-rod {
  position: absolute;
  left: 34%;
  top: 30%;
  width: 66%;
  height: 4px;
  transform-origin: 0 50%;
  transform: rotate(27deg);
  background: linear-gradient(90deg,
    rgba(206, 178, 130, 0.7) 0%,
    rgba(96, 66, 36, 0.92) 22%,
    rgba(58, 38, 20, 0.96) 68%,
    rgba(30, 20, 12, 0.98) 100%);
  border-radius: 2px 3px 3px 2px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
/* 缠把那一截（竿尾那段缠线） */
.lake-rod::after {
  content: '';
  position: absolute;
  right: 0;
  top: -1px;
  width: 20%;
  height: 6px;
  border-radius: 3px;
  background: repeating-linear-gradient(90deg, rgba(28, 18, 10, 0.95) 0 3px, rgba(74, 52, 30, 0.95) 3px 5px);
}
/* 挂在竿头的那一串：反着竿的角度转回来，线就基本是垂的（差三度，看着像被竿带着一点） */
.lake-rig {
  position: absolute;
  left: 0;
  top: 50%;
  width: 0;
  height: 0;
  transform: rotate(-24deg);
  transform-origin: 0 0;
}
/* 那根线：1px、半透明、不抢眼（创作者：别画成粗绳子） */
.lake-line {
  position: absolute;
  left: 0;
  top: 0;
  width: 1px;
  height: 66px;
  background: linear-gradient(180deg, rgba(232, 240, 248, 0.3), rgba(226, 236, 245, 0.16));
  transition: height 0.22s ease;
}
/* 咬钩：线被往水里带一截（竿那头不动，看着就是被拽了一下） */
.fish-stage[data-phase="bite"] .lake-line { height: 74px; }

/*
 * 鱼漂：一线一点，漂身一半暗红一半白（夜里看得见的那种），下面压两圈水波。
 * 14px —— 再大就成了屏幕中间一颗靶子（创作者：不要太大）。
 */
/*
 * 鱼漂：吊在线尾（线的尽头是 rig 里 66px 那一处，漂顶那截小杆正好接上）。
 * 14px 宽——再大就成了屏幕中间一颗靶子（创作者：鱼漂不要太大）。
 */
.lake-bobber {
  position: absolute;
  left: 0;
  top: 60px;
  width: 14px;
  height: 20px;
  margin-left: -7px;
  animation: bobberFloat 4.6s ease-in-out infinite;
}
/* 漂那一截小杆 */
.lake-bobber::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 0;
  width: 2px;
  height: 9px;
  transform: translateX(-50%);
  background: rgba(236, 226, 200, 0.72);
}
/* 漂身：上半月光白、下半暗红，中间一道极细的暗线 */
.lake-bobber::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 11px;
  height: 13px;
  transform: translateX(-50%);
  border-radius: 50% 50% 46% 46%;
  background: linear-gradient(180deg,
    rgba(240, 234, 218, 0.95) 0 38%,
    rgba(40, 26, 20, 0.9) 38% 46%,
    rgba(150, 46, 38, 0.95) 46% 100%);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.55), 0 0 8px rgba(255, 210, 150, 0.18);
}
@keyframes bobberFloat {
  0%   { transform: translateY(0) rotate(0); }
  35%  { transform: translateY(-3px) rotate(-2deg); }
  70%  { transform: translateY(2px) rotate(1.5deg); }
  100% { transform: translateY(0) rotate(0); }
}
/* 咬钩：猛地往下一沉、快速抖两下（短、明显，然后交给那一条跃出水面） */
.fish-stage[data-phase="bite"] .lake-bobber {
  animation: bobberBite 0.9s cubic-bezier(0.2, 0.9, 0.3, 1) both;
}
@keyframes bobberBite {
  0%   { transform: translateY(0) rotate(0); }
  18%  { transform: translateY(7px) rotate(-6deg); }
  34%  { transform: translateY(2px) rotate(5deg); }
  52%  { transform: translateY(10px) rotate(-4deg); }
  72%  { transform: translateY(4px) rotate(2deg); }
  100% { transform: translateY(7px) rotate(0); }
}

/* 漂底下那两圈水波：慢慢扩到没有，无限循环（不是 loading 圈，只有一线边） */
.lb-ring {
  position: absolute;
  left: 50%;
  bottom: -3px;
  width: 16px;
  height: 6px;
  transform: translateX(-50%);
  border: 1px solid rgba(214, 232, 244, 0.3);
  border-radius: 50%;
  opacity: 0;
  animation: ringOut 3.6s ease-out infinite;
}
.lb-ring.two { animation-delay: 1.8s; }
@keyframes ringOut {
  0%   { opacity: 0;    transform: translateX(-50%) scale(0.5); }
  18%  { opacity: 0.55; }
  100% { opacity: 0;    transform: translateX(-50%) scale(3.4); }
}
.fish-stage[data-phase="bite"] .lb-ring { animation-duration: 1.2s; border-color: rgba(236, 226, 200, 0.5); }

/*
 * 水底那道影子：模糊、暗、看不清是什么（创作者：别让玩家认出是哪条鱼）。
 * 平时慢慢晃，咬钩那一下窜到漂底下来。
 */
.lake-shadow {
  position: absolute;
  left: 0;
  top: 94px;
  margin-left: -23px;
  width: 46px;
  height: 13px;
  border-radius: 50% 50% 46% 54% / 60% 60% 40% 40%;
  background: linear-gradient(90deg, rgba(6, 10, 14, 0.05), rgba(6, 10, 14, 0.8) 46%, rgba(6, 10, 14, 0.2));
  filter: blur(5px);
  opacity: 0.16;
  animation: shadowSway 11s ease-in-out infinite alternate;
}
/* 尾巴那一摆 */
.lake-shadow::after {
  content: '';
  position: absolute;
  right: -7px;
  top: 2px;
  width: 0;
  height: 0;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  border-left: 8px solid rgba(6, 10, 14, 0.55);
}
@keyframes shadowSway {
  0%   { transform: translate3d(0, 0, 0) rotate(-2deg); opacity: 0.1; }
  50%  { opacity: 0.2; }
  100% { transform: translate3d(-26px, 8px, 0) rotate(3deg); opacity: 0.12; }
}
.fish-stage[data-phase="bite"] .lake-shadow {
  animation: shadowDart 0.9s ease-out both;
}
@keyframes shadowDart {
  0%   { transform: translate3d(0, 6px, 0) rotate(0); opacity: 0.14; }
  40%  { opacity: 0.34; }
  100% { transform: translate3d(-34px, -22px, 0) rotate(-9deg) scale(1.12); opacity: 0.26; }
}

/* ── 三、水边那几行字 ── */

/*
 * 状态那一行挪到**水面下半**，压在一条极淡的暗带上（不是卡片：没有边、没有圆角框，
 * 只有中间实、两头化开的一点黑，字才读得清）。
 */
.lake-status {
  position: absolute;
  left: 50%;
  bottom: 15%;
  transform: translateX(-50%);
  z-index: 1;
  width: min(92%, 30rem);
  padding: 6px 10px;
  text-align: center;
  font-size: var(--fs-s);
  line-height: 1.7;
  letter-spacing: 0.04em;
  color: #e6d5a8;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.92), 0 0 16px rgba(0, 0, 0, 0.7);
  background: linear-gradient(90deg,
    rgba(9, 7, 5, 0) 0%,
    rgba(9, 7, 5, 0.5) 18%,
    rgba(9, 7, 5, 0.5) 82%,
    rgba(9, 7, 5, 0) 100%);
  pointer-events: none;
}
.lake-status.bite { color: #f2e2b6; }

/* ───────── 塞德里克在岸上（创作者 2026-10-17） ─────────
 * 黄灰一族，取的是赫奇帕奇那枚麦金（data/houses.js 的 #8a6a1c 往灰里压）。
 * 两颗气泡：抛竿正下方那颗常驻"在守护你"（不带头像），右上角那颗每竿的评价亮三秒就散。
 */
.fish-guard {
  position: absolute;
  left: 50%;
  bottom: 4px;
  transform: translateX(-50%);
  z-index: 3;
  padding: 3px 10px;
  border-radius: 999px;
  color: #f2e8c9;
  background:
    radial-gradient(120% 140% at 18% 0%, rgba(200, 170, 96, 0.28), transparent 64%),
    linear-gradient(180deg, rgba(58, 52, 36, 0.94), rgba(34, 30, 21, 0.96));
  border: 1px solid rgba(186, 160, 96, 0.55);
  box-shadow: 0 5px 14px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(245, 235, 200, 0.18);
  font-family: var(--font-body);
  font-size: 10px;
  line-height: 1.35;
  white-space: nowrap;
  pointer-events: none;
}
.fish-guard-txt { white-space: nowrap; }

/* 右上角那颗评价：贴着水面右上角（位置由 ui/fishing.js 按 .fish-stage 现算） */
.fish-cedric-say {
  position: fixed;
  z-index: 60;
  display: flex;
  align-items: flex-start;
  gap: 8px;
  max-width: min(66vw, 22rem);
  padding: 7px 12px 8px 7px;
  border-radius: 13px;
  color: #f2e8c9;
  background:
    radial-gradient(120% 140% at 18% 0%, rgba(200, 170, 96, 0.3), transparent 64%),
    linear-gradient(180deg, rgba(58, 52, 36, 0.95), rgba(34, 30, 21, 0.96));
  border: 1px solid rgba(186, 160, 96, 0.6);
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(245, 235, 200, 0.2);
  font-family: var(--font-body);
  font-size: var(--fs-s);
  line-height: 1.45;
  opacity: 0;
  animation: fishCedricSay 3s ease forwards;
  pointer-events: none;
}
.fish-cedric-face {
  width: 26px; height: 26px;
  flex: none;
  border-radius: 50%;
  object-fit: cover;
  object-position: 50% 12%;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
}
.fish-cedric-txt { text-align: left; }
@keyframes fishCedricSay {
  0%   { opacity: 0; transform: translateY(-6px) scale(0.96); }
  10%  { opacity: 1; transform: none; }
  80%  { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translateY(-4px); }
}

/* ── 四、钓上来的那一条：从水里跃出来 ── */

/*
 * 原来是"从空气里浮起来一句名字"。现在：水花先炸、那条鱼带一点旋转往上跃、
 * 落定在水面上方一点，然后淡去。时长照旧 2.4s（JS 里 CATCH_MS=2500 是它的兜底，别改短）。
 */
.fish-catch {
  z-index: 2;
  animation: catchLeap 2.4s cubic-bezier(0.2, 0.8, 0.3, 1) forwards;
}
@keyframes catchLeap {
  0%   { opacity: 0; transform: translate(-50%, -50%) translateY(26px) scale(0.72) rotate(-9deg); }
  16%  { opacity: 1; transform: translate(-50%, -50%) translateY(-14px) scale(1.04) rotate(4deg); }
  30%  { transform: translate(-50%, -50%) translateY(-20px) scale(1) rotate(-2deg); }
  52%  { opacity: 1; transform: translate(-50%, -50%) translateY(-16px) scale(1) rotate(0); }
  100% { opacity: 0; transform: translate(-50%, -50%) translateY(-30px) scale(0.98) rotate(0); }
}
.fc-art { position: relative; }
/* 跃出来时甩开的水花：五颗，各奔一个方向，只在前半程有 */
.fc-splash { position: absolute; inset: 0; pointer-events: none; }
.fc-splash .drop {
  position: absolute;
  left: 50%;
  top: 60%;
  width: 4px;
  height: 6px;
  border-radius: 60% 60% 50% 50%;
  background: linear-gradient(180deg, rgba(226, 240, 250, 0.8), rgba(150, 186, 214, 0.35));
  opacity: 0;
  animation: dropOut 0.9s ease-out forwards;
}
.fc-splash .d0 { animation-delay: 0.02s; }
.fc-splash .d1 { animation-delay: 0.08s; }
.fc-splash .d2 { animation-delay: 0.14s; }
.fc-splash .d3 { animation-delay: 0.2s; }
.fc-splash .d4 { animation-delay: 0.26s; }
@keyframes dropOut {
  0%   { opacity: 0;   transform: translate(-50%, 0) scale(0.6); }
  22%  { opacity: 0.9; }
  100% { opacity: 0;   transform: translate(calc(-50% + var(--dx, 0px)), var(--dy, -26px)) scale(0.8); }
}
/* 五颗各奔一处（CSS 里没有随机，方向写死在这一条上） */
.fc-splash .d0 { --dx: -26px; --dy: -20px; }
.fc-splash .d1 { --dx: -12px; --dy: -30px; }
.fc-splash .d2 { --dx: 4px;   --dy: -34px; }
.fc-splash .d3 { --dx: 18px;  --dy: -26px; }
.fc-splash .d4 { --dx: 30px;  --dy: -14px; }

/* ── 五、两颗钮：抛竿是木牌，鱼竿是旧物标签 ── */

/*
 * 抛竿：钉在水边的一块深色木牌（金细边、木纹、下面一道投影）。
 * hover 抬一点、边亮一点；按下去沉回去——三态都照创作者那一段来。
 */
.fish-cast-chip {
  padding: 7px 20px 8px;
  border-radius: 3px;
  background:
    linear-gradient(90deg, rgba(0, 0, 0, 0.4), transparent 18%, transparent 82%, rgba(0, 0, 0, 0.4)),
    repeating-linear-gradient(180deg, rgba(58, 37, 20, 0.94) 0 8px, rgba(41, 25, 14, 0.94) 8px 16px);
  border: 1px solid rgba(224, 178, 100, 0.42);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 232, 178, 0.12);
  backdrop-filter: none;
  transition: border-color 0.16s ease, background 0.16s ease, transform 0.16s ease, box-shadow 0.16s ease;
}
.fish-cast-chip:hover {
  border-color: var(--cine-gold);
  background:
    linear-gradient(90deg, rgba(0, 0, 0, 0.34), transparent 18%, transparent 82%, rgba(0, 0, 0, 0.34)),
    repeating-linear-gradient(180deg, rgba(76, 49, 26, 0.96) 0 8px, rgba(54, 33, 18, 0.96) 8px 16px);
  transform: translateY(-2px);
  box-shadow: 0 7px 16px rgba(0, 0, 0, 0.58), inset 0 1px 0 rgba(255, 232, 178, 0.16);
}
.fish-cast-chip:active {
  transform: translateY(1px);
  box-shadow: 0 2px 7px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 232, 178, 0.08);
}
/* 咬钩那 0.9 秒里它按不动（disabled）：给一点"这一竿还没完"的暗 */
.fish-cast-chip:disabled { opacity: 0.55; cursor: default; transform: none; }
.fcc-name { color: #f0dfb0; }
.fcc-sub { color: rgba(224, 178, 100, 0.82); }

/*
 * 手里那根竿：它是装备信息，不是主操作，所以**比抛竿弱一级**——
 * 小一号、暗一档、边上少一道金（原来它和抛竿长得几乎一样，两颗抢一个画面）。
 */
.fish-rod-chip {
  padding: 4px 9px;
  font-size: var(--fs-xs);
  opacity: 0.86;
  background: rgba(30, 20, 12, 0.66);
  border-color: rgba(224, 178, 100, 0.22);
}
.fish-rod-chip:hover { opacity: 1; }

/* ── 六、窄屏与"别晕" ── */

@media (max-width: 560px) {
  .fish-stage { min-height: 48vh; }
  /* 手机上这一屏更矮，竿与线按百分比走会显得太长：收一档 */
  .lake-rod { width: 62%; }
  .lake-shadow { width: 38px; }
  .lake-status { bottom: 17%; font-size: var(--fs-xs); }
}

/*
 * 晕动症：环境层全停（水面、雾、萤火、水波、影子），咬钩那一下只留**位移最小的淡入淡出**。
 * 钓上来的那一条本来就有这一支（见第 21 节 catchFade），照旧。
 */
@media (prefers-reduced-motion: reduce) {
  .lake-water::before,
  .lake-water::after,
  .lake-glint,
  .lake-mist,
  .lake-fireflies .ff,
  .lake-bobber,
  .lb-ring,
  .lake-shadow { animation: none; }
  .fish-stage[data-phase="bite"] .lake-bobber { animation: bobberBiteCalm 0.9s ease both; }
  @keyframes bobberBiteCalm {
    0% { opacity: 1; } 50% { opacity: 0.6; } 100% { opacity: 1; }
  }
}

/* ============================================================
   36 · 他个人的界面：Galgame 那一层（ui/screens-char.js）
   创作者 2026-10-03 给的那一份规格：这一屏不该是「背景图 + 一个立绘 + 右边四个按钮」，
   要像「一个可以点的、属于魔法世界的视觉小说场景」。
   ⚠️ 这一屏**不跟场景绑定**（从寝室门、地图偶遇、聊完天回来都画它，随时在任何地方调出来），
      所以底下这些氛围层全部挂在 `body.char-live` 这一颗开关上（在 main.js 的 render 里
      每次换场面重对一次），铺在**当前那张底图之上**——不挑图、不认图，换任何场景都成立。
   ⚠️ 只改长相：好感算法、互动 / 亲密 / 送礼 / 畅聊那四条去处、礼物消耗、日期精力金币、
      存档，一个字没动。
   本节写在第 27b 节之后，同一批类名以这里为准（CSS 靠顺序赢）。
   ============================================================ */

/* ── 一、整个舞台：暗角、柔光、贴地的雾 ── */

/*
 * 把共用那层 scrim 换成"书桌上摊着一页"的调子：
 * 顶上压黑（顶栏要退到后面去）、中间偏左提一点柔光（人站在那儿）、
 * 右边轻微暗下去（那叠纸才亮）、底部渐黑（托盘与旁白得住得下字）。
 */
body.char-live .stage-scrim {
  background:
    /* 角色区域的柔光：落在左中，不居中——居中就成了舞台追光 */
    radial-gradient(46% 52% at 30% 54%, rgba(255, 232, 188, 0.09), transparent 68%),
    /* 左右暗角 */
    radial-gradient(135% 105% at 50% 50%, transparent 46%, rgba(7, 5, 3, 0.74) 100%),
    /* 竖向：顶压黑 → 中段正常 → 底渐黑 */
    linear-gradient(180deg,
      rgba(7, 5, 3, 0.78) 0%,
      rgba(7, 5, 3, 0.3) 18%,
      rgba(7, 5, 3, 0.12) 44%,
      rgba(7, 5, 3, 0.5) 76%,
      rgba(7, 5, 3, 0.9) 100%),
    /* 右侧轻微暗化：那叠便签是亮的，底子得先沉下去 */
    linear-gradient(90deg, rgba(0, 0, 0, 0) 44%, rgba(9, 7, 5, 0.42) 100%);
}
/* 一层极慢的"空气"（0.03–0.06 那一档）：看得见的是场景活着，看不见的是动画 */
body.char-live .stage::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(58% 26% at 22% 88%, rgba(196, 210, 226, 0.05), transparent 72%),
    radial-gradient(46% 20% at 74% 92%, rgba(196, 210, 226, 0.04), transparent 74%);
  animation: charAir 12s ease-in-out infinite alternate;
}
@keyframes charAir {
  from { opacity: 0.5; transform: translate3d(-2%, 0, 0); }
  to   { opacity: 1;   transform: translate3d(2%, -1%, 0); }
}

/* ── 二、顶栏退到后面去，日期那一块当"旧日记页眉" ── */

body.char-live .hud { opacity: 0.9; }
/* 日期底下补一道旧金线（§22 那条横线）：只在这一屏加，别的屏的顶栏不动 */
body.char-live .hud-date {
  padding-bottom: 2px;
  border-bottom: 1px solid rgba(176, 138, 74, 0.34);
}

/* ── 三、进场与"呼吸" ── */

/*
 * 进场：框与人一起从左边进来一点、淡上（§5 那 400–600ms）。
 * 挂在 .cr-stage 而不是 .cr-portrait 上——立绘自己那条 transform 要留给呼吸用
 * （两条动画抢同一个 transform，后写的那条会整条盖掉前一条，进场就没了）。
 */
.cr-stage { animation: charEnter 0.56s ease both; }
@keyframes charEnter {
  from { opacity: 0; transform: translateX(-18px); }
  to   { opacity: 1; transform: translateX(0); }
}
/*
 * 呼吸：4.6s 一次、上下 2px。关键帧里必须把 --pz 那个放大倍数一起写上——
 * 立绘平时那条 transform 就是它，动画一跑要是不带着，人会当场缩回原尺寸。
 */
.cr-portrait { animation: charBreath 4.6s ease-in-out infinite; }
@keyframes charBreath {
  0%   { transform: scale(var(--pz, 1.12)) translateY(0); }
  50%  { transform: scale(var(--pz, 1.12)) translateY(-2px); }
  100% { transform: scale(var(--pz, 1.12)) translateY(0); }
}

/*
 * 脚底那一团（DOM 里的 .cr-ground）：一片椭圆影 + 一层贴着地的雾。
 * 有了它，人物才"站"在画上；没有它，就是一张 PNG 贴在背景上。
 */
.cr-ground {
  position: absolute;
  left: 10%;
  right: 10%;
  bottom: -1%;
  height: 14%;
  z-index: 4;
  pointer-events: none;
}
.cr-ground::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(50% 58% at 50% 50%, rgba(0, 0, 0, 0.22), transparent 72%);
  filter: blur(5px);
}
.cr-ground::after {
  content: '';
  position: absolute;
  left: -14%;
  right: -14%;
  bottom: -18%;
  height: 120%;
  background: radial-gradient(46% 70% at 40% 60%, rgba(206, 216, 228, 0.07), transparent 70%);
  filter: blur(10px);
  animation: charGroundMist 9s ease-in-out infinite alternate;
}
@keyframes charGroundMist {
  from { transform: translateX(-3%); opacity: 0.6; }
  to   { transform: translateX(3%); opacity: 1; }
}

/* ── 四、右边那一叠：四张便签，不是四个按钮 ── */

/*
 * 纸的质感全在 CSS 里叠出来（§7 / §29）：
 *   一层竖着的纸纹（当噪点用）+ 左上那束斜光 + 一道折痕 + 底下那层纸色渐变。
 * 边缘暗化交给 inset 阴影，不再描一圈"现代卡片"的实边。
 */
.cr-card {
  border-radius: 2px;
  border-color: rgba(64, 48, 31, 0.3);
  background:
    repeating-linear-gradient(96deg, rgba(120, 92, 56, 0.045) 0 1px, transparent 1px 4px),
    radial-gradient(80% 60% at 14% 2%, rgba(255, 255, 255, 0.5), transparent 60%),
    linear-gradient(178deg, rgba(150, 116, 66, 0.14) 0 1px, transparent 1px) 62% 34% / 46% 2px no-repeat,
    linear-gradient(180deg, #f8efd9 0%, #ead9b8 62%, #ddc79f 100%);
  box-shadow:
    0 1px 0 rgba(255, 252, 240, 0.5) inset,
    0 -10px 16px rgba(120, 90, 50, 0.1) inset,
    /* 第二层纸（§12 那"一叠"）：一道硬边的纸色投影，从右下角探出一点 */
    5px 7px 0 -2px #d5bd93,
    6px 10px 14px rgba(0, 0, 0, 0.34),
    0 2px 0 rgba(50, 37, 24, 0.3),
    0 9px 20px rgba(0, 0, 0, 0.46);
  transition: transform 0.28s ease, box-shadow 0.28s ease, border-color 0.28s ease, filter 0.28s ease;
}
/*
 * ⚠️ 那"第二层纸"是**投影**，不是一张 ::after 纸片。
 *    便签自己那条 transform（每张歪一点）会开一个 layer context，
 *    里头 z-index:-1 的子元素照样画在**父元素背景之上**——纸角会糊在字底下。
 *    投影没有这个问题（它天生在元素后面）。
 */
/*
 * 蜡封 / 墨印（§8）：原来那颗 5px 的"钉子"留着位置，长得像按上去的一坨蜡——
 * 边缘不规则、深红、带一点斜压的阴影。四张各歪一点、色各偏一点（§6 不要一模一样）。
 */
.cr-card::before {
  top: 5px;
  left: 6px;
  width: 9px;
  height: 9px;
  border-radius: 48% 52% 45% 55% / 52% 46% 54% 48%;
  background: radial-gradient(circle at 34% 30%, #a8443a, #6d2a24 68%, #4c1c18 100%);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.45), inset 0 -1px 1px rgba(0, 0, 0, 0.4);
  transform: rotate(-8deg);
  transition: transform 0.28s ease;
}
/* 四张错落：各歪一点、各让一档（§6：不等宽不等距不严格对齐，但幅度要收着） */
.cr-card:nth-child(1) { transform: rotate(-1.4deg); margin-right: 2px; }
.cr-card:nth-child(2) { transform: rotate(1.1deg); margin-right: 12px; margin-top: 4px; }
.cr-card:nth-child(3) { transform: rotate(-0.7deg); margin-right: 4px; margin-top: 6px; }
.cr-card:nth-child(4) { transform: rotate(1.6deg); margin-right: 14px; }
.cr-card:nth-child(2)::before { transform: rotate(9deg); }
.cr-card:nth-child(3)::before { transform: rotate(-3deg); background: radial-gradient(circle at 34% 30%, #9d4038, #63261f 68%, #451815 100%); }
.cr-card:nth-child(4)::before { transform: rotate(14deg); }
/* 纸的透明度也差半档：最上面那张刚放上去、最下面那张压得久一点 */
.cr-card:nth-child(2) { opacity: 0.985; }
.cr-card:nth-child(3) { opacity: 0.97; }
.cr-card:nth-child(4) { opacity: 0.955; }

/*
 * hover（§10）：像手指碰到一张纸——抬 4px、原来的歪角归正、影子加深、纸亮一档、
 * 蜡封跟着放大一点、字压深。**不用 scale**（那是现代按钮的做法）。
 */
.cr-card.on:hover {
  border-color: rgba(112, 78, 36, 0.5);
  filter: brightness(1.05);
  transform: rotate(0deg) translateY(-4px);
  box-shadow:
    0 1px 0 rgba(255, 252, 240, 0.55) inset,
    0 -10px 16px rgba(120, 90, 50, 0.1) inset,
    6px 9px 0 -2px #d5bd93,
    8px 14px 20px rgba(0, 0, 0, 0.4),
    0 3px 0 rgba(50, 37, 24, 0.28),
    0 16px 26px rgba(0, 0, 0, 0.52);
}
.cr-card.on:hover::before { transform: rotate(0deg) scale(1.18); }
.cr-card.on:hover .crc-label { color: #241a12; }
/* active（§11）：手按了一下纸——往下两格、影子收掉，120ms 就够 */
.cr-card.on:active {
  transform: rotate(0deg) translateY(2px);
  box-shadow: 0 1px 0 rgba(50, 37, 24, 0.3), 0 5px 10px rgba(0, 0, 0, 0.4);
  transition-duration: 0.12s;
}
/* 锁着的那三张：不抬、不亮，蜡封也压暗（按不动的东西不该有 hover 的反馈） */
.cr-card.soon::before { background: radial-gradient(circle at 34% 30%, #7d6a55, #4a3d31 70%); }
.cr-card.soon:hover { transform: none; filter: none; }

/* ── 五、底下那块：从"角色卡"改成"档案牌 + 学院印章" ── */

/*
 * 牌子：旧皮革（两道斜纹 + 中间亮、四角暗）+ 一圈**金细边**（§13 要的不是塑料感高光）。
 * 学院色仍然从 data/houses.js 那一份走（--plate），这里只往上压质感。
 */
.cr-plate {
  border-radius: 2px;
  border: 1px solid rgba(0, 0, 0, 0.5);
  outline: 1px solid rgba(176, 138, 74, 0.34);
  outline-offset: -4px;
  background:
    repeating-linear-gradient(52deg, rgba(255, 255, 255, 0.022) 0 2px, transparent 2px 6px),
    radial-gradient(120% 90% at 22% 8%, rgba(255, 240, 210, 0.1), transparent 58%),
    linear-gradient(180deg, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.34)),
    var(--plate, #4a4a44);
  box-shadow: 0 3px 0 rgba(20, 14, 9, 0.52), 0 14px 28px rgba(0, 0, 0, 0.52),
              inset 0 1px 0 rgba(255, 246, 224, 0.12);
}
/* 院徽当"学院印章"：稍微压出牌子一点、带投影、歪两度（§14，不要霓虹发光） */
.cp-crest {
  left: -26px;
  transform: translateY(-50%) rotate(-3deg);
  filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.66));
}
/*
 * 好感那一窄签：原来是 999px 的胶囊（§27 明确不要大圆角）——改成一张**别在牌子上的窄纸条**。
 */
.cp-aff {
  border-radius: 2px;
  border-color: rgba(64, 48, 31, 0.46);
  background:
    repeating-linear-gradient(96deg, rgba(120, 92, 56, 0.05) 0 1px, transparent 1px 4px),
    linear-gradient(180deg, #f8efd9, #e2cfa8);
  transform: rotate(-1.6deg);
}
/*
 * 十格刻度（§15）：手绘的小方墨格——每格歪一点点、边是墨线不是描边，
 * 填上的是深红墨水（边缘洇一点），空格子只是笔勾出来的框。
 */
.cpa-bar i {
  width: 8px;
  height: 8px;
  border-radius: 2px 3px 2px 3px;
  background: transparent;
  box-shadow: inset 0 0 0 1px rgba(64, 48, 31, 0.34);
}
.cpa-bar i:nth-child(2n) { transform: rotate(2deg); }
.cpa-bar i:nth-child(3n) { transform: rotate(-2.5deg); }
.cpa-bar i.on {
  background: radial-gradient(circle at 38% 34%, #b04a3c, #7c2b24 72%);
  box-shadow: 0 0 2px rgba(124, 43, 36, 0.5);
}
/* 刚涨的那一次（.fresh 由 ui/screens-char.js 判，见那边的 seenAff）：墨水先洇开再填上 */
.cr-plate.fresh .cpa-bar i.on { animation: inkSpread 0.5s ease both; }
@keyframes inkSpread {
  0%   { background-size: 30% 30%; filter: blur(1.4px); transform: scale(0.7); opacity: 0.35; }
  60%  { filter: blur(0.3px); transform: scale(1.08); opacity: 1; }
  100% { filter: blur(0); transform: scale(1); opacity: 1; }
}

/* ── 六、场景旁白那一行（§17） ── */

/*
 * 视觉小说的旁白：小、压在底部那条暗带上（§17 说不要放顶部）。
 * 话不是现编的——是脚下那一处地点自己的描述、或偶遇那块名牌上那句（main.js 的 charNarr）。
 * 没有就不摆这一行。
 */
.cr-narr {
  position: absolute;
  left: 4%;
  right: 4%;
  bottom: 8px;
  margin: 0;
  text-align: center;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  line-height: 1.7;
  letter-spacing: 0.04em;
  color: #d8c39b;
  opacity: 0.8;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.9), 0 0 14px rgba(0, 0, 0, 0.7);
  pointer-events: none;
  animation: charNarrIn 0.7s ease both;
}
@keyframes charNarrIn {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 0.8; transform: translateY(0); }
}

/* ── 七、【离开】那一颗：暗色木牌，不是白纸条（§24） ── */

body.char-live .tray > .btn.exit {
  background:
    repeating-linear-gradient(180deg, rgba(0, 0, 0, 0.14) 0 7px, transparent 7px 14px),
    linear-gradient(180deg, #3b2a1c, #241a12);
  border-color: rgba(176, 138, 74, 0.3);
  border-radius: 2px;
  box-shadow: 0 2px 0 rgba(20, 14, 9, 0.5), 0 8px 16px rgba(0, 0, 0, 0.46);
}
body.char-live .tray > .btn.exit:hover {
  background:
    repeating-linear-gradient(180deg, rgba(0, 0, 0, 0.1) 0 7px, transparent 7px 14px),
    linear-gradient(180deg, #4a3524, #2c2016);
}

/* ── 八、窄屏与"别晕" ── */

@media (max-width: 480px) {
  /* 窄屏上第二层纸那角收回去一点：牌子本来就挤，别让它看着像没对齐 */
  .cr-card { box-shadow: 0 1px 0 rgba(255, 252, 240, 0.5) inset, 0 -10px 16px rgba(120, 90, 50, 0.1) inset, 3px 5px 0 -2px #d5bd93, 4px 7px 12px rgba(0, 0, 0, 0.34), 0 2px 0 rgba(50, 37, 24, 0.3), 0 9px 20px rgba(0, 0, 0, 0.46); }
  .cr-card:nth-child(2) { margin-right: 6px; }
  .cr-card:nth-child(4) { margin-right: 8px; }
  .cp-crest { left: -20px; }
  .cr-narr { bottom: 4px; font-size: 10px; }
}

/*
 * 晕动症：进场、呼吸、雾、空气、墨水洇开全停。
 * 停的是**动**，纸的质感、暗角、错落与蜡封都留着——那些不是动画。
 */
@media (prefers-reduced-motion: reduce) {
  .cr-stage,
  .cr-portrait,
  .cr-ground::after,
  .cr-narr,
  body.char-live .stage::after,
  .cr-plate.fresh .cpa-bar i.on { animation: none; }
}

/* ============================================================
   37 · 遭遇战改成「打在地图上」（ui/battle.js）
   ------------------------------------------------------------
   创作者 2026-10-03 的规格一句话版本：**删掉那块把整场战斗框起来的深色大面板**，
   让两只动物直接站在脚下那一处场景里对打，只留最前面一层卡牌当操作区。
   所以这一节干三件事：
     1. 把第 30 节的 .battle 外壳（底色 / 边框 / 圆角 / 大投影 / 标题栏那道线）整个撤掉；
     2. 立绘改成**近大远小**（敌方小、位置高；我方大、位置低），脚底各加一片椭圆影；
     3. 名牌与 HP 从"面板里的进度条"改成**飘在角色附近的小字**，卡牌摊成一手牌。
   ⚠️ 只改视觉与层级：卡牌系统、2 点魔力、费用、抽牌弃牌、回合、伤害与暴击闪避、
      敌方 AI、捕捉、胜负判定，全在 engine/battle.js 与 data/battle.js，一个字没动。
   ⚠️ 第 32 节那张「捕捉纸」（.sheet.cpt-night）借的是战斗那一身**色**，不是这块面板，
      它自己带着底与边，这一节撤面板不影响它。
   本节写在第 30 / 32 节之后，同名规则以这里为准。
   ============================================================ */

/* ── 一、外壳：没有面板了 ── */

/*
 * 颜色令牌留着（第 30 / 32 节到处在引用），**壳**撤掉：
 * 不铺底、不描边、不投影、不留内边距——整屏就是那一处场景。
 * 规格第四十八节：地图至少七成得看得见，需要遮的只有字与牌后面那一条。
 */
.battle {
  --bt-bg: #211712;
  --bt-panel: #2d2018;
  --bt-panel-dark: #19110d;
  --bt-gold: #c49a55;
  --bt-gold-light: #e0bd78;
  --bt-gold-dark: #76552c;
  --bt-paper: #d5c09a;
  --bt-text: #f1dfbd;
  --bt-danger: #8f4a3d;

  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0;
  width: 100%;
  margin: 0 auto;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  color: var(--bt-text);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85), 0 0 12px rgba(0, 0, 0, 0.6);
}

/* 顶上那一行：只剩左边一小行金字 + 右边那枚小木牌，中间那道分隔线不要了 */
.bt-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: 2px 2px 0;
  border-bottom: 0;
}
.bt-turn {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.22em;
  color: var(--bt-gold);
  opacity: 0.92;
  animation: btTurnIn 300ms ease both;
}
@keyframes btTurnIn {
  from { opacity: 0; }
  to   { opacity: 0.92; }
}

/* 【逃跑】：右上角一枚暗色小木牌，不抢画面（规格第三十节） */
.bt-head .btn.quit {
  flex: none;
  width: auto;
  min-width: 0;
  padding: 3px 12px 4px;
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  color: rgba(247, 222, 217, 0.86);
  background:
    repeating-linear-gradient(180deg, rgba(0, 0, 0, 0.16) 0 6px, transparent 6px 12px),
    linear-gradient(180deg, rgba(74, 26, 26, 0.86), rgba(40, 14, 14, 0.92));
  border: 1px solid rgba(158, 66, 60, 0.6);
  border-radius: 2px;
  box-shadow: 0 2px 0 rgba(10, 7, 5, 0.5), 0 6px 14px rgba(0, 0, 0, 0.42);
  backdrop-filter: none;
}
.bt-head .btn.quit:hover {
  color: #fff2ee;
  border-color: rgba(214, 110, 100, 0.9);
  transform: none;
}
.bt-head .btn.quit:active { transform: translateY(1px); }

/* ── 二、场地：近大远小 ── */

/*
 * 一屏拉满（顶栏与托盘之间那点余量给足），敌方贴上面、我方压下面，
 * 中间那一段**空着**——那片空地就是"两只动物之间的距离"（规格三十三）。
 */
.bt-arena {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  gap: 0;
  min-height: min(66vh, 560px);
  padding: var(--sp-2) 0 var(--sp-3);
}
/* 两边各自不再是一块"卡"：没有内边距、没有边框、没有圆角 */
.bt-side {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  transition: none;
}
.enemy { z-index: 5; }
.mine { z-index: 10; }

/*
 * 投放区亮起来：原来是给整边套一圈圆角框（那正是"UI 框"），
 * 规格第四十六 / 四十七节要的是**脚底一圈很淡的金色圆环**——换成椭圆光晕。
 * 用 .bp-stage::before（平时整个透明），框本身仍然看不见。
 */
.bt-side.drop-hot { border-color: transparent; background: none; box-shadow: none; }
.bp-stage::before {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 2%;
  width: 74%;
  height: 26%;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(50% 50% at 50% 50%, rgba(224, 189, 120, 0.2), transparent 70%);
  box-shadow: inset 0 0 0 1px rgba(224, 189, 120, 0.34);
  opacity: 0;
  transition: opacity 200ms ease;
  pointer-events: none;
}
.bt-side.drop-hot .bp-stage::before { opacity: 1; }

/*
 * 两个舞台的**大小差**就是景深：敌方矮一档、我方高一档。
 * 每只动物自己的胖瘦仍由 data/battle.js 的 BATTLE_PORTRAITS（scale / x / y）调——
 * 敌我比例差放在这里给，比给每只写两套数好维护（加新动物只调一个 scale）。
 */
.bp-stage { width: min(50%, 224px); height: min(20vh, 168px); }
.enemy-portrait-stage { width: min(42%, 190px); height: min(17vh, 148px); opacity: 0.96; }
.mine .bp-stage { width: min(64%, 300px); height: min(27vh, 236px); }

/*
 * 脚底那片椭圆影（规格第三十四节）：透明 PNG 直接贴在照片上最容易"飘"，
 * 有了这一片才叫站在地上。敌方那片小、我方那片大。
 */
.bp-stage::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 1%;
  width: 58%;
  height: 14px;
  transform: translateX(-50%);
  background: radial-gradient(50% 50% at 50% 50%, rgba(0, 0, 0, 0.4), transparent 72%);
  filter: blur(4px);
  pointer-events: none;
}
.enemy-portrait-stage::after { width: 46%; height: 10px; }
.mine .bp-stage::after { width: 64%; height: 18px; }

/* 立绘只许 translate / scale / rotate，不许动宽高（规格第二十一节） */
.bp-img { filter: drop-shadow(0 8px 16px rgba(0, 0, 0, 0.35)); }

/*
 * 倒下那一下顺手修一处老毛病：原来这里写的是 `transform: translate(...) scale(...)`，
 * 而立绘的 transform 是 JS 按舞台现算后**写在行内 style 上的**（fit()），
 * 行内永远赢过样式表 → 那条缩放其实从来没生效过，只有 opacity 生效。
 * 换成独立的 translate / scale 两个属性就不抢那条行内 transform 了。
 */
.bp-stage.down .bp-img {
  translate: 0 8px;
  scale: 0.94;
  opacity: 0;
}

/* ── 三、名牌：飘在角色附近的小字，不是面板里的一格 ── */

.bt-plate {
  z-index: 15;
  gap: 1px;
  margin-top: -2px;
}
.bt-nm {
  font-size: var(--fs-s);
  letter-spacing: 0.16em;
  color: var(--bt-paper);
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.7), 0 0 12px rgba(0, 0, 0, 0.55);
}
.bt-hprow { gap: 6px; }
/* HP 条收细、只留一圈古铜金细边，不再是一块"巨大血条"（规格第十一节） */
.bt-hp {
  width: min(38vw, 158px);
  height: 5px;
  border: 1px solid rgba(196, 154, 85, 0.42);
  border-radius: 2px;
  background: rgba(10, 7, 5, 0.55);
  box-shadow: none;
}
.mine .bt-hp { width: min(44vw, 186px); }
.bt-hp i { background: linear-gradient(180deg, #b8503c, #7a281f); box-shadow: none; }
.bt-hpnum { font-size: 10px; letter-spacing: 0.08em; opacity: 0.82; }
/* 五维那行退成辅助信息：平时淡着，鼠标碰到这一边才抬起来（规格第三十二节） */
.bt-sub { font-size: 10px; opacity: 0.46; transition: opacity 200ms ease; }
.bt-side:hover .bt-sub { opacity: 0.95; }

/* 防御中那一枚：跟着变方、变淡（和全局那套 2px 圆角一路） */
.bt-shield {
  border-radius: 2px;
  background: rgba(18, 28, 36, 0.62);
  border-color: rgba(150, 190, 210, 0.3);
}

/* ── 四、战况那一行：一行字，不是一个框（规格第十三节） ── */

.bt-log {
  position: relative;
  z-index: 15;
  width: min(100%, 34rem);
  margin: 0 auto;
  padding: 4px 12px 6px;
  min-height: 2.2em;
  font-size: var(--fs-xs);
  line-height: 1.6;
  letter-spacing: 0.06em;
  text-align: center;
  color: rgba(241, 223, 189, 0.9);
  border: 0;
  border-radius: 0;
  /* 只留中间实、两头化开的一点暗：字读得清，又不成一块底板 */
  background: linear-gradient(90deg,
    rgba(9, 7, 5, 0) 0%,
    rgba(9, 7, 5, 0.46) 16%,
    rgba(9, 7, 5, 0.46) 84%,
    rgba(9, 7, 5, 0) 100%);
}

/* ── 五、底下那一条：魔力在上、一手牌摊开、结束回合在右边 ── */

.bt-bar {
  position: relative;
  z-index: 30;
  gap: 2px;
  padding: var(--sp-2) 0 calc(var(--sp-2) + env(safe-area-inset-bottom));
  border-top: 0;
  /* 卡牌这一带要压得住底图，才看得清卡面那几行小字（第四十八节允许的这一条淡渐变） */
  background: linear-gradient(180deg, rgba(9, 7, 5, 0) 0%, rgba(9, 7, 5, 0.4) 42%, rgba(9, 7, 5, 0.66) 100%);
}
.bt-mana { gap: 6px; }
/* 魔力宝石：可用的暖金带一点光，用掉的只留一圈边（第十八节：不要现代蓝条） */
.bt-gem { width: 11px; height: 11px; border-color: rgba(196, 154, 85, 0.5); }
.bt-gem.on { box-shadow: 0 0 8px rgba(224, 189, 120, 0.55); }

/* 牌堆那一横排：手牌在中间，【结束回合】挂在右边 */
.bt-deck {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: var(--sp-3);
  width: 100%;
}
.bt-hand {
  flex: 0 1 auto;
  gap: 4px;
  min-height: 148px;
  padding: 10px 0 4px;
}
/*
 * 一手牌该像**摊在桌上的牌**：左右两张各让开一点、往里歪，中间那张略突出，
 * 三张之间压一点（重叠靠负 margin，不用 transform——拖牌时那张复制品要按原位摆）。
 * 幅度收得很小（2.5°、6px），手机上再收一档，别影响点按。
 * ⚠️ 歪角用独立的 `rotate` 属性：hover 那条抬牌走的是 `transform`，
 *    两个各管各的才不会互相盖掉（同一属性后写的赢）。
 */
.bt-hand .bt-card:nth-child(1) { margin-right: -6px; rotate: -2.5deg; }
.bt-hand .bt-card:nth-child(2) { margin-bottom: 8px; }
.bt-hand .bt-card:nth-child(3) { margin-left: -6px; rotate: 2.5deg; }

/* 卡：圆角收到 3px（第十七节：不要大圆角现代卡），边仍是古铜金 */
.bt-card {
  border-radius: 3px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 235, 200, 0.1);
  transition: transform 220ms ease, box-shadow 220ms ease, border-color 220ms ease, opacity 180ms ease;
}
.bt-card::before { inset: 3px; border-radius: 2px; }
/* hover：抬起来、影子加重（第十六节那两个数：-12px / 1.04） */
.bt-card:hover {
  transform: translateY(-12px) scale(1.04);
  z-index: 3;
}
/* 让位那两条照旧（左右邻居各挪一点），只是幅度跟着收小 */
.bt-card:has(+ .bt-card:hover) { transform: translateX(-7px); }
.bt-card:hover + .bt-card { transform: translateX(7px); }

/* 【结束回合】：牌堆右边一块小木牌，视觉等级低于卡牌（第十九节） */
.bt-deck .btn.bt-go {
  flex: none;
  width: auto;
  min-width: 0;
  margin-bottom: 10px;
  padding: 5px 14px 6px;
  font-size: var(--fs-xs);
  letter-spacing: 0.14em;
  color: rgba(224, 189, 120, 0.92);
  background:
    repeating-linear-gradient(180deg, rgba(0, 0, 0, 0.14) 0 6px, transparent 6px 12px),
    linear-gradient(180deg, rgba(58, 41, 28, 0.9), rgba(28, 19, 14, 0.94));
  border: 1px solid rgba(196, 154, 85, 0.42);
  border-radius: 2px;
  box-shadow: 0 2px 0 rgba(10, 7, 5, 0.5), 0 6px 14px rgba(0, 0, 0, 0.42);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
}
.bt-deck .btn.bt-go:hover {
  color: #fff2d2;
  border-color: rgba(224, 189, 120, 0.7);
  transform: none;
}
.bt-deck .btn.bt-go:active { transform: translateY(1px); }

/* ── 六、特效与胜负那一行：层级按第三十七节排 ── */

.bt-fx { z-index: 20; }
.bt-num { text-shadow: 0 2px 8px rgba(0, 0, 0, 0.9); }
/* 暴击不要那行大写 CRITICAL（第二十六节）：只留金字 + 数字大一档 */
.bt-num.crit::after { content: ''; }
.bt-banner {
  z-index: 35;
  background: rgba(12, 8, 6, 0.5);
  border-top: 1px solid rgba(224, 189, 120, 0.42);
  border-bottom: 1px solid rgba(224, 189, 120, 0.42);
}

/* ── 七、窄屏（第三十六节） ── */

@media (max-width: 560px) {
  .bt-arena { min-height: min(58vh, 460px); }
  .bp-stage { width: min(56%, 200px); height: min(18vh, 150px); }
  .enemy-portrait-stage { width: min(46%, 164px); height: min(15vh, 132px); }
  .mine .bp-stage { width: min(70%, 244px); height: min(23vh, 200px); }
  /* 手机上牌不能挤：重叠与歪角都收掉，只留中间那张抬高一档 */
  .bt-hand { gap: 7px; min-height: 126px; padding-top: 6px; }
  .bt-hand .bt-card { margin-left: 0; margin-right: 0; rotate: 0deg; }
  .bt-hand .bt-card:nth-child(2) { margin-bottom: 6px; }
  .bt-card { width: 80px; height: 114px; }
  /* 结束回合挪到牌堆下面一行（空间不够时按规格第十九节） */
  .bt-deck { flex-wrap: wrap; gap: var(--sp-2); }
  .bt-deck .btn.bt-go { margin-bottom: 0; }
}

/* 晕动症：飘字、抬牌、回合淡入全停；近大远小与脚底影子是静态的，留着 */
@media (prefers-reduced-motion: reduce) {
  .bt-turn { animation: none; }
  .bt-card,
  .bt-card:hover,
  .bt-hand .bt-card:nth-child(n) { transition: none; transform: none; }
  .bp-stage.hurt .bp-img,
  .bp-stage.dodge .bp-img,
  .bp-stage.lunge .bp-img { animation: none; }
}

/* ============================================================
   38 · 手抄本：翻开的是一本私人手札（ui/screens-meta.js）
   ------------------------------------------------------------
   创作者 2026-10-03 的规格：这一页不该是"一个属性菜单"，而像"翻开自己天天在用的魔法手札"。
   所以每一块都做成**一样东西**：
     钱袋 = 一只旧皮袋（里有几枚金币，鼓瘪跟着钱走）
     精力 = 十颗魔法火花（亮着的才是力气）
     声望 = 三条不同的记录（学院荣誉 / 官方档案 / 地下关系）
     温室 = 夹在书里的一排老玻璃窗格
     属性 = 魔法能力记录（两排三格，运气那格有点光泽）
     日记 = 一叠折着的便签（默认只露一行，点开才摊平）
     底部 = 三本收藏册
   ⚠️ 只改长相：钱袋读的仍是 state.gold、精力读的仍是 stamina/maxStamina、
      温室读的仍是 s.plots、属性读的仍是 s.skills、日记读的仍是 s.journal，
      底部三颗走的仍是原来那三条跳转。一条账都没动。
   ⚠️ 声望是**新摆的一块**（原来的「关于你自己」整块撤了）：值暂时住在
      data/reputation.js（没进存档），读写口 get / set / update 都在那边。
   ============================================================ */

/* ── 一、钱袋与精力：两样东西并排 ── */

.led-top-pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}

/*
 * 钱袋：一只旧皮革小袋，袋口露出几枚金币。
 * 鼓瘪走 --p（0–1，由 ui/screens-meta.js 按总额算出来）：袋身 scaleY 变一点、
 * 露出的金币 3–6 枚——看一眼就知道里头大概有多少，不必先读那行字。
 */
.purse {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  min-height: 104px;
  padding: 6px 4px 2px;
}
/* 袋身：梯形（下宽上窄）+ 皮纹 + 左侧一道高光 */
.pu-body {
  position: relative;
  z-index: 2;
  width: 78px;
  height: 56px;
  transform-origin: 50% 100%;
  transform: scaleY(calc(0.78 + 0.22 * var(--p, 0.5)));
  background:
    radial-gradient(90% 70% at 26% 22%, rgba(255, 226, 176, 0.16), transparent 62%),
    repeating-linear-gradient(72deg, rgba(0, 0, 0, 0.07) 0 2px, transparent 2px 5px),
    linear-gradient(180deg, #6b4a2c 0%, #4a3120 46%, #33210f 100%);
  border: 1px solid rgba(28, 18, 10, 0.7);
  border-radius: 34% 34% 44% 44% / 26% 26% 62% 62%;
  box-shadow: inset 0 -6px 12px rgba(0, 0, 0, 0.45), 0 6px 14px rgba(0, 0, 0, 0.35);
}
/* 袋口那圈布边（缝线）：一排小针脚 */
.pu-body::before {
  content: '';
  position: absolute;
  left: 8%;
  right: 8%;
  top: 5px;
  height: 4px;
  background: repeating-linear-gradient(90deg, rgba(232, 210, 170, 0.5) 0 3px, transparent 3px 6px);
  border-radius: 2px;
  opacity: 0.9;
}
/* 束口那根金绳 + 一个小结 */
.pu-tie {
  position: absolute;
  z-index: 3;
  top: 26px;
  left: 50%;
  width: 88px;
  height: 5px;
  transform: translateX(-50%);
  border-radius: 3px;
  background: linear-gradient(180deg, #e0bd78, #9a6f2c);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
.pu-tie::after {
  content: '';
  position: absolute;
  left: 50%;
  top: -3px;
  width: 9px;
  height: 9px;
  transform: translateX(-50%) rotate(45deg);
  border-radius: 2px;
  background: linear-gradient(180deg, #f0d79a, #a87c33);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
}
/* 袋口露出来的金币：3–6 枚，各自错着一点（位置写死在 .c0–.c5 上，不用随机）。
 * z-index 压在袋身之上——它们是"从袋口冒出来"的，不该被袋子吃掉。 */
.pu-coins {
  position: absolute;
  z-index: 3;
  left: 50%;
  bottom: 68px;
  width: 0;
  height: 0;
}
.pu-coin {
  position: absolute;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #ffe7ab, #c9963c 62%, #8a6222 100%);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5), inset 0 -1px 1px rgba(0, 0, 0, 0.3);
}
.pu-coin.c0 { left: -22px; bottom: -4px; }
.pu-coin.c1 { left: -6px; bottom: 4px; }
.pu-coin.c2 { left: 10px; bottom: -2px; }
.pu-coin.c3 { left: -15px; bottom: 10px; transform: scale(0.92); }
.pu-coin.c4 { left: 3px; bottom: 13px; transform: scale(0.86); }
.pu-coin.c5 { left: 21px; bottom: 8px; transform: scale(0.9); }
/* 金额：辅助信息，小字、压在袋子下面 */
.pu-tx {
  display: block;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  color: var(--ink-soft);
  white-space: nowrap;
}

/*
 * 精力：十颗四角星（亮＝还有的力气，灭＝用掉的）。
 * 形状用 clip-path 画，不用素材；亮的那几颗带一点暖光。
 * 打开这张纸时**从左到右依次点亮**（每颗 140ms，靠 --i 错开；见下面那条动画）。
 */
.vigor {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  min-height: 104px;
  padding: 6px 4px 2px;
}
.vg-sparks {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 5px;
  padding: 4px 2px;
}
.vg-spark {
  width: 16px;
  height: 16px;
  background: rgba(64, 48, 31, 0.22);
  clip-path: polygon(50% 0, 61% 39%, 100% 50%, 61% 61%, 50% 100%, 39% 61%, 0 50%, 39% 39%);
  opacity: 0.9;
  animation: vgIn 160ms ease both;
  animation-delay: calc(var(--i) * 90ms);
}
.vg-spark.on {
  background: linear-gradient(180deg, #ffe9b4, #d8a94e 62%, #a97c2c);
  box-shadow: 0 0 6px rgba(224, 189, 120, 0.5);
  opacity: 1;
}
@keyframes vgIn {
  from { opacity: 0; transform: scale(0.6); }
  to   { opacity: 1; transform: scale(1); }
}
.vg-tx {
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  color: var(--ink-soft);
}

/* ── 二、三条声望：三张**各自独立**的魔法档案 ──────────────────────
 *
 * 创作者 2026-10-03 定的：这一块不再要「同一个白框 + 换颜色的进度条」。
 * 三框共用的只有**骨架**（左边一枚徽记、右边抬头 + 那一横槽 + 数），
 * 而纸、边、光、装饰、动效、粒子、那一条槽怎么"长出来"——七样各写各的：
 *   学院   = 淡金旧羊皮纸，金墨写在纸槽上，偶尔反一道光，飘几点金粉；
 *   巫师界 = 午夜蓝档案夹，暗金压边、内圈一道蓝线，右上角挂着 № 编号牌，
 *            符文环在角落里慢慢转，能量封在槽里从左往右点亮；
 *   地下   = 嵌在石墙里的一块墨绿石板，四角旧铁箍，缝里流着毒绿的液体。
 * ⚠️ 全部纯 CSS（渐变 / 阴影 / 伪元素 / clip-path），**一张图都没引**；
 *    动效只在自己框里（.rep 一律 overflow:hidden），数仍从 data/reputation.js 读。
 */

/* 三张纸摞在一列，中间不再有一张大外框 */
.rep-stack { display: flex; flex-direction: column; gap: var(--sp-3); }

/* 骨架：三框共用这一副位置关系（样子一律不在这里定） */
.rep {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 2px var(--sp-2);
  padding: 10px 12px 11px;
  overflow: hidden;
  transition: transform 180ms ease, box-shadow 260ms ease, border-color 260ms ease;
}
.rep-body { min-width: 0; }
/* 抬头这排：窄屏上宁可英文掉到第二行，也不许把中文挤成一列竖字（巫师界那一框最先撞） */
.rep-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 6px; }
.rep-zh { font-size: var(--fs-s); letter-spacing: 0.12em; white-space: nowrap; }
.rep-en {
  font-family: var(--font-hand);
  font-size: 10px;
  letter-spacing: 0.1em;
  white-space: nowrap;
  opacity: 0.8;
}
.rep-line { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.rep-track { position: relative; flex: 1 1 auto; min-width: 0; overflow: hidden; }
.rep-fill {
  display: block;
  position: relative;
  width: 100%;
  height: 100%;
  transform-origin: left center;
  transform: scaleX(var(--v, 0));
  /* 打开这张纸时从左往右写/灌进来（三条依次差 140ms） */
  animation: repIn 620ms cubic-bezier(0.22, 0.8, 0.3, 1) var(--d, 0ms) both;
}
@keyframes repIn {
  from { transform: scaleX(0); }
  to   { transform: scaleX(var(--v, 0)); }
}
.rep-v {
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  min-width: 2ch;
  text-align: right;
}
/* 徽记那一格的位置（形状在各框自己那节里画） */
.rep-mark { position: relative; flex: none; width: 26px; height: 26px; }
/* 那一圈沿边框走的光：只有巫师界给它样子，另两框留空 */
.rep-ring { position: absolute; inset: 0; display: none; pointer-events: none; border-radius: inherit; }

/* 微粒：位置/快慢由 JS 递进来的 --x --dl --du 决定，只动 transform 与 opacity */
.rep-mote {
  position: absolute;
  left: var(--x, 50%);
  bottom: 5px;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
  animation: repMote var(--du, 9s) linear var(--dl, 0s) infinite;
}
@keyframes repMote {
  0%   { transform: translate3d(0, 4px, 0);            opacity: 0; }
  14%  { opacity: var(--mo, 0.3); }
  62%  { opacity: var(--mo, 0.3); }
  100% { transform: translate3d(6px, -32px, 0);        opacity: 0; }
}

/* ══ 学院：荣誉册上撕下来的一页 ══ */

.rep.academy {
  border: 1px solid rgba(176, 138, 74, 0.52);
  border-radius: 3px 12px 4px 14px;
  background:
    /* 纸面起球：几处很淡的明斑 */
    radial-gradient(46% 70% at 10% 4%, rgba(255, 252, 241, 0.9), transparent 62%),
    radial-gradient(38% 60% at 88% 96%, rgba(176, 138, 74, 0.14), transparent 60%),
    /* 纸纹：一道一道极细的横丝 */
    repeating-linear-gradient(0deg, rgba(176, 138, 74, 0.05) 0 1px, transparent 1px 5px),
    linear-gradient(155deg, #f8eed6 0%, #f1e2c1 44%, #e5cfa3 100%);
  /* 边缘深一点（旧纸），里面压一道白 */
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.55),
    inset 0 0 26px rgba(176, 138, 74, 0.2),
    0 2px 5px rgba(64, 48, 31, 0.16);
  color: #55391a;
}
/* 四角那道极淡的装饰线（往里缩一圈） */
.rep.academy::before {
  content: '';
  position: absolute;
  inset: 4px;
  border: 1px solid rgba(176, 138, 74, 0.3);
  border-radius: 2px 9px 3px 10px;
  pointer-events: none;
}
/* 每 9 秒反一道金光：像徽章偶尔照到一下窗户光 */
.rep.academy::after {
  content: '';
  position: absolute;
  inset: -60% -30%;
  background: linear-gradient(100deg, transparent 44%, rgba(255, 250, 232, 0.85) 50%, transparent 56%);
  opacity: 0.16;
  transform: translateX(-70%);
  pointer-events: none;
  animation: repSweep 9s ease-in-out 1.1s infinite;
}
@keyframes repSweep {
  0%, 72% { transform: translateX(-70%); }
  92%, 100% { transform: translateX(70%); }
}
/* 徽记：金色菱形 + 十字 + 四颗小点（全用背景层画的） */
.rep.academy .rep-mark {
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
  background: linear-gradient(135deg, #edcc86, #b9873f 55%, #8a611f);
  animation: repFadeUp 520ms ease var(--d, 0ms) both;
}
.rep.academy .rep-mark::before {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(rgba(255, 250, 235, 0.8), rgba(255, 250, 235, 0.8)) center / 1.5px 62% no-repeat,
    linear-gradient(rgba(255, 250, 235, 0.8), rgba(255, 250, 235, 0.8)) center / 62% 1.5px no-repeat;
}
.rep.academy .rep-mark::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle, #fff7e2 1.1px, transparent 1.9px) 50% 16% / 4px 4px no-repeat,
    radial-gradient(circle, #fff7e2 1.1px, transparent 1.9px) 50% 84% / 4px 4px no-repeat,
    radial-gradient(circle, #fff7e2 1.1px, transparent 1.9px) 16% 50% / 4px 4px no-repeat,
    radial-gradient(circle, #fff7e2 1.1px, transparent 1.9px) 84% 50% / 4px 4px no-repeat;
}
@keyframes repFadeUp {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.rep.academy .rep-zh { color: #4a3113; }
.rep.academy .rep-en { color: rgba(122, 88, 28, 0.8); }
/* 槽 = 纸上是压出来的一道沟（不是现代进度条的坑） */
.rep.academy .rep-track {
  height: 8px;
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(64, 48, 31, 0.16), rgba(255, 255, 255, 0.55));
  box-shadow: inset 0 1px 2px rgba(64, 48, 31, 0.3), inset 0 -1px 0 rgba(255, 255, 255, 0.7);
}
/* 填充 = 一道金墨写在沟里，墨里那条极细的高光慢慢往右走 */
.rep.academy .rep-fill {
  border-radius: 999px;
  background: linear-gradient(90deg, #a9762f, #d8ab55 45%, #f0d79a 80%, #e6bd76);
  box-shadow: 0 0 7px rgba(224, 189, 120, 0.5);
}
.rep.academy .rep-fill::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(100deg, transparent 30%, rgba(255, 252, 236, 0.9) 46%, transparent 62%);
  background-size: 240% 100%;
  animation: repSheen 9s linear infinite;
}
@keyframes repSheen {
  from { background-position: 140% 0; }
  to   { background-position: -100% 0; }
}
/* 数 = 金墨写的荣誉数 */
.rep.academy .rep-v {
  color: #8a5f22;
  font-size: var(--fs-m);
  text-shadow: 0 0 7px rgba(224, 189, 120, 0.55);
}
.rep.academy .rep-mote {
  --mo: 0.32;
  background: radial-gradient(circle, #fff3d4, #d9ac55 65%, transparent 70%);
}
/* Hover：金边亮一点，只抬 1px */
.rep.academy:hover {
  transform: translateY(-1px);
  border-color: rgba(200, 156, 72, 0.9);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.65),
    inset 0 0 26px rgba(176, 138, 74, 0.26),
    0 4px 10px rgba(64, 48, 31, 0.2),
    0 0 12px rgba(224, 189, 120, 0.35);
}

/* ══ 巫师界：魔法部那份归档 ══ */

.rep.wizarding {
  border: 1px solid rgba(206, 170, 96, 0.55);   /* 外层暗金 */
  border-radius: 4px;
  background:
    radial-gradient(120% 150% at 92% -12%, rgba(96, 140, 190, 0.3), transparent 58%),
    linear-gradient(165deg, #1d3047, #16263b 55%, #101b2c);
  /* 内层那道蓝线 + 中间一点蓝色内发光 */
  box-shadow:
    inset 0 0 0 1px rgba(120, 164, 214, 0.32),
    inset 0 0 26px rgba(46, 92, 148, 0.42),
    0 2px 6px rgba(6, 14, 26, 0.55);
  color: #dce7f4;
}
/* 若隐若现的符文环：只露一角，慢慢转 */
.rep.wizarding::before {
  content: '';
  position: absolute;
  right: -30px;
  top: -36px;
  width: 116px;
  height: 116px;
  border-radius: 50%;
  border: 1px dashed rgba(150, 196, 238, 0.5);
  box-shadow: inset 0 0 0 7px rgba(120, 170, 220, 0.07), 0 0 0 13px rgba(120, 170, 220, 0.05);
  opacity: 0.11;
  pointer-events: none;
  animation: repRune 22s linear infinite;
}
@keyframes repRune {
  to { transform: rotate(360deg); }
}
/* 一条蓝光沿着框边走一圈：外面这一圈只留 1px（遮罩把中间挖掉），
   里面那团锥形光慢慢转——转到哪边，哪边的边就亮一下。
   ⚠️ 只有巫师界用得上这一圈（另两框的 .rep-ring 是空的，见上面那条 display:none） */
.rep.wizarding .rep-ring {
  display: block;
  padding: 1px;
  overflow: hidden;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}
.rep.wizarding .rep-ring::before {
  content: '';
  position: absolute;
  inset: -60%;
  background: conic-gradient(from 0deg, transparent 0 76%, rgba(150, 205, 245, 0.9) 86%, transparent 94%);
  animation: repRune 7s linear infinite;
}
/* 徽记 = 一枚圆形魔法印（外圈暗金、内圈虚线符文、中心一点星） */
.rep.wizarding .rep-mark {
  border-radius: 50%;
  border: 1px solid rgba(206, 170, 96, 0.62);
  background: radial-gradient(circle at 46% 40%, #2b4c74, #16283e 62%, #0c1626);
  box-shadow: 0 0 0 2px rgba(120, 164, 214, 0.13), inset 0 0 9px rgba(120, 164, 214, 0.4);
  animation: repFadeUp 620ms ease var(--d, 0ms) both;
}
.rep.wizarding .rep-mark::before {
  content: '';
  position: absolute;
  inset: 4px;
  border-radius: 50%;
  border: 1px dashed rgba(160, 204, 244, 0.55);
}
.rep.wizarding .rep-mark::after {
  content: '✧';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  color: #cfe4f7;
  text-shadow: 0 0 7px rgba(140, 190, 240, 0.85);
}
.rep.wizarding .rep-zh { color: #eef4fb; letter-spacing: 0.14em; }
.rep.wizarding .rep-en { color: #8fb3d6; }
/* 槽 = 刻出来的符文沟：底下发黑、两侧一道蓝线、沟里每隔一段一格刻痕 */
.rep.wizarding .rep-track {
  height: 11px;
  border-radius: 2px;
  background: linear-gradient(180deg, #0b1421, #16283e);
  box-shadow: inset 0 0 0 1px rgba(120, 164, 214, 0.32), inset 0 2px 5px rgba(0, 0, 0, 0.65);
}
.rep.wizarding .rep-track::after {
  content: '';
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(90deg, transparent 0 15px, rgba(150, 196, 238, 0.22) 15px 16px);
  pointer-events: none;
}
/* 填充 = 封在沟里的蓝色能量：颜色本身在缓慢流动（规格第十一节点名要 background-position） */
.rep.wizarding .rep-fill {
  border-radius: 1px;
  background: linear-gradient(90deg, #24466a, #5b89b8 40%, #a8d0ef 62%, #24466a);
  background-size: 260% 100%;
  box-shadow: 0 0 9px rgba(96, 150, 205, 0.55), inset 0 1px 0 rgba(226, 240, 252, 0.5);
  animation:
    repIn 620ms cubic-bezier(0.22, 0.8, 0.3, 1) var(--d, 0ms) both,
    repFlow 6.5s linear 620ms infinite;
}
@keyframes repFlow {
  from { background-position: 140% 0; }
  to   { background-position: -120% 0; }
}
/* 数 = 档案编号牌，钉在右上角（不是孤零零挂在右边的那个数字了） */
.rep.wizarding .rep-v {
  position: absolute;
  right: 9px;
  top: 8px;
  min-width: 0;
  padding: 1px 7px;
  font-size: 12px;
  letter-spacing: 0.08em;
  color: #e9dcac;
  background: rgba(9, 18, 30, 0.6);
  border: 1px solid rgba(206, 170, 96, 0.5);
  border-radius: 2px;
  box-shadow: inset 0 0 8px rgba(120, 164, 214, 0.25);
}
.rep.wizarding .rep-v::before { content: '№ '; opacity: 0.7; }
/* 抬头给那枚编号牌让出位子：窄屏上"巫师界声望 / Wizarding World"不许钻到牌底下 */
.rep.wizarding .rep-head { padding-right: 54px; }
.rep.wizarding .rep-line { margin-top: 9px; }
.rep.wizarding .rep-mote {
  --mo: 0.26;
  width: 2px;
  height: 2px;
  background: radial-gradient(circle, #dcefff, #5b89b8 60%, transparent 70%);
}
/* Hover：符文亮起一点，抬 2px */
.rep.wizarding:hover {
  transform: translateY(-2px);
  box-shadow:
    inset 0 0 0 1px rgba(150, 196, 238, 0.5),
    inset 0 0 32px rgba(66, 118, 180, 0.55),
    0 5px 12px rgba(6, 14, 26, 0.6);
}
.rep.wizarding:hover::before { opacity: 0.26; }

/* ══ 地下：石墙里那块不安分的档案 ══ */

.rep.underground {
  border: 1px solid rgba(126, 158, 110, 0.32);
  border-radius: 13px 3px 15px 4px;   /* 凿出来的，不是圆角按钮 */
  background:
    /* 苔：左下角一滩暗绿 */
    radial-gradient(42% 66% at 10% 110%, rgba(74, 120, 72, 0.2), transparent 62%),
    /* 锈：右上角一点黄 */
    radial-gradient(30% 48% at 84% -10%, rgba(126, 100, 52, 0.16), transparent 62%),
    /* 裂纹：两道极细的斜线 */
    linear-gradient(112deg, transparent 61.6%, rgba(0, 0, 0, 0.4) 62%, transparent 62.6%),
    linear-gradient(76deg, transparent 28.6%, rgba(0, 0, 0, 0.32) 29%, transparent 29.5%),
    radial-gradient(70% 90% at 50% 50%, rgba(30, 54, 38, 0.55), transparent 72%),
    linear-gradient(150deg, #182b20, #14231c 52%, #101914);
  box-shadow:
    inset 0 0 0 1px rgba(0, 0, 0, 0.55),
    inset 0 2px 14px rgba(0, 0, 0, 0.65),
    0 3px 9px rgba(8, 16, 10, 0.55);
  color: #c6d6c2;
}
/* 四角那副旧铁箍（八条背景层拼出来的，不用图） */
.rep.underground::before {
  content: '';
  position: absolute;
  inset: 4px;
  pointer-events: none;
  background:
    linear-gradient(#4c5d45, #4c5d45) left top / 13px 2px no-repeat,
    linear-gradient(#4c5d45, #4c5d45) left top / 2px 13px no-repeat,
    linear-gradient(#4c5d45, #4c5d45) right top / 13px 2px no-repeat,
    linear-gradient(#4c5d45, #4c5d45) right top / 2px 13px no-repeat,
    linear-gradient(#4c5d45, #4c5d45) left bottom / 13px 2px no-repeat,
    linear-gradient(#4c5d45, #4c5d45) left bottom / 2px 13px no-repeat,
    linear-gradient(#4c5d45, #4c5d45) right bottom / 13px 2px no-repeat,
    linear-gradient(#4c5d45, #4c5d45) right bottom / 2px 13px no-repeat;
  opacity: 0.8;
}
/* 呼吸式暗光：只动这一层的 opacity（比直接动 box-shadow 省，玩家要盯很久才发现） */
.rep.underground::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  box-shadow: inset 0 0 15px rgba(60, 100, 60, 0.55), 0 0 14px rgba(60, 100, 60, 0.22);
  opacity: 0;
  animation: repBreathe 6.5s ease-in-out 1.4s infinite;
}
@keyframes repBreathe {
  0%, 100% { opacity: 0; }
  46% { opacity: 0.22; }
  62% { opacity: 0.1; }
}
/* 徽记 = 一颗钉在石板上的铁钉帽，中间透着一点绿光 */
.rep.underground .rep-mark {
  clip-path: polygon(50% 0, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%);
  background: linear-gradient(150deg, #2e4534, #131f17 72%);
  box-shadow: inset 0 0 0 1px rgba(140, 170, 130, 0.26);
  animation: repFadeUp 700ms ease var(--d, 0ms) both;
}
.rep.underground .rep-mark::before {
  content: '';
  position: absolute;
  inset: 34%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(170, 240, 170, 0.85), rgba(60, 120, 70, 0.25) 68%, transparent 74%);
  animation: repBreathe 7s ease-in-out infinite;
}
.rep.underground .rep-mark::after {
  content: '';
  position: absolute;
  left: 50%;
  top: -2px;
  width: 1px;
  height: 30px;
  background: linear-gradient(rgba(0, 0, 0, 0.65), rgba(0, 0, 0, 0.15));
  transform: rotate(19deg);
}
.rep.underground .rep-zh { color: #d6e6d0; }
.rep.underground .rep-en { color: #6f9c74; }
/* 槽 = 石缝：里面是黑的，边上蹭了一点苔绿 */
.rep.underground .rep-track {
  height: 12px;
  border-radius: 7px 2px 8px 3px;
  background: linear-gradient(180deg, #0a120d, #050b07);
  box-shadow: inset 0 0 0 1px rgba(96, 130, 96, 0.22), inset 0 2px 6px rgba(0, 0, 0, 0.8);
}
/* 填充 = 缝里慢慢流的墨绿液体（不是血条：顶部那点毒绿高光是液面） */
.rep.underground .rep-fill {
  border-radius: inherit;
  background:
    repeating-linear-gradient(115deg, rgba(126, 190, 126, 0.1) 0 6px, transparent 6px 15px),
    linear-gradient(180deg, #3f6c46, #1f3a26 62%, #132718);
  background-size: 200% 100%, 100% 100%;
  box-shadow: inset 0 1px 0 rgba(150, 220, 150, 0.32), 0 0 9px rgba(70, 130, 80, 0.32);
  animation:
    repIn 620ms cubic-bezier(0.22, 0.8, 0.3, 1) var(--d, 0ms) both,
    repSludge 11s linear 620ms infinite;
}
@keyframes repSludge {
  from { background-position: 0 0, 0 0; }
  to   { background-position: -200% 0, 0 0; }
}
.rep.underground .rep-fill::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, rgba(150, 230, 150, 0.45) 32%, rgba(196, 255, 196, 0.7) 58%, transparent);
  animation: repVein 7s ease-in-out infinite;
}
@keyframes repVein {
  0%, 100% { opacity: 0.3; transform: translateX(-8%); }
  50% { opacity: 0.75; transform: translateX(8%); }
}
/* 数 = 危险关系等级，压在一块小铁牌上 */
.rep.underground .rep-v {
  min-width: 0;
  padding: 1px 7px;
  font-size: 13px;
  letter-spacing: 0.06em;
  color: #abd6a6;
  background: rgba(6, 14, 9, 0.62);
  border: 1px solid rgba(120, 160, 110, 0.3);
  text-shadow: 0 0 8px rgba(120, 200, 120, 0.45);
}
.rep.underground .rep-mote {
  --mo: 0.3;
  background: radial-gradient(circle, #b8e6b4, #3f6c46 62%, transparent 70%);
}
/* 第三颗是孢子：大一团、散开，飘得比别的慢 */
.rep.underground .rep-mote:nth-of-type(3) {
  width: 15px;
  height: 15px;
  filter: blur(5px);
  background: radial-gradient(circle, rgba(126, 200, 126, 0.5), transparent 70%);
}
/* Hover：绿光压得重一点，抬 1px */
.rep.underground:hover {
  transform: translateY(-1px);
  border-color: rgba(150, 190, 140, 0.5);
  box-shadow:
    inset 0 0 0 1px rgba(0, 0, 0, 0.6),
    inset 0 2px 14px rgba(0, 0, 0, 0.7),
    0 5px 12px rgba(8, 16, 10, 0.6),
    0 0 16px rgba(60, 110, 65, 0.28);
}
/* 那层呼吸光自己就在动，Hover 只把节奏压快一点（再写 opacity 也压不过动画，等于假话） */
.rep.underground:hover::after { animation-duration: 3.4s; }

/* ── 声望刚动过那一下：三框三种演法（400–700ms，规格第三十节） ── */

/* 学院：金光从徽章上炸开一下，墨跟着亮一拍 */
.rep.academy.chg-up .rep-mark { animation: repGoldFlash 640ms ease both; }
.rep.academy.chg-up .rep-fill { animation: repBright 640ms ease both; }
.rep.academy.chg-down .rep-fill { animation: repDim 640ms ease both; }
@keyframes repGoldFlash {
  0% { filter: brightness(1); box-shadow: 0 0 0 rgba(232, 200, 130, 0); }
  35% { filter: brightness(1.65); box-shadow: 0 0 16px rgba(232, 200, 130, 0.85); }
  100% { filter: brightness(1); box-shadow: 0 0 0 rgba(232, 200, 130, 0); }
}
@keyframes repBright {
  0%, 100% { filter: brightness(1); }
  32% { filter: brightness(1.5); }
}
@keyframes repDim {
  0%, 100% { filter: brightness(1); }
  32% { filter: brightness(0.72); }
}
/* 巫师界：符文环往外扩散一圈 */
.rep.wizarding.chg-up::before { animation: repRuneSpread 700ms ease-out both; }
.rep.wizarding.chg-down::before { animation: repRuneFade 700ms ease-out both; }
@keyframes repRuneSpread {
  0% { opacity: 0.11; transform: scale(1) rotate(0deg); }
  30% { opacity: 0.5; }
  100% { opacity: 0.11; transform: scale(1.35) rotate(60deg); }
}
@keyframes repRuneFade {
  0% { opacity: 0.11; }
  30% { opacity: 0.34; filter: grayscale(1); }
  100% { opacity: 0.11; filter: none; }
}
.rep.wizarding.chg-up .rep-fill { animation: repIn 620ms both, repFlow 6.5s linear infinite, repBright 700ms ease both; }
/* 地下：那层暗绿整个波动一下 */
.rep.underground.chg-up::after { animation: repToxicWave 700ms ease-out both; }
.rep.underground.chg-down::after { animation: repToxicChoke 700ms ease-out both; }
@keyframes repToxicWave {
  0% { opacity: 0; }
  28% { opacity: 0.55; }
  100% { opacity: 0; }
}
@keyframes repToxicChoke {
  0% { opacity: 0; }
  28% { opacity: 0.4; filter: hue-rotate(-40deg); }
  100% { opacity: 0; filter: none; }
}
.rep.underground.chg-up .rep-fill {
  animation: repIn 620ms both, repSludge 11s linear infinite, repBright 700ms ease both;
}

/* ── 窄屏：粒子少一半、光收一点（规格第三十四节） ── */
@media (max-width: 560px) {
  .rep { padding: 9px 10px 10px; }
  .rep-mote:nth-of-type(n + 4) { display: none; }
  .rep.wizarding::before { width: 88px; height: 88px; right: -26px; top: -28px; }
  .rep.wizarding .rep-ring { padding: 1px; }
  .rep.academy::after { opacity: 0.1; }
  .rep.underground .rep-mote { --mo: 0.22; }
  .rep.academy .rep-mote { --mo: 0.24; }
  .rep.wizarding .rep-mote { --mo: 0.2; }
  .rep.underground::after { animation-duration: 8s; }
}

/* ── 三、温室：夹在书里的一排老玻璃窗格 ── */

/* 原先那张淡绿的卡留着（土里的事用土的颜色），只把玻璃那点意思加上 */
.card.beds-card {
  background:
    radial-gradient(70% 40% at 18% 2%, rgba(255, 255, 255, 0.66), transparent 60%),
    linear-gradient(180deg, #eef5e4, #cfe0bd);
}
/* 一格 = 一扇小玻璃窗：玻璃底 + 十字窗棂（两道光都是背景层画的） */
.bed-box {
  position: relative;
  overflow: hidden;
  padding: 7px 3px 6px;
  border: 1px solid rgba(94, 124, 74, 0.5);
  border-radius: 3px;
  background:
    linear-gradient(90deg, rgba(255, 255, 255, 0.5) 0 1px, transparent 1px 100%) 50% 0 / 100% 100%,
    linear-gradient(180deg, rgba(255, 255, 255, 0.45) 0 1px, transparent 1px 100%) 0 50% / 100% 100%,
    radial-gradient(80% 60% at 20% 8%, rgba(255, 255, 255, 0.75), transparent 62%),
    linear-gradient(180deg, rgba(246, 252, 240, 0.92), rgba(206, 226, 190, 0.92));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7), 0 1px 3px rgba(30, 48, 24, 0.12);
}
/* 玻璃上那道斜高光：所有窗格都有，很淡 */
.bed-box::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(118deg, rgba(255, 255, 255, 0.5) 0 18%, transparent 42%);
  pointer-events: none;
}
/* 塌着的那几格：暗玻璃 + 一把小锁（规格第二十节：不是灰按钮，是"还没开放的窗"） */
.bed-box.shut {
  border-color: rgba(64, 48, 31, 0.3);
  background:
    radial-gradient(80% 60% at 20% 8%, rgba(255, 255, 255, 0.16), transparent 62%),
    linear-gradient(180deg, rgba(74, 70, 62, 0.5), rgba(46, 42, 36, 0.62));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16);
}
.bed-box.shut .bb-no,
.bed-box.shut .bb-val { color: rgba(238, 232, 218, 0.62); }
.bed-box.shut::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 46%;
  width: 10px;
  height: 8px;
  transform: translate(-50%, -50%);
  border-radius: 2px;
  background: linear-gradient(180deg, #8a7a5e, #4a3d2c);
  box-shadow: inset 0 1px 0 rgba(255, 240, 200, 0.3);
}
.bed-box.shut::before {
  content: '';
  position: absolute;
  left: 50%;
  top: calc(46% - 9px);
  width: 7px;
  height: 7px;
  transform: translateX(-50%);
  border: 2px solid #8a7a5e;
  border-bottom: 0;
  border-radius: 5px 5px 0 0;
  background: none;
}
/* 熟了的那一格：绿一点 + 一道金高光扫过 + 角上一颗小星（规格第十九节：一眼看得出） */
.bed-box.ripe {
  border-color: rgba(176, 138, 74, 0.85);
  background:
    radial-gradient(80% 60% at 20% 8%, rgba(255, 255, 255, 0.8), transparent 62%),
    linear-gradient(180deg, rgba(228, 245, 206, 0.96), rgba(178, 214, 150, 0.96));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.75), 0 0 0 1px rgba(224, 189, 120, 0.35);
}
.bed-box.ripe::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: -40%;
  width: 40%;
  background: linear-gradient(100deg, transparent, rgba(255, 246, 210, 0.75), transparent);
  animation: paneGlint 3.6s ease-in-out infinite;
  pointer-events: none;
}
@keyframes paneGlint {
  0% { left: -40%; }
  55% { left: 120%; }
  100% { left: 120%; }
}
.bed-box.ripe::after {
  content: '✦';
  position: absolute;
  right: 4px;
  top: 2px;
  font-size: 10px;
  color: rgba(176, 138, 74, 0.95);
}
.bed-box.ripe .bb-tag { color: #8a3a2a; }

/* ── 四、属性：两排三格的魔法能力记录 ── */

/*
 * 六格改 2×3（规格第二十二节：不要再是一排六个一样的方块）。
 * 温室那几格仍然是六格一排（.bed-boxes 那条不动）——所以这条只写 .attr-boxes。
 */
.attr-boxes { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.attr-box {
  position: relative;
  gap: 0;
  padding: 7px 6px 6px;
  border-color: rgba(64, 48, 31, 0.24);
  border-radius: 3px;
  background:
    radial-gradient(80% 60% at 20% 4%, rgba(255, 255, 255, 0.6), transparent 62%),
    linear-gradient(180deg, rgba(255, 253, 246, 0.72), rgba(238, 228, 206, 0.72));
  box-shadow: 0 1px 2px rgba(40, 28, 16, 0.12);
  transition: transform 220ms ease, box-shadow 220ms ease, border-color 220ms ease;
}
.attr-box:hover {
  transform: translateY(-2px);
  border-color: rgba(176, 138, 74, 0.5);
  box-shadow: 0 4px 10px rgba(40, 28, 16, 0.2);
}
.ab-glyph {
  font-size: 13px;
  line-height: 1;
  color: rgba(120, 92, 56, 0.7);
  transition: color 220ms ease;
}
.attr-box:hover .ab-glyph { color: #b9873f; }
.attr-box .ab-name { min-height: 1.4em; font-size: 11px; }
.attr-box .ab-val { font-size: var(--fs-m); }

/*
 * 运气那一格（最后一格）稍微不一样（规格第二十三节）：
 * 金细边 + 淡金渐变 + 金色数字 + 一道极慢扫过的光。**必须很淡**。
 */
.attr-box:last-child {
  border-color: rgba(176, 138, 74, 0.6);
  background:
    linear-gradient(115deg, transparent 0 38%, rgba(255, 240, 200, 0.5) 50%, transparent 62%) 0 0 / 300% 100%,
    linear-gradient(180deg, rgba(255, 250, 232, 0.86), rgba(246, 230, 192, 0.86));
  animation: luckSheen 5.5s linear infinite;
}
.attr-box:last-child .ab-glyph,
.attr-box:last-child .ab-val { color: #a8761f; }
.attr-box:last-child .ab-name { color: rgba(120, 92, 56, 0.9); }
@keyframes luckSheen {
  from { background-position: -120% 0, 0 0; }
  to   { background-position: 220% 0, 0 0; }
}

/* ── 五、日记：一叠折着的便签 ── */

/* 装便签那一块不再是一张纸（容器撤了纸，纸在每一张便签自己身上） */
.jr-stack { display: block; padding-bottom: 2px; }

.jr-note {
  display: block;
  width: 100%;
  margin: 0 0 8px;
  padding: 8px 12px 9px;
  font-family: var(--font-body);
  text-align: left;
  color: var(--ink);
  cursor: pointer;
  border: 1px solid rgba(64, 48, 31, 0.28);
  border-radius: 2px;
  background:
    repeating-linear-gradient(180deg, transparent 0 21px, rgba(64, 48, 31, 0.07) 21px 22px),
    linear-gradient(180deg, #fbf5e6, #efe2c6);
  box-shadow: 0 2px 0 rgba(50, 37, 24, 0.22), 0 6px 14px rgba(0, 0, 0, 0.16);
  transition: transform 260ms ease, box-shadow 260ms ease, background 260ms ease;
}
/* 三张一个循环：各歪一点点、纸色也各差半档（幅度收在 0.6° 以内，别成一片乱墙） */
.jr-note[data-i="0"] { transform: rotate(-0.5deg); }
.jr-note[data-i="1"] { transform: rotate(0.6deg); background:
  repeating-linear-gradient(180deg, transparent 0 21px, rgba(64, 48, 31, 0.07) 21px 22px),
  linear-gradient(180deg, #faf2e0, #ebdcbc); }
.jr-note[data-i="2"] { transform: rotate(-0.2deg); background:
  repeating-linear-gradient(180deg, transparent 0 21px, rgba(64, 48, 31, 0.07) 21px 22px),
  linear-gradient(180deg, #fdf7ea, #f2e7cf); }
.jr-note:hover { transform: rotate(0deg) translateY(-1px); box-shadow: 0 3px 0 rgba(50, 37, 24, 0.2), 0 9px 18px rgba(0, 0, 0, 0.2); }
/* 日期：左上角一小行棕墨（不是现代的 badge） */
.jn-day {
  display: block;
  font-family: var(--font-hand);
  font-size: 11px;
  letter-spacing: 0.1em;
  color: rgba(120, 72, 34, 0.85);
  margin-bottom: 2px;
}
/* 折着时露出的那一行（多的切掉——折着的那一面只该露一行） */
.jn-peek {
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: var(--fs-s);
  line-height: 1.6;
  color: rgba(51, 39, 29, 0.86);
  transition: opacity 200ms ease;
}
/* 摊开之后才有的全文：高度从 0 长起来（纸张展开那一下） */
.jn-full {
  display: block;
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  font-size: var(--fs-s);
  line-height: 1.85;
  color: var(--ink);
  transition: max-height 420ms cubic-bezier(0.22, 0.8, 0.3, 1), opacity 300ms ease 60ms;
}
.jr-note.open {
  transform: rotate(0deg);
  box-shadow: 0 3px 0 rgba(50, 37, 24, 0.24), 0 12px 22px rgba(0, 0, 0, 0.22);
}
.jr-note.open .jn-peek { opacity: 0; max-height: 0; overflow: hidden; }
.jr-note.open .jn-full { max-height: 60vh; opacity: 1; }

/* ── 六、底部那三本收藏册 ── */

/*
 * 三颗都是 .btn.paper-entry（同一副骨架、同一张图纸素材），
 * 这里只给**各自的皮**：相片本暖、图鉴暗金、信件夹米白压暗红。
 * 不新增素材——色与纹都是 CSS。
 */
.panel-acts > .paper-entry { border-radius: 2px; }
.panel-acts > .paper-entry.ph-album {
  background: linear-gradient(180deg, #fdf6e6, #ecdcbd);
  border-color: rgba(150, 108, 46, 0.4);
}
.panel-acts > .paper-entry.ph-codex {
  background:
    repeating-linear-gradient(90deg, rgba(0, 0, 0, 0.04) 0 3px, transparent 3px 6px),
    linear-gradient(180deg, #e8e2cf, #cfc7ad);
  border-color: rgba(94, 108, 74, 0.45);
}
.panel-acts > .paper-entry.ph-letter {
  background: linear-gradient(180deg, #fbf4e4, #efe0c4);
  border-color: rgba(140, 47, 47, 0.38);
}
/* hover：整本往上抬 4px，边上多一道很淡的金线（规格第三十三节） */
.panel-acts > .paper-entry:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.22), 0 0 0 1px rgba(176, 138, 74, 0.5);
}
.panel-acts > .paper-entry:active { transform: translateY(-1px) scale(0.98); }

/* ── 七、这一本自己的抬头、书扣与滚动条（只认这一张纸） ── */

/* 抬头那道线：一金一红两道极细的线（像书页上的套印） */
.panel:has(.ledger-top) .ledger-top {
  border-bottom: 0;
  padding-bottom: var(--sp-3);
  box-shadow: 0 1px 0 rgba(176, 138, 74, 0.5), 0 2px 0 rgba(140, 47, 47, 0.22);
}
.panel:has(.ledger-top) .panel-title .pt-zh { letter-spacing: 0.2em; }
/* 那枚关页的叉：这一张纸上换成旧金属书扣的调子（别的纸不动） */
.panel:has(.ledger-top) .x-btn {
  filter: sepia(0.35) brightness(0.92);
  border-radius: 2px;
}
/* 滚动条：窄、暗棕、半透明，别用浏览器那条灰的 */
.panel:has(.ledger-top) .panel-body { scrollbar-width: thin; scrollbar-color: rgba(120, 88, 50, 0.55) transparent; }
.panel:has(.ledger-top) .panel-body::-webkit-scrollbar { width: 8px; }
.panel:has(.ledger-top) .panel-body::-webkit-scrollbar-track { background: transparent; }
.panel:has(.ledger-top) .panel-body::-webkit-scrollbar-thumb {
  background: rgba(120, 88, 50, 0.5);
  border-radius: 4px;
  border: 2px solid transparent;
  background-clip: content-box;
}

/* ── 八、窄屏 ── */

@media (max-width: 480px) {
  /* 钱袋与精力仍然并排（规格第三十六节），只把两块各收窄一点 */
  .led-top-pair { gap: var(--sp-2); }
  .pu-body { width: 68px; height: 50px; }
  .pu-tie { width: 78px; top: 22px; }
  .pu-coins { bottom: 62px; }
  .vg-spark { width: 13px; height: 13px; }
  .vg-sparks { gap: 4px; }
  /* 属性保持两排三格 */
  .attr-boxes { gap: 6px; }
  .attr-box .ab-name { font-size: 10px; }
  .attr-box .ab-val { font-size: var(--fs-s); }
  /* 温室六格在手机上太挤：改成三格一排两行，仍是"窗格" */
  .bed-boxes { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/*
 * 晕动症：点亮、写进去、扫光、窗格高光全停——静态的样子（形状、颜色、错位）都留着。
 */
@media (prefers-reduced-motion: reduce) {
  .vg-spark,
  .rep-fill,
  .rep-fill::after,
  .rep.chg-up .rep-fill,
  .rep.chg-down .rep-fill,
  .rep-mark,
  .rep-mote,
  /* 三框各自的循环光：金扫光 / 符文环与那圈边光 / 地下呼吸光 */
  .rep.academy::after,
  .rep.wizarding::before,
  .rep.wizarding .rep-ring::before,
  .rep.underground::after,
  .rep.underground .rep-mark::before,
  .bed-box.ripe::before,
  .attr-box:last-child { animation: none; }
  .rep-fill { transform: scaleX(var(--v, 0)); }
  .rep-mote { display: none; }
  .jr-note, .jn-full, .jn-peek { transition: none; }
}

/* ============================================================
   39 · 遭遇战第二次：地图就是战场，UI 全浮在上面（ui/battle.js）
   ------------------------------------------------------------
   创作者 2026-10-03 的第二份规格，一句话版本：**别再是"一层一层堆在中间的 UI"**。
   从上到下只有这么几件事——
     氛围（聚光 / 暗角 / 两团极慢的雾 / 几点光尘）
       → 敌方：信息在上（像 Boss 条那样浮着）、立绘在下，而且**比上一版大一档**
       → 中间空着（那一段是两只动物之间的距离，只有一行浮着的战况字）
       → 我方：立绘沉到底、脚被卡牌压掉一小截
       → 卡牌（整屏最前面的一层）
       → 我方信息（姓名 / 血 / 五维，在卡牌**下面**）
   还有第二重点：**拖牌时那一片目标区要一眼看得见能放**（第十七~二十四节），
   以及打中 / 举盾 / 奶回来那三下要有反应（第二十五~二十九节）。
   ⚠️ 只改视觉与判定用的那一块节点：卡牌、费用、2 点魔力、回合、伤害、暴击闪避、
      敌方 AI、捕捉、胜负，全在 engine/battle.js 与 data/battle.js，一个字没动。
   ⚠️ 一张图都没新增：雾、光尘、符文环、护盾、冲击波全是渐变 / 阴影 / 伪元素。
   ⚠️ 尺寸一律 % / vh / clamp（规格第四十节：不许写死只适合某张截图的像素）。
   本节写在第 30 / 37 节之后，同名规则以这里为准。
   ============================================================ */

/* ── 一、氛围层 ─────────────────────────────────────────
 *   这一整块（.bt-atmos 与它下面每一层：雾 / 树影 / 环境光 / 光尘 / 事件余光）
 *   在 2026-10-03 深夜按那份「只改背景」的规格整块搬去文件末尾的**第 45 节**重写了。
 *   那 66 行当场删掉、不留在这儿——留着就是第二份会互相盖的旧值。
 */

/* ── 二、场地：敌上我下，中间留白 ── */

.bt-head { position: relative; z-index: 2; }
/*
 * 场地那一格**按整屏剩下多少来长**：顶上留一小行金字、底下要放牌堆与我方信息，
 * 剩下的全给这一格——多出来的那一段就是两只动物之间那片空地（规格第三十六节）。
 * 写成 calc(100dvh - 372px) 而不是一个像素数，换屏幕自己会跟着变（第四十节）。
 */
.bt-arena { min-height: clamp(360px, calc(100dvh - 372px), 560px); padding: 0 0 clamp(6px, 2vh, 18px); }
/* 敌方那一块：信息牌在上、立绘在下（规格第二、三十六节） */
.bt-side.enemy { gap: 2px; }
/* 我方那一块只剩立绘：整块往下沉，脚被卡牌栏压掉一截（规格第八、九节） */
.bt-side.mine { justify-content: flex-end; }

/*
 * 两个舞台的大小差：这一版**敌方比上一版大一档**（规格第五节：+20~35%），
 * 我方比敌方略小、但位置最低（规格第七节：靠"高一点 / 低一点"拉纵深，
 * 不是把敌人缩小）。每只动物自己的胖瘦仍由 data/battle.js 那三个数调。
 */
.bp-stage { width: min(52%, 250px); height: min(22vh, 196px); }
.enemy-portrait-stage { width: min(62%, 300px); height: min(26vh, 232px); opacity: 0.98; }
.mine .bp-stage { width: min(54%, 262px); height: min(21vh, 188px); }

/* 脚底那片影：敌方淡一点（远）、我方重一点（近）（规格第三十四、三十五节） */
.enemy-portrait-stage::after { width: 52%; height: 13px; opacity: 0.72; }
.mine .bp-stage::after { width: 66%; height: 20px; opacity: 1; }

/* ── 三、战况那一行：一行浮着的字，不是横在中间的一块框（第十二节） ── */

.bt-log {
  position: absolute;
  left: 50%;
  top: 58%;
  z-index: 16;
  width: min(84%, 30rem);
  transform: translateX(-50%);
  padding: 0;
  min-height: 0;
  border: 0;
  background: none;
  font-size: var(--fs-xs);
  line-height: 1.65;
  letter-spacing: 0.08em;
  text-align: center;
  color: rgba(247, 233, 205, 0.86);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9), 0 0 14px rgba(0, 0, 0, 0.7);
  pointer-events: none;
}
/* 每换一句淡浮出来一次（只动 opacity / translate，不占布局） */
.bt-log.say { animation: btSay 2.6s ease both; }
@keyframes btSay {
  0% { opacity: 0; transform: translate(-50%, 5px); }
  12% { opacity: 1; transform: translate(-50%, 0); }
  72% { opacity: 1; }
  100% { opacity: 0.62; }
}

/* ── 四、敌方信息牌：浮在场景里的 Boss 条，没有底板（第三、四节） ── */

.bt-plate { gap: 0; }
.bt-side.enemy .bt-plate { margin-bottom: 2px; }
.bt-side.enemy .bt-nm { font-size: var(--fs-m); letter-spacing: 0.2em; color: #f0dcb4; }
/* 属性那行必须轻：半透明暗金，不抢立绘 */
.bt-side.enemy .bt-sub { color: rgba(196, 154, 85, 0.62); opacity: 1; }
/* 血条收在 4px 高、只留一圈古铜金细边（不要现代大血条） */
.bt-hp {
  width: min(34vw, 150px);
  height: 4px;
  border-radius: 1px;
  background: rgba(10, 7, 5, 0.6);
}
.bt-side.enemy .bt-hp { width: min(30vw, 136px); }
.bt-hp i { box-shadow: none; }
.bt-hpnum { font-size: 10px; opacity: 0.86; }

/* ── 五、我方信息：钉在卡牌栏**下面**（第十、十一节） ── */

.bt-under {
  position: relative;
  z-index: 30;
  display: flex;
  justify-content: center;
  padding: 2px 0 calc(4px + env(safe-area-inset-bottom));
  background: linear-gradient(180deg, rgba(9, 7, 5, 0.5) 0%, rgba(9, 7, 5, 0.66) 100%);
}
.bt-under .bt-plate { gap: 0; }
.bt-under .bt-nm { font-size: var(--fs-s); letter-spacing: 0.18em; color: #e7d3ac; }
.bt-under .bt-hp { width: min(40vw, 176px); }
.bt-under .bt-sub { opacity: 0.5; }

/* ── 六、卡牌栏：整屏最前面的一层，往上压住我方立绘那一小截脚 ── */

.bt-bar {
  z-index: 40;
  /* 抬上去这一段就是"被卡牌挡住的那 5~12%"（第八节） */
  margin-top: calc(-1 * clamp(14px, 4.2vh, 38px));
  padding-top: clamp(6px, 1.6vh, 14px);
  /* 不要黑色大矩形：只留一层往脚下加深的一点暗渐变，让卡面小字读得清 */
  background: linear-gradient(180deg, rgba(9, 7, 5, 0) 0%, rgba(9, 7, 5, 0.34) 46%, rgba(9, 7, 5, 0.6) 100%);
}
/* 魔力那一小排挤在牌堆上方，不占大块（第三十八节） */
.bt-mana { gap: 5px; }
.bt-mana-lb { font-size: 10px; letter-spacing: 0.2em; opacity: 0.8; }
.bt-gem { width: 10px; height: 10px; }
.bt-gem.on { animation: btGem 3.2s ease-in-out infinite; }
@keyframes btGem {
  0%, 100% { box-shadow: 0 0 7px rgba(224, 189, 120, 0.5); }
  50% { box-shadow: 0 0 12px rgba(224, 189, 120, 0.78); }
}
/* 一手牌摊开：左右两张各歪一点、中间那张略突出（第十五节，幅度收着） */
.bt-hand { min-height: clamp(112px, 17vh, 150px); padding: 8px 0 2px; }
.bt-hand .bt-card:nth-child(1) { margin-right: -5px; rotate: -3deg; }
.bt-hand .bt-card:nth-child(2) { margin-bottom: 7px; }
.bt-hand .bt-card:nth-child(3) { margin-left: -5px; rotate: 3deg; }

/* 拖起来的那张：影子加重、抬到所有东西前面（第二十三节） */
.bt-card.drag {
  box-shadow: 0 20px 38px rgba(0, 0, 0, 0.66), 0 0 0 1px rgba(224, 189, 120, 0.55);
  transition: transform 90ms linear;
}
/* 原位留一圈淡淡的卡轮廓（同一节）——不是半透明的一张"影子牌" */
.bt-card.lifted {
  opacity: 1;
  background: none;
  border: 1px dashed rgba(224, 189, 120, 0.42);
  box-shadow: none;
}
.bt-card.lifted::before,
.bt-card.lifted > * { opacity: 0; }

/* ── 七、拖牌时那一片目标区（第十七~二十四节） ── */

/*
 * 判定区 = 立绘那一块舞台（.bp-stage）。它平时一个像素都看不见，
 * 牌拖进来才亮：一圈不完整的符文环 + 一层边缘光 + 一行很短的字。
 * 攻击给暗金偏红、防御给银白偏金、恢复给柔和金绿——三种颜色三种说法。
 * ⚠️ 舞台的 overflow:hidden 不能开：那张图本来就是按"裁掉边缘"摆的，
 *    一开就当场露馅（动物整只溢出来）。所以环与字都画在舞台**里面**。
 */
.bp-stage.drop-hot .bp-img { scale: 1.02; transition: scale 200ms ease; }
.bp-stage.drop-hot .bp-glow { opacity: 1; }
/* 不完整的符文环（虚线圆 + 两段缺口，慢慢转） */
.bp-glow::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 48%;
  width: 74%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 1.5px dashed currentColor;
  border-top-color: transparent;
  border-right-color: transparent;
  box-shadow: 0 0 20px rgba(224, 189, 120, 0.22), inset 0 0 26px rgba(224, 189, 120, 0.16);
  opacity: 0;
  transition: opacity 200ms ease;
  animation: btRing 6s linear infinite;
}
/* 那行短字：像刻在空气上的符文，不是提示框（贴在舞台**内侧**下沿，外面会被裁掉） */
.bp-glow::after {
  position: absolute;
  left: 50%;
  bottom: 1%;
  transform: translateX(-50%);
  font-family: var(--font-hand);
  font-size: 11px;
  letter-spacing: 0.3em;
  white-space: nowrap;
  opacity: 0;
  transition: opacity 200ms ease;
}
@keyframes btRing {
  to { transform: translate(-50%, -50%) rotate(360deg); }
}
/* 有效 / 无效差得很明显：不在这一片上时上面这些全是灭的（第二十节） */
.bp-stage.drop-hot .bp-glow::before,
.bp-stage.drop-hot .bp-glow::after { opacity: 1; }

/* 攻击 → 敌方：暗金 + 一点血红 */
.bp-stage.drop-hot.hot-atk { color: rgba(224, 189, 120, 0.85); }
.bp-stage.drop-hot.hot-atk .bp-glow {
  background:
    radial-gradient(58% 58% at 50% 50%, rgba(224, 189, 120, 0.16), transparent 70%),
    radial-gradient(70% 70% at 50% 50%, transparent 52%, rgba(143, 74, 61, 0.3) 100%);
  animation: btHot 1.6s ease-in-out infinite;
}
.bp-stage.drop-hot.hot-atk .bp-glow::after { content: '施放于此'; color: #f2d9a4; text-shadow: 0 0 10px rgba(224, 189, 120, 0.75); }
/* 防御 → 我方：银白偏金的护盾 */
.bp-stage.drop-hot.hot-def { color: rgba(206, 224, 236, 0.85); }
.bp-stage.drop-hot.hot-def .bp-glow {
  background:
    radial-gradient(56% 56% at 50% 50%, rgba(200, 220, 235, 0.14), transparent 70%),
    radial-gradient(64% 64% at 50% 50%, transparent 56%, rgba(224, 189, 120, 0.26) 100%);
  animation: btHot 2.2s ease-in-out infinite;
}
.bp-stage.drop-hot.hot-def .bp-glow::after { content: '护住此处'; color: #dfeaf3; text-shadow: 0 0 10px rgba(180, 210, 235, 0.7); }
/* 恢复 → 我方：柔和金绿 */
.bp-stage.drop-hot.hot-heal { color: rgba(178, 224, 176, 0.85); }
.bp-stage.drop-hot.hot-heal .bp-glow {
  background:
    radial-gradient(56% 56% at 50% 52%, rgba(158, 224, 168, 0.16), transparent 70%),
    radial-gradient(64% 64% at 50% 50%, transparent 56%, rgba(224, 200, 130, 0.22) 100%);
  animation: btHot 2.6s ease-in-out infinite;
}
.bp-stage.drop-hot.hot-heal .bp-glow::after { content: '恢复于此'; color: #cdeccd; text-shadow: 0 0 10px rgba(160, 220, 170, 0.7); }
@keyframes btHot {
  0%, 100% { opacity: 0.72; }
  50% { opacity: 1; }
}

/* ── 八、落牌之后的那三下反应（第二十五~二十九节） ── */

/* 受击：立绘抖 + 一圈红金冲击波从身上散开（伪元素画的，不引图） */
.bp-stage.hurt .bp-glow { animation: btShock 420ms ease-out both; }
@keyframes btShock {
  0% {
    opacity: 0.9;
    transform: scale(0.82);
    background: radial-gradient(50% 50% at 50% 50%, rgba(255, 232, 170, 0.5), rgba(160, 60, 44, 0.34) 46%, transparent 66%);
  }
  100% {
    opacity: 0;
    transform: scale(1.16);
    background: radial-gradient(50% 50% at 50% 50%, rgba(255, 232, 170, 0.14), rgba(160, 60, 44, 0.06) 46%, transparent 66%);
  }
}
/* 暴击：只多一行小小的金字（第二十八节：不要大号 CRITICAL） */
.bt-num.crit::after { content: ' 暴击'; font-size: 11px; letter-spacing: 0.2em; color: #ffe6a8; }
/* 举盾：一圈合上的护盾环，400~700ms 慢慢灭（第二十六节） */
.bp-stage.shielded .bp-glow {
  opacity: 1;
  background: radial-gradient(60% 60% at 50% 50%, rgba(206, 226, 240, 0.16), transparent 68%);
}
.bp-stage.shielded .bp-glow::before {
  opacity: 1;
  color: rgba(226, 214, 180, 0.7);
  animation: btRing 9s linear infinite, btFadeOut 640ms ease-out both;
}
/* 奶回来：身上浮起几点金绿的光（第二十二节：不要现代 RPG 的绿血条感） */
.bp-stage.healing .bp-glow {
  opacity: 1;
  background: radial-gradient(58% 58% at 50% 58%, rgba(158, 224, 168, 0.22), transparent 68%);
}
.bp-stage.healing .bp-glow::before {
  opacity: 1;
  color: rgba(190, 236, 186, 0.75);
  animation: btRing 5s linear infinite reverse, btFadeOut 700ms ease-out both;
}
@keyframes btFadeOut {
  from { opacity: 1; }
  to { opacity: 0; }
}

/* ── 九、窄屏（第三十九节：空间不够先压氛围，再压敌方尺寸） ── */

@media (max-width: 560px) {
  .bt-arena { min-height: clamp(340px, calc(100dvh - 368px), 524px); }
  .bp-stage { width: min(58%, 220px); height: min(19vh, 168px); }
  .enemy-portrait-stage { width: min(70%, 258px); height: min(23vh, 202px); }
  .mine .bp-stage { width: min(60%, 230px); height: min(18vh, 160px); }
  .bt-log { width: 88%; top: 56%; }
  .bt-hand .bt-card { margin-left: 0; margin-right: 0; rotate: 0deg; }
  .bt-hand .bt-card:nth-child(2) { margin-bottom: 5px; }
  .bt-card { width: 80px; height: 114px; }
}
/* 高一点的屏（PC 竖着看）：把两只动物拉开，中间那片空地给足 */
@media (min-height: 780px) {
  .bt-log { top: 54%; }
}
/*
 * 矮屏（笔电横屏、手机横过来、小窗）：规格第三十九节说得很明白——
 * 空间不够先压氛围与立绘尺寸，别把卡牌与我方信息挤到屏幕外面去。
 */
@media (max-height: 780px) {
  .bt-arena { min-height: clamp(300px, calc(100dvh - 356px), 420px); padding-bottom: 0; }
  .enemy-portrait-stage { width: min(56%, 232px); height: min(21dvh, 176px); }
  .mine .bp-stage { width: min(50%, 214px); height: min(17dvh, 144px); }
  .bt-side.enemy .bt-plate { gap: 0; }
  .bt-hand { min-height: clamp(96px, 14dvh, 124px); padding-top: 4px; }
  .bt-card { width: 76px; height: 106px; }
  .bt-under { padding-bottom: 4px; }
}

/* 晕动症：宝石呼吸、符文环、冲击波全停；
   近大远小、脚底影、聚光与暗角是静态的，留着（那才是"站在地图里"）。
   ⚠️ 背景那一片（雾 / 树影 / 光尘 / 余光）的降级写在第 45 节末尾。 */
@media (prefers-reduced-motion: reduce) {
  .bt-gem.on,
  .bp-glow::before,
  .bp-stage.drop-hot .bp-glow,
  .bp-stage.hurt .bp-glow,
  .bp-stage.shielded .bp-glow::before,
  .bp-stage.healing .bp-glow::before,
  .bt-log.say { animation: none; }
  .bt-log { opacity: 0.9; }
}

/* ============================================================
   40 · 斯拉格&吉格斯药房：一整面木制药柜（ui/screens-alley.js）
   ------------------------------------------------------------
   创作者 2026-10-03 的规格一句话版本：**这一屏不该是"背景图上摆四张白卡片的商品页"，
   而是"玩家站在一家古老魔法药房里，面前一整面木柜，柜门后是玻璃，玻璃里摆着草药"**。
   所以这一节做的事：
     一、给这一屏压一层**暖棕滤镜 + 中间那道蜡烛光**（顶暗、中亮、底暗）；
     二、店名改成**招牌**（黄铜横线 + 两端一小片叶子），说明改成**柜台边贴的手写告示**；
     三、货格从"白纸卡"改成**嵌在木柜里的玻璃小格**：木框、柜内暖光、斜反光、
         羊皮纸标签、黄铜加减钮、木牌买走；四格共用一块木背板与几道梁 → 一整面柜；
     四、极轻的动效：玻璃每 9~14 秒扫过一道反光、草药 4~6 秒呼吸一下、柜里浮几点药粉；
     五、买成那一下：那一格的木牌亮一拍、药粉往上腾一下（400~700ms）。
   ⚠️ 一个数都没改：价钱、你有几件、今天还剩几个、步进器、买走、排队、扣钱、时间，
      全走 shopgrid.js 与 engine/alley.js 原来那条口（规格第三十九、四十三节）。
   ⚠️ 一张新图都没加，背景图也没换：木、玻璃、灯光、叶子、粒子全是渐变 / 阴影 / 伪元素。
   ⚠️ 作用域：整节只认 `.scene.apo`（这一屏的抬头挂了这个类）与 `.mkt-cell.glass`
      （只有药房那两栏传了 glass: true）。密林商人 / 翻倒巷 / 书摊 / 魔杖店那一套
      .mkt-* 零件**样子一律照旧**。
   ============================================================ */

/* ── 一、这一屏的空气：暖棕滤镜 + 中间一道烛光 ── */

.view:has(.scene.apo)::after {
  content: '';
  position: absolute;
  inset: -12px -20px;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(58% 34% at 50% 32%, rgba(216, 166, 76, 0.17), transparent 64%),
    linear-gradient(180deg,
      rgba(30, 19, 12, 0.66) 0%,
      rgba(38, 24, 15, 0.3) 32%,
      rgba(38, 24, 15, 0.4) 64%,
      rgba(22, 14, 9, 0.76) 100%);
}

/* ── 二、招牌与告示 ── */

/* 店名底下那道 34px 的红杠换成一条黄铜横杆，两头各一小片叶子 */
.scene.apo .name::after {
  width: min(78%, 300px);
  height: 1px;
  margin-top: var(--sp-2);
  background: linear-gradient(90deg, transparent, rgba(224, 178, 100, 0.85) 18%, rgba(224, 178, 100, 0.85) 82%, transparent);
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.5);
  opacity: 1;
}
.scene.apo .name {
  color: #ffeecb;
  text-shadow: 0 2px 0 rgba(24, 15, 9, 0.85), 0 0 18px rgba(224, 178, 100, 0.28);
}
/* 英文那行小字（招牌下面挂的一块小铜牌） */
.apo-sub {
  margin: 3px 0 var(--sp-2);
  font-family: var(--font-hand);
  font-size: 11px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: rgba(226, 190, 128, 0.82);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
}
/* 两端那两小片叶子：一片用 border-radius 削出来，纯 CSS */
.apo-sub::before,
.apo-sub::after {
  content: '';
  display: inline-block;
  width: 9px;
  height: 5px;
  margin: 0 8px;
  vertical-align: middle;
  background: linear-gradient(120deg, rgba(150, 176, 118, 0.9), rgba(88, 112, 70, 0.55));
  border-radius: 0 100% 0 100%;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}
.apo-sub::after { transform: scaleX(-1); }
/* 店里那句规矩：老板贴在柜台边的手写便签（歪一点点、上面一枚铜钉） */
.apo-notice {
  position: relative;
  width: fit-content;
  max-width: 100%;
  margin: 0 0 var(--sp-4) auto;
  padding: 6px 12px 7px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  line-height: 1.7;
  letter-spacing: 0.02em;
  color: #4a3218;
  text-align: left;
  text-shadow: none;
  background:
    radial-gradient(70% 60% at 12% 6%, rgba(255, 252, 240, 0.7), transparent 60%),
    linear-gradient(180deg, rgba(238, 224, 190, 0.92), rgba(216, 196, 156, 0.9));
  border: 1px solid rgba(120, 90, 44, 0.42);
  border-radius: 1px 3px 1px 3px;
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.5);
  rotate: -0.6deg;
}
/* 一枚铜钉（左上角那点光） */
.apo-notice::before {
  content: '';
  position: absolute;
  left: 8px;
  top: -4px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #f2d79a, #9a7229 68%, #5c431a);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.7);
}

/* ── 三、一整面柜子：木背板 + 中间那道竖梁 + 上下两道横梁 ── */

.mkt-cabinet {
  position: relative;
  padding: clamp(8px, 1.5vh, 14px) clamp(7px, 1.6vw, 12px);
  border: 1px solid rgba(20, 13, 8, 0.9);
  border-radius: 4px 4px 2px 2px;
  background:
    /* 竖向木纹 */
    repeating-linear-gradient(90deg,
      rgba(255, 226, 176, 0.035) 0 1px,
      transparent 1px 7px,
      rgba(0, 0, 0, 0.06) 7px 8px,
      transparent 8px 15px),
    linear-gradient(180deg, #4a3020, #3b281c 46%, #2a1b13);
  box-shadow:
    inset 0 1px 0 rgba(255, 220, 160, 0.14),
    inset 0 -2px 6px rgba(0, 0, 0, 0.6),
    0 6px 18px rgba(0, 0, 0, 0.5);
}
/* 中间那道竖梁（两列之间那条缝里露出来的木） */
.mkt-cabinet::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 6px;
  bottom: 6px;
  width: clamp(6px, 1.5vw, 10px);
  transform: translateX(-50%);
  background: linear-gradient(90deg, rgba(0, 0, 0, 0.5), #55381f 40%, #6a482a 55%, rgba(0, 0, 0, 0.5));
  box-shadow: 0 0 6px rgba(0, 0, 0, 0.55);
  pointer-events: none;
}
/* 上下两道横梁（柜顶与柜底那两条厚木） */
.mkt-cabinet::after {
  content: '';
  position: absolute;
  left: -1px;
  right: -1px;
  top: 0;
  height: 100%;
  pointer-events: none;
  background:
    linear-gradient(180deg, #6b4a2b, #3a2718 70%, transparent 100%) top / 100% 9px no-repeat,
    linear-gradient(0deg, #6b4a2b, #3a2718 70%, transparent 100%) bottom / 100% 9px no-repeat;
  box-shadow: inset 0 8px 6px -6px rgba(0, 0, 0, 0.7), inset 0 -8px 6px -6px rgba(0, 0, 0, 0.7);
}
/* 格与格的间距 = 梁的宽度：缝里露的是木，不是背景图 */
.mkt-cabinet .mkt-grid {
  position: relative;
  z-index: 1;
  gap: clamp(9px, 2.2vw, 15px);
}
/* 柜子里那一栏的小标题（每样一天最多 3 个）跟着变浅，别压在木头上看不清 */
.mkt-cabinet + .chapter { margin-top: var(--sp-4); }

/* ── 四、一格：嵌在木柜里的玻璃小格 ── */

.mkt-cell.glass {
  position: relative;
  padding: clamp(9px, 1.3vh, 13px) 7px 10px;
  text-align: center;
  color: var(--cine-text);
  /* 木框：外圈深木、内圈一道浅棕细线 */
  border: 1px solid rgba(18, 12, 7, 0.92);
  border-radius: 3px;
  background:
    radial-gradient(72% 44% at 50% 0%, rgba(228, 178, 92, 0.24), transparent 72%),
    linear-gradient(180deg, rgba(44, 34, 25, 0.72), rgba(24, 18, 13, 0.84));
  box-shadow:
    inset 0 0 0 1px rgba(150, 108, 56, 0.4),
    inset 0 0 24px rgba(220, 170, 80, 0.1),
    inset 0 1px 0 rgba(255, 226, 172, 0.16),
    0 3px 9px rgba(0, 0, 0, 0.5);
  transition: translate 220ms ease, box-shadow 260ms ease;
}
/* 手工做的柜子：四格各歪一点点、各挪一两个像素（幅度极小，只破掉"电商网格"那股味） */
.mkt-cabinet .mkt-cell:nth-child(1) { translate: 0 -2px; rotate: -0.3deg; }
.mkt-cabinet .mkt-cell:nth-child(2) { translate: 0 3px; rotate: 0.24deg; }
.mkt-cabinet .mkt-cell:nth-child(3) { translate: 0 1px; rotate: 0.3deg; }
.mkt-cabinet .mkt-cell:nth-child(4) { translate: 0 -1px; rotate: -0.2deg; }
/* 卖满了的那一格：整格压暗一档（原来的 .short 只改 opacity，木柜上要更明显一点） */
.mkt-cell.glass.short { opacity: 0.62; }
.mkt-cell.glass.short .mkt-go { color: rgba(232, 208, 164, 0.5); }

/* Hover：抬 2px、柜里的灯亮一点、木框边泛铜光（不 scale，不做网页卡片那套） */
.mkt-cell.glass:hover {
  translate: 0 -2px;
  box-shadow:
    inset 0 0 0 1px rgba(196, 148, 82, 0.55),
    inset 0 0 30px rgba(232, 182, 96, 0.18),
    inset 0 1px 0 rgba(255, 226, 172, 0.2),
    0 6px 14px rgba(0, 0, 0, 0.55),
    0 0 14px rgba(224, 178, 100, 0.2);
}
.mkt-cell.glass:hover .mx-glass::before { opacity: 0.22; }
.mkt-cell.glass:hover .mkt-art-img { translate: 0 -2px; }
.mkt-cell.glass:hover .mkt-name { box-shadow: 0 1px 2px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.6), 0 0 10px rgba(224, 178, 100, 0.35); }

/*
 * 玻璃那一层：常驻一道斜反光 + 每 9~14 秒扫过去的一道。
 * 它盖在整格上面，但 pointer-events:none，那颗买走照样点得到。
 */
.mx-glass {
  position: absolute;
  inset: 0;
  z-index: 3;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
}
.mx-glass::before {
  content: '';
  position: absolute;
  left: -18%;
  top: -46%;
  width: 46%;
  height: 190%;
  rotate: 22deg;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.14), transparent);
  opacity: 0.12;
  transition: opacity 260ms ease;
}
.mx-glass::after {
  content: '';
  position: absolute;
  left: -70%;
  top: -46%;
  width: 30%;
  height: 190%;
  rotate: 22deg;
  background: linear-gradient(90deg, transparent, rgba(255, 246, 224, 0.2), transparent);
  opacity: 0;
  animation: mxSheen 11s ease-in-out infinite;
}
@keyframes mxSheen {
  0%, 70% { transform: translateX(0); opacity: 0; }
  78% { opacity: 0.9; }
  100% { transform: translateX(420%); opacity: 0; }
}
/* 四格各差几秒，别整面柜子一起闪 */
.mkt-cabinet .mkt-cell:nth-child(1) .mx-glass::after { animation-delay: 1.2s; }
.mkt-cabinet .mkt-cell:nth-child(2) .mx-glass::after { animation-delay: 5.6s; animation-duration: 13s; }
.mkt-cabinet .mkt-cell:nth-child(3) .mx-glass::after { animation-delay: 8.4s; animation-duration: 9.5s; }
.mkt-cabinet .mkt-cell:nth-child(4) .mx-glass::after { animation-delay: 3.1s; animation-duration: 14.5s; }

/* 柜里浮着的那几点药粉（一格两粒，颜色由 --mx 给） */
.mx-dust {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 26%;
  height: 56%;
  z-index: 2;
  overflow: hidden;
  pointer-events: none;
}
.mx-dust::before,
.mx-dust::after {
  content: '';
  position: absolute;
  bottom: 0;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--mx, #ffe9b8), transparent 70%);
  opacity: 0;
  animation: mxFloat 7s ease-in-out infinite;
}
.mx-dust::before { left: 32%; }
/*
 * 第二粒只给隔一格挂（规格第二十三节：普通草药 1~2 粒、别每格一样），
 * 顺带把这一屏同时在跑的动画数砍掉一截——手机上这一柜子有十几格。
 */
.mkt-cabinet .mkt-cell:nth-child(2n) .mx-dust::after {
  left: 66%;
  width: 2px;
  height: 2px;
  animation-duration: 9s;
  animation-delay: 2.4s;
}
.mkt-cabinet .mkt-cell:nth-child(2n + 1) .mx-dust::after { display: none; }
@keyframes mxFloat {
  0% { transform: translate3d(0, 6px, 0); opacity: 0; }
  22% { opacity: 0.26; }
  70% { opacity: 0.16; }
  100% { transform: translate3d(4px, -34px, 0); opacity: 0; }
}
/* 三格淡金、一格偏绿：一眼看过去不是同一张贴纸印出来的 */
.mkt-cabinet .mkt-cell:nth-child(2) { --mx: #d8f0c2; }
.mkt-cabinet .mkt-cell:nth-child(3) { --mx: #cfe2f2; }

/* ── 五、玻璃里的货：草药、标签、库存、配额 ── */

/* 图那一格放大（规格第十四节：图片区占这一格的四成上下），仍然 object-fit: contain */
.mkt-cell.glass .mkt-item { gap: 4px; }
.mkt-cell.glass .mkt-art {
  /* 图片区占这一格的四成上下（规格第十四节）：柜里那块地方得像个"摆着东西的格" */
  width: clamp(72px, 19vw, 116px);
  height: clamp(72px, 19vw, 116px);
  font-size: 62px;
  /* 柜里那一小块地方：暗底 + 从上面打下来的一点暖光 */
  border-radius: 2px;
  background:
    radial-gradient(60% 48% at 50% 6%, rgba(232, 186, 104, 0.22), transparent 70%),
    rgba(28, 21, 15, 0.5);
  box-shadow: inset 0 0 14px rgba(0, 0, 0, 0.6), inset 0 0 0 1px rgba(150, 108, 56, 0.28);
}
.mkt-cell.glass .mkt-art-img {
  max-width: 92%;
  max-height: 92%;
  filter: drop-shadow(0 3px 5px rgba(0, 0, 0, 0.6));
  transition: translate 240ms ease;
  /* 药材在柜里随空气轻轻摇：4~6 秒一个来回，各格差几秒 */
  animation: mxSway 5.4s ease-in-out infinite;
}
.mkt-cabinet .mkt-cell:nth-child(2) .mkt-art-img { animation-duration: 6.2s; animation-delay: -1.8s; }
.mkt-cabinet .mkt-cell:nth-child(3) .mkt-art-img { animation-duration: 4.6s; animation-delay: -3.1s; }
.mkt-cabinet .mkt-cell:nth-child(4) .mkt-art-img { animation-duration: 5.8s; animation-delay: -0.9s; }
@keyframes mxSway {
  0%, 100% { transform: translateY(1px); }
  50% { transform: translateY(-1px); }
}
/* 名字 = 钉在柜子沿上的一张羊皮纸小标签 */
.mkt-cell.glass .mkt-name {
  padding: 2px 10px 3px;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.05em;
  line-height: 1.4;
  color: #45301a;
  text-shadow: none;
  background: linear-gradient(180deg, #f2e5c6, #dbc598);
  border: 1px solid rgba(120, 90, 44, 0.5);
  border-radius: 1px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.55);
  transition: box-shadow 240ms ease;
}
/* 价钱 = 黄铜刻的那一行 */
.mkt-cell.glass .mkt-price {
  margin-top: 1px;
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  letter-spacing: 0.06em;
  color: #eecf96;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
}
/* 库存 = 老板写在库存本上的那一笔（字比原来大一号、走手写体） */
.mkt-cell.glass .mkt-have {
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  letter-spacing: 0.04em;
  color: rgba(240, 224, 192, 0.86);
}
/* 今日配额 = 一小条挂在标签下的纸片 */
.mkt-cell.glass .mkt-note {
  margin-top: 2px;
  padding: 1px 7px 2px;
  font-size: 10px;
  line-height: 1.5;
  letter-spacing: 0.02em;
  color: #6b4d24;
  text-shadow: none;
  background: rgba(238, 224, 190, 0.88);
  border: 1px solid rgba(120, 90, 44, 0.4);
  border-radius: 1px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
  opacity: 1;
}

/* ── 六、称量的黄铜钮与那块木牌 ── */

.mkt-cell.glass .mkt-step { margin-top: 3px; gap: 3px; }
.mkt-cell.glass .ms-b {
  width: 22px;
  height: 20px;
  padding: 0;
  font-size: 9px;
  line-height: 1;
  color: #f0dcae;
  background: linear-gradient(180deg, #6d5222, #43320f 62%, #2c2110);
  border: 1px solid rgba(224, 178, 100, 0.5);
  border-radius: 2px;
  box-shadow: inset 0 1px 0 rgba(255, 232, 178, 0.22), 0 1px 3px rgba(0, 0, 0, 0.6);
}
.mkt-cell.glass .ms-b:hover {
  color: #fff3d6;
  border-color: rgba(246, 214, 148, 0.85);
  background: linear-gradient(180deg, #82632a, #55401a 62%, #352713);
}
.mkt-cell.glass .ms-b:active { transform: translateY(1px); }
.mkt-cell.glass .mkt-n {
  min-width: 26px;
  font-family: var(--font-hand);
  font-size: var(--fs-m);
  color: #f8ebc7;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85);
}
/* 买走 = 柜台上一块小木牌：按下去真的往下压一格 */
.mkt-cell.glass .mkt-go {
  margin-top: 5px;
  padding: 4px 16px 5px;
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  color: #f2dcae;
  background:
    repeating-linear-gradient(180deg, rgba(0, 0, 0, 0.16) 0 5px, transparent 5px 10px),
    linear-gradient(180deg, #5b3c25, #3b2718 68%, #2a1b13);
  border: 1px solid rgba(224, 178, 100, 0.46);
  border-radius: 2px;
  box-shadow: 0 2px 0 rgba(12, 8, 5, 0.75), inset 0 1px 0 rgba(255, 226, 172, 0.18);
  transition: color 200ms ease, border-color 200ms ease, background 200ms ease, transform 120ms ease;
}
.mkt-cell.glass .mkt-go:hover {
  color: #fff2cf;
  border-color: rgba(246, 214, 148, 0.85);
  background:
    repeating-linear-gradient(180deg, rgba(0, 0, 0, 0.14) 0 5px, transparent 5px 10px),
    linear-gradient(180deg, #6d4a2c, #46301d 68%, #31200f);
}
.mkt-cell.glass .mkt-go:active { transform: translateY(1px); box-shadow: 0 1px 0 rgba(12, 8, 5, 0.75); }
.mkt-cell.glass .mkt-go:disabled {
  color: rgba(232, 208, 164, 0.45);
  border-color: rgba(150, 118, 70, 0.3);
  box-shadow: none;
}

/* ── 七、买成那一下：木牌亮一拍、药粉往上腾一下（400~700ms） ── */

.mkt-cell.glass.bought { animation: mxBought 700ms ease both; }
@keyframes mxBought {
  0% {
    box-shadow:
      inset 0 0 0 1px rgba(150, 108, 56, 0.4),
      inset 0 0 24px rgba(220, 170, 80, 0.1),
      inset 0 1px 0 rgba(255, 226, 172, 0.16),
      0 3px 9px rgba(0, 0, 0, 0.5);
  }
  30% {
    box-shadow:
      inset 0 0 0 1px rgba(226, 180, 104, 0.7),
      inset 0 0 32px rgba(255, 208, 124, 0.34),
      inset 0 1px 0 rgba(255, 236, 190, 0.3),
      0 3px 9px rgba(0, 0, 0, 0.5),
      0 0 18px rgba(224, 178, 100, 0.4);
  }
  100% {
    box-shadow:
      inset 0 0 0 1px rgba(150, 108, 56, 0.4),
      inset 0 0 24px rgba(220, 170, 80, 0.1),
      inset 0 1px 0 rgba(255, 226, 172, 0.16),
      0 3px 9px rgba(0, 0, 0, 0.5);
  }
}
.mkt-cell.glass.bought .mkt-name { animation: mxPlate 620ms ease both; }
@keyframes mxPlate {
  0%, 100% { filter: brightness(1); }
  34% { filter: brightness(1.22); }
}
/* 那两粒平时几乎看不见（奇数格只留一粒），成交这一下两粒一起腾一把 */
.mkt-cell.glass.bought .mx-dust::before,
.mkt-cell.glass.bought .mx-dust::after {
  display: block;
  animation: mxPop 640ms ease-out both;
}
@keyframes mxPop {
  0% { transform: translate3d(0, 8px, 0); opacity: 0; }
  26% { opacity: 0.7; }
  100% { transform: translate3d(6px, -40px, 0); opacity: 0; }
}

/* ── 八、这一屏其余几样：分区标题、收药那块柜台、底下的托盘 ── */

/* 分区标题（买药材 / 买种子 / 收成品药）：黄铜刻的那道，两侧短线夹着 */
.scene.apo ~ .chapter { color: rgba(232, 196, 132, 0.92); letter-spacing: 0.28em; }
.scene.apo ~ .chapter .ch-en { color: rgba(206, 168, 108, 0.66); }

/* 收成品药那一块：同一张店页里的纸卡改成柜台那块木牌（只动颜色，不动结构） */
.view:has(.scene.apo) > .card {
  color: #f0dcae;
  background:
    repeating-linear-gradient(180deg, rgba(0, 0, 0, 0.14) 0 6px, transparent 6px 12px),
    linear-gradient(180deg, rgba(74, 48, 32, 0.9), rgba(38, 26, 18, 0.94));
  border-color: rgba(224, 178, 100, 0.4);
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 226, 172, 0.14);
}
.view:has(.scene.apo) > .card .muted { color: rgba(238, 214, 174, 0.72); }
/* 那颗【出售成品药】：纸上那一身奶油色压在木柜台上太亮，跟着换成同一块木牌 */
.view:has(.scene.apo) > .card .btn {
  color: #f2dcae;
  background:
    repeating-linear-gradient(180deg, rgba(0, 0, 0, 0.15) 0 5px, transparent 5px 10px),
    linear-gradient(180deg, #5b3c25, #3b2718 68%, #2a1b13);
  border: 1px solid rgba(224, 178, 100, 0.46);
  border-radius: 2px;
  box-shadow: 0 2px 0 rgba(12, 8, 5, 0.75), inset 0 1px 0 rgba(255, 226, 172, 0.18);
  backdrop-filter: none;
}
.view:has(.scene.apo) > .card .btn:hover {
  color: #fff2cf;
  border-color: rgba(246, 214, 148, 0.85);
  transform: none;
}
.view:has(.scene.apo) > .card .btn:active { transform: translateY(1px); }

/* 回街上 / 出售成品药那两颗：木牌化（.btn.exit 本来是磨砂玻璃那一身） */
.scene.apo .btn.exit {
  color: #f0dcae;
  background:
    repeating-linear-gradient(180deg, rgba(0, 0, 0, 0.15) 0 5px, transparent 5px 10px),
    linear-gradient(180deg, rgba(91, 60, 37, 0.92), rgba(42, 27, 19, 0.95));
  border: 1px solid rgba(224, 178, 100, 0.46);
  border-radius: 2px;
  box-shadow: 0 2px 0 rgba(12, 8, 5, 0.7), inset 0 1px 0 rgba(255, 226, 172, 0.16);
  backdrop-filter: none;
}
.scene.apo .btn.exit:hover {
  color: #fff2cf;
  border-color: rgba(246, 214, 148, 0.85);
  rotate: -0.8deg;
}
.scene.apo .btn.exit:active { transform: translateY(1px); rotate: 0deg; }

/* 底下那条托盘：黑雾收薄、换一点木色，让柜子能一直铺到屏幕下沿 */
.view:has(.scene.apo) ~ .tray {
  padding-top: var(--sp-3);
  background: linear-gradient(0deg,
    rgba(26, 17, 11, 0.94) 26%,
    rgba(26, 17, 11, 0.66) 58%,
    rgba(26, 17, 11, 0) 100%);
}

/* ── 九、窄屏与"别晕" ── */

@media (max-width: 420px) {
  /* 两列不能再窄：标签与木牌的字收一号，图跟着小一档，格子不缩 */
  .mkt-cell.glass .mkt-art { width: clamp(62px, 22vw, 84px); height: clamp(62px, 22vw, 84px); font-size: 48px; }
  .mkt-cell.glass .mkt-go { padding: 4px 12px 5px; letter-spacing: 0.1em; }
  .mkt-cell.glass .ms-b { width: 20px; height: 19px; }
  .mkt-cell.glass .mkt-n { min-width: 22px; }
  .apo-sub { letter-spacing: 0.18em; }
  .apo-sub::before, .apo-sub::after { width: 7px; height: 4px; margin: 0 5px; }
}
/* 高屏：柜子上下那点余量跟着长，别把木头压成一条 */
@media (min-height: 820px) {
  .mkt-cabinet { padding: clamp(12px, 2vh, 18px) clamp(9px, 2vw, 14px); }
}

@media (prefers-reduced-motion: reduce) {
  .mx-glass::after,
  .mx-dust::before,
  .mx-dust::after,
  .mkt-cell.glass .mkt-art-img,
  .mkt-cell.glass.bought,
  .mkt-cell.glass.bought .mkt-name { animation: none; }
  .mkt-cell.glass,
  .mkt-cell.glass .mkt-art-img,
  .mkt-cell.glass .mkt-name { transition: none; }
}

/* ============================================================
   41 · 禁林那五只：一片正在活动的森林（ui/wild.js）
   ------------------------------------------------------------
   创作者 2026-10-03 的规格一句话版本：**别再是"五个头像横着排队"**。
   这一屏要的是"今天的禁林正在活动"——五只分散在林子里，有远有近、
   有大有小，雾在走、光尘在浮、叶子偶尔飘下来；
   而**层级要一眼看得出来**：3 级自己发金光、2 级自己发紫光、普通的不抢戏。
   所以这一节做的事：
     一、.wild-field 一块没有边框没有底色的观察区，五只按固定摆位绝对定位
        （位置稳定、不随机跳；顺序按层级排 → 稀有那只一定在最前景最大位）；
     二、氛围那一摞：两团雾（15~30 秒）+ 一层树影 + 一整片光尘 + 三片叶子 + 一串脚印；
     三、每只：近大远小、3~5 秒上下浮一下、出场一只晚 100ms、脚下淡影、
        名字做成"观察标签"（一条淡线，不是一张卡片）；
     四、层级：金/紫那两圈不只是边框——一圈转着的光 + 两粒往上飘的粉 + 呼吸；
        当天有 3 级 → 整片林子多一层金色光尘 + 开场一道金波（只放一次）；
        有 2 级 → 紫色光尘；都没有 → 林子保持安静，不弹"今日无稀有"那种报表；
     五、点下去先放 240ms 的"发现"（头像抬、光圈散开、名字亮），再进现有战斗。
   ⚠️ 业务一个字没动：每天刷几只、层级、精力、那一格、捕捉、战斗，
      全在 engine/wild.js 与 engine/battle.js；这儿只摆位置与光。
   ⚠️ 一张新图都没加、背景没换：雾、树影、光尘、叶子、脚印、光环全是渐变 / 伪元素。
   ⚠️ 粒子按规格第五十一节节流：整片林子只挂 5 个装饰节点（光尘是一整层背景点，
      不是一颗一个节点），稀有那只再多两粒。
   本节写在第 29 节之后，同名规则以这里为准。
   ============================================================ */

/* ── 一、整屏压暗一档：背景是林子，动物才该是亮的（第四十二节） ── */

.view:has(.wild-field)::after {
  content: '';
  position: absolute;
  inset: -12px -20px;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(52% 34% at 50% 56%, rgba(150, 178, 132, 0.09), transparent 66%),
    linear-gradient(180deg,
      rgba(8, 14, 10, 0.5) 0%,
      rgba(10, 16, 11, 0.34) 30%,
      rgba(10, 16, 11, 0.42) 66%,
      rgba(6, 10, 7, 0.72) 100%);
}

/* ── 二、观察笔记那一行抬头（第三十五节：不要矩形卡片） ── */

.wild-part { margin-top: var(--sp-2); }
.wild-cap {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 2px 8px;
  padding: 4px 12px 6px;
  text-align: center;
  /* 只有一团极淡的纸 smear，画不出矩形边界——像本子上随手记的一行 */
  background: radial-gradient(64% 130% at 50% 40%, rgba(226, 210, 172, 0.09), transparent 72%);
}
.wc-zh {
  font-size: var(--fs-xs);
  letter-spacing: 0.26em;
  color: #e8dab8;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9);
}
.wc-en {
  font-family: var(--font-hand);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(196, 176, 134, 0.66);
}
/* 稀有踪迹：小字，金 / 紫，不红不公告（第二十四、五十六节） */
.wc-rare {
  width: 100%;
  font-size: 10px;
  letter-spacing: 0.3em;
  color: var(--cine-gold);
  text-shadow: 0 0 10px rgba(224, 178, 100, 0.5);
  animation: wcRare 3.4s ease-in-out infinite;
}
.wc-rare.g2 {
  color: #c3aede;
  text-shadow: 0 0 10px rgba(165, 138, 199, 0.5);
}
@keyframes wcRare {
  0%, 100% { opacity: 0.72; }
  50% { opacity: 1; }
}

/* ── 三、观察区：一块没有边框、没有底色的林子（第五、六节） ── */

.wild-field {
  position: relative;
  height: clamp(300px, 48vh, 440px);
  margin-top: var(--sp-2);
  overflow: hidden;
  border: 0;
  border-radius: 0;
  background: none;
}
/*
 * 底下那行「点一只：−10 精力 · 一格」不能被吸底托盘压住（实测这一屏的托盘高 229px，
 * 而 .view 原来只留 24px 下边距）——留一段能滚过去的地方，最后一行才永远读得到。
 */
.view:has(.wild-field) { padding-bottom: clamp(96px, 26vh, 236px); }
/* 局部聚光：林子中间那一块稍微亮一点（第四十三节：不是每只都给大光圈） */
.wild-field::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(40% 30% at 68% 74%, rgba(214, 196, 150, 0.08), transparent 70%),
    radial-gradient(34% 26% at 24% 52%, rgba(196, 214, 176, 0.06), transparent 72%);
}
/* 当天有 3 级：开场一道金波从中间散出去，只放一次（第二十七节） */
.wild-part.has-tier-3 .wild-field::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 62%;
  width: 46%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 1px solid rgba(224, 189, 120, 0.5);
  box-shadow: 0 0 26px rgba(224, 189, 120, 0.3), inset 0 0 26px rgba(224, 189, 120, 0.2);
  opacity: 0;
  pointer-events: none;
  animation: wfWave 1100ms ease-out 260ms 1 normal both;
}
@keyframes wfWave {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.3); }
  26% { opacity: 0.85; }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(2.6); }
}

/* ── 四、氛围那一摞：雾 / 树影 / 光尘 / 叶子 / 脚印 ── */

/* 两团雾：15~30 秒横着挪一次，透明度 0.05~0.14（第八节） */
.wf-fog {
  position: absolute;
  left: -30%;
  bottom: 4%;
  width: 160%;
  height: 34%;
  pointer-events: none;
  background: radial-gradient(50% 50% at 32% 50%, rgba(214, 228, 226, 0.5), transparent 70%);
  opacity: 0.1;
  filter: blur(12px);
  animation: wfFog 24s ease-in-out infinite alternate;
}
.wf-fog.two {
  bottom: auto;
  top: 12%;
  height: 26%;
  background: radial-gradient(50% 50% at 68% 50%, rgba(206, 214, 226, 0.42), transparent 70%);
  opacity: 0.07;
  animation-duration: 31s;
  animation-delay: -9s;
}
@keyframes wfFog {
  from { transform: translate3d(-7%, 0, 0) scale(1); }
  to { transform: translate3d(7%, -3%, 0) scale(1.08); }
}
/* 树影：几片斜着压下来的暗叶影，12~20 秒极小幅度晃（第九节） */
.wf-shade {
  position: absolute;
  inset: -6% -4% auto -4%;
  height: 46%;
  pointer-events: none;
  background:
    radial-gradient(30% 60% at 12% 0%, rgba(4, 10, 6, 0.5), transparent 68%),
    radial-gradient(26% 52% at 46% -6%, rgba(4, 10, 6, 0.42), transparent 66%),
    radial-gradient(34% 64% at 84% 2%, rgba(4, 10, 6, 0.46), transparent 68%);
  opacity: 0.5;
  transform-origin: 50% 0;
  animation: wfShade 17s ease-in-out infinite alternate;
}
@keyframes wfShade {
  from { transform: translateX(-6px) rotate(-0.5deg); }
  to { transform: translateX(6px) rotate(0.5deg); }
}
/*
 * 光尘：一整层小点（三个背景层 × 各自平铺），不是 15 个节点（第五十一节）。
 * 颜色跟着当天的层级走：3 级金、2 级紫、没有稀有就保持林子的淡金绿（第二十六节）。
 */
.wf-motes {
  --dust: 232, 208, 152;
  position: absolute;
  inset: 6% 0 10%;
  pointer-events: none;
  background:
    radial-gradient(circle, rgba(var(--dust), 0.9) 0.9px, transparent 1.8px) 14% 22% / 150px 150px,
    radial-gradient(circle, rgba(var(--dust), 0.65) 0.8px, transparent 1.6px) 62% 48% / 190px 190px,
    radial-gradient(circle, rgba(196, 226, 176, 0.55) 0.7px, transparent 1.5px) 36% 74% / 170px 170px;
  opacity: 0.16;
  animation: wfMotes 19s ease-in-out infinite alternate;
}
.wild-part.has-tier-3 .wf-motes { --dust: 244, 208, 132; opacity: 0.24; }
.wild-part.has-tier-2 .wf-motes { --dust: 196, 168, 226; opacity: 0.21; }
.wild-part.no-rare .wf-motes { --dust: 214, 224, 186; opacity: 0.13; }
@keyframes wfMotes {
  from { transform: translate3d(0, 8px, 0); opacity: 0.1; }
  to { transform: translate3d(-14px, -12px, 0); opacity: 0.22; }
}
/* 三片叶子从上面慢慢飘下来（第三十八节）：一片一个 i，各差周期与延迟 */
.wf-leaves i,
.wf-leaves::before,
.wf-leaves::after {
  content: '';
  position: absolute;
  top: -6%;
  width: 9px;
  height: 5px;
  border-radius: 0 100% 0 100%;
  background: linear-gradient(120deg, rgba(168, 192, 132, 0.85), rgba(96, 118, 74, 0.5));
  opacity: 0;
  animation: wfLeaf 12s linear infinite;
}
.wf-leaves { position: absolute; inset: 0; pointer-events: none; }
.wf-leaves::before { left: 22%; animation-delay: 2.5s; }
.wf-leaves::after { left: 63%; animation-duration: 14s; animation-delay: 7.5s; }
.wf-leaves i { left: 42%; width: 7px; height: 4px; animation-duration: 10s; animation-delay: 10.5s; }
@keyframes wfLeaf {
  0% { transform: translate3d(0, 0, 0) rotate(0deg); opacity: 0; }
  12% { opacity: 0.28; }
  70% { opacity: 0.2; }
  100% { transform: translate3d(26px, 320px, 0) rotate(220deg); opacity: 0; }
}
/* 一串脚印：三个小爪印，6~10 秒淡过一次（第三十六、三十七节） */
.wf-tracks {
  position: absolute;
  left: 8%;
  bottom: 12%;
  width: 46%;
  height: 22px;
  pointer-events: none;
  background:
    radial-gradient(circle at 6px 8px, rgba(226, 210, 172, 0.9) 2.2px, transparent 3px),
    radial-gradient(circle at 26px 4px, rgba(226, 210, 172, 0.9) 2.2px, transparent 3px),
    radial-gradient(circle at 46px 10px, rgba(226, 210, 172, 0.9) 2.2px, transparent 3px);
  opacity: 0;
  animation: wfTrack 9s ease-in-out infinite;
}
@keyframes wfTrack {
  0%, 58% { opacity: 0; }
  68% { opacity: 0.2; }
  86% { opacity: 0.12; }
  100% { opacity: 0; }
}

/* ── 五、一只：站在林子某个位置上的观察点 ── */

.wild-one {
  position: absolute;
  left: var(--x, 50%);
  top: var(--y, 50%);
  /* 居中只用 transform 这一条，永远不动它；缩放 / 抬起各走各的独立属性（免得互相盖） */
  transform: translate(-50%, -50%);
  z-index: var(--z, 10);
  scale: var(--s, 1);
  padding: 8px;
  color: var(--cine-text);
  /* 出场：一只晚 100ms（第十五节）；淡入 + 从 0.7 长到本来的大小 */
  animation: wfIn 560ms cubic-bezier(0.22, 0.8, 0.3, 1) backwards;
  animation-delay: calc(var(--i, 0) * 100ms);
  transition: scale 220ms ease, translate 220ms ease;
}
@keyframes wfIn {
  from { opacity: 0; scale: calc(var(--s, 1) * 0.7); }
  to { opacity: 1; scale: var(--s, 1); }
}
/* 脚下那一小片地影（第三十三节）：椭圆、没边界、跟着远近大小 */
.wild-one::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 2px;
  width: 62%;
  height: 10px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(50% 50% at 50% 50%, rgba(0, 0, 0, 0.42), transparent 72%);
  filter: blur(3px);
  pointer-events: none;
}
/* 出场那一下周围淡开的一圈光（第十六节）：只放一次 */
.wild-one::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 34%;
  width: 116%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(50% 50% at 50% 50%, rgba(198, 214, 168, 0.3), transparent 68%);
  opacity: 0;
  pointer-events: none;
  animation: wfBorn 900ms ease-out both;
  animation-delay: calc(var(--i, 0) * 100ms + 180ms);
}
@keyframes wfBorn {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.5); }
  34% { opacity: 0.8; }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.5); }
}
/* 普通那只不抢视觉（第十一、二十三节）：融进背景一点 */
/* 近大远小那一档：远处那几只连透明度也低一点（第十三节，数由 JS 的 slot 递进来） */
.wild-one .wild-av { opacity: var(--o, 0.9); }

/* 头像：尺寸按层级给（第四十五节），里面还是那张图，没拉伸没换 */
.wild-av {
  width: clamp(58px, 16vw, 78px);
  height: clamp(58px, 16vw, 78px);
  /* 3~5 秒上下浮一下，各只不同步（第十四节） */
  animation: wfFloat var(--fl, 4s) ease-in-out var(--dl, 0s) infinite;
}
.wild-one.g2 .wild-av { width: clamp(66px, 18vw, 88px); height: clamp(66px, 18vw, 88px); }
.wild-one.g3 .wild-av { width: clamp(74px, 20vw, 98px); height: clamp(74px, 20vw, 98px); }
@keyframes wfFloat {
  0%, 100% { transform: translateY(1px); }
  50% { transform: translateY(-3px); }
}

/*
 * 那一圈转着的光（第十八、二十二、四十六节）：
 * 头像背后一片比它大一圈、糊开的光，里面一道锥形光慢慢转 →
 * 看着就是"有光绕着它走"，稀有度不用看数字也知道。
 * 普通那只压根不给（第二十三节）。
 */
.wa-aura {
  position: absolute;
  left: 50%;
  top: 34%;
  width: 118%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  z-index: -1;
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
}
.wild-one.g3 .wa-aura {
  background: conic-gradient(from 0deg, transparent 0 58%, rgba(240, 200, 122, 0.9) 76%, transparent 92%);
  filter: blur(10px);
  animation: wfSpin 7s linear infinite, wfBreath 3.4s ease-in-out infinite;
}
.wild-one.g2 .wa-aura {
  background: conic-gradient(from 0deg, transparent 0 62%, rgba(178, 150, 214, 0.85) 78%, transparent 92%);
  filter: blur(11px);
  animation: wfSpin 11s linear infinite, wfBreath 5.2s ease-in-out infinite;
}
@keyframes wfSpin {
  to { transform: translate(-50%, -50%) rotate(360deg); }
}
/* 呼吸那一档收得很小（规格第十八节：不要做成一个巨大的金色发光圆圈） */
@keyframes wfBreath {
  0%, 100% { opacity: 0.26; }
  50% { opacity: 0.48; }
}
/* 稀有那两只：圈边再往上飘两粒粉（第十九、二十二节） */
.wild-one.g2 .wa-aura::before,
.wild-one.g3 .wa-aura::before,
.wild-one.g3 .wa-aura::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 18%;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--mx, #ffeec2), transparent 70%);
  opacity: 0;
  animation: wfMote 6.5s ease-in-out infinite;
}
.wild-one.g3 .wa-aura::after { left: 34%; animation-duration: 8s; animation-delay: 2.2s; }
.wild-one.g2 .wa-aura::before { --mx: #ddc9f2; left: 62%; animation-duration: 7.4s; animation-delay: 1.1s; }
@keyframes wfMote {
  0% { transform: translate3d(0, 6px, 0); opacity: 0; }
  24% { opacity: 0.55; }
  100% { transform: translate3d(5px, -34px, 0); opacity: 0; }
}
/* 3 级右上角那一枚小星芒（第二十节）：很轻地闪 */
.wild-one.g3 .wild-nm::after {
  content: '✦';
  margin-left: 4px;
  font-size: 9px;
  color: #f0d79a;
  text-shadow: 0 0 8px rgba(224, 189, 120, 0.8);
  animation: wcRare 2.8s ease-in-out infinite;
}

/* Hover（第三十一节）：抬 3px、大一档、光晕加重——不许 scale(1.2) */
.wild-one:hover {
  scale: calc(var(--s, 1) * 1.06);
  translate: 0 -3px;
}
.wild-one:hover .wild-nm { color: #fff4d8; }
.wild-one:hover .wild-av { opacity: 1; }

/* 点下去那 240ms（第二十八~三十节）：抬一下 + 一圈对应层级的光散开 */
.wild-one.found { scale: calc(var(--s, 1) * 1.08); }
.wild-one.found .wa-aura {
  opacity: 1;
  animation: wfFound 300ms ease-out both;
}
.wild-one.found .wa-aura { background: radial-gradient(50% 50% at 50% 50%, rgba(240, 200, 122, 0.5), transparent 68%); }
.wild-one.g2.found .wa-aura { background: radial-gradient(50% 50% at 50% 50%, rgba(178, 150, 214, 0.5), transparent 68%); }
@keyframes wfFound {
  0% { transform: translate(-50%, -50%) scale(0.7); opacity: 0.9; filter: blur(4px); }
  100% { transform: translate(-50%, -50%) scale(1.9); opacity: 0; filter: blur(10px); }
}
.wild-one.found .wild-nm { color: #fff4d8; text-shadow: 0 0 12px rgba(224, 189, 120, 0.6); }

/* 名字 = 观察标签（第三十二节）：一行淡金字，底下一条快看不见的线，不是一张卡片 */
.wild-nm {
  position: relative;
  max-width: 92px;
  padding-bottom: 3px;
  font-size: 11px;
  line-height: 1.35;
  letter-spacing: 0.08em;
  text-align: center;
  color: #e9dcbc;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.92);
  transition: color 200ms ease;
}
.wild-nm::before {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 26px;
  height: 1px;
  transform: translateX(-50%);
  background: linear-gradient(90deg, transparent, rgba(224, 189, 120, 0.5), transparent);
}
.wild-one.g2 .wild-nm { color: #dcccf0; }
.wild-one.g3 .wild-nm { color: #f6e3b0; }

.wild-cost { margin: var(--sp-2) 0 0; text-align: center; }
.wild-none { margin: var(--sp-4) 0 0; text-align: center; }

/* ── 六、窄屏（第五十二节：手机竖屏优先，五只不许挤成一排） ── */

@media (max-width: 560px) {
  .wild-field { height: clamp(300px, 48vh, 420px); }
  .wild-one { padding: 6px; }
  .wf-motes { opacity: 0.13; }
  .wf-fog { opacity: 0.08; }
}
@media (min-width: 720px) {
  /* PC 上这片林子更宽，五个位子往两边摊开一点 */
  .wild-field { height: clamp(380px, 56vh, 520px); }
}

/* 晕动症：雾、树影、光尘、叶子、脚印、光环转、呼吸、出场金波全停；
   远近大小、层级颜色、脚下那片影是静态的，留着（那才是"分散在林子里"） */
@media (prefers-reduced-motion: reduce) {
  .wf-fog,
  .wf-shade,
  .wf-motes,
  .wf-leaves::before,
  .wf-leaves::after,
  .wf-leaves i,
  .wf-tracks,
  .wc-rare,
  .wild-one,
  .wild-one::before,
  .wild-av,
  .wa-aura,
  .wa-aura::before,
  .wa-aura::after,
  .wild-part.has-tier-3 .wild-field::after,
  .wild-one.found .wa-aura { animation: none; }
  .wild-one { opacity: 1; }
  .wild-one .wild-av { opacity: 0.94; }
  .wild-one.g3 .wa-aura, .wild-one.g2 .wa-aura { opacity: 0.42; }
  .wild-part.has-tier-3 .wild-field::after { opacity: 0.5; }
}

/* ============================================================
   42 · 禁林林边：站在林子入口，不是在看一份菜单（ui/screens-forest.js）
   ------------------------------------------------------------
   创作者 2026-10-03 的规格一句话版本：**一个标题 + 一个大绿按钮 + 六块一样的牌子**
   太像网页导航。这一屏要改成"我站在禁林里，前面有几条路"。
   所以这一节做的事：
     一、.forest-atmos 一摞空气：三层雾（远 / 中 / 近，各走各的方向，18~34 秒）、
         顶上树影、右上与左下两枝前景枝影、一整片光尘、三点萤火、一层"森林呼吸"；
     二、抬头改成**古老的林地标记**：主标题 + 一行小英文 + 一条苔绿夹淡金的细线，
         线两头各一片小叶子；那句地点描述变成**手写的一行记录**（歪一点、没框）；
     三、【深入探索】不再是矩形按钮——一块**林口的旧木牌**：不规则边角（clip-path）、
         木纹 + 苔痕 + 暗金细边、顶上垂藤蔓、底下趴根须、右边一串往里去的脚印，
         每 5 秒里面那道绿光轻轻亮一次（"这是个进得去的地方"）；
     四、六处地方**散在这片林子里**：各自的百分比坐标、各自远近、各自钉歪一点，
         而且**六种长相**（月光空地 / 马人篝火 / 黑绿的蛛巢 / 水池 / 沼泽 / 石碑阵），
         中间几道几乎看不见的小路与树根把它们连成同一片地方；
     五、Hover 抬 3px + 光晕 + 名字亮 + 小路跟着亮一点；按下压一下再扩一圈光。
   ⚠️ 业务一个字没动：解锁、六处、点进去、精力、那一格、抽事件，
      全在 engine/forest.js 与 main.js 的 walkForest；这儿只换长相与位置。
   ⚠️ 背景图没换、没加任何素材：雾、枝叶、萤火、苔、根须、脚印、小路全是渐变 / 伪元素。
   ⚠️ 只在这一屏生效：作用域是 .scene.wood（这屏抬头挂的标记）与 .forest-atmos /
      .forest-map / .node-* 这些只有这儿才有的类。某一处自己的屏（第 41 节）不受影响。
   ============================================================ */

/* ── 一、那一摞空气 ── */

.forest-atmos {
  position: absolute;
  /* 左右不越界：越界的那几十像素会一路顶到 html 上，变成一条横向滚动 */
  inset: -10px 0 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
/* 这一屏的孩子都得站在空气上面 */
.view:has(> .forest-atmos) > :not(.forest-atmos) { position: relative; z-index: 1; }

/* 三层雾：远、中、近，方向与周期都不同（第五节） */
.fa-fog {
  position: absolute;
  left: -30%;
  width: 160%;
  background: radial-gradient(50% 50% at 40% 50%, rgba(206, 222, 214, 0.55), transparent 70%);
  filter: blur(14px);
  animation: faFog 26s ease-in-out infinite alternate;
}
.fa-fog.far { top: 6%; height: 26%; opacity: 0.1; animation-duration: 34s; }
.fa-fog.mid { top: 40%; height: 30%; opacity: 0.13; animation-duration: 24s; animation-delay: -7s; }
.fa-fog.near { bottom: 2%; height: 34%; opacity: 0.07; animation-duration: 18s; animation-delay: -12s; animation-direction: alternate-reverse; }
@keyframes faFog {
  from { transform: translate3d(-4%, 0, 0) scale(1); }
  to { transform: translate3d(4%, -2%, 0) scale(1.07); }
}
/* 顶上压下来的树影：12~18 秒极小幅度晃（第七节） */
.fa-shade {
  position: absolute;
  inset: -4% -6% auto -6%;
  height: 44%;
  background:
    radial-gradient(28% 62% at 14% 0%, rgba(4, 10, 6, 0.62), transparent 66%),
    radial-gradient(24% 54% at 44% -8%, rgba(4, 10, 6, 0.5), transparent 64%),
    radial-gradient(30% 66% at 76% 0%, rgba(4, 10, 6, 0.58), transparent 68%),
    radial-gradient(22% 48% at 96% -6%, rgba(4, 10, 6, 0.46), transparent 64%);
  transform-origin: 50% 0;
  animation: faShade 16s ease-in-out infinite alternate;
}
@keyframes faShade {
  from { transform: translateX(-7px) rotate(-0.5deg); }
  to { transform: translateX(7px) rotate(0.5deg); }
}
/* 前景那两枝虚掉的树枝：右上、左下（第三十四节） */
.fa-branch {
  position: absolute;
  width: 46%;
  height: 22%;
  background:
    radial-gradient(60% 100% at 20% 30%, rgba(3, 8, 5, 0.8), transparent 62%),
    radial-gradient(40% 80% at 62% 58%, rgba(3, 8, 5, 0.66), transparent 60%),
    linear-gradient(122deg, rgba(6, 12, 8, 0.72) 0 6%, transparent 12%);
  filter: blur(6px);
  opacity: 0.62;
  transform-origin: 100% 0;
  animation: faBranch 20s ease-in-out infinite alternate;
}
.fa-branch.tl { top: -2%; right: -6%; left: auto; }
.fa-branch.br {
  bottom: -2%;
  left: -6%;
  top: auto;
  transform: scaleX(-1);
  transform-origin: 0 100%;
  animation-duration: 25s;
  animation-delay: -9s;
}
@keyframes faBranch {
  from { rotate: -0.6deg; }
  to { rotate: 0.7deg; }
}
/* 光尘：一整层小点（三个平铺层），不是几十个节点（第四十九节性能） */
.fa-motes {
  position: absolute;
  inset: 10% 0 16%;
  background:
    radial-gradient(circle, rgba(236, 216, 160, 0.85) 0.9px, transparent 1.8px) 18% 26% / 160px 160px,
    radial-gradient(circle, rgba(198, 232, 186, 0.7) 0.8px, transparent 1.7px) 64% 52% / 210px 210px,
    radial-gradient(circle, rgba(190, 224, 226, 0.6) 0.7px, transparent 1.6px) 40% 78% / 180px 180px;
  opacity: 0.14;
  animation: faMotes 21s ease-in-out infinite alternate;
}
@keyframes faMotes {
  from { transform: translate3d(0, 10px, 0); opacity: 0.09; }
  to { transform: translate3d(-16px, -14px, 0); opacity: 0.17; }
}
/* 三点萤火：慢慢挪位置 + 偶尔闪一下（第三十六节：不要一直亮） */
.fa-fly {
  position: absolute;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: radial-gradient(circle, #f6efc0, rgba(196, 226, 150, 0.5) 55%, transparent 72%);
  box-shadow: 0 0 8px 2px rgba(226, 236, 168, 0.4);
  opacity: 0;
  animation: faWander 13s ease-in-out infinite;
}
.fa-fly.one { left: 22%; top: 30%; }
.fa-fly.two { left: 68%; top: 22%; animation-duration: 16s; animation-delay: -5s; }
.fa-fly.three { left: 48%; top: 62%; animation-duration: 11s; animation-delay: -8s; }
@keyframes faWander {
  0% { transform: translate3d(0, 0, 0); opacity: 0; }
  12% { opacity: 0.75; }
  20% { opacity: 0.2; }
  30% { opacity: 0.8; transform: translate3d(14px, -12px, 0); }
  46% { opacity: 0.25; }
  60% { opacity: 0.7; transform: translate3d(-10px, 8px, 0); }
  78% { opacity: 0.15; }
  100% { transform: translate3d(6px, -6px, 0); opacity: 0; }
}
/* 森林呼吸：一整层亮度极小的起伏（第六节，10 秒一次，别闪） */
.fa-breathe {
  position: absolute;
  inset: 0;
  background: radial-gradient(58% 40% at 50% 42%, rgba(214, 226, 190, 0.1), transparent 70%);
  animation: faBreathe 10s ease-in-out infinite;
}
@keyframes faBreathe {
  0%, 100% { opacity: 0.25; }
  50% { opacity: 0.6; }
}

/* ── 二、招牌与那行手写记录 ── */

.scene.wood .name {
  color: #f0e8cf;
  text-shadow: 0 2px 0 rgba(6, 12, 8, 0.85), 0 0 18px rgba(120, 150, 110, 0.25);
}
/* 标题底下那道 34px 的红杠 → 一条苔绿夹淡金的细线，两头各一片叶子 */
.scene.wood .name::after {
  width: min(72%, 260px);
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(118, 150, 106, 0.9) 16%, rgba(185, 154, 88, 0.9) 52%, rgba(118, 150, 106, 0.9) 84%, transparent);
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.6);
}
.fw-en {
  margin: 3px 0 var(--sp-2);
  font-family: var(--font-hand);
  font-size: 10px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: rgba(178, 196, 158, 0.72);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85);
}
/* 线两头那两片小叶子（第十一节：树枝 / 叶片 / 符号，纯 CSS） */
.fw-en::before,
.fw-en::after {
  content: '';
  display: inline-block;
  width: 8px;
  height: 4px;
  margin: 0 7px;
  vertical-align: middle;
  background: linear-gradient(120deg, rgba(150, 180, 130, 0.85), rgba(84, 105, 70, 0.5));
  border-radius: 0 100% 0 100%;
}
.fw-en::after { transform: scaleX(-1); }
/* 描述 = 记在本子上的那行字：手写体、歪一点、没有任何框（第十二节） */
.scene.wood .note {
  width: fit-content;
  max-width: 92%;
  margin-top: var(--sp-2);
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  line-height: 1.85;
  letter-spacing: 0.02em;
  color: rgba(214, 222, 190, 0.86);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9);
  rotate: -0.35deg;
}

/* ── 三、林口那块木牌：【深入探索】 ── */

.forest-deep {
  position: relative;
  width: clamp(210px, 72%, 420px);
  margin: var(--sp-4) auto var(--sp-2);
}
/* 顶上垂下来的两三条藤蔓 */
.fd-vine {
  position: absolute;
  left: 6%;
  right: 6%;
  top: -14px;
  height: 16px;
  background:
    radial-gradient(50% 100% at 12% 0%, rgba(96, 124, 78, 0.8), transparent 62%),
    radial-gradient(50% 100% at 38% 0%, rgba(84, 112, 70, 0.7), transparent 60%),
    radial-gradient(50% 100% at 74% 0%, rgba(96, 124, 78, 0.75), transparent 62%),
    linear-gradient(90deg, transparent 8%, rgba(70, 96, 60, 0.55) 8% 9%, transparent 9% 37%, rgba(70, 96, 60, 0.5) 37% 38%, transparent 38% 70%, rgba(70, 96, 60, 0.5) 70% 71%, transparent 71%);
  opacity: 0.7;
  pointer-events: none;
}
/* 底下趴着的根须 */
.fd-roots {
  position: absolute;
  left: 2%;
  right: 2%;
  bottom: -12px;
  height: 14px;
  background:
    radial-gradient(60% 120% at 20% 100%, rgba(58, 40, 27, 0.9), transparent 62%),
    radial-gradient(50% 110% at 52% 100%, rgba(48, 34, 22, 0.85), transparent 60%),
    radial-gradient(60% 120% at 82% 100%, rgba(58, 40, 27, 0.9), transparent 62%);
  opacity: 0.8;
  pointer-events: none;
}
/* 右边往林子里去的三串脚印（第十六节：不用 emoji，CSS 点出来的） */
.fd-tracks {
  position: absolute;
  right: 0;
  top: -6%;
  width: 26%;
  height: 22px;
  background:
    radial-gradient(circle at 8px 6px, rgba(226, 214, 178, 0.85) 2.2px, transparent 3.2px),
    radial-gradient(circle at 26px 12px, rgba(226, 214, 178, 0.7) 2px, transparent 3px),
    radial-gradient(circle at 46px 4px, rgba(226, 214, 178, 0.55) 1.8px, transparent 2.8px);
  opacity: 0;
  pointer-events: none;
  animation: fdTrack 11s ease-in-out infinite;
}
@keyframes fdTrack {
  0%, 52% { opacity: 0; }
  64% { opacity: 0.3; }
  86% { opacity: 0.16; }
  100% { opacity: 0; }
}

/*
 * 那颗按钮本体：一块钉在林口的旧木牌。
 * 边角故意不齐（clip-path 削掉两个角），圆角只有 3~5px（第四十二节：不要现代大圆角），
 * 阴影走"贴木头的内阴影 + 脚下那点自然暗部"，不用 0 10px 30px 那种网页卡片影（第四十三节）。
 */
.btn.forest {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: clamp(74px, 11vh, 104px);
  padding: var(--sp-3) var(--sp-4);
  color: #dfe8cf;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
  background:
    /* 木纹 */
    repeating-linear-gradient(96deg, rgba(255, 236, 200, 0.045) 0 1px, transparent 1px 9px),
    radial-gradient(70% 60% at 50% 0%, rgba(150, 190, 130, 0.14), transparent 66%),
    linear-gradient(180deg, #2b3f2b, #1d2c20 52%, #14211a);
  border: 1px solid rgba(126, 150, 106, 0.42);
  border-radius: 4px 3px 5px 3px;
  clip-path: polygon(0 6%, 3% 0, 97% 2%, 100% 8%, 99% 94%, 96% 100%, 4% 98%, 0 92%);
  box-shadow:
    inset 0 1px 0 rgba(226, 244, 210, 0.12),
    inset 0 -3px 10px rgba(0, 0, 0, 0.55),
    0 3px 8px rgba(4, 10, 6, 0.55);
  transition: rotate 220ms ease, border-color 220ms ease, scale 120ms ease;
}
/* 里面那道绿光：每 5 秒亮一次（第四十节——"这是个进得去的地方"） */
.btn.forest::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(58% 70% at 50% 108%, rgba(140, 214, 150, 0.3), transparent 62%);
  opacity: 0;
  pointer-events: none;
  animation: fdBreath 5.2s ease-in-out infinite;
}
@keyframes fdBreath {
  0%, 62% { opacity: 0.12; }
  78% { opacity: 0.5; }
  100% { opacity: 0.12; }
}
/* 按下：压一下 + 一圈淡绿光波散开（第十五节） */
.btn.forest::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 1px solid rgba(150, 214, 158, 0.6);
  opacity: 0;
  pointer-events: none;
}
.btn.forest:active { scale: 0.975; }
.btn.forest:active::after { animation: fdRipple 420ms ease-out; }
@keyframes fdRipple {
  from { opacity: 0.8; transform: scale(1); }
  to { opacity: 0; transform: scale(1.08); }
}
/* Hover：木牌轻轻晃一下 + 里面的绿光起来一点 */
.btn.forest:hover {
  rotate: -0.5deg;
  border-color: rgba(158, 196, 140, 0.7);
}
.btn.forest:hover::before { opacity: 0.55; }
/* 牌子上的字：这一颗是**横着读**的一块招牌（名字在上、代价在下），
   下面那六块窄牌子仍然竖排——它们是钉着的木牌，不是同一种东西 */
.btn.forest .forest-row { flex-direction: column; align-items: center; gap: 3px; }
.btn.forest .forest-row .fr-name {
  writing-mode: horizontal-tb;
  font-size: var(--fs-l);
  letter-spacing: 0.34em;
  text-indent: 0.34em;
  color: #eaf0d8;
}
.btn.forest .forest-row .fr-cost {
  align-self: center;
  font-size: var(--fs-xs);
  letter-spacing: 0.1em;
  color: rgba(196, 214, 168, 0.8);
}
/* 池子空了那一档：还是这块牌子，只是不再亮、边角更灰（原来的规矩留着） */
.btn.forest.done {
  color: #93a88c;
  border-style: dashed;
  border-color: rgba(126, 190, 132, 0.22);
  box-shadow: inset 0 -3px 10px rgba(0, 0, 0, 0.5);
  cursor: default;
}
.btn.forest.done::before { animation: none; opacity: 0.06; }
.btn.forest.done:hover { rotate: 0deg; }
.btn.forest.done .forest-row .fr-name { font-size: var(--fs-m); letter-spacing: 0.2em; }

/* ── 四、这一片林子：抬头与地图区 ── */

.forest-part { margin-top: var(--sp-5); }
.forest-cap {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: var(--sp-1);
  font-size: var(--fs-xs);
  letter-spacing: 0.24em;
  color: #a8c09c;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85);
}
.fc-en {
  font-family: var(--font-hand);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(160, 180, 148, 0.6);
}
/* 一块没有边框、没有底色的地方：六处就散在这片里（第二十节） */
/* 底下那条托盘是吸底的（实测这一屏它高 229px）：留一段能滚过去的地方，
   最下面那两块牌子才不会被压住（和第 41 节那一屏同一个处理） */
.view:has(.forest-map) { padding-bottom: clamp(96px, 24vh, 224px); }
.forest-map {
  position: relative;
  /* 高度跟着"认下了几处"长：两处时不必撑出一大片空林子，六处时才摊成一张地图 */
  height: clamp(250px, calc(132px + var(--n, 6) * 62px), 520px);
  border: 0;
  background: none;
}
/*
 * 几道几乎看不见的小路：两个大椭圆的虚线边，被这块地方裁掉一半，
 * 剩下的弧正好从几个点之间穿过去（第二十一节：只是让人觉得"在同一片林子里"）。
 */
.fm-paths {
  position: absolute;
  inset: 0;
  /* 两道弧自己裁在自己这一格里：不然椭圆的右半边会把这块地图顶出一条横向滚动 */
  overflow: hidden;
  pointer-events: none;
  opacity: 0.5;
  transition: opacity 300ms ease;
}
.fm-paths::before,
.fm-paths::after {
  content: '';
  position: absolute;
  border: 1px dashed rgba(176, 168, 132, 0.34);
  border-radius: 50%;
}
.fm-paths::before { left: -6%; top: 22%; width: 78%; height: 52%; rotate: -14deg; }
.fm-paths::after { left: 30%; top: -8%; width: 74%; height: 74%; rotate: 18deg; border-color: rgba(150, 168, 128, 0.26); }
/* 地上蔓延的树根：两道更粗更暗的弧（第二十二节） */
.fm-roots {
  position: absolute;
  inset: auto 0 0 0;
  height: 40%;
  pointer-events: none;
  background:
    radial-gradient(80% 46% at 18% 100%, rgba(38, 26, 17, 0.5), transparent 62%),
    radial-gradient(70% 40% at 78% 100%, rgba(38, 26, 17, 0.42), transparent 62%);
  opacity: 0.7;
}
/* 鼠标进到这片林子里，小路亮一点（第二十九节最后那句） */
.forest-map:hover .fm-paths { opacity: 0.85; }

/* ── 五、六处地方：六种长相，散在各自的位置上 ── */

.trail {
  position: absolute;
  left: var(--x, 50%);
  top: var(--y, 50%);
  /* 居中与歪斜各走各的独立属性：抬起用 transform，不会把 rotate 顶掉 */
  translate: -50% -50%;
  rotate: var(--r, 0deg);
  scale: var(--s, 1);
  z-index: 6;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: clamp(92px, 13vh, 118px);
  padding: var(--sp-3) 9px;
  color: #d9e6cb;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
  background:
    repeating-linear-gradient(94deg, rgba(255, 236, 200, 0.04) 0 1px, transparent 1px 8px),
    linear-gradient(180deg, #2c3527, #1b2419 58%, #141c13);
  border: 1px solid rgba(126, 150, 106, 0.4);
  border-radius: 3px 4px 2px 3px;
  box-shadow:
    inset 0 1px 0 rgba(226, 244, 210, 0.1),
    inset 0 -3px 8px rgba(0, 0, 0, 0.5),
    0 3px 7px rgba(4, 10, 6, 0.5);
  transition: transform 220ms ease, box-shadow 260ms ease, border-color 260ms ease;
  /* 出场：一块晚一块（和禁林里那五只同一套手感） */
  animation: ndIn 520ms cubic-bezier(0.22, 0.8, 0.3, 1) backwards;
}
.forest-map .trail:nth-child(3) { animation-delay: 60ms; }
.forest-map .trail:nth-child(4) { animation-delay: 130ms; }
.forest-map .trail:nth-child(5) { animation-delay: 200ms; }
.forest-map .trail:nth-child(6) { animation-delay: 270ms; }
.forest-map .trail:nth-child(7) { animation-delay: 340ms; }
.forest-map .trail:nth-child(8) { animation-delay: 410ms; }
@keyframes ndIn {
  from { opacity: 0; scale: calc(var(--s, 1) * 0.94); }
  to { opacity: 1; scale: var(--s, 1); }
}
.trail .forest-row .fr-name { font-size: var(--fs-s); letter-spacing: 0.14em; }
.trail .forest-row .fr-cost { font-size: 9px; letter-spacing: 0.06em; color: rgba(190, 206, 168, 0.72); }
/* 牌面上那一点装饰（苔 / 裂 / 网 / 水光…）由各形状自己画 */
.nd-mark { position: absolute; inset: 0; pointer-events: none; opacity: 0.9; }

/* 通用抬起 + 光晕（第二十三、二十九节） */
.trail:hover {
  transform: translateY(-3px);
  border-color: rgba(168, 200, 148, 0.72);
  box-shadow:
    inset 0 1px 0 rgba(226, 244, 210, 0.14),
    inset 0 -3px 8px rgba(0, 0, 0, 0.5),
    0 6px 12px rgba(4, 10, 6, 0.55),
    0 0 16px var(--nd-glow, rgba(150, 200, 140, 0.28));
}
.trail:hover .fr-name { color: #f2f7e4; }
.trail:active { transform: translateY(0) scale(0.96); }

/* ① 月光空地（独角兽深林）：淡银蓝、头顶一圈月光 */
.node-glade {
  --nd-glow: rgba(206, 224, 236, 0.34);
  color: #e6eef2;
  border-color: rgba(176, 198, 210, 0.42);
  background:
    radial-gradient(70% 34% at 50% 0%, rgba(226, 238, 246, 0.2), transparent 68%),
    linear-gradient(180deg, #26333c, #1a252c 56%, #131c22);
  border-radius: 50% 50% 4px 4px / 26% 26% 4px 4px;
}
.node-glade .nd-mark::before {
  content: '';
  position: absolute;
  left: 50%;
  top: -3px;
  width: 22px;
  height: 22px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(circle at 40% 36%, rgba(240, 246, 252, 0.9), rgba(180, 204, 224, 0.25) 58%, transparent 66%);
  box-shadow: 0 0 14px rgba(214, 232, 246, 0.5);
  animation: ndGlow 4.6s ease-in-out infinite;
}
/* ② 马人营地：暖棕 + 底下那堆火的橙光（一跳一跳） */
.node-camp {
  --nd-glow: rgba(224, 154, 84, 0.34);
  color: #f0dfc4;
  border-color: rgba(168, 122, 74, 0.46);
  background:
    radial-gradient(60% 40% at 50% 104%, rgba(232, 138, 60, 0.42), transparent 66%),
    linear-gradient(180deg, #3a2a1d, #2a1d14 58%, #1d1510);
}
.node-camp .nd-mark::before {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 4px;
  width: 16px;
  height: 10px;
  transform: translateX(-50%);
  border-radius: 50% 50% 40% 40%;
  background: radial-gradient(circle at 50% 70%, #ffd9a0, #e07f2c 55%, transparent 72%);
  filter: blur(1px);
  animation: ndFlicker 2.6s ease-in-out infinite;
}
@keyframes ndFlicker {
  0%, 100% { opacity: 0.72; transform: translateX(-50%) scaleY(1); }
  40% { opacity: 1; transform: translateX(-50%) scaleY(1.16); }
  70% { opacity: 0.6; transform: translateX(-50%) scaleY(0.92); }
}
/* ③ 黑绿的深处（八眼巨蛛巢穴）：最暗、边上挂几点丝，紫红一线危险味（第三十八节） */
.node-deep {
  --nd-glow: rgba(150, 108, 168, 0.34);
  color: #dfe6d6;
  border-color: rgba(126, 144, 120, 0.56);
  background:
    radial-gradient(80% 40% at 50% 0%, rgba(120, 96, 140, 0.16), transparent 62%),
    linear-gradient(180deg, #141d16, #0e1610 56%, #0a0f0b);
  border-radius: 46% 46% 3px 3px / 20% 20% 3px 3px;
  box-shadow:
    inset 0 0 14px rgba(0, 0, 0, 0.7),
    inset 0 -3px 8px rgba(0, 0, 0, 0.6),
    0 3px 7px rgba(4, 10, 6, 0.6);
}
.node-deep .nd-mark::before {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(122deg, transparent 0 12%, rgba(226, 236, 224, 0.16) 12.3% 12.8%, transparent 13%),
    linear-gradient(64deg, transparent 0 26%, rgba(226, 236, 224, 0.13) 26.3% 26.8%, transparent 27%),
    linear-gradient(150deg, transparent 0 58%, rgba(226, 236, 224, 0.1) 58.3% 58.8%, transparent 59%);
}
/* ④ 水边（溪流水池）：深蓝绿 + 一条慢慢走的银色反光 */
.node-water {
  --nd-glow: rgba(132, 196, 200, 0.34);
  color: #dcecf0;
  border-color: rgba(108, 152, 158, 0.46);
  background:
    linear-gradient(180deg, #1b3038, #14262c 56%, #0e1c21);
  overflow: hidden;
}
.node-water .nd-mark::before {
  content: '';
  position: absolute;
  left: -40%;
  top: 0;
  width: 60%;
  height: 100%;
  background: linear-gradient(100deg, transparent, rgba(226, 244, 248, 0.22), transparent);
  animation: ndGlint 7.5s ease-in-out infinite;
}
@keyframes ndGlint {
  0%, 66% { transform: translateX(0); }
  100% { transform: translateX(280%); }
}
/* ⑤ 沼泽（泥）：浊褐绿 + 几颗慢慢冒上来的泡 */
.node-bog {
  --nd-glow: rgba(126, 148, 96, 0.3);
  color: #d6dcc2;
  border-color: rgba(104, 112, 78, 0.44);
  background:
    radial-gradient(70% 40% at 50% 106%, rgba(66, 84, 56, 0.5), transparent 66%),
    linear-gradient(180deg, #2a2e1f, #1e2217 58%, #151810);
  border-radius: 6px 3px 8px 4px;
}
.node-bog .nd-mark::before,
.node-bog .nd-mark::after {
  content: '';
  position: absolute;
  left: 34%;
  bottom: 6px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  border: 1px solid rgba(206, 220, 186, 0.4);
  animation: ndBubble 6.5s ease-in-out infinite;
}
.node-bog .nd-mark::after { left: 62%; bottom: 14px; width: 3px; height: 3px; animation-duration: 8.5s; animation-delay: 2.4s; }
@keyframes ndBubble {
  0%, 55% { opacity: 0; transform: translateY(6px) scale(0.6); }
  68% { opacity: 0.7; }
  100% { opacity: 0; transform: translateY(-16px) scale(1); }
}
/* ⑥ 石碑阵（石头）：灰棕石面、顶上缺一个口、字像刻上去的 */
.node-stone {
  --nd-glow: rgba(196, 178, 138, 0.3);
  color: #ddd8c6;
  border-color: rgba(150, 142, 122, 0.42);
  background:
    radial-gradient(60% 30% at 30% 0%, rgba(226, 222, 206, 0.1), transparent 62%),
    linear-gradient(180deg, #3a382f, #2a2822 58%, #1e1d18);
  clip-path: polygon(0 8%, 22% 0, 78% 5%, 100% 14%, 98% 100%, 2% 100%);
  border-radius: 0;
}
.node-stone .fr-name { text-shadow: 0 1px 0 rgba(255, 250, 235, 0.14), 0 -1px 2px rgba(0, 0, 0, 0.85); }
.node-stone .nd-mark::before {
  content: '';
  position: absolute;
  left: 58%;
  top: 10%;
  width: 1px;
  height: 46%;
  background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.2));
  rotate: 9deg;
}
/* ⑦ 兜底（以后新加的地方没写进上面那张表时）：一块普通木牌 */
.node-wood { border-radius: 4px 3px 5px 3px; }

/* 稀有度那两档之外：危险的那处附近雾更浓一点（第三十八节，不加红不加点叹号） */
.node-deep::before {
  content: '';
  position: absolute;
  inset: -18% -26%;
  border-radius: 50%;
  background: radial-gradient(50% 50% at 50% 50%, rgba(18, 26, 20, 0.55), transparent 68%);
  filter: blur(8px);
  z-index: -1;
  pointer-events: none;
}
@keyframes ndGlow {
  0%, 100% { opacity: 0.6; }
  50% { opacity: 1; }
}

/* ── 六、底下那两行小字 ── */

.forest-note { color: #d9e6cb; text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9); }
.forest-hint { margin-top: var(--sp-2); color: rgba(178, 198, 160, 0.82); }

/* ── 七、窄屏与宽屏（第四十六、四十七节：不写死像素） ── */

@media (max-width: 560px) {
  .forest-map { height: clamp(240px, calc(118px + var(--n, 6) * 56px), 452px); }
  .trail { min-height: clamp(84px, 12vh, 104px); padding: var(--sp-2) 7px; }
  .trail .forest-row .fr-name { font-size: var(--fs-xs); letter-spacing: 0.1em; }
  /* 位子由 JS 写在行内 style 上，这里改不动（也不该改）：坐标一律走 PLACE_NODES */
  .fd-tracks { width: 22%; }
  .fa-branch { width: 56%; height: 18%; }
  .fa-fly.two { display: none; }
}
@media (min-width: 720px) {
  .forest-map { height: clamp(420px, 58vh, 560px); }
  .forest-deep { width: clamp(280px, 52%, 460px); }
}

/* 晕动症：雾、枝叶、萤火、呼吸、火苗、水光、泡、绿光起伏全停；
   六处的位置、形状、颜色是静态的，留着（那才是"散在一片林子里"） */
@media (prefers-reduced-motion: reduce) {
  .fa-fog,
  .fa-shade,
  .fa-branch,
  .fa-motes,
  .fa-fly,
  .fa-breathe,
  .fd-tracks,
  .btn.forest::before,
  .trail,
  .node-glade .nd-mark::before,
  .node-camp .nd-mark::before,
  .node-water .nd-mark::before,
  .node-bog .nd-mark::before,
  .node-bog .nd-mark::after { animation: none; }
  .trail { opacity: 1; }
  .btn.forest::before { opacity: 0.2; }
  .fa-motes { opacity: 0.11; }
}

/* ============================================================
   43 · 战斗里那三张牌：古老魔法卡牌，不是六个漂亮按钮（ui/battle.js）
   ------------------------------------------------------------
   创作者 2026-10-03 那份卡牌规格（五十八节）的核心一句：
   「不要把卡牌设计成六个漂亮按钮，而要设计成六张真正存在于巫师世界里的
   古老魔法卡牌。」上一版（这一节的木牌）方向对了一半——它像工具，不像牌。
   所以这一节整块重写，按那份规格做的几件：
     一、**三层框**（规格第四节）：深色外框 = .bt-card 自己的边，
         金属细线 = inset 0 0 0 1px var(--line)，内框 + 四角装饰 = .bc-in 那层；
         四角不是圆角，是**两把短角 bracket + 一颗暗金小点**（第五节，全 CSS）；
     二、卡面**不是纯色**（第六、七节）：中心亮四周暗 + 两层极轻的纸纹当羊皮纸颗粒，
         每张牌再挂一条自己的纹理 var(--tex)（第十四节重击的暗红漆、第四十四节攻击牌的裂痕…）；
     三、卡面**中心有图腾**（第八、十~十五节）：轻击 = 三道爪痕；普通攻击 = 一把短剑；
         重击 = 爆裂符文（旋转方菱 + 放射裂纹）；快攻 = 三道速度线；防御 = 盾 + 慢慢转的结界阵；
         恢复 = 生命圆环 + 两片叶 + 几点星尘。图腾是**占位置的一格**，不浮在字后面挡字；
     四、费用从"金币/铜钉"改成**切面魔法宝石**（第十六节）：0 费 = 灰金双圆环（空心、一眼知道不耗魔力），
         1 费 = 一颗晶石，2 费 = 大一档的**双层晶石**；数字照旧印在石头上，一眼能算（第十七节末句）；
     五、六张牌**六套颜色**（第九、十四~十五节）：攻击 = 暗金铜 / 深红古铜 / 暗红金，
         防御 = 深蓝银金，快攻 = 暗金青灰，恢复 = **深绿青绿淡金**；
         ⚠️ 恢复牌在 data 里那个类叫 c-purple，规格要的是绿色系（第十五、五十六节都点了），
            所以这里把 c-purple 画成生命绿——数据表一个字没动，只是这一身皮换了；
     六、符文若隐若现（第二十二节）：牌两侧各一枚 · ✦ ◇ 的小符，opacity 只在 .14~.34 之间慢慢呼吸，
         每张牌错开相位（第四十三节：不许同步闪）；
     七、Hover（第十九~二十节）：抬 8px、放 1.04、边框亮、内部一层极慢的光晕，
         外加一道 2.1 秒从左下扫到右上的**魔法扫光**（不是网页按钮扫光：透明度压得很低）
         和 2~4 粒向上漂的光尘；左右邻居各让 8px（第四十一节）；
     八、拖拽（第二十三节）：复制品由 JS 摆 scale(1.08) + 顺移动方向歪一点，光尘照旧出现；
         留在手里的空位只剩框与角饰（牌面内容藏掉）；
     九、**出牌两段飞**（第二十九、三十节）：先手一停抬到 1.1，再飞到目标缩成 0.3，
         途中撒三撮小粒子（用项目已有的 effects.js 粒子，不引库）；
     十、补牌那一下才演"从下面递上来 + 边框闪一下"（第三十八节，250~400ms；
         打出一张后重排的两张不演，见 ui/battle.js 的 paintHand(dealt)）；
    十一、魔力不足（第十七节）：**降饱和 + 压暗**，不是 opacity .3；宝石跟着变灰；
    十二、拖到不该去的那一片（第三十一节）：目标区亮暗红 + 一行"此处不收这张牌"，
         松手再补一次暗红收缩的拒绝反馈（.drop-wrong / .reject 在 ui/battle.js 里挂）。
   底下那一条大 UI 板（第三十六节）也在这节收掉：.bt-bar / .bt-under 的暗渐变减半，
   牌脚下只留一层极淡的雾与影子（第三十七节）。
   ⚠️ 只改样子。六张卡的费、倍率、目标、类型、抽牌弃牌、0 费限一次、魔力、回合、伤害
      全在 data/battle.js 与 engine/battle.js，一个数字没动；规格第五十节点名不要的
      稀有度 / 升级 / 等级 / 收集一样没加。
   ⚠️ 一张图都没加（第五十二节 CSS first）：框、角饰、纸纹、图腾、宝石、扫光、粒子全是渐变与伪元素。
   ⚠️ 动画一律 transform / opacity（第五十四节），补牌那条用 fill-mode: backwards——
      用 both 会把 translate 冻住，之后的 hover 抬牌就永远不生效（这一版踩过）。
   本节写在第 30 / 39 节之后，同名规则以这里为准。
   ============================================================ */

/* ── 一、牌身：三层框 + 中心亮四周暗 + 羊皮纸颗粒 ── */

.bt-card {
  /* 每一身换七个变量就够了：底色 / 亮心 / 金属线 / 内框 / 字色 / 外光 / 粒子色 */
  --p1: #3d2f1f;
  --p2: #1b1410;
  --lit: rgba(224, 189, 120, 0.13);
  --line: rgba(224, 189, 120, 0.4);
  --hair: rgba(224, 198, 142, 0.26);
  --corner: rgba(238, 214, 158, 0.55);
  --ink: #f0dcb4;
  --desc: rgba(214, 196, 160, 0.8);
  --glow: rgba(224, 189, 120, 0.16);
  --spark: rgba(240, 214, 150, 0.9);
  --core: rgba(232, 206, 148, 0.5);
  --rim: rgba(104, 78, 42, 0.85);
  --gem1: #f4e2b2;
  --gem2: #b9873f;
  --gem3: #55381a;
  --tex: none;

  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  width: clamp(84px, 23.5vw, 104px);
  height: clamp(126px, 34.5vw, 156px);
  padding: 8px 6px 7px;
  text-align: center;
  color: var(--bt-text);
  /* 第一层：深色外框。边角不齐（3~6px 的小圆角，第四十二节不许 10px） */
  border: 1px solid var(--frame, #241a12);
  border-radius: 4px 5px 3px 5px;
  background:
    /* 各张牌自己的纹理（漆痕 / 裂痕 / 铁色…在下面的六套里给） */
    var(--tex),
    /* 中心略亮、四角压暗（第六节那两个数） */
    radial-gradient(62% 44% at 50% 36%, var(--lit), transparent 68%),
    radial-gradient(118% 108% at 50% 46%, transparent 40%, rgba(0, 0, 0, 0.52) 100%),
    /* 羊皮纸颗粒：两个方向的极细线，叠出来是纸不是渐变（第七节：不要明显） */
    repeating-linear-gradient(58deg, rgba(255, 240, 208, 0.032) 0 1px, transparent 1px 4px),
    repeating-linear-gradient(-32deg, rgba(0, 0, 0, 0.05) 0 1px, transparent 1px 6px),
    linear-gradient(178deg, var(--p1), var(--p2) 92%);
  /* 第二层：金属细线（inset 一圈）；外面只给规格第四十二节那两个数 + 一点魔法光 */
  box-shadow:
    inset 0 0 0 1px var(--line),
    inset 0 1px 0 rgba(255, 244, 214, 0.12),
    inset 0 -7px 15px rgba(0, 0, 0, 0.42),
    0 5px 15px rgba(0, 0, 0, 0.35),
    0 0 13px var(--glow);
  cursor: grab;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  /* 抬牌走 transform，手里的歪角走 rotate / translate —— 三个各管各的才不互相盖 */
  transition: transform 210ms ease, box-shadow 240ms ease, border-color 240ms ease, filter 240ms ease, opacity 220ms ease;
}
/* 第三层内框 + 四角装饰（第二、五节）：一张牌该有的三层到这里齐了 */
.bc-in {
  position: absolute;
  inset: 4px;
  z-index: 2;
  border: 1px solid var(--hair);
  border-radius: 2px;
  pointer-events: none;
  opacity: 0.78;
  transition: opacity 260ms ease;
}
/* 四把短角（每角一横一竖，共 8 段渐变）：不靠图片，也不靠真的 border */
.bc-in::before {
  content: '';
  position: absolute;
  inset: -1px;
  background:
    linear-gradient(var(--corner), var(--corner)) left top / 11px 1px no-repeat,
    linear-gradient(var(--corner), var(--corner)) left top / 1px 11px no-repeat,
    linear-gradient(var(--corner), var(--corner)) right top / 11px 1px no-repeat,
    linear-gradient(var(--corner), var(--corner)) right top / 1px 11px no-repeat,
    linear-gradient(var(--corner), var(--corner)) left bottom / 11px 1px no-repeat,
    linear-gradient(var(--corner), var(--corner)) left bottom / 1px 11px no-repeat,
    linear-gradient(var(--corner), var(--corner)) right bottom / 11px 1px no-repeat,
    linear-gradient(var(--corner), var(--corner)) right bottom / 1px 11px no-repeat;
  opacity: 0.9;
}
/* 角里再各点一颗暗金小符章（第五节那几个"小型符文"） */
.bc-in::after {
  content: '';
  position: absolute;
  inset: -1px;
  background:
    radial-gradient(6px 6px at 6px 6px, var(--corner), transparent 70%) 0 0 / 100% 100% no-repeat,
    radial-gradient(6px 6px at calc(100% - 6px) 6px, var(--corner), transparent 70%) 0 0 / 100% 100% no-repeat,
    radial-gradient(6px 6px at 6px calc(100% - 6px), var(--corner), transparent 70%) 0 0 / 100% 100% no-repeat,
    radial-gradient(6px 6px at calc(100% - 6px) calc(100% - 6px), var(--corner), transparent 70%) 0 0 / 100% 100% no-repeat;
  opacity: 0.5;
}
/* 牌面上一层斜打下来的烛光（伪元素留给角饰与文字，这一层单独挂） */
.bt-card::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
  pointer-events: none;
  background:
    linear-gradient(118deg, transparent 28%, rgba(255, 244, 214, 0.05) 40%, transparent 54%),
    var(--plate, none);
}
/* Hover 才亮的那层光晕（第十九节"内部出现非常缓慢的魔法光晕"） */
.bt-card::after {
  content: '';
  position: absolute;
  inset: 4px;
  z-index: 3;
  border-radius: 2px;
  pointer-events: none;
  background: radial-gradient(58% 40% at 50% 40%, var(--lit), transparent 72%);
  opacity: 0;
  transition: opacity 520ms ease;
}

/* ── 二、牌面文字：名 / 说明都在最上层，不许被特效压住（第四十七节） ── */

.bc-name {
  position: relative;
  z-index: 4;
  margin-top: 2px;
  font-size: var(--fs-s);
  font-weight: 700;
  letter-spacing: 0.1em;
  line-height: 1.3;
  /* 第四十八节：牌名是羊皮纸色 / 淡金的字，不是一块标签 */
  color: var(--ink);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
}
.bc-desc {
  position: relative;
  z-index: 4;
  width: 100%;
  margin-top: 5px;
  padding: 3px 2px 2px;
  font-size: 9.5px;
  font-weight: 400;
  line-height: 1.45;
  letter-spacing: 0.02em;
  text-align: center;
  color: var(--desc);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.7);
}
/* 说明上下各一把极细的装饰线（第四十九节：像牌上手写的说明，不是现代小标签） */
.bc-desc::before,
.bc-desc::after {
  content: '';
  position: absolute;
  left: 16%;
  right: 16%;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--hair), transparent);
}
.bc-desc::before { top: 0; }
.bc-desc::after { bottom: 0; }

/* 若隐若现的符文（第二十二节：只在 .14~.34 之间慢慢呼吸，两张牌不同相位） */
.bc-rune {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  font-family: var(--font-hand);
  color: var(--corner);
}
.bc-rune::before,
.bc-rune::after {
  position: absolute;
  font-size: 8px;
  line-height: 1;
}
.bc-rune::before {
  left: 5px;
  top: 42%;
  content: '✦';
  animation: btRune 7.4s ease-in-out infinite;
  animation-delay: calc(var(--d, 0) * -2.6s);
}
.bc-rune::after {
  right: 5px;
  top: 56%;
  content: '◇';
  font-size: 7px;
  animation: btRune 9.1s ease-in-out infinite;
  animation-delay: calc(var(--d, 0) * -3.7s - 1.4s);
}
@keyframes btRune {
  0%, 100% { opacity: 0.14; }
  50% { opacity: 0.34; }
}

/* ── 三、费用 = 切面魔法宝石（第十六~十八节：一眼看得懂消耗几点） ── */

.bc-cost {
  position: relative;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 21px;
  height: 21px;
  margin: 0 auto 3px;
  font-family: var(--font-hand);
  font-size: 12.5px;
  font-weight: 700;
  color: #231708;
  text-shadow: 0 1px 0 rgba(255, 246, 216, 0.55);
  clip-path: polygon(30% 0, 70% 0, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0 70%, 0 30%);
  background: radial-gradient(circle at 34% 28%, var(--gem1), var(--gem2) 60%, var(--gem3) 100%);
}
/* 宝石外头那圈暗金属镶托 */
.bc-cost::before {
  content: '';
  position: absolute;
  inset: -3px;
  border: 1px solid var(--rim);
  clip-path: polygon(30% 0, 70% 0, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0 70%, 0 30%);
}
/* 石头表面的一道刻面高光 */
.bc-cost::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(126deg, rgba(255, 252, 235, 0.5) 0 18%, transparent 26% 62%, rgba(0, 0, 0, 0.3) 74%);
}

/* 宝石的石色跟着每一身走。这一条要写成六张牌并选（0,3,0）才压得住第 30 节
   那几条老的 .bt-card.c-xx .bc-cost（同一个费率的规则写在后面才赢）。 */
.bt-card.c-grey  .bc-cost,
.bt-card.c-gold  .bc-cost,
.bt-card.c-red   .bc-cost,
.bt-card.c-green .bc-cost,
.bt-card.c-blue  .bc-cost,
.bt-card.c-purple .bc-cost {
  background: radial-gradient(circle at 34% 28%, var(--gem1), var(--gem2) 60%, var(--gem3) 100%);
}
/* 0 费：灰金双圆环（空心 = 不耗魔力，第十八节：但别做成醒目的免费标签） */
.bt-card[data-cost='0'] .bc-cost {
  clip-path: none;
  border: 1px solid rgba(208, 194, 156, 0.5);
  border-radius: 50%;
  background: radial-gradient(circle at 50% 46%, rgba(232, 214, 168, 0.12), rgba(8, 6, 4, 0.62) 74%);
  color: rgba(226, 208, 164, 0.78);
  text-shadow: none;
}
.bt-card[data-cost='0'] .bc-cost::before {
  inset: -5px;
  border-radius: 50%;
  border-color: rgba(208, 194, 156, 0.32);
  clip-path: none;
}
.bt-card[data-cost='0'] .bc-cost::after { opacity: 0.35; }
/* 2 费：大一档的双层晶石（第十六节给的另一方案） */
.bt-card[data-cost='2'] .bc-cost { width: 25px; height: 25px; }
.bt-card[data-cost='2'] .bc-cost::after {
  inset: 4px;
  border: 1px solid rgba(255, 244, 214, 0.32);
  clip-path: polygon(30% 0, 70% 0, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0 70%, 0 30%);
  background: none;
}

/* ── 四、卡面中心图腾（第八、十~十五节）：占位置的一格，六张牌六个画法 ── */

.bc-totem {
  position: relative;
  z-index: 3;
  flex: none;
  width: 34px;
  height: 30px;
  margin: auto 0;
  pointer-events: none;
}
.bc-totem::before,
.bc-totem::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
}
/* ① 轻击（c-grey）：三道爪痕（第十节：剑痕 / 爪痕） */
.bt-card.c-grey .bc-totem {
  background:
    repeating-linear-gradient(64deg, var(--core) 0 1.6px, transparent 1.6px 7px) 50% 50% / 23px 21px no-repeat;
  opacity: 0.85;
}
.bt-card.c-grey .bc-totem::before {
  width: 25px;
  height: 23px;
  translate: -50% -50%;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%, transparent 58%, var(--hair) 60% 66%, transparent 68%);
}
/* ② 普通攻击（c-gold）：一把短剑（第十一节：魔法剑 / 攻击符号） */
.bt-card.c-gold .bc-totem::before {
  width: 6px;
  height: 21px;
  translate: -50% -54%;
  clip-path: polygon(50% 0, 100% 24%, 100% 100%, 0 100%, 0 24%);
  background: linear-gradient(96deg, var(--gem1), var(--gem2) 68%, var(--gem3));
}
.bt-card.c-gold .bc-totem::after {
  width: 15px;
  height: 3px;
  translate: -50% 9px;
  border-radius: 1px;
  background: linear-gradient(180deg, var(--gem1), var(--gem3));
  box-shadow: 0 4px 0 -1px var(--gem2);
}
/* ③ 重击（c-red）：爆裂符文（第十二节：视觉最有力量感的那张） */
.bt-card.c-red .bc-totem {
  background: repeating-conic-gradient(from 12deg at 50% 50%, var(--core) 0 3deg, transparent 3deg 45deg) 50% 50% / 30px 30px no-repeat;
  opacity: 0.5;
}
.bt-card.c-red .bc-totem::before {
  width: 17px;
  height: 17px;
  translate: -50% -50%;
  rotate: 45deg;
  border: 1.6px solid var(--core);
  box-shadow: 0 0 8px rgba(196, 86, 58, 0.4);
}
.bt-card.c-red .bc-totem::after {
  width: 7px;
  height: 7px;
  translate: -50% -50%;
  rotate: 45deg;
  background: linear-gradient(135deg, var(--gem1), var(--gem2) 60%);
}
/* ④ 快攻（c-green）：三道速度线（第十四节：强调速度、斜向细线） */
.bt-card.c-green .bc-totem {
  rotate: -14deg;
  background:
    linear-gradient(90deg, transparent, var(--core)) 4% 24% / 26px 1.6px no-repeat,
    linear-gradient(90deg, transparent, var(--core)) 18% 50% / 21px 1.6px no-repeat,
    linear-gradient(90deg, transparent, var(--core)) 34% 76% / 15px 1.6px no-repeat;
  opacity: 0.9;
}
.bt-card.c-green .bc-totem::before {
  width: 0;
  height: 0;
  translate: 6px -50%;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  border-left: 7px solid var(--core);
}
/* ⑤ 防御（c-blue）：盾 + 后面一圈极慢转着的结界阵（第十三、四十五节） */
.bt-card.c-blue .bc-totem::before {
  width: 22px;
  height: 24px;
  translate: -50% -50%;
  clip-path: polygon(50% 0, 100% 16%, 100% 60%, 50% 100%, 0 60%, 0 16%);
  background: linear-gradient(152deg, rgba(214, 232, 244, 0.5), rgba(84, 116, 140, 0.7) 52%, rgba(24, 38, 48, 0.9));
  box-shadow: inset 0 0 0 1px rgba(226, 240, 250, 0.4);
}
.bt-card.c-blue .bc-totem::after {
  width: 30px;
  height: 30px;
  translate: -50% -50%;
  border: 1px dashed var(--core);
  border-radius: 50%;
  opacity: 0.16;
  animation: btCircle 16s linear infinite;
}
@keyframes btCircle {
  to { rotate: 360deg; }
}
/* ⑥ 恢复（c-purple 这一身是生命绿）：圆环 + 两片叶 + 几点星尘（第十五、四十六节） */
.bt-card.c-purple .bc-totem {
  background:
    radial-gradient(circle, var(--core) 0 1px, transparent 1.4px) 6% 22% / 4px 4px no-repeat,
    radial-gradient(circle, var(--core) 0 0.8px, transparent 1.2px) 88% 30% / 4px 4px no-repeat,
    radial-gradient(circle, var(--core) 0 0.8px, transparent 1.2px) 74% 84% / 4px 4px no-repeat;
}
.bt-card.c-purple .bc-totem::before {
  width: 22px;
  height: 22px;
  translate: -50% -50%;
  border: 1px solid var(--core);
  border-radius: 50%;
  opacity: 0.7;
}
.bt-card.c-purple .bc-totem::after {
  width: 9px;
  height: 13px;
  translate: -4px -50%;
  border-radius: 0 100% 0 100%;
  background: linear-gradient(135deg, rgba(200, 234, 190, 0.72), rgba(74, 122, 96, 0.9));
  box-shadow: -7px 5px 0 -2px rgba(120, 168, 128, 0.45);
}

/* ── 五、六套颜色（第九节：低饱和，不是彩虹） ── */

/* ① 轻击：暗金 + 温暖铜（第十节） */
.bt-card.c-grey {
  --frame: #2b2216;
  --p1: #493826;
  --p2: #211a11;
  --lit: rgba(222, 186, 118, 0.12);
  --line: rgba(196, 162, 96, 0.42);
  --hair: rgba(212, 184, 128, 0.26);
  --corner: rgba(232, 206, 148, 0.6);
  --ink: #f0dcb4;
  --glow: rgba(206, 168, 98, 0.15);
  --spark: rgba(238, 208, 144, 0.9);
  --core: rgba(226, 196, 132, 0.52);
  --rim: rgba(112, 88, 48, 0.85);
  --gem1: #f0dea6;
  --gem2: #b8942f;
  --gem3: #5a4419;
}
/* ② 普通攻击：深红 + 古铜，边框比轻击复杂一档（第十一节：两个小符文） */
.bt-card.c-gold {
  --frame: #2c1810;
  --p1: #4a2a1d;
  --p2: #221210;
  --lit: rgba(212, 138, 86, 0.13);
  --line: rgba(192, 128, 76, 0.48);
  --hair: rgba(226, 168, 116, 0.26);
  --corner: rgba(238, 190, 132, 0.62);
  --ink: #f6dcb8;
  --desc: rgba(222, 188, 152, 0.8);
  --glow: rgba(178, 96, 58, 0.2);
  --spark: rgba(240, 190, 130, 0.9);
  --core: rgba(226, 160, 100, 0.55);
  --rim: rgba(108, 66, 34, 0.9);
  --gem1: #f6d3a2;
  --gem2: #a9682f;
  --gem3: #522915;
  --tex:
    linear-gradient(90deg, transparent, rgba(238, 200, 142, 0.2) 12%, transparent 24%) 0 22% / 100% 1px no-repeat,
    linear-gradient(90deg, transparent 76%, rgba(238, 200, 142, 0.18) 88%, transparent 100%) 0 78% / 100% 1px no-repeat;
}
/* ③ 重击：暗红 / 深棕 / 金，四角金属装饰最重 + 红色粒子（第十二节） */
.bt-card.c-red {
  --frame: #301310;
  --p1: #4d1f18;
  --p2: #1e0c0a;
  --lit: rgba(206, 96, 66, 0.14);
  --line: rgba(206, 120, 92, 0.56);
  --hair: rgba(240, 180, 140, 0.3);
  --corner: rgba(246, 196, 138, 0.7);
  --ink: #f8ddd0;
  --desc: rgba(226, 186, 158, 0.82);
  --glow: rgba(196, 86, 58, 0.28);
  --spark: rgba(236, 140, 104, 0.92);
  --core: rgba(244, 170, 120, 0.6);
  --rim: rgba(116, 52, 32, 0.92);
  --gem1: #f6c9a6;
  --gem2: #a8442c;
  --gem3: #4a1911;
  /* 牌面刷一道斜的暗红漆 + 右下角一点砸出来的焦 */
  --tex:
    linear-gradient(122deg, transparent 38%, rgba(143, 74, 61, 0.42) 41% 50%, rgba(143, 74, 61, 0.2) 51% 54%, transparent 56%),
    radial-gradient(26% 18% at 96% 96%, rgba(18, 10, 8, 0.75), transparent 68%);
}
.bt-card.c-red .bc-in::before {
  background:
    linear-gradient(var(--corner), var(--corner)) left top / 15px 1.6px no-repeat,
    linear-gradient(var(--corner), var(--corner)) left top / 1.6px 15px no-repeat,
    linear-gradient(var(--corner), var(--corner)) right top / 15px 1.6px no-repeat,
    linear-gradient(var(--corner), var(--corner)) right top / 1.6px 15px no-repeat,
    linear-gradient(var(--corner), var(--corner)) left bottom / 15px 1.6px no-repeat,
    linear-gradient(var(--corner), var(--corner)) left bottom / 1.6px 15px no-repeat,
    linear-gradient(var(--corner), var(--corner)) right bottom / 15px 1.6px no-repeat,
    linear-gradient(var(--corner), var(--corner)) right bottom / 1.6px 15px no-repeat;
}
/* ④ 快攻：暗金 + 青灰（第十四节） */
.bt-card.c-green {
  --frame: #1e241f;
  --p1: #3a4038;
  --p2: #1a1e1b;
  --lit: rgba(214, 214, 168, 0.1);
  --line: rgba(180, 192, 164, 0.44);
  --hair: rgba(214, 220, 190, 0.24);
  --corner: rgba(226, 222, 176, 0.58);
  --ink: #ececd2;
  --desc: rgba(206, 208, 180, 0.78);
  --glow: rgba(196, 180, 120, 0.14);
  --spark: rgba(232, 226, 168, 0.9);
  --core: rgba(216, 216, 176, 0.55);
  --rim: rgba(86, 96, 78, 0.88);
  --gem1: #eae4ba;
  --gem2: #9c9463;
  --gem3: #494e2e;
}
/* ⑤ 防御：深蓝 / 银灰 / 暗金（第十三节） */
.bt-card.c-blue {
  --frame: #16202a;
  --p1: #2b3a49;
  --p2: #131b24;
  --lit: rgba(178, 210, 234, 0.12);
  --line: rgba(158, 192, 214, 0.46);
  --hair: rgba(206, 226, 240, 0.26);
  --corner: rgba(222, 236, 246, 0.6);
  --ink: #e4eff7;
  --desc: rgba(192, 212, 228, 0.78);
  --glow: rgba(150, 190, 220, 0.18);
  --spark: rgba(214, 234, 246, 0.9);
  --core: rgba(212, 232, 246, 0.6);
  --rim: rgba(66, 92, 112, 0.9);
  --gem1: #dbeaf4;
  --gem2: #5d84a0;
  --gem3: #233846;
}
/* ⑥ 恢复：深绿 / 青绿 / 淡金（第十五节；c-purple 这个名字保留，颜色按规格走） */
.bt-card.c-purple {
  --frame: #14241a;
  --p1: #264032;
  --p2: #111f18;
  --lit: rgba(178, 224, 176, 0.12);
  --line: rgba(150, 196, 154, 0.44);
  --hair: rgba(196, 228, 190, 0.26);
  --corner: rgba(232, 240, 176, 0.6);
  --ink: #e2f0d6;
  --desc: rgba(190, 214, 180, 0.78);
  --glow: rgba(140, 200, 140, 0.18);
  --spark: rgba(196, 238, 182, 0.92);
  --core: rgba(200, 234, 190, 0.62);
  --rim: rgba(56, 92, 66, 0.9);
  --gem1: #dcf0ca;
  --gem2: #5c9a63;
  --gem3: #24422a;
}

/* ── 六、扫光与光尘（第二十~二十一节：只在 hover / 拖拽时出现） ── */

/* 一道从左下慢慢扫到右上的能量（不是网页按钮扫光：透明度压到几乎看不见） */
.bc-sheen {
  position: absolute;
  inset: 2px;
  z-index: 5;
  overflow: hidden;
  border-radius: 3px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 320ms ease;
}
.bc-sheen::before {
  content: '';
  position: absolute;
  top: -30%;
  bottom: -30%;
  left: -36%;
  width: 30%;
  rotate: 20deg;
  background: linear-gradient(90deg, transparent, rgba(255, 246, 220, 0.14), transparent);
}
/* 两粒光尘（各自带 1~2 个 box-shadow 分身，一张牌总共 3~5 粒，第五十三节的封顶） */
.bc-spark {
  position: absolute;
  inset: 0;
  z-index: 6;
  pointer-events: none;
  opacity: 0;
  transition: opacity 320ms ease;
}
.bc-spark::before,
.bc-spark::after {
  position: absolute;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  background: var(--spark);
}
.bc-spark::before {
  left: 22%;
  bottom: 24%;
  box-shadow: 13px -9px 0 -0.4px var(--spark), 25px 6px 0 -0.6px var(--spark);
  animation: btSpark 2.9s ease-in-out infinite;
  animation-delay: calc(var(--d, 0) * -0.9s);
}
.bc-spark::after {
  left: 64%;
  bottom: 32%;
  box-shadow: -9px 11px 0 -0.5px var(--spark);
  animation: btSpark 3.7s ease-in-out infinite;
  animation-delay: calc(var(--d, 0) * -1.3s - 1.6s);
}
@keyframes btSpark {
  0% { opacity: 0; translate: 0 6px; }
  28% { opacity: 0.9; }
  100% { opacity: 0; translate: -5px -19px; }
}

/* ── 七、摊在手里那一叠（第三十四节：中间那张略靠前） ── */

.bt-hand {
  position: relative;
  gap: 5px;
  min-height: clamp(134px, 20vh, 168px);
  padding: 10px 0 4px;
}
.bt-hand .bt-card:nth-child(1) { margin-right: -5px; rotate: -2deg; }
.bt-hand .bt-card:nth-child(2) { translate: 0 -4px; }
.bt-hand .bt-card:nth-child(3) { margin-left: -5px; rotate: 2deg; }
/* 牌脚下那一层极淡的雾 + 影子（第三十六、三十七节：不许出现明显 UI 框） */
.bt-hand::before {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 0;
  translate: -50% 0;
  width: min(84%, 330px);
  height: 30px;
  pointer-events: none;
  background: radial-gradient(50% 62% at 50% 100%, rgba(224, 189, 120, 0.07), transparent 72%);
}

/* ── 八、反馈：Hover / 让位 / 拖拽 / 空位 ── */

.bt-hand .bt-card:hover {
  transform: translateY(-8px) scale(1.04);
  z-index: 8;
  border-color: var(--corner);
  box-shadow:
    inset 0 0 0 1px var(--line),
    inset 0 1px 0 rgba(255, 244, 214, 0.16),
    inset 0 -7px 15px rgba(0, 0, 0, 0.4),
    0 9px 18px rgba(0, 0, 0, 0.42),
    0 0 18px var(--glow);
}
.bt-card:hover::after { opacity: 1; }
.bt-card:hover .bc-in { opacity: 1; }
.bt-card:hover .bc-sheen { opacity: 1; }
.bt-card:hover .bc-sheen::before { animation: btSheen 2.1s linear infinite; }
.bt-card:hover .bc-spark { opacity: 1; }
.bt-card:hover .bc-rune::before,
.bt-card:hover .bc-rune::after { color: var(--corner); }
/* 防御牌：结界阵 hover 才看得清（第四十五节那两个数） */
.bt-card.c-blue:hover .bc-totem::after { opacity: 0.32; }
@keyframes btSheen {
  from { translate: -18% 10%; }
  to { translate: 330% -10%; }
}
/* 邻居让开 8px（第四十一节）：中间被拿起来时左右各挪一点 */
.bt-card:has(+ .bt-card:hover) { transform: translateX(-8px); }
.bt-card:hover + .bt-card { transform: translateX(8px); }

/* 拖起来的那张（第二十三节）：scale / 歪角由 JS 摆，这儿只管光、影与粒子 */
.bt-card.drag {
  z-index: 100;
  border-color: var(--corner);
  box-shadow:
    inset 0 0 0 1px var(--line),
    inset 0 -7px 15px rgba(0, 0, 0, 0.4),
    0 14px 22px rgba(0, 0, 0, 0.5),
    0 0 22px var(--glow);
  transition: box-shadow 200ms ease;
}
.bt-card.drag .bc-sheen { opacity: 1; }
.bt-card.drag .bc-sheen::before { animation: btSheen 1.7s linear infinite; }
.bt-card.drag .bc-spark { opacity: 1; }
/* 留在手里的那个空位：只剩框与角饰（牌面内容藏掉，位置不许动） */
.bt-card.lifted {
  opacity: 1;
  border-color: rgba(224, 189, 120, 0.34);
  box-shadow: inset 0 2px 9px rgba(0, 0, 0, 0.5);
  rotate: 0deg;
  translate: 0 0;
}
.bt-card.lifted > .bc-cost,
.bt-card.lifted > .bc-name,
.bt-card.lifted > .bc-desc,
.bt-card.lifted > .bc-totem,
.bt-card.lifted > .bc-rune,
.bt-card.lifted > .bc-sheen,
.bt-card.lifted > .bc-spark { opacity: 0; }
.bt-card.lifted .bc-in { opacity: 0.42; }

/* 魔力不足 / 0 费这一回合用过了（第十七节：降饱和，不许整张看不清） */
.bt-card.off {
  cursor: not-allowed;
  /* 不许整张看不清（第十七节点名反对 opacity .3 那种做法）；opacity 那一条是老规矩，压回 1 */
  opacity: 1;
  filter: saturate(0.3) brightness(0.72);
}
.bt-card.off:not([data-cost='0']) .bc-cost {
  background: radial-gradient(circle at 34% 28%, var(--gem1), var(--gem2) 60%, var(--gem3) 100%);
}
.bt-card.off .bc-in,
.bt-card.off .bc-rune { opacity: 0.4; }
.bt-card.off .bc-sheen,
.bt-card.off .bc-spark { opacity: 0; }
.bt-hand .bt-card.off:hover {
  transform: translateY(-3px);
  box-shadow:
    inset 0 0 0 1px var(--line),
    inset 0 -7px 15px rgba(0, 0, 0, 0.4),
    0 5px 12px rgba(0, 0, 0, 0.32);
}

/* ── 九、补牌那一下（第三十八节：从下面递上来 + 边框闪一下） ── */

.bt-hand.dealt .bt-card {
  animation: btDeal 340ms cubic-bezier(0.2, 0.8, 0.3, 1) backwards;
  animation-delay: calc(var(--d, 0) * 80ms);
}
/* 用 backwards 不用 both：both 会把 translate 冻在终值，之后的 hover 抬牌当场失效 */
@keyframes btDeal {
  from { opacity: 0; translate: 0 30px; }
  to { opacity: 1; translate: 0 0; }
}
.bt-hand.dealt .bc-in {
  animation: btFlash 520ms ease-out backwards;
  animation-delay: calc(var(--d, 0) * 80ms + 100ms);
}
@keyframes btFlash {
  0% { opacity: 0.1; }
  36% { opacity: 1; }
  100% { opacity: 0.78; }
}

/* ── 十、拖到不该去的那一片：暗红拒绝反馈（第三十一节） ── */

.bp-stage.drop-wrong .bp-glow {
  opacity: 1;
  color: rgba(214, 88, 62, 0.95);
  background:
    radial-gradient(circle at 50% 50%, rgba(178, 62, 46, 0.24), transparent 64%),
    radial-gradient(circle at 50% 50%, transparent 56%, rgba(150, 44, 32, 0.5) 74%, transparent 86%);
}
.bp-stage.drop-wrong .bp-glow::before,
.bp-stage.drop-wrong .bp-glow::after { opacity: 1; }
.bp-stage.drop-wrong .bp-glow::after {
  content: '此处不收这张牌';
  color: #e6b6a6;
  text-shadow: 0 0 8px rgba(150, 56, 42, 0.7);
}
/* 松手那一瞬间再补一次收缩（动画 460ms，类由 JS 定时摘掉） */
.bp-stage.reject .bp-glow { animation: btReject 460ms ease-out; }
@keyframes btReject {
  0% { opacity: 1; scale: 0.9; }
  45% { opacity: 0.9; scale: 1.04; }
  100% { opacity: 0; scale: 0.82; }
}

/* ── 十一、底下那一大片 UI 板收淡（第三十六节） ── */

.bt-bar {
  background: linear-gradient(180deg, rgba(9, 7, 5, 0) 0%, rgba(9, 7, 5, 0.22) 52%, rgba(9, 7, 5, 0.44) 100%);
}
.bt-under {
  background: linear-gradient(180deg, rgba(9, 7, 5, 0.3) 0%, rgba(9, 7, 5, 0.48) 100%);
}

/* ── 十二、窄屏（第五十五节：文字必须读得清，宁可让牌小一点） ── */

@media (max-width: 560px) {
  .bt-card {
    width: clamp(80px, 25vw, 90px);
    height: clamp(122px, 36vw, 146px);
    padding: 7px 5px 6px;
  }
  .bc-name { font-size: var(--fs-xs); letter-spacing: 0.08em; }
  .bc-desc { font-size: 9px; line-height: 1.4; }
  .bc-cost { width: 19px; height: 19px; font-size: 11.5px; }
  .bt-card[data-cost='2'] .bc-cost { width: 23px; height: 23px; }
  .bc-totem { width: 30px; height: 26px; }
  .bt-hand { gap: 4px; min-height: clamp(126px, 19vh, 150px); }
  /* 手机上三张牌不许挤成一堆：歪角与重叠都收掉（第三十五节：名字不能被遮） */
  .bt-hand .bt-card:nth-child(1),
  .bt-hand .bt-card:nth-child(3) { margin: 0; rotate: 0deg; }
  .bt-card:hover .bc-sheen::before,
  .bt-card.drag .bc-sheen::before { animation-duration: 2.6s; }
}

/* ── 十三、"别晕"：动效降级（第四十九节） ── */

@media (prefers-reduced-motion: reduce) {
  .bt-hand .bt-card:hover,
  .bt-card:has(+ .bt-card:hover),
  .bt-card:hover + .bt-card,
  .bt-hand .bt-card.off:hover { transform: none; }
  .bt-hand.dealt .bt-card,
  .bt-hand.dealt .bc-in,
  .bc-rune::before,
  .bc-rune::after,
  .bc-sheen::before,
  .bc-spark::before,
  .bc-spark::after,
  .bt-card.c-blue .bc-totem::after,
  .bt-card.drag .bc-sheen::before,
  .bp-stage.reject .bp-glow { animation: none; }
  .bt-card:hover .bc-sheen,
  .bc-spark { opacity: 0; }
  .bc-rune::before,
  .bc-rune::after { opacity: 0.24; }
  .bt-card.c-blue .bc-totem::after { opacity: 0.2; }
}
/* ============================================================
   44 · 战斗立绘：舞台放大 + 光效改成圆环（ui/battle.js）
   ------------------------------------------------------------
   创作者 2026-10-03 看截图后指的两条：
     一、**立绘那块地方要大**（他框出来的那一圈），而且长身体的几只
         （火蜥蜴、角驼兽、海蛇那种横条）**不许再被掐头去尾**；
     二、拖牌 / 受击那一下的光**是一个方框**，难看——要**圆形的光环**，
         并且来一点流动的光。
   所以这一节做的事：
     · 舞台从 250×196 那一档加到**又宽又大**（宽到屏面的九成、高按 vh 走），
       同时把 ui/battle.js 的 FILL_MAX 改成 1 —— 图只许缩小、不许放大，
       于是"填满舞台"那一步整个取消，object-fit: contain 原样摆进去，一个像素都不裁；
     · .bp-glow 从"铺满舞台的一个矩形柔光"改成**一个正圆**（border-radius:50%
       把自己的背景裁成圆，方边从根上没了），半径按舞台高度算，永远不出方边；
     · 新增 .bp-aura 那一圈**转着的流动光**（锥形光 + 糊开 + 一直转），
       拖牌进来 / 受击 / 举盾 / 奶回来 / 暴击都借它亮，颜色跟着 currentColor 走；
     · 那几档原来会Paint到四角的"外圈渐变"（就是那个方框的元凶）全部撤掉，
       换成只在圆内收拢的一层边光。
   ⚠️ 战斗规则一个字没动：伤害、暴击、闪避、魔力、回合、抽牌、捕捉全在
      engine/battle.js；这一节只有尺寸、半径、圈。
   ⚠️ 一张图都没加：光环、流动光、边光全是 conic / radial 渐变 + filter: blur。
   本节写在第 30 / 37 / 39 节之后，同名规则以这里为准。
   ============================================================ */

/* ── 一、舞台：又宽又大，图一律不裁 ── */

/*
 * 宽 = 屏面的九成（横条动物终于放得下了），高按 vh 走；
 * 敌方仍比我方大一档（第 39 节那套近大远小：敌远而大、我方低而小）。
 */
.bp-stage { width: min(90%, 480px); height: clamp(146px, 23vh, 214px); }
.enemy-portrait-stage { width: min(90%, 480px); height: clamp(154px, 25vh, 232px); opacity: 0.98; }
.mine .bp-stage { width: min(90%, 480px); height: clamp(146px, 23vh, 214px); }
/* 场地那一格跟着让位（不然整屏要溢出到能滚） */
.bt-arena { min-height: clamp(330px, calc(100dvh - 404px), 500px); }
/* 脚底那片影跟着牌面变宽 */
.bp-stage::after { width: 46%; }
.enemy-portrait-stage::after { width: 42%; }
.mine .bp-stage::after { width: 50%; }

/* ── 二、那一层光：改成正圆 ── */

/*
 * 原来是 inset:0 铺满舞台的一块矩形柔光，里面那道"外圈渐变"会一路画到四角，
 * 于是屏幕上就是一个方框（创作者指的那个）。
 * 现在：半径按舞台高度算的一个正圆，border-radius 把自己的背景裁圆，
 * 方边从根上不存在了。
 */
.bp-glow {
  inset: auto;
  left: 50%;
  top: 50%;
  height: 80%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
}
/* 符文环：也按高度算成正圆（原来按宽度算，宽舞台上会被切掉上下） */
.bp-glow::before {
  left: 50%;
  top: 50%;
  width: auto;
  height: 100%;
  aspect-ratio: 1;
  box-shadow: none;
}
/* 那行短字挂在圆下沿内侧 */
.bp-glow::after { bottom: 4%; }

/*
 * 流动的光：一圈糊开的锥形光，一直慢慢转（第二条要的那个"流动"）。
 * 平时 opacity:0 完全不参与，拖牌 / 受击 / 举盾 / 奶回来时才亮，
 * 颜色一律 currentColor —— 各档只改 color，这一圈就跟着变色。
 */
.bp-aura {
  position: absolute;
  left: 50%;
  top: 50%;
  width: auto;
  height: 86%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: conic-gradient(from 0deg, transparent 0 46%, currentColor 62%, rgba(255, 255, 255, 0.14) 72%, transparent 86%);
  filter: blur(7px);
  opacity: 0;
  pointer-events: none;
  animation: btAuraSpin 5.5s linear infinite;
  transition: opacity 220ms ease;
}
@keyframes btAuraSpin {
  to { transform: translate(-50%, -50%) rotate(360deg); }
}
.bp-stage.drop-hot .bp-aura,
.bp-stage.shielded .bp-aura,
.bp-stage.healing .bp-aura,
.bp-stage.crit .bp-aura { opacity: 0.85; }
.bp-stage.hurt .bp-aura { animation: btAuraSpin 5.5s linear infinite, btAuraPop 460ms ease-out both; }
@keyframes btAuraPop {
  0% { opacity: 1; scale: 0.72; }
  55% { opacity: 0.8; }
  100% { opacity: 0; scale: 1.3; }
}

/* ── 三、四档光各自的颜色与那一层圆内边光（把会涂到四角的那层删了） ── */

/* 攻击 → 敌方：暗金 + 一点血红 */
.bp-stage.drop-hot.hot-atk { color: rgba(228, 192, 122, 0.9); }
.bp-stage.drop-hot.hot-atk .bp-glow {
  background:
    radial-gradient(circle at 50% 50%, rgba(224, 189, 120, 0.16), transparent 62%),
    radial-gradient(circle at 50% 50%, transparent 58%, rgba(160, 82, 62, 0.3) 74%, transparent 84%);
}
/* 防御 → 我方：银白偏金的护盾 */
.bp-stage.drop-hot.hot-def { color: rgba(208, 226, 238, 0.9); }
.bp-stage.drop-hot.hot-def .bp-glow {
  background:
    radial-gradient(circle at 50% 50%, rgba(200, 220, 235, 0.14), transparent 62%),
    radial-gradient(circle at 50% 50%, transparent 60%, rgba(224, 189, 120, 0.24) 76%, transparent 86%);
}
/* 恢复 → 我方：柔和金绿 */
.bp-stage.drop-hot.hot-heal { color: rgba(178, 228, 176, 0.9); }
.bp-stage.drop-hot.hot-heal .bp-glow {
  background:
    radial-gradient(circle at 50% 52%, rgba(158, 224, 168, 0.16), transparent 64%),
    radial-gradient(circle at 50% 50%, transparent 60%, rgba(206, 224, 150, 0.22) 76%, transparent 86%);
}
/* 受击那一下：圆的一圈红金冲击波（原来那两条渐变也换成圆内收拢） */
@keyframes btShock {
  0% {
    opacity: 0.95;
    transform: translate(-50%, -50%) scale(0.72);
    background: radial-gradient(circle at 50% 50%, rgba(255, 232, 170, 0.42), rgba(168, 64, 46, 0.34) 52%, transparent 74%);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1.34);
    background: radial-gradient(circle at 50% 50%, rgba(255, 232, 170, 0.1), rgba(168, 64, 46, 0.06) 52%, transparent 74%);
  }
}
/* 举盾 / 奶回来：圆内那一层光跟着收在圆里（原来那两档没问题，只补上流动圈的颜色） */
.bp-stage.shielded { color: rgba(214, 226, 238, 0.85); }
.bp-stage.healing { color: rgba(178, 228, 176, 0.85); }
.bp-stage.crit { color: rgba(255, 226, 150, 0.95); }
.bp-stage.crit .bp-glow {
  background: radial-gradient(circle at 50% 48%, rgba(255, 226, 150, 0.4), transparent 66%);
}

/* ── 四、窄屏与矮屏 ── */

@media (max-width: 560px) {
  .bp-stage { width: min(94%, 420px); height: clamp(132px, 20vh, 178px); }
  .enemy-portrait-stage { width: min(94%, 420px); height: clamp(140px, 22vh, 192px); }
  .mine .bp-stage { width: min(94%, 420px); height: clamp(132px, 20vh, 178px); }
  .bt-arena { min-height: clamp(300px, calc(100dvh - 392px), 470px); }
}
@media (max-height: 780px) {
  .enemy-portrait-stage { height: clamp(126px, 19vh, 168px); }
  .mine .bp-stage { height: clamp(118px, 17vh, 152px); }
}

/* 晕动症：转着的那圈光停住（停在原位，还是一圈圆光，只是不转）；
   受击那一下的冲击波留着——那是"打中了"的说明，不是装饰 */
@media (prefers-reduced-motion: reduce) {
  .bp-aura { animation: none; }
  .bp-glow::before { animation: none; }
}
/* ============================================================
   45 · 战斗那片背景：把"一张森林图"变成"正在打架的森林"（ui/battle.js）
   ------------------------------------------------------------
   创作者 2026-10-03 深夜那份规格只准动一件事：【背景】。
   立绘、血条、名字、属性字、卡牌、魔力、两颗按钮、拖拽与全部战斗逻辑一律不许碰——
   这一节确实一行都没碰：JS 那头只多挂了几个空 <i>、一个量两条水平线的 atmosFit()、
   和一个往氛围层挂类的 bgFx()（第十九~二十六节要的就是"背景回应战斗"）。
   现在的问题：背景是张死图。第 39 节那版只给了遮罩 + 两团雾 + 一片光尘，
   站在那儿像"贴了一张森林照片再摞两只动物"。这一节按那份规格把它拆成八层：
     · ::before 暗色遮罩 + 左右两侧压暗（第五、十、二十八节）
     · ::after  四周暗角，中心透明（第二十九节）
     · .bt-shade 树影 / 光影慢慢挪（第十五节，27 秒一趟，不画真叶子）
     · .bt-mist.far / .mid / .low 远处淡雾、中间横薄雾、贴地底雾（第八、九节；
       底雾 14 秒左右挪 26px，永远压在卡牌**下面**——第三十一节点名不许糊住卡面字）
     · .bt-field 战斗区那片魔力场：中央偏下暗金 + 敌我之间灰绿 + 一点琥珀，
       5.4 秒在 .05~.12 之间呼吸一次（第六、七节："空气里残留着魔法"，不是放了一盏灯）
     · .bt-amb.en / .bt-amb.my 敌方那一片暖红、我方那一片淡金（第十八~二十节）——
       两条水平线由 JS 量出来（--en-y / --my-y），所以窗口改大小、换动物都贴得住
     · .bt-mote ×8 魔法尘埃：1~3px、透明度 .3~.5、慢慢往上浮 28~50px、左右漂 ±9~16px、
       周期 5.4~8.6 秒、每颗自己一个负延迟（第十一~十四、三十六节；定死的一张表，
       不随机——每重画一次换一批星会像坏了）。顶部那一截不放（第三十节：敌方血条在那儿）
     · .bt-spore / .bt-ripple / .bt-flash 恢复时那几粒绿孢子、敌我之间一圈淡波纹、
       以及攻击 / 暴击 / 敌方出手 / 防御 / 恢复 / 新回合各自那一下环境余光
   ⚠️ 全是 transform / opacity（第三十五节），雾与树影一律**不再用 filter: blur**
      （旧版两团雾各糊 10px，手机上白烧 GPU；渐变本身就是软的）。
   ⚠️ 整层 z-index:0、pointer-events:none，永远在立绘与卡牌下面（第三十二节那个优先级）。
      粒子不许飘在谁脸上：光尘八颗全在中下部，且被立绘那一层盖住。
   ⚠️ 背景图片没换、没加任何面板、没画巨大魔法阵（第二十七、三十九节）。
      呼吸与微动只作用在战斗这一屏（body:has(.battle)），别的场面照旧走那条 52 秒的慢推。
   本节写在第 39 / 44 节之后，同名规则以这里为准（第 39 节那段氛围层已经删掉，不留第二份）。
   ============================================================ */

/* ── 一、整层与遮罩、暗角 ── */

.bt-atmos {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
/* 遮罩：顶稍暗（血条在那儿）、中间留给立绘、底稍暗；左右两侧再压一档成自然暗边 */
.bt-atmos::before {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(90deg,
      rgba(9, 14, 10, 0.5) 0%, rgba(9, 14, 10, 0.14) 15%, transparent 30% 70%,
      rgba(9, 14, 10, 0.14) 85%, rgba(9, 14, 10, 0.5) 100%),
    linear-gradient(180deg,
      rgba(10, 14, 10, 0.26) 0%, rgba(8, 11, 8, 0.08) 26% 56%, rgba(5, 8, 6, 0.4) 100%);
}
/* 暗角：中心必须透明（第二十九节） */
.bt-atmos::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 50% 46%, transparent 45%, rgba(4, 6, 5, 0.38) 100%);
}

/* ── 二、树影 / 光影（第十五节：像树叶被风吹动，但不真的长叶子） ── */

.bt-shade {
  position: absolute;
  inset: -8% -12%;
  background:
    repeating-linear-gradient(104deg, transparent 0 44px, rgba(6, 11, 8, 0.5) 44px 76px, transparent 76px 152px),
    repeating-linear-gradient(74deg, transparent 0 62px, rgba(8, 13, 9, 0.36) 62px 88px, transparent 88px 196px);
  -webkit-mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.92), transparent 44% 60%, rgba(0, 0, 0, 0.8));
  mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.92), transparent 44% 60%, rgba(0, 0, 0, 0.8));
  opacity: 0.17;
  animation: btShade 27s ease-in-out infinite alternate;
}
@keyframes btShade {
  from { transform: translate3d(-15px, 0, 0) skewX(0deg); }
  to   { transform: translate3d(17px, 7px, 0) skewX(-1.1deg); }
}

/* ── 三、三层雾（第八、九节） ── */

.bt-mist {
  position: absolute;
  pointer-events: none;
  filter: none;
}
/* 远处那层淡雾：贴在两只动物中间，几乎看不出动 */
.bt-mist.far {
  left: -8%;
  right: -8%;
  top: 30%;
  height: 26%;
  background: linear-gradient(180deg, transparent, rgba(198, 214, 206, 0.5) 46%, transparent);
  opacity: 0.055;
  animation: btMistFar 31s ease-in-out infinite alternate;
}
@keyframes btMistFar {
  from { transform: translate3d(0, 0, 0) scaleY(1); }
  to   { transform: translate3d(0, -1.4%, 0) scaleY(1.08); }
}
/* 中间横向薄雾 */
.bt-mist.mid {
  left: -24%;
  width: 148%;
  top: 52%;
  height: 15%;
  background: radial-gradient(50% 50% at 32% 50%, rgba(206, 214, 226, 0.46), transparent 70%);
  opacity: 0.065;
  animation: btMistMove 22s linear infinite;
}
/* 贴地那层底雾：卡牌**下面**，14 秒左右挪 26px（第九节那两个数） */
.bt-mist.low {
  left: -16%;
  width: 132%;
  bottom: 2%;
  height: 23%;
  background: radial-gradient(52% 46% at 40% 62%, rgba(214, 226, 224, 0.5), transparent 72%);
  opacity: 0.085;
  animation: btMistMove 14s linear infinite;
}
@keyframes btMistMove {
  0%   { transform: translate3d(-26px, 0, 0); }
  50%  { transform: translate3d(26px, -3px, 0); }
  100% { transform: translate3d(-26px, 0, 0); }
}

/* ── 四、战斗区那一片魔力环境光（第六、七节：不是放了一盏灯） ── */

.bt-field {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(46% 24% at 50% 62%, rgba(224, 189, 120, 0.1), transparent 70%),
    radial-gradient(68% 28% at 50% 44%, rgba(150, 178, 140, 0.07), transparent 72%),
    radial-gradient(28% 15% at 50% 30%, rgba(226, 176, 110, 0.06), transparent 70%);
  opacity: 0.55;
  animation: btBreath 5.4s ease-in-out infinite;
}
@keyframes btBreath {
  0%, 100% { opacity: 0.5; }
  50% { opacity: 1; }
}

/* 敌我各自那一片：暖红在敌方、淡金在我方，两条水平线由 JS 量（--en-y / --my-y） */
.bt-amb {
  position: absolute;
  left: 50%;
  width: 88%;
  height: 27%;
  translate: -50% -50%;
  pointer-events: none;
  transition: opacity 320ms ease;
}
.bt-amb.en {
  top: var(--en-y, 30%);
  background: radial-gradient(50% 50% at 50% 50%, rgba(160, 66, 44, 0.5), transparent 70%);
  opacity: 0.1;
}
.bt-amb.my {
  top: var(--my-y, 74%);
  background: radial-gradient(50% 50% at 50% 50%, rgba(214, 182, 110, 0.44), transparent 70%);
  opacity: 0.09;
}

/* ── 五、八颗魔法尘埃（第十一~十四节） ── */

.bt-mote {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--sz);
  height: var(--sz);
  border-radius: 50%;
  background: var(--tint);
  box-shadow: 15px 27px 0 -0.5px var(--tint);
  opacity: 0;
  animation: btMote var(--dur) linear infinite;
  animation-delay: var(--dl);
}
@keyframes btMote {
  0% { opacity: 0; translate: 0 0; }
  16% { opacity: 0.46; }
  62% { opacity: 0.2; }
  100% { opacity: 0; translate: var(--drift) var(--rise); }
}

/* ── 六、事件那一下：余光 / 波纹 / 绿孢子（第十九~二十六节） ── */

.bt-flash,
.bt-ripple,
.bt-spore {
  position: absolute;
  pointer-events: none;
  opacity: 0;
}
.bt-flash { inset: 0; }
/* 波纹挂在两只动物正中间那一格 */
.bt-ripple {
  left: 50%;
  top: calc((var(--en-y, 30%) + var(--my-y, 74%)) / 2);
  width: 26%;
  aspect-ratio: 1;
  translate: -50% -50%;
  border: 1px solid currentColor;
  border-radius: 50%;
  color: rgba(224, 189, 120, 0.3);
}
/* 恢复时从地上飘起来的几粒绿（第二十三节） */
.bt-spore {
  left: 40%;
  bottom: 7%;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  background: rgba(168, 224, 166, 0.62);
  box-shadow:
    26px 9px 0 -0.4px rgba(168, 224, 166, 0.5),
    -23px 15px 0 -0.5px rgba(206, 232, 176, 0.44),
    54px 21px 0 -0.6px rgba(168, 224, 166, 0.4);
}

/* ① 我方攻击命中：敌方那一片暖红亮一下，同时那圈波纹散开（第二十一节：不是白屏闪光） */
.bt-atmos.fx-hit .bt-flash {
  background: radial-gradient(52% 30% at 50% var(--en-y, 30%), rgba(196, 96, 62, 0.46), transparent 70%);
  animation: btFx 320ms ease-out;
}
.bt-atmos.fx-hit .bt-amb.en { opacity: 0.17; }
.bt-atmos.fx-hit .bt-ripple { color: rgba(214, 120, 86, 0.4); animation: btRipple 760ms ease-out; }
/* ② 暴击：同一句说法，足一点、久一点 */
.bt-atmos.fx-crit .bt-flash {
  background:
    radial-gradient(56% 32% at 50% var(--en-y, 30%), rgba(226, 150, 86, 0.5), transparent 68%),
    radial-gradient(80% 44% at 50% 52%, rgba(120, 52, 34, 0.3), transparent 74%);
  animation: btFx 420ms ease-out;
}
.bt-atmos.fx-crit .bt-amb.en { opacity: 0.2; }
.bt-atmos.fx-crit .bt-ripple { color: rgba(236, 176, 108, 0.5); animation: btRipple 880ms ease-out; }
/* ③ 敌方出手：暖色从敌方那侧往画面中心推一点（第二十五节，200~300ms） */
.bt-atmos.fx-enemy .bt-flash {
  background: radial-gradient(46% 26% at 50% var(--en-y, 30%), rgba(176, 78, 52, 0.44), transparent 72%);
  animation: btFxPush 300ms ease-out;
}
/* ④ 防御：我方那一片蓝金（第二十二节，500~800ms） */
.bt-atmos.fx-def .bt-flash {
  background: radial-gradient(50% 28% at 50% var(--my-y, 74%), rgba(150, 196, 226, 0.42), transparent 70%);
  animation: btFx 700ms ease-out;
}
.bt-atmos.fx-def .bt-amb.my { opacity: 0.18; }
.bt-atmos.fx-def .bt-ripple { color: rgba(178, 210, 236, 0.42); animation: btRipple 820ms ease-out; }
/* ⑤ 恢复：淡绿 + 那几粒往上飘的绿孢子（第二十三节） */
.bt-atmos.fx-heal .bt-flash {
  background: radial-gradient(50% 28% at 50% var(--my-y, 74%), rgba(146, 214, 150, 0.42), transparent 70%);
  animation: btFx 900ms ease-out;
}
.bt-atmos.fx-heal .bt-amb.my { opacity: 0.16; }
.bt-atmos.fx-heal .bt-spore { animation: btSpore 900ms ease-out; }
.bt-atmos.fx-heal .bt-ripple { color: rgba(168, 224, 166, 0.4); animation: btRipple 860ms ease-out; }
/* ⑥ 新回合：中央亮 5~8%，400~600ms，不许有大动画（第二十四节） */
.bt-atmos.fx-turn .bt-flash {
  background: radial-gradient(58% 34% at 50% 50%, rgba(224, 189, 120, 0.3), transparent 72%);
  animation: btFxTurn 520ms ease-out;
}

@keyframes btFx {
  0% { opacity: 0; }
  28% { opacity: 0.28; }
  100% { opacity: 0; }
}
@keyframes btFxPush {
  0% { opacity: 0; transform: scale(0.94); }
  30% { opacity: 0.3; }
  100% { opacity: 0; transform: scale(1.06); }
}
@keyframes btFxTurn {
  0% { opacity: 0; }
  40% { opacity: 0.2; }
  100% { opacity: 0; }
}
/* 第二十六节那两个数：scale 0.6→1.4、opacity 0.3→0，600~900ms */
@keyframes btRipple {
  0% { opacity: 0.3; scale: 0.6; }
  100% { opacity: 0; scale: 1.4; }
}
@keyframes btSpore {
  0% { opacity: 0; translate: 0 12px; }
  30% { opacity: 0.85; }
  100% { opacity: 0; translate: -7px -124px; }
}

/* ── 七、背景图片本身：只在这一屏改呼吸与微动（第十六、十七节） ── */

body:has(.battle) .stage-img {
  background-position: 50% 44%;
  animation: btBgBreathe 19s ease-in-out infinite alternate;
}
@keyframes btBgBreathe {
  from { transform: scale(1.02) translate3d(0, 0, 0); }
  to   { transform: scale(1.035) translate3d(-0.7%, -0.5%, 0); }
}

/* ── 八、窄屏（第三十五节：手机必须流畅，先减粒子再减层） ── */

@media (max-width: 560px) {
  /* 八颗留四颗：偶数位那四颗收掉（每颗自己还带一个分身，屏幕上仍有 8 点） */
  .bt-mote:nth-child(2n) { display: none; }
  .bt-mist.far { opacity: 0.04; }
  .bt-shade { opacity: 0.12; }
  .bt-field { animation-duration: 6.8s; }
}

/* ── 九、晕动症：背景全部钉死（第三十七节） ── */

@media (prefers-reduced-motion: reduce) {
  body:has(.battle) .stage-img,
  .bt-shade,
  .bt-mist.far,
  .bt-mist.mid,
  .bt-mist.low,
  .bt-field,
  .bt-mote,
  .bt-atmos.fx-hit .bt-flash,
  .bt-atmos.fx-crit .bt-flash,
  .bt-atmos.fx-enemy .bt-flash,
  .bt-atmos.fx-def .bt-flash,
  .bt-atmos.fx-heal .bt-flash,
  .bt-atmos.fx-turn .bt-flash,
  .bt-atmos .bt-ripple,
  .bt-atmos .bt-spore { animation: none; }
  /* 静态的那一份留着：暗角、遮罩、两侧压暗、两片环境光——那才是"站在森林里" */
  .bt-mote { opacity: 0.3; }
  .bt-field { opacity: 0.8; }
  .bt-flash,
  .bt-ripple,
  .bt-spore { opacity: 0; }
}
/* ============================================================
   46 · 龙斗场：那张每周节目单是活的 + Boss 技能那一下的光（ui/arena.js / ui/battle.js）
   ------------------------------------------------------------
   创作者 2026-10-03 那份周本规格第二条写得很明白：**沿用龙斗场内部同风格、同 CSS**。
   所以这一节不另起一套视觉，只做两件小事：
     一、第 34 节里那块【每周节目】的锁牌子换成同一块的"活"版本——还是那张钉在木框上的
         羊皮纸节目单（.arena-gate.playbill 那一身原封不动复用），只是牌面上多了
         本周对手那颗头（闪耀火龙头像，登记在 data/assets.js）、五维一行小字、
         【技能名】一行、出战那一只是谁，底下一颗真【挑战】钮；
         ⚠️ 它是 div：里面真有一颗 button，button 套 button 不合法。
         演完那一周整块压暗一档、钮收掉、状态行写「下周再来看」——锁相沿用 .locked 那一路的做法。
     二、战斗屏认 Boss：名字下面那行数字换成 Boss 自己的四项（它的数与词条、好感无关），
         再下面一行【技能名】比数字更小一档（规格第十四节：不许抢名字与血）；
         技能露头那一下，立绘那一圈糊一道**这只自己的颜色**（--cast 由 JS 挂，
         数在 data/bosses.js 的 ability.cue）+ 一行飘字（规格第十五、十六节：300~800ms、不挡人）。
   ⚠️ 一张图都没加：头像用的是现成那批闪耀火龙，其余全是渐变与伪元素。
   ⚠️ 只改长相与那一下反馈。Boss 的数值、周抽、奖励、战斗规则全在
      data/bosses.js / engine/arena.js / engine/battle.js，这一节一个字都没参与。
   本节写在第 34 / 44 节之后，同名规则以这里为准。
   ============================================================ */

/* ── 一、节目单：同一块羊皮纸，牌面上有内容了 ── */

.arena-gate.playbill.live { cursor: default; }
.arena-gate.playbill.live:hover { transform: none; }
.arena-gate.playbill .ag-sub { margin-bottom: 2px; }

/* 对手那一格：左边一颗头，右边三行字 */
.ab-bill {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  margin-top: var(--sp-2);
  padding-top: var(--sp-3);
  border-top: 1px solid rgba(88, 62, 30, 0.32);
  text-align: left;
}
/* 那颗头：切个八角当镶框，边上压一道旧纸色（和卡牌那颗费用宝石同一套语言） */
.ab-face {
  flex: none;
  width: clamp(46px, 13vw, 58px);
  height: clamp(46px, 13vw, 58px);
  object-fit: cover;
  object-position: 22% 14%;
  clip-path: polygon(28% 0, 72% 0, 100% 28%, 100% 72%, 72% 100%, 28% 100%, 0 72%, 0 28%);
  background: #2a1d12;
  /* clip-path 会把 box-shadow 整条裁掉（这工程踩过）：外面这道影走 drop-shadow */
  filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.55));
}
.ab-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ab-name {
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: 0.1em;
  color: #2e2010;
  text-shadow: 0 1px 0 rgba(255, 246, 220, 0.4);
}
.ab-stats {
  font-size: 10px;
  line-height: 1.6;
  letter-spacing: 0.02em;
  color: rgba(64, 44, 22, 0.86);
}
.ab-abil {
  font-size: 10px;
  letter-spacing: 0.08em;
  color: #7a4c14;
}
/* 出战那一只：一行读数，不是解释（工程规矩：按钮旁边不挂小字说明） */
.ab-mine {
  width: 100%;
  margin-top: var(--sp-2);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: rgba(64, 46, 24, 0.72);
}
/* 演完了那一周：整块压暗一档（和 .locked 同一句说法），钮已经收掉了 */
.arena-gate.playbill.cleared { filter: saturate(0.8) brightness(0.92); }
.arena-gate.playbill.cleared .ag-state { letter-spacing: 0.24em; }

/* 【挑战】：压在节目单底下一颗小暗色钮，别比牌名抢眼 */
.btn.arena-go {
  flex: none;
  width: auto;
  min-width: 0;
  margin-top: var(--sp-3);
  padding: 6px 22px 7px;
  font-size: var(--fs-s);
  letter-spacing: 0.2em;
  color: #f2ddac;
  background: linear-gradient(180deg, #4a3220, #2a1a10);
  border: 1px solid rgba(196, 154, 85, 0.5);
  border-radius: 3px;
  box-shadow: 0 2px 0 rgba(20, 12, 6, 0.6), inset 0 1px 0 rgba(255, 236, 198, 0.16);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
}
.btn.arena-go:hover {
  color: #fff2d2;
  border-color: rgba(224, 189, 120, 0.8);
  transform: translateY(-1px);
}
.btn.arena-go:active { transform: translateY(1px); }

/* ── 二、战斗屏那两行：Boss 自己的四项 + 【技能名】 ── */

/* 技能名比上面那行数字还小一档、颜色压住（规格第十四节：不许抢名字与血） */
.bt-abil {
  margin-top: 1px;
  font-size: 10px;
  letter-spacing: 0.12em;
  color: rgba(226, 189, 120, 0.72);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
}

/* 技能露头那一下：立绘那一圈糊一道这只自己的颜色（--cast 由 ui/battle.js 挂在舞台上） */
.bp-stage.cast .bp-glow {
  opacity: 1;
  background:
    radial-gradient(circle at 50% 50%, transparent 38%, var(--cast, rgba(224, 189, 120, 0.6)) 62%, transparent 78%);
  animation: btCast 620ms ease-out;
}
@keyframes btCast {
  0% { opacity: 0; scale: 0.86; }
  30% { opacity: 1; }
  100% { opacity: 0; scale: 1.12; }
}
/* 那一行飘字：字比伤害数字小、不粗，走这只技能的颜色 */
.bt-num.abil {
  font-size: var(--fs-xs);
  font-weight: 400;
  letter-spacing: 0.16em;
  color: #f0dcae;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.95), 0 0 10px rgba(224, 189, 120, 0.4);
}

/* ── 三、窄屏 ── */

@media (max-width: 560px) {
  .ab-bill { gap: var(--sp-2); }
  .ab-stats { font-size: 9px; }
  .btn.arena-go { padding: 6px 18px 7px; }
}

/* ── 四、"别晕"：技能那一下与节目单的动效降级 ── */

@media (prefers-reduced-motion: reduce) {
  .bp-stage.cast .bp-glow { animation: none; opacity: 0.7; }
  .btn.arena-go:hover,
  .btn.arena-go:active { transform: none; }
}
/* ============================================================
   47 · 丽痕书店 / 钓具店 / 冰淇淋店：三套各自的做法（ui/screens-alley.js）
   ------------------------------------------------------------
   创作者 2026-10-03 要的是「照药房那一屏的风格，把这三家也优化一遍」。
   药房那一屏立住的是**一条路子**，不是一张脸：整屏压一层自己的空气 → 店名改成招牌
   → 规矩写在店里的一件实物上 → 货格改成"这店里真会有的那件东西" → 极轻的动。
   所以这三家**各长各的**（同一个框换颜色是要挨骂的那种）：
     甲 · 丽痕书店 = **一整面立在架板上的书**。高窗的光从左上落下来；店名底下那根杠
         改成一小排立着的书脊；规矩写在一张从书里滑出来的**索书卡**上（左边那格戳号、
         右下角一枚紫墨圆戳）；每一栏是一块架板，板前一道黄铜挡条加两颗铜螺丝；
         每一格是一本布面精装——左边书脊、右边那摞纸口、面上两道压印金线，
         六种布面轮着来；【买走】是吊在一根线上的一张借书卡，hover 轻轻晃。
     乙 · 钓具店 = **一只敞口的湿木箱**。水光从右边来、脚下发潮；店名底下垂一根钓线；
         规矩写在柜台边那块**石板**上（木框、粉笔字、板沿擦过的一道）；箱体是横木板 +
         板缝 + 左上罩下来那张网 + 四角麻绳十字，箱沿上四颗浮子慢慢起伏；
         每一格是一捆**上了蜡的帆布包**（帆布纹、中间一道麻绳扎带、右上角一个铜扣眼）；
         【买下】是一块钻了孔的木牌价签。
     丙 · 冰淇淋店 = **一只甜品柜**（创作者点名的暖黄 / 米白 / 浅粉，要泡泡、温馨可爱）。
         光从顶上打下来、脚下泛一点粉；店名底下那根杠改成一把**粉白条纹小遮阳篷**（下沿波浪）；
         规矩写在柜台那张奶油小圆牌上（虚线粉边、右下角一颗樱桃）；柜体米白、圆肩、
         前面一道弧形玻璃慢慢扫光；每一格是柜里的一球——碟子、几点糖针、
         【来一个】是一颗覆盆子色的圆章，顶上一点樱桃；柜里往上飘三串泡泡（各串自己一个速度）。
   ⚠️ 三家**只改长相**。价钱、你有几件、今天还剩几个、步进器、买走、一天一个、读过了、
      钱不够、扣钱落盘，全走 shopgrid.js 与 engine 原来那条口，一个数都没动。
   ⚠️ 一张新图都没加：书脊、纸口、麻绳、网、浮子、遮阳篷、碟子、糖针、泡泡全是渐变 /
      伪元素 / box-shadow 分身。作用域各认自己那一个抬头类（.scene.bk / .tk / .ic）
      与一个格子皮（.mkt-cell.shelf / .crate / .scoop）——药房（第 40 节）、
      密林商人、海格小摊、翻倒巷那一套 .mkt-* 一律不受影响。
      ⚠️ goodsCell 那个参数已从布尔 glass 换成字符串 skin（'glass'/'shelf'/'crate'/'scoop'），
         药房那两处传的是 skin:'glass'，样子与第 40 节完全一致。
   ⚠️ 动效一律 transform / opacity（遮罩只裁形状，不参与动画）。
   本节写在第 40 节之后，同名规则以这里为准。
   ============================================================ */

/* ── 四家店共用的两个结构类（各自穿皮） ── */

.shop-sub {
  margin: 3px 0 var(--sp-2);
  font-family: var(--font-hand);
  font-size: 11px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
}
/* 柜台上那块"店里的规矩"：四家店都是这一件东西，各长各的样 */
.shop-note {
  position: relative;
  width: fit-content;
  max-width: 100%;
  margin: 0 0 var(--sp-4) auto;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  line-height: 1.75;
}

/* ============================================================
   甲 · 丽痕书店
   ============================================================ */

/* 一、这一屏的空气：高窗的光从左上落下来，脚下暗 */
.view:has(.scene.bk)::after {
  content: '';
  position: absolute;
  inset: -12px -20px;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(44% 28% at 22% 12%, rgba(238, 216, 170, 0.15), transparent 66%),
    radial-gradient(64% 40% at 78% 92%, rgba(20, 13, 8, 0.5), transparent 72%),
    linear-gradient(180deg,
      rgba(24, 17, 11, 0.6) 0%,
      rgba(30, 22, 15, 0.26) 30%,
      rgba(26, 18, 12, 0.48) 72%,
      rgba(15, 10, 7, 0.8) 100%);
}

/* 二、招牌：店名底下那根杠 = 一小排立着的书脊 */
.scene.bk .name {
  color: #f7eac9;
  text-shadow: 0 2px 0 rgba(20, 13, 8, 0.9), 0 0 16px rgba(214, 178, 110, 0.2);
}
.scene.bk .name::after {
  width: min(74%, 272px);
  height: 10px;
  margin-top: var(--sp-2);
  background: repeating-linear-gradient(90deg,
    rgba(146, 58, 47, 0.95) 0 7px, transparent 7px 9px,
    rgba(74, 104, 76, 0.95) 9px 15px, transparent 15px 17px,
    rgba(58, 80, 112, 0.95) 17px 25px, transparent 25px 27px,
    rgba(186, 152, 88, 0.95) 27px 33px, transparent 33px 35px);
  border-bottom: 1px solid rgba(18, 12, 7, 0.8);
  box-shadow: 0 2px 0 rgba(18, 12, 7, 0.6);
  opacity: 1;
}
.scene.bk .shop-sub { color: rgba(228, 202, 152, 0.8); }

/* 三、索书卡（规矩写在一张从书里滑出来的卡上，不是界面在报状态） */
.bk-note {
  padding: 7px 26px 8px 30px;
  color: #3e2d18;
  text-shadow: none;
  background: linear-gradient(180deg, #f5ebd0, #e2d1a9);
  border: 1px solid rgba(112, 86, 44, 0.4);
  border-radius: 1px;
  box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.55);
  rotate: -0.5deg;
}
/* 左边那一格写戳号 */
.bk-note::before {
  content: '';
  position: absolute;
  left: 7px;
  top: 6px;
  bottom: 6px;
  width: 15px;
  border-right: 1px solid rgba(112, 86, 44, 0.35);
  background: repeating-linear-gradient(180deg, transparent 0 6px, rgba(112, 86, 44, 0.2) 6px 7px);
}
/* 右下角一枚紫墨圆戳 */
.bk-note::after {
  content: '';
  position: absolute;
  right: 8px;
  bottom: 7px;
  width: 16px;
  height: 16px;
  border: 1px solid rgba(96, 58, 110, 0.5);
  border-radius: 50%;
  box-shadow: inset 0 0 0 2px rgba(96, 58, 110, 0.16);
  opacity: 0.5;
}

/* 四、架板：书立在板上，板前一道黄铜挡条 + 两颗螺丝 */
.bk-shelf {
  position: relative;
  margin-bottom: var(--sp-4);
  padding: var(--sp-3) var(--sp-2) 16px;
  background:
    repeating-linear-gradient(90deg, rgba(255, 238, 206, 0.035) 0 1px, transparent 1px 9px),
    linear-gradient(180deg, rgba(40, 26, 16, 0.74), rgba(26, 17, 10, 0.64));
  border: 1px solid rgba(16, 11, 6, 0.85);
  border-radius: 3px;
  box-shadow: inset 0 9px 18px rgba(0, 0, 0, 0.52), inset 0 -1px 0 rgba(214, 178, 110, 0.12);
}
.bk-shelf::after {
  content: '';
  position: absolute;
  left: 6px;
  right: 6px;
  bottom: 6px;
  height: 4px;
  border-radius: 2px;
  background: linear-gradient(180deg, rgba(228, 188, 112, 0.86), rgba(120, 88, 38, 0.92));
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 240, 200, 0.5);
}
/* 两颗铜螺丝（一条横带上两个圆点） */
.bk-shelf::before {
  content: '';
  position: absolute;
  left: 13px;
  right: 13px;
  bottom: 5px;
  height: 6px;
  background:
    radial-gradient(circle at 3px 3px, #f0d79c, #7a5a24 62%, transparent 66%) left center / 6px 6px no-repeat,
    radial-gradient(circle at 3px 3px, #f0d79c, #7a5a24 62%, transparent 66%) right center / 6px 6px no-repeat;
}

/* 五、一本立在架上的书 */
.mkt-cell.shelf {
  position: relative;
  color: #f3e6c8;
  border: 1px solid rgba(14, 9, 5, 0.72);
  border-radius: 2px 3px 3px 2px;
  background:
    /* 布面上两道压印金线 */
    linear-gradient(180deg, transparent 0 12px, rgba(228, 188, 112, 0.32) 12px 13px, transparent 13px 18px),
    linear-gradient(180deg, transparent calc(100% - 16px), rgba(228, 188, 112, 0.26) calc(100% - 16px) calc(100% - 15px), transparent calc(100% - 15px)),
    radial-gradient(72% 46% at 38% 18%, rgba(255, 240, 210, 0.12), transparent 66%),
    linear-gradient(180deg, var(--bk1, #5c2f28), var(--bk2, #38191a));
  box-shadow:
    0 3px 0 rgba(12, 8, 5, 0.7),
    0 8px 16px rgba(0, 0, 0, 0.5),
    inset 1px 0 0 rgba(255, 236, 200, 0.14);
}
/* 左边那道书脊（内侧压暗 + 一道金线） */
.mkt-cell.shelf::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 8px;
  background: linear-gradient(90deg, rgba(0, 0, 0, 0.5), rgba(255, 240, 205, 0.1) 62%, transparent);
  border-right: 1px solid rgba(228, 188, 112, 0.26);
}
/* 右边那摞纸口（一页一页） */
.mkt-cell.shelf::after {
  content: '';
  position: absolute;
  right: 0;
  top: 4px;
  bottom: 4px;
  width: 6px;
  border-radius: 0 2px 2px 0;
  background: repeating-linear-gradient(180deg, #efe4c6 0 1.5px, #c9bb98 1.5px 3px);
  opacity: 0.86;
}
/* 六种布面轮着来（同一套做法，只是布的颜色不同） */
.mkt-grid > .mkt-cell.shelf:nth-child(6n+1) { --bk1: #5d2f28; --bk2: #371a18; }
.mkt-grid > .mkt-cell.shelf:nth-child(6n+2) { --bk1: #2f4634; --bk2: #1a2a1f; }
.mkt-grid > .mkt-cell.shelf:nth-child(6n+3) { --bk1: #2d3a52; --bk2: #1a2130; }
.mkt-grid > .mkt-cell.shelf:nth-child(6n+4) { --bk1: #6a5326; --bk2: #3a2c13; }
.mkt-grid > .mkt-cell.shelf:nth-child(6n+5) { --bk1: #4a2c46; --bk2: #291727; }
.mkt-grid > .mkt-cell.shelf:nth-child(6n+6) { --bk1: #4c3a2c; --bk2: #2a1f17; }
.mkt-cell.shelf .mkt-name { color: #f8ead0; text-shadow: 0 1px 0 rgba(0, 0, 0, 0.72); }
.mkt-cell.shelf .mkt-price { color: #ecc87e; }
.mkt-cell.shelf .mkt-have,
.mkt-cell.shelf .mkt-note { color: rgba(238, 222, 186, 0.72); }
/* 【买走】= 吊在一根线上的一张借书卡（hover 轻轻晃两下） */
.mkt-cell.shelf .mkt-go {
  position: relative;
  margin-top: 7px;
  color: #4a3517;
  text-shadow: none;
  background: linear-gradient(180deg, #f4e8ca, #dcc79c);
  border: 1px solid rgba(112, 86, 44, 0.5);
  border-radius: 1px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.6);
}
.mkt-cell.shelf .mkt-go::before {
  content: '';
  position: absolute;
  left: 50%;
  top: -8px;
  width: 1px;
  height: 8px;
  background: rgba(230, 210, 166, 0.55);
}
.mkt-cell.shelf .mkt-go:hover:not(:disabled) { transform: none; filter: none; }
.mkt-cell.shelf:hover .mkt-go:not(:disabled) { animation: bkTag 1.7s ease-in-out infinite; }
@keyframes bkTag {
  0%, 100% { rotate: -1.4deg; }
  50% { rotate: 1.4deg; }
}
.mkt-cell.shelf .mkt-go:disabled {
  color: rgba(74, 53, 23, 0.5);
  background: linear-gradient(180deg, #e8ddc6, #d3c5a4);
}

/* ============================================================
   乙 · 钓具店
   ============================================================ */

/* 一、这一屏的空气：水光从右边来，脚下发潮 */
.view:has(.scene.tk)::after {
  content: '';
  position: absolute;
  inset: -12px -20px;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(50% 30% at 76% 16%, rgba(178, 216, 222, 0.13), transparent 68%),
    radial-gradient(70% 40% at 50% 98%, rgba(10, 20, 22, 0.55), transparent 72%),
    linear-gradient(180deg,
      rgba(12, 20, 22, 0.62) 0%,
      rgba(14, 22, 24, 0.28) 32%,
      rgba(10, 16, 17, 0.54) 74%,
      rgba(6, 11, 12, 0.82) 100%);
}
/* 二、招牌：店名底下垂一根钓线 */
.scene.tk .name {
  color: #e9f1ea;
  text-shadow: 0 2px 0 rgba(8, 16, 16, 0.9), 0 0 14px rgba(150, 200, 205, 0.16);
}
.scene.tk .name::after {
  width: 1px;
  height: 24px;
  margin-top: var(--sp-2);
  background: linear-gradient(180deg, rgba(216, 234, 230, 0.55), rgba(216, 234, 230, 0.12));
  box-shadow: 0 3px 0 0 rgba(216, 234, 230, 0.4);
  opacity: 1;
}
.scene.tk .shop-sub { color: rgba(198, 218, 222, 0.76); }

/* 三、柜台边那块石板：木框 + 粉笔字 + 擦过的一道 */
.tk-note {
  padding: 8px 14px 9px;
  color: rgba(234, 245, 238, 0.92);
  text-shadow: 0 0 7px rgba(190, 220, 215, 0.25);
  background:
    radial-gradient(60% 40% at 18% 12%, rgba(255, 255, 255, 0.05), transparent 60%),
    linear-gradient(160deg, #24382f, #16241f);
  border: 5px solid #4a3624;
  border-radius: 2px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.6), inset 0 0 0 1px rgba(0, 0, 0, 0.5);
  rotate: 0.4deg;
}
.tk-note::after {
  content: '';
  position: absolute;
  left: 14px;
  right: 42%;
  bottom: 4px;
  height: 2px;
  border-radius: 2px;
  background: rgba(236, 246, 240, 0.14);
}

/* 四、那只敞口的湿木箱 */
.tk-crate {
  position: relative;
  padding: clamp(12px, 2.2vh, 18px) clamp(9px, 1.9vw, 14px) 20px;
  background:
    /* 横木板与板缝 */
    repeating-linear-gradient(180deg, rgba(0, 0, 0, 0.36) 0 2px, transparent 2px 27px),
    repeating-linear-gradient(90deg, rgba(255, 240, 210, 0.03) 0 1px, transparent 1px 13px),
    linear-gradient(180deg, #3b3023, #241b13);
  border: 1px solid rgba(12, 10, 7, 0.9);
  border-radius: 3px;
  box-shadow: inset 0 7px 15px rgba(0, 0, 0, 0.55), 0 6px 16px rgba(0, 0, 0, 0.5);
}
/* 四角那两团麻绳十字（左上 / 右下） */
.tk-crate::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 3px;
  pointer-events: none;
  background:
    repeating-linear-gradient(45deg, rgba(200, 172, 116, 0.5) 0 2px, transparent 2px 5px) left top / 46px 46px no-repeat,
    repeating-linear-gradient(-45deg, rgba(200, 172, 116, 0.4) 0 2px, transparent 2px 5px) right bottom / 46px 46px no-repeat;
  opacity: 0.5;
}
/* 罩在左上角那张网（边缘用遮罩化开） */
.tk-net {
  position: absolute;
  left: -4px;
  top: -12px;
  width: 54%;
  height: 68px;
  pointer-events: none;
  rotate: -3deg;
  background:
    repeating-linear-gradient(58deg, rgba(216, 228, 222, 0.17) 0 1px, transparent 1px 7px),
    repeating-linear-gradient(-58deg, rgba(216, 228, 222, 0.14) 0 1px, transparent 1px 7px);
  -webkit-mask-image: radial-gradient(72% 100% at 22% 0, #000 42%, transparent 80%);
  mask-image: radial-gradient(72% 100% at 22% 0, #000 42%, transparent 80%);
}
/* 箱沿上四颗浮子：一起在水线上慢慢起伏（各自大小与颜色不同） */
.tk-floats {
  position: absolute;
  left: 9%;
  top: -8px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #f4e4c4, #b8462f 62%, #6d2418);
  box-shadow:
    88px 3px 0 -1px #d9cbb0,
    182px -2px 0 0 #b8462f,
    274px 4px 0 -1.5px #e8d6b4;
  animation: tkBob 3.8s ease-in-out infinite alternate;
}
@keyframes tkBob {
  from { transform: translateY(-2px); }
  to { transform: translateY(3px); }
}

/* 五、一捆上了蜡的帆布包 */
.mkt-cell.crate {
  position: relative;
  color: #eaf0e7;
  border: 1px solid rgba(9, 15, 13, 0.82);
  border-radius: 2px;
  background:
    repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.035) 0 1px, transparent 1px 5px),
    repeating-linear-gradient(0deg, rgba(0, 0, 0, 0.05) 0 1px, transparent 1px 5px),
    radial-gradient(70% 48% at 30% 16%, rgba(228, 242, 238, 0.1), transparent 66%),
    linear-gradient(180deg, #3d4b45, #222d2a);
  box-shadow:
    0 3px 0 rgba(8, 12, 10, 0.7),
    0 8px 16px rgba(0, 0, 0, 0.5),
    inset 0 1px 0 rgba(228, 242, 238, 0.12);
}
/* 两头各一道麻绳扎带（从帆布边上探出来，不横穿字——第一版横穿过去压住了价钱那行） */
.mkt-cell.crate::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 42%;
  height: 9px;
  background:
    linear-gradient(180deg, rgba(212, 184, 126, 0.92), rgba(148, 122, 78, 0.95)) left center / 16px 9px no-repeat,
    linear-gradient(180deg, rgba(212, 184, 126, 0.92), rgba(148, 122, 78, 0.95)) right center / 16px 9px no-repeat;
  opacity: 0.9;
}
/* 右上角一个铜扣眼 */
.mkt-cell.crate::after {
  content: '';
  position: absolute;
  right: 7px;
  top: 7px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #f2deaa, #8a6a2c 66%, #3d2c12);
  box-shadow: inset 0 0 0 2px rgba(18, 12, 7, 0.55);
}
.mkt-cell.crate .mkt-name { color: #f0f5ec; text-shadow: 0 1px 0 rgba(0, 0, 0, 0.75); }
.mkt-cell.crate .mkt-price { color: #dcc88c; }
.mkt-cell.crate .mkt-have,
.mkt-cell.crate .mkt-note { color: rgba(226, 236, 228, 0.66); }
/* 木牌价签 */
.mkt-cell.crate .mkt-go {
  color: #f2e8d2;
  background: linear-gradient(180deg, #5e4b2f, #3b2e1b);
  border-color: rgba(9, 14, 11, 0.85);
  border-radius: 2px;
}

/* ============================================================
   丙 · 冰淇淋店
   ============================================================ */

/* 一、这一屏的空气：顶上打下来的暖光，脚下泛一点粉 */
.view:has(.scene.ic)::after {
  content: '';
  position: absolute;
  inset: -12px -20px;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(54% 30% at 50% 12%, rgba(255, 238, 194, 0.18), transparent 68%),
    radial-gradient(70% 38% at 50% 100%, rgba(244, 178, 190, 0.15), transparent 72%),
    linear-gradient(180deg,
      rgba(42, 27, 20, 0.5) 0%,
      rgba(48, 31, 23, 0.2) 32%,
      rgba(40, 25, 20, 0.42) 74%,
      rgba(30, 18, 14, 0.7) 100%);
}
/* 二、招牌：店名底下那根杠 = 一把粉白条纹小遮阳篷（下沿波浪） */
.scene.ic .name {
  color: #fff6e6;
  text-shadow: 0 2px 0 rgba(64, 32, 26, 0.6), 0 0 18px rgba(244, 176, 186, 0.32);
}
.scene.ic .name::after {
  width: min(76%, 280px);
  height: 13px;
  margin-top: var(--sp-2);
  background: repeating-linear-gradient(90deg,
    rgba(242, 160, 172, 0.95) 0 13px, rgba(255, 250, 238, 0.95) 13px 26px);
  -webkit-mask-image: radial-gradient(circle at 6.5px 100%, transparent 0 6px, #000 6.5px);
  -webkit-mask-size: 13px 100%;
  -webkit-mask-repeat: repeat-x;
  mask-image: radial-gradient(circle at 6.5px 100%, transparent 0 6px, #000 6.5px);
  mask-size: 13px 100%;
  mask-repeat: repeat-x;
  opacity: 1;
}
.scene.ic .shop-sub { color: rgba(248, 190, 198, 0.92); text-shadow: 0 1px 0 rgba(80, 40, 34, 0.5); }

/* 三、柜台上那张奶油小圆牌：虚线粉边 + 右下角一颗樱桃 */
.ic-note {
  padding: 7px 26px 8px 14px;
  color: #5c3b31;
  text-shadow: none;
  background: linear-gradient(180deg, #fffbf2, #fdeee5);
  border: 1px dashed rgba(216, 142, 152, 0.75);
  border-radius: 14px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.9);
  rotate: -0.7deg;
}
.ic-note::after {
  content: '';
  position: absolute;
  right: 10px;
  bottom: 9px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #ffd6dc, #d2455c 70%);
  box-shadow: 0 -5px 0 -3px rgba(122, 162, 92, 0.85);
}

/* 四、那只甜品柜 */
.ic-case {
  position: relative;
  padding: clamp(18px, 3.2vh, 26px) clamp(9px, 2vw, 15px) 18px;
  background: linear-gradient(180deg, #fffaf0, #f8ecdd 60%, #efd9c6);
  border: 1px solid rgba(126, 78, 54, 0.5);
  border-radius: 16px 16px 6px 6px;
  box-shadow:
    0 8px 20px rgba(0, 0, 0, 0.42),
    inset 0 -8px 16px rgba(196, 142, 110, 0.24),
    inset 0 1px 0 rgba(255, 255, 255, 0.8);
}
/* 柜顶那条遮阳篷（下沿也是波浪） */
.ic-awning {
  position: absolute;
  left: -2px;
  right: -2px;
  top: -15px;
  height: 21px;
  border-radius: 9px 9px 0 0;
  background: repeating-linear-gradient(90deg,
    rgba(242, 160, 172, 0.96) 0 15px, rgba(255, 250, 238, 0.96) 15px 30px);
  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.35);
}
.ic-awning::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -7px;
  height: 8px;
  background: inherit;
  -webkit-mask-image: radial-gradient(circle at 7.5px 0, transparent 0 6.5px, #000 7px);
  -webkit-mask-size: 15px 100%;
  -webkit-mask-repeat: repeat-x;
  mask-image: radial-gradient(circle at 7.5px 0, transparent 0 6.5px, #000 7px);
  mask-size: 15px 100%;
  mask-repeat: repeat-x;
}
/* 前面那块弧形玻璃：一道慢慢扫过去的高光 */
.ic-glass {
  position: absolute;
  inset: 8px 10px 26px;
  border-radius: 12px;
  pointer-events: none;
  overflow: hidden;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5);
}
.ic-glass::before {
  content: '';
  position: absolute;
  top: -20%;
  bottom: -20%;
  left: -40%;
  width: 26%;
  rotate: 16deg;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent);
  animation: icWipe 11s ease-in-out infinite;
}
@keyframes icWipe {
  0%, 62% { translate: 0 0; opacity: 0; }
  70% { opacity: 0.9; }
  100% { translate: 520% 0; opacity: 0; }
}
/* 三串往上飘的泡泡（每串自己一个速度、自己一个起飘早晚） */
.ic-bubbles,
.ic-bubbles::before,
.ic-bubbles::after {
  position: absolute;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, rgba(255, 255, 255, 0.95), rgba(255, 224, 232, 0.28) 58%, rgba(244, 176, 186, 0.4));
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5);
  pointer-events: none;
  animation: icRise 7.4s linear infinite;
}
.ic-bubbles { left: 14%; bottom: 12%; }
.ic-bubbles::before {
  content: '';
  left: 74px;
  bottom: -18px;
  width: 6px;
  height: 6px;
  animation: icRise 9.2s linear infinite;
  animation-delay: -3.4s;
}
.ic-bubbles::after {
  content: '';
  left: 152px;
  bottom: 6px;
  width: 11px;
  height: 11px;
  animation: icRise 6.2s linear infinite;
  animation-delay: -1.6s;
}
@keyframes icRise {
  0% { opacity: 0; translate: 0 0; }
  16% { opacity: 0.85; }
  74% { opacity: 0.4; }
  100% { opacity: 0; translate: 12px -140px; }
}

/* 五、柜里那一球 */
.mkt-cell.scoop {
  position: relative;
  color: #5a3a30;
  border: 1px solid rgba(158, 102, 78, 0.42);
  border-radius: 13px 13px 8px 8px;
  background:
    /* 几点糖针 */
    radial-gradient(5px 2px at 20% 14%, rgba(242, 158, 172, 0.85), transparent 62%),
    radial-gradient(2px 5px at 82% 22%, rgba(248, 214, 138, 0.9), transparent 62%),
    radial-gradient(5px 2px at 70% 84%, rgba(186, 214, 176, 0.8), transparent 62%),
    radial-gradient(2px 5px at 26% 76%, rgba(242, 158, 172, 0.7), transparent 62%),
    radial-gradient(62% 42% at 50% 20%, rgba(255, 244, 224, 0.95), transparent 64%),
    linear-gradient(180deg, #fffcf4, #fdeee6 56%, #f8ded6);
  box-shadow:
    0 4px 0 rgba(186, 128, 100, 0.32),
    0 10px 18px rgba(0, 0, 0, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.92);
  transition: transform 160ms ease, box-shadow 160ms ease;
}
/* 球底下那个小碟（画在字后面） */
.mkt-cell.scoop::before {
  content: '';
  position: absolute;
  z-index: 0;
  left: 14%;
  right: 14%;
  top: 40px;
  height: 30px;
  border-radius: 50%;
  background: radial-gradient(50% 50% at 50% 50%, rgba(255, 255, 255, 0.8), rgba(246, 214, 206, 0.5) 58%, transparent 74%);
}
.mkt-cell.scoop > * { position: relative; z-index: 1; }
.mkt-cell.scoop:hover { transform: translateY(-2px); }
.mkt-cell.scoop .mkt-price { color: #b8566a; }
.mkt-cell.scoop .mkt-have,
.mkt-cell.scoop .mkt-note { color: rgba(122, 84, 70, 0.78); }
/* 【来一个】= 一颗覆盆子色圆章，顶上一小点樱桃 */
.mkt-cell.scoop .mkt-go {
  position: relative;
  padding: 4px 16px 5px;
  color: #5c2c33;
  text-shadow: none;
  background: linear-gradient(180deg, #ffdae0, #f7b9c3);
  border: 1px solid rgba(184, 112, 122, 0.55);
  border-radius: 999px;
  box-shadow: 0 2px 0 rgba(186, 122, 132, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.8);
}
.mkt-cell.scoop .mkt-go::before {
  content: '';
  position: absolute;
  right: 8px;
  top: -3px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #ffd6dc, #cf3f56 70%);
}
.mkt-cell.scoop .mkt-go:disabled {
  color: rgba(92, 44, 51, 0.45);
  background: linear-gradient(180deg, #f4ece6, #e6dcd4);
  box-shadow: 0 1px 0 rgba(186, 128, 100, 0.25);
}
.mkt-cell.scoop.short { opacity: 0.8; }

/* ── 窄屏：三家各自收一点（粒子先收，装饰其次，字不许挤） ── */

@media (max-width: 560px) {
  .bk-shelf { padding: var(--sp-2) 6px 14px; }
  .mkt-cell.shelf::after { width: 5px; }
  .tk-net { width: 62%; height: 52px; }
  .tk-floats { left: 6%; box-shadow: 62px 3px 0 -1px #d9cbb0, 132px -2px 0 0 #b8462f; }
  .ic-case { padding-top: clamp(16px, 3vh, 22px); }
  /* 手机上泡泡留两串 */
  .ic-bubbles::after { display: none; }
  .mkt-cell.scoop::before { top: 36px; height: 26px; }
}

/* ── "别晕"：三家的动效全钉死，静态那一层留着 ── */

@media (prefers-reduced-motion: reduce) {
  .mkt-cell.shelf:hover .mkt-go:not(:disabled),
  .tk-floats,
  .ic-glass::before,
  .ic-bubbles,
  .ic-bubbles::before,
  .ic-bubbles::after { animation: none; }
  .ic-glass::before { opacity: 0.25; }
  .ic-bubbles,
  .ic-bubbles::before,
  .ic-bubbles::after { opacity: 0.5; }
  .mkt-cell.scoop:hover { transform: none; }
}

/* ============================================================
   48. 喂食纸顶上那一排：动物食物（.ft-grid / .ft-cell）
   ------------------------------------------------------------
   背包那种方格的皮（同一张羊皮纸、同一圈描边、同一道下唇阴影），
   只是**不挂图标**：从上到下 名字 / 给什么 / 份数 / 一颗【喂养】，四行全部居中。
   手上一份都没有也照样摆着一格（写着 ×0、那颗钮是灰的）——
   这一排的位置不该随"今天做没做"而跳（创作者 2026-10-03 要的就是这个样子）。
   格子固定两列：眼下正好两样，第三样将来加进来自己换行。
   ============================================================ */
.ft-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-2);
  margin-top: var(--sp-1);
}

.ft-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 2px;
  padding: var(--sp-2) 6px var(--sp-2);
  font-family: var(--font-body);
  color: var(--ink);
  background:
    radial-gradient(70% 50% at 50% 4%, rgba(255, 255, 255, 0.5), transparent 65%),
    linear-gradient(180deg, #fbf4e4, #f0e3c7);
  border: 1px solid rgba(64, 48, 31, 0.34);
  border-bottom-color: rgba(64, 48, 31, 0.5);
  border-radius: var(--r);
  box-shadow: 0 2px 0 rgba(120, 96, 62, 0.42), 0 6px 14px rgba(0, 0, 0, 0.2);
}

/* 空着那一格：整块压暗一档（那颗钮自己已经是灰的，这里只把纸也褪一点） */
.ft-cell.empty {
  filter: saturate(0.72);
  box-shadow: 0 2px 0 rgba(120, 96, 62, 0.24), 0 4px 10px rgba(0, 0, 0, 0.14);
}

.ft-name {
  font-weight: 700;
  font-size: clamp(12px, 3.4vw, 14px);
  line-height: 1.25;
  letter-spacing: 0.02em;
  text-align: center;
  overflow-wrap: break-word;
}

.ft-use {
  font-size: var(--fs-xs);
  color: var(--ink-soft);
  text-align: center;
  line-height: 1.3;
}

/* 份数走手写字那一味（和背包格子里那个 ×3 一味） */
.ft-qty {
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  color: var(--ink-soft);
}

.ft-cell > .btn {
  width: 100%;
  margin-top: 3px;
  padding: 5px 8px 6px;
  font-size: var(--fs-s);
  letter-spacing: 0.06em;
  text-align: center;
  box-shadow: 0 2px 0 rgba(50, 37, 24, 0.34), 0 5px 12px rgba(0, 0, 0, 0.3);
}

/* ============================================================
   49. 【通讯】那张纸：魔法学院 · 猫头鹰通讯档案
   ------------------------------------------------------------
   只改皮：JS / DOM / 数据 / 事件一个字没动，功能 100% 照旧。
   所有条子一律挂在 `.sheet:has(.mail-tabs)` 上——全游戏只有 ui/mail.js
   那张纸带 .mail-tabs，所以设置、手抄本、日程、商店……别处摊开的弹页
   一概不受牵连（这条比给这张纸加一个类名便宜，而加类名要改 JS）。
   同名规则本节写在第 6 节（.sheet / .mail-tab / .mail-contact / .x-btn）之后，以这里为准。

   配色只用现成那一套：--paper / --ink / --ink-soft / --red / --glass-line（暗金）。
   装饰一律走低透明度：第一眼先看见人，第二眼才看见羽毛与墨。
   ============================================================ */

/* ── 一、那张纸本身：摸过多年的旧羊皮 ── */
.sheet:has(.mail-tabs) {
  position: relative;
  /*
   * 四层压在 --parchment-wide 那张纸图上（先写的在上面）：
   * 四角起色 → 一道三折收抽屉留下的折痕 → 笔尖落过的一片墨渍 → 原本的纸。
   * 每层都薄到"看不出加了东西"，合起来才不像一块米黄底色。
   */
  background-image:
    radial-gradient(120% 92% at 50% 48%, transparent 50%, rgba(101, 71, 36, 0.15) 87%, rgba(70, 47, 22, 0.27) 100%),
    linear-gradient(90deg, transparent 0 33.1%, rgba(122, 92, 54, 0.065) 33.5% 34%, transparent 34.4% 66.2%, rgba(122, 92, 54, 0.055) 66.6% 67.1%, transparent 67.5%),
    radial-gradient(34% 11% at 24% 5.5%, rgba(64, 48, 31, 0.11), transparent 72%),
    var(--parchment-wide);
  background-size: 100% 100%;
  box-shadow:
    0 28px 70px rgba(0, 0, 0, 0.66),
    inset 0 0 46px rgba(96, 68, 34, 0.15),
    inset 0 0 0 1px rgba(122, 92, 54, 0.14);
}

/* ── 二、抬头：档案的题名，不是网页标题 ── */
.sheet:has(.mail-tabs) .sheet-head {
  position: relative;
  /* 第 6 节那条 2px 实线 + 一道红影太"印刷"，这张纸上换成手绘的一笔 */
  border-bottom: 0;
  box-shadow: none;
}
/* 题名左下一片极淡的墨晕（笔尖在那儿停过） */
.sheet:has(.mail-tabs) .sheet-head::before {
  content: '';
  position: absolute;
  left: -6px;
  bottom: 2px;
  width: 34%;
  height: 26px;
  background: radial-gradient(60% 70% at 22% 70%, rgba(64, 48, 31, 0.13), transparent 70%);
  pointer-events: none;
}
/* 手绘墨线：两头淡下去、中段略沉，整笔偏一点点角度 */
.sheet:has(.mail-tabs) .sheet-head::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 3px;
  height: 2px;
  background: linear-gradient(90deg,
    transparent 0, rgba(64, 48, 31, 0.52) 9%, rgba(64, 48, 31, 0.72) 44%,
    rgba(140, 47, 47, 0.46) 61%, rgba(64, 48, 31, 0.5) 88%, transparent 100%);
  transform: rotate(-0.25deg);
}
.sheet:has(.mail-tabs) .sheet-head h2 {
  letter-spacing: 0.16em;
  color: #33240f;
  /* 上面一道纸的反光、下面一点洇开：字是吃进纸里的，不是浮在上面的 */
  text-shadow: 0 1px 0 rgba(255, 248, 232, 0.5), 0 0 14px rgba(64, 48, 31, 0.12);
}

/* ── 三、关掉它：一枚嵌在纸上的铜座小牌（图形仍是原来那张，不动结构） ── */
.sheet:has(.mail-tabs) .x-btn {
  background:
    radial-gradient(66% 58% at 32% 24%, rgba(224, 178, 100, 0.26), transparent 72%),
    linear-gradient(180deg, #6a5232, #3b2c18);
  border: 1px solid rgba(26, 18, 10, 0.62);
  border-radius: var(--r);
  box-shadow:
    inset 0 1px 0 rgba(240, 214, 160, 0.26),
    inset 0 -1px 0 rgba(0, 0, 0, 0.3),
    0 2px 6px rgba(0, 0, 0, 0.32);
  transition: filter 0.18s ease, box-shadow 0.18s ease, transform 0.14s cubic-bezier(0.16, 1, 0.3, 1);
}
/* 图缩进去一点，铜座才读得出是一圈边 */
.sheet:has(.mail-tabs) .x-btn .x-img {
  width: 86%;
  height: 86%;
}
.sheet:has(.mail-tabs) .x-btn:hover {
  filter: brightness(1.13);
  box-shadow:
    inset 0 1px 0 rgba(246, 222, 172, 0.34),
    inset 0 -1px 0 rgba(0, 0, 0, 0.28),
    0 4px 10px rgba(0, 0, 0, 0.38);
}

/* ── 四、分类标签：写在纸上的魔法分类，不是网页 Tab ── */
.sheet:has(.mail-tabs) .mail-tabs {
  position: relative;
  gap: var(--sp-2);
  padding-bottom: 7px;
  /* 第 6 节那条 1px 实线换成一道两头淡的笔 */
  border-bottom: 0;
}
.sheet:has(.mail-tabs) .mail-tabs::after {
  content: '';
  position: absolute;
  left: 4%;
  right: 4%;
  bottom: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(64, 48, 31, 0.34) 22%, rgba(64, 48, 31, 0.3) 78%, transparent);
}
.sheet:has(.mail-tabs) .mail-tab {
  position: relative;
  padding: 4px 2px 8px;
  font-size: var(--fs-s);
  letter-spacing: 0.1em;
  color: rgba(125, 104, 73, 0.95);
  background: transparent;
  border: 0;
  border-radius: 0;
  transition: color 0.2s ease, background 0.2s ease;
}
/* 每一格底下那一笔：平时收着，hover 才落下去 */
.sheet:has(.mail-tabs) .mail-tab::after {
  content: '';
  position: absolute;
  left: 16%;
  right: 16%;
  bottom: 2px;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(64, 48, 31, 0.6), transparent);
  transform: scaleX(0);
  transition: transform 0.2s ease;
}
.sheet:has(.mail-tabs) .mail-tab:hover {
  color: var(--ink);
  background: transparent;
}
.sheet:has(.mail-tabs) .mail-tab:hover::after {
  transform: scaleX(1);
}
/*
 * 选中那一格：暗红棕的墨 + 头顶一片极淡的纸张色差 + 落定的一笔。
 * 不摆矩形色块——那是网页的做法；档案里被翻开的这一页，靠的是墨色与笔。
 */
.sheet:has(.mail-tabs) .mail-tab.on {
  color: var(--red);
  font-weight: 700;
  background: linear-gradient(180deg, rgba(140, 47, 47, 0.07), rgba(140, 47, 47, 0) 76%);
}
.sheet:has(.mail-tabs) .mail-tab.on::after {
  height: 2px;
  background: linear-gradient(90deg, transparent, rgba(140, 47, 47, 0.7) 28%, rgba(140, 47, 47, 0.75) 72%, transparent);
  transform: scaleX(1);
}

/* ── 五、一条人物档案：写在纸上的那一行 ── */
.sheet:has(.mail-tabs) .mail-contact {
  position: relative;
  padding: 10px var(--sp-3) 11px calc(var(--sp-3) + 4px);
  background: linear-gradient(90deg, rgba(148, 112, 66, 0.10), rgba(148, 112, 66, 0.03) 44%, transparent 82%);
  /* 行与行之间那道分隔：一条压在纸里的淡墨，不是 border */
  border-bottom: 0;
  box-shadow: inset 0 -1px 0 rgba(64, 48, 31, 0.15);
  transition: background 0.22s ease, box-shadow 0.22s ease;
}
.sheet:has(.mail-tabs) .mail-contact:last-of-type {
  box-shadow: none;
}
/* 左边一道极细的暗金压印：平时没有，这一行被"激活"时才浮出来 */
.sheet:has(.mail-tabs) .mail-contact::before {
  content: '';
  position: absolute;
  left: 1px;
  top: 14%;
  bottom: 14%;
  width: 2px;
  background: linear-gradient(180deg, transparent, rgba(224, 178, 100, 0.52), transparent);
  opacity: 0;
  transition: opacity 0.22s ease;
}
/* 变暖，不抬起：像那行字被人用指腹抹了一下 */
.sheet:has(.mail-tabs) .mail-contact:hover {
  background: linear-gradient(90deg, rgba(160, 120, 68, 0.19), rgba(160, 120, 68, 0.06) 48%, rgba(255, 248, 232, 0.1) 100%);
}
.sheet:has(.mail-tabs) .mail-contact:hover::before {
  opacity: 1;
}

/* ── 六、学生肖像：画框是图自带的，这里只加落在纸上的影子 ── */
.sheet:has(.mail-tabs) .mail-contact .who-face {
  transition: transform 0.2s ease, filter 0.2s ease, box-shadow 0.2s ease;
}
.sheet:has(.mail-tabs) .mail-contact .who-face.art {
  filter: drop-shadow(0 3px 7px rgba(43, 29, 12, 0.4));
}
.sheet:has(.mail-tabs) .mail-contact:hover .who-face.art {
  transform: scale(1.02);
  filter: drop-shadow(0 5px 10px rgba(43, 29, 12, 0.48)) brightness(1.05);
}
/* 玩家自己传的那种圆像（没有画框）：补一圈暗棕 + 一道极细的暗金内沿 */
.sheet:has(.mail-tabs) .mail-contact .who-face:not(.art) {
  border-color: rgba(64, 48, 31, 0.6);
  box-shadow:
    0 3px 8px rgba(43, 29, 12, 0.32),
    inset 0 0 0 1px rgba(224, 178, 100, 0.32);
}

/* ── 七、三级信息：姓名 > 英文名 > 关系 ── */
.sheet:has(.mail-tabs) .mc-tx {
  min-width: 0;
}
.sheet:has(.mail-tabs) .mc-name {
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: 0.08em;
  color: #3a2917;
  text-shadow: 0 1px 0 rgba(255, 248, 232, 0.45);
  transition: color 0.2s ease;
}
.sheet:has(.mail-tabs) .mail-contact:hover .mc-name {
  color: #2a1c0b;
}
/* 英文是次级：小一档、不折行（长了截在省略号上，别把行高撑开） */
.sheet:has(.mail-tabs) .mc-en {
  margin-top: 2px;
  font-size: 10px;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  opacity: 0.62;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/*
 * 关系那一行 = 好感度的现有读法（affinityStage 那几个字：同学 / 朋友 …）。
 * DOM 里只有这一个文字节点、没有数值容器，所以这儿做的是"一枚落笔的印记"：
 * 心形符号 + 暗红棕的手写体 + 后面拖一道没停住的墨痕。
 * 真要那条槽有刻度，得让那一行带上数（一行 JS 的事），纯 CSS 变不出别人的账。
 */
.sheet:has(.mail-tabs) .mc-note {
  margin-top: 5px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  color: rgba(140, 47, 47, 0.9);
}
.sheet:has(.mail-tabs) .mc-note::before {
  content: '♡';
  margin-right: 0.44em;
  font-size: 0.92em;
  color: rgba(140, 47, 47, 0.6);
}
.sheet:has(.mail-tabs) .mc-note::after {
  content: '';
  display: inline-block;
  vertical-align: middle;
  width: 20%;
  min-width: 22px;
  height: 1px;
  margin-left: 0.7em;
  background: linear-gradient(90deg, rgba(140, 47, 47, 0.32), transparent);
}
.sheet:has(.mail-tabs) .mail-contact:hover .mc-note {
  color: #7c2626;
}
.sheet:has(.mail-tabs) .mail-contact:hover .mc-note::after {
  background: linear-gradient(90deg, rgba(140, 47, 47, 0.5), transparent);
}

/* ============================================================
   50. 【教室】那一层：七块课程铭牌，一门课一张脸
   ------------------------------------------------------------
   只改皮：JS / DOM / 数据 / 点击一律没动。
   钩子的选择：这一屏的七个按钮在 ui/screens-map.js 里全叫 `.subject`，
   身上没有课程 id、也没有 data-*，而 DOM 结构不许改——所以身份只能按
   **位置**认：`.subject-list > .subject:nth-child(n)`。
   顺序由 data/locations.js 的 SUBJECTS 定死（魔咒 → 变形 → 魔药 → 防御 →
   草药 → 魔法史 → 天文），创作者这次明确要求「禁止改变课程顺序」，
   两头正好对上。哪天真要重排课表，这一节的 1~7 要跟着挪。
   整节都关在 `.view:has(.subject-list)` 里——眼下两屏共用这一套：
   霍格沃茨的【教室】层，以及家里那一屏【复习课本】（ui/home.js 的 screenHomeReview）。
   它按位置认身份，所以两边都必须照 data/locations.js 的 SUBJECTS 顺序排。
   ============================================================ */

/* ── 一、共同的框架：一套铭牌，七个主题 ── */
.view:has(.subject-list) .subject-list {
  gap: var(--sp-3);
  margin-top: var(--sp-3);
  /* 左上一点暖光：让铭牌像被走廊那头的灯照着，而不是把页面压黑 */
  background-image: radial-gradient(120% 58% at 10% 0%, rgba(224, 178, 100, 0.07), transparent 62%);
}
.view:has(.subject-list) .subject {
  position: relative;
  overflow: hidden;
  /* 左边多让出一条带子，符号就刻在那条带上 */
  padding: var(--sp-3) var(--sp-4) var(--sp-3) calc(var(--sp-4) + 15px);
  border-radius: var(--r);
  box-shadow: 0 7px 20px rgba(0, 0, 0, 0.46), inset 0 1px 0 rgba(255, 235, 200, 0.07);
  transition: background-color 0.2s ease, border-color 0.2s ease,
    box-shadow 0.2s ease, transform 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
/* 课程名 = 第一视觉；教授名退到第三档 */
.view:has(.subject-list) .subject .s-zh {
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: 0.12em;
  color: #f7e9c9;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}
.view:has(.subject-list) .subject .s-en {
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  color: rgba(224, 196, 148, 0.62);
}
/* 符号（刻在左边那条带上）：统一尺寸、统一位置，只换字与色 */
.view:has(.subject-list) .subject::before {
  content: '';
  position: absolute;
  left: 9px;
  top: 50%;
  transform: translateY(-50%);
  font-size: var(--fs-s);
  line-height: 1;
  pointer-events: none;
  opacity: 0.8;
  transition: opacity 0.22s ease, transform 0.22s ease;
}
/* 氛围层：平时收着，hover 才按各课的语言亮起来（不吃点击） */
.view:has(.subject-list) .subject::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.24s ease, transform 0.3s ease;
}
.view:has(.subject-list) .subject:hover {
  transform: translateY(-1px);
}
.view:has(.subject-list) .subject:hover::after {
  opacity: 1;
}
.view:has(.subject-list) .subject:active {
  transform: translateY(0) scale(0.99);
}

/* ── 二、1 · 魔咒学：暗金 / 琥珀，杖尖刚落下的一点火星 ── */
.view:has(.subject-list) .subject-list .subject:nth-child(1) {
  background-color: rgba(64, 44, 22, 0.74);
  background-image:
    linear-gradient(90deg, rgba(224, 178, 100, 0.85) 0 3px, transparent 3px),
    radial-gradient(60% 130% at 88% -20%, rgba(224, 178, 100, 0.16), transparent 60%),
    radial-gradient(circle at 72% 30%, rgba(255, 236, 190, 0.5) 0 1px, transparent 2px),
    radial-gradient(circle at 84% 62%, rgba(255, 236, 190, 0.34) 0 1px, transparent 2px),
    radial-gradient(circle at 62% 74%, rgba(255, 236, 190, 0.26) 0 1px, transparent 2px);
  border-color: rgba(224, 178, 100, 0.34);
}
.view:has(.subject-list) .subject-list .subject:nth-child(1)::before {
  content: '✦';
  color: #f0d79a;
}
.view:has(.subject-list) .subject-list .subject:nth-child(1)::after {
  background: radial-gradient(58% 150% at 82% 40%, rgba(255, 226, 158, 0.2), transparent 62%);
}
.view:has(.subject-list) .subject-list .subject:nth-child(1):hover {
  border-color: rgba(246, 214, 148, 0.72);
}
.view:has(.subject-list) .subject-list .subject:nth-child(1):hover::before {
  transform: translateY(-50%) scale(1.14);
}

/* ── 三、2 · 变形术：暗紫 / 古铜，一套正在换形的几何 ── */
.view:has(.subject-list) .subject-list .subject:nth-child(2) {
  background-color: rgba(46, 32, 52, 0.76);
  background-image:
    linear-gradient(90deg, rgba(168, 132, 196, 0.8) 0 3px, transparent 3px),
    repeating-linear-gradient(45deg, rgba(190, 156, 214, 0.1) 0 1px, transparent 1px 11px),
    repeating-linear-gradient(-45deg, rgba(190, 156, 214, 0.08) 0 1px, transparent 1px 11px),
    radial-gradient(70% 150% at 90% 120%, rgba(120, 84, 150, 0.2), transparent 62%);
  border-color: rgba(176, 140, 202, 0.3);
}
.view:has(.subject-list) .subject-list .subject:nth-child(2)::before {
  content: '◇';
  color: #cfaee2;
}
/* 变形的那一下：整张几何网轻轻转个角度 */
.view:has(.subject-list) .subject-list .subject:nth-child(2)::after {
  background:
    repeating-linear-gradient(38deg, rgba(214, 178, 240, 0.13) 0 1px, transparent 1px 9px),
    repeating-linear-gradient(-52deg, rgba(214, 178, 240, 0.1) 0 1px, transparent 1px 9px);
  transform: rotate(-0.6deg) scale(1.04);
}
.view:has(.subject-list) .subject-list .subject:nth-child(2):hover {
  border-color: rgba(214, 178, 240, 0.62);
}

/* ── 四、3 · 魔药学：深琥珀 + 墨绿，瓶底那层药液与气泡 ── */
.view:has(.subject-list) .subject-list .subject:nth-child(3) {
  background-color: rgba(48, 44, 26, 0.78);
  background-image:
    linear-gradient(90deg, rgba(196, 152, 74, 0.8) 0 3px, transparent 3px),
    linear-gradient(180deg, transparent 58%, rgba(58, 96, 66, 0.26) 78%, rgba(38, 74, 52, 0.4) 100%),
    radial-gradient(circle at 78% 84%, rgba(214, 240, 200, 0.16) 0 3px, transparent 4px),
    radial-gradient(circle at 86% 70%, rgba(214, 240, 200, 0.12) 0 2px, transparent 3px),
    radial-gradient(circle at 68% 92%, rgba(214, 240, 200, 0.1) 0 2px, transparent 3px);
  border-color: rgba(196, 152, 74, 0.3);
}
.view:has(.subject-list) .subject-list .subject:nth-child(3)::before {
  content: '○';
  color: #d8c07a;
}
/* 液面轻轻晃一下：高光浮上来，气泡往上走一点 */
.view:has(.subject-list) .subject-list .subject:nth-child(3)::after {
  background:
    linear-gradient(180deg, transparent 62%, rgba(150, 200, 150, 0.16) 84%, rgba(190, 226, 176, 0.2) 100%),
    radial-gradient(50% 90% at 20% 0%, rgba(255, 244, 210, 0.1), transparent 58%);
  transform: translateY(3px);
}
.view:has(.subject-list) .subject-list .subject:nth-child(3):hover {
  border-color: rgba(226, 190, 116, 0.66);
}

/* ── 五、4 · 黑魔法防御术：黑褐 + 暗红，一圈刻着的防护阵 ── */
.view:has(.subject-list) .subject-list .subject:nth-child(4) {
  background-color: rgba(40, 24, 24, 0.8);
  background-image:
    linear-gradient(90deg, rgba(158, 52, 48, 0.85) 0 3px, transparent 3px),
    radial-gradient(circle at 88% 50%, transparent 0 26px, rgba(178, 66, 58, 0.16) 27px, transparent 29px),
    radial-gradient(circle at 88% 50%, transparent 0 16px, rgba(178, 66, 58, 0.12) 17px, transparent 19px),
    linear-gradient(122deg, transparent 0 46%, rgba(20, 12, 10, 0.5) 46.4% 47%, transparent 47.4%);
  border-color: rgba(158, 52, 48, 0.34);
}
.view:has(.subject-list) .subject-list .subject:nth-child(4)::before {
  content: '◉';
  color: #cf7268;
}
/* 危险的那圈边浮起来一点——是"古老而危险"，不是血，也不是霓虹 */
.view:has(.subject-list) .subject-list .subject:nth-child(4)::after {
  background:
    radial-gradient(120% 160% at 100% 50%, rgba(150, 40, 36, 0.24), transparent 58%),
    radial-gradient(circle at 88% 50%, transparent 0 26px, rgba(214, 96, 84, 0.3) 27px, transparent 30px);
}
.view:has(.subject-list) .subject-list .subject:nth-child(4):hover {
  border-color: rgba(206, 84, 72, 0.7);
}

/* ── 六、5 · 草药学：深墨绿 + 苔色，牌子边上长出来的藤 ── */
.view:has(.subject-list) .subject-list .subject:nth-child(5) {
  background-color: rgba(28, 46, 32, 0.78);
  background-image:
    linear-gradient(90deg, rgba(126, 168, 96, 0.82) 0 3px, transparent 3px),
    radial-gradient(22% 9% at 95% 20%, rgba(150, 196, 122, 0.2), transparent 72%),
    radial-gradient(18% 7% at 88% 84%, rgba(150, 196, 122, 0.16), transparent 72%),
    radial-gradient(14% 6% at 78% 12%, rgba(150, 196, 122, 0.12), transparent 72%),
    repeating-linear-gradient(76deg, rgba(120, 168, 110, 0.07) 0 1px, transparent 1px 26px);
  border-color: rgba(126, 168, 96, 0.3);
}
.view:has(.subject-list) .subject-list .subject:nth-child(5)::before {
  content: '❧';
  color: #a8cf8a;
}
/* 藤长了一寸：叶影轻轻侧移一点 */
.view:has(.subject-list) .subject-list .subject:nth-child(5)::after {
  background:
    radial-gradient(24% 10% at 96% 16%, rgba(176, 222, 146, 0.24), transparent 72%),
    radial-gradient(20% 8% at 89% 86%, rgba(176, 222, 146, 0.18), transparent 72%);
  transform: translateX(-3px);
}
.view:has(.subject-list) .subject-list .subject:nth-child(5):hover {
  border-color: rgba(168, 214, 140, 0.62);
}

/* ── 七、6 · 魔法史：旧纸 / 古铜，一摞抄过无数遍的档案线 ── */
.view:has(.subject-list) .subject-list .subject:nth-child(6) {
  background-color: rgba(58, 46, 32, 0.8);
  background-image:
    linear-gradient(90deg, rgba(196, 168, 120, 0.8) 0 3px, transparent 3px),
    repeating-linear-gradient(180deg, rgba(226, 206, 168, 0.07) 0 1px, transparent 1px 9px),
    radial-gradient(30% 60% at 92% 30%, rgba(64, 48, 31, 0.34), transparent 70%);
  border-color: rgba(196, 168, 120, 0.28);
}
.view:has(.subject-list) .subject-list .subject:nth-child(6)::before {
  content: '❦';
  color: #dcc79c;
}
/* 这门没有光效：只是那页纸被翻得亮了一点 */
.view:has(.subject-list) .subject-list .subject:nth-child(6)::after {
  background: linear-gradient(90deg, rgba(246, 232, 200, 0.1), rgba(246, 232, 200, 0.02) 46%, transparent 78%);
}
.view:has(.subject-list) .subject-list .subject:nth-child(6):hover {
  border-color: rgba(226, 200, 152, 0.56);
}

/* ── 八、7 · 天文学：深蓝黑 / 靛，一块刻着星点的夜空 ── */
.view:has(.subject-list) .subject-list .subject:nth-child(7) {
  background-color: rgba(24, 32, 54, 0.8);
  background-image:
    linear-gradient(90deg, rgba(150, 168, 214, 0.8) 0 3px, transparent 3px),
    radial-gradient(70% 120% at 20% 120%, rgba(96, 110, 176, 0.18), transparent 62%),
    radial-gradient(circle at 70% 26%, rgba(240, 244, 255, 0.62) 0 1px, transparent 2px),
    radial-gradient(circle at 80% 48%, rgba(240, 244, 255, 0.44) 0 1px, transparent 2px),
    radial-gradient(circle at 90% 30%, rgba(240, 244, 255, 0.36) 0 1px, transparent 2px),
    radial-gradient(circle at 64% 66%, rgba(240, 244, 255, 0.3) 0 1px, transparent 2px),
    linear-gradient(104deg, transparent 0 62%, rgba(200, 212, 246, 0.1) 62.4% 62.8%, transparent 63.2%);
  border-color: rgba(150, 168, 214, 0.3);
}
.view:has(.subject-list) .subject-list .subject:nth-child(7)::before {
  content: '✧';
  color: #cfd8f4;
}
/* 星子亮起来，星座那道连线也清楚一点 */
.view:has(.subject-list) .subject-list .subject:nth-child(7)::after {
  background:
    radial-gradient(circle at 70% 26%, rgba(255, 255, 255, 0.5) 0 1.5px, transparent 3px),
    radial-gradient(circle at 80% 48%, rgba(255, 255, 255, 0.4) 0 1.5px, transparent 3px),
    radial-gradient(circle at 90% 30%, rgba(255, 255, 255, 0.34) 0 1.5px, transparent 3px),
    linear-gradient(104deg, transparent 0 60%, rgba(214, 226, 255, 0.2) 60.4% 61%, transparent 61.4%);
}
.view:has(.subject-list) .subject-list .subject:nth-child(7):hover {
  border-color: rgba(196, 210, 248, 0.62);
}

/*
 * 「今天就是这一堂」那条金边压在所有主题之上——不能因为换了课的颜色就看不见。
 * 写在七块铭牌**之后**：它要和 `.subject:nth-child(n)` 那七条打平（同为 0,5,0），
 * 而平手时后写的赢。
 */
.view:has(.subject-list) .subject-list .subject.today {
  border-color: rgba(224, 178, 100, 0.86);
  box-shadow: 0 7px 20px rgba(0, 0, 0, 0.46),
    inset 0 0 0 1px rgba(224, 178, 100, 0.22),
    0 0 16px rgba(224, 178, 100, 0.16);
}

/* ── 九、题名：学院目录的抬头 ── */
.view:has(.subject-list) .map-cap .mc-zh {
  letter-spacing: 0.2em;
  color: #f6e8c6;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
}
.view:has(.subject-list) .map-cap .mc-en {
  letter-spacing: 0.34em;
  color: rgba(224, 178, 100, 0.74);
}

/* ── 十、回大厅：一块钉在墙上的旧门牌（功能、位置一律不动） ── */
.view:has(.subject-list) .map-cap .btn.exit {
  background: linear-gradient(180deg, rgba(74, 52, 32, 0.94), rgba(38, 26, 16, 0.95));
  border: 1px solid rgba(224, 178, 100, 0.42);
  border-radius: 2px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(246, 222, 172, 0.16);
  transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
.view:has(.subject-list) .map-cap .btn.exit:hover {
  background: linear-gradient(180deg, rgba(92, 66, 38, 0.96), rgba(50, 34, 20, 0.96));
  border-color: rgba(240, 206, 150, 0.72);
  box-shadow: 0 5px 14px rgba(0, 0, 0, 0.54), inset 0 1px 0 rgba(246, 222, 172, 0.22);
}

/* ── 十一、窄屏：带子与符号收一点，别把课程名挤到折行 ── */
@media (max-width: 560px) {
  .view:has(.subject-list) .subject {
    padding-left: calc(var(--sp-3) + 14px);
  }
  .view:has(.subject-list) .subject-list .subject:nth-child(4) {
    background-image:
      linear-gradient(90deg, rgba(158, 52, 48, 0.85) 0 3px, transparent 3px),
      radial-gradient(circle at 92% 50%, transparent 0 20px, rgba(178, 66, 58, 0.16) 21px, transparent 23px),
      linear-gradient(122deg, transparent 0 46%, rgba(20, 12, 10, 0.5) 46.4% 47%, transparent 47.4%);
  }
}

/* ============================================================
   51. 地点铭牌：一摞牌子里，每一处地点有自己的材质、光线与呼吸
   ------------------------------------------------------------
   只改皮：HTML / JS / 数据 / 点击 / 顺序一律没动。
   共同牌框挂在 `.room-map > .room-btn:not(.arena)`（屋里那一摞牌子共用的容器），
   每一处地点再按**它自己已有的钩子**认领一身：
     · 大礼堂【吃饭】、黑湖边【钓鱼】  → `.market:only-child`（这一摞只有它一块）
     · 海格的小摊                    → `.market`（小屋那摞里唯一的非独占 market）
     · 大煮锅                        → `.steel:first-child`
     · 兽栏                          → `.steel:last-child`
     · 医疗翼那两枚                  → `:has(.rb-tag):not(:has(.rb-sub))`
   ⚠️ 两处认不出的地方（DOM 里长得一模一样，纯 CSS 分不开，没有改 HTML 的许可）：
     ① 大煮锅与兽栏同为 `.room-btn.big.steel` —— 只能按先后分（锅第一、栏第三）；
        哪天真要挪这两块的顺序，这两条要跟着挪。
     ② 大礼堂的【吃饭】与黑湖边的【钓鱼】同为「一摞里唯一一块 .market」——
        所以它们共用这一身暖烛光木牌。要分开，得给其中一个补一个 tone（一行 JS）。
   厨房那口锅（house-pot）、种植箱（plot）、探索牌（forage）、黑街（blackmkt）、
   龙斗场（arena）各自已有的那一身**不动**，只统一吃到下面的牌框。
   ============================================================ */

/* ── 一、共同的牌框：外圈一道、内沿一道、四个角各一枚小 ornament ── */
.room-map > .room-btn:not(.arena) {
  position: relative;
  overflow: hidden;
  border-radius: calc(var(--r) + 1px);
  box-shadow:
    0 9px 24px rgba(0, 0, 0, 0.5),
    inset 0 1px 0 rgba(255, 236, 200, 0.08);
  transition:
    background-color 0.22s ease, border-color 0.22s ease,
    box-shadow 0.22s ease, transform 0.24s cubic-bezier(0.16, 1, 0.3, 1);
}
/* 内沿那道极细的高光线 + 四角（一块渐变层，不额外加节点） */
.room-map > .room-btn:not(.arena)::before {
  content: '';
  position: absolute;
  inset: 3px;
  border: 1px solid rgba(224, 178, 100, 0.1);
  border-radius: calc(var(--r) - 1px);
  pointer-events: none;
  background:
    linear-gradient(135deg, var(--loc-corner, rgba(224, 178, 100, 0.42)) 0 6px, transparent 6px) left top / 8px 8px no-repeat,
    linear-gradient(225deg, var(--loc-corner, rgba(224, 178, 100, 0.42)) 0 6px, transparent 6px) right top / 8px 8px no-repeat,
    linear-gradient(45deg, var(--loc-corner, rgba(224, 178, 100, 0.42)) 0 6px, transparent 6px) left bottom / 8px 8px no-repeat,
    linear-gradient(315deg, var(--loc-corner, rgba(224, 178, 100, 0.42)) 0 6px, transparent 6px) right bottom / 8px 8px no-repeat;
  opacity: 0.55;
  transition: opacity 0.24s ease;
}
/* 环境层：每一身往里灌自己的光与动画（平时淡淡，hover 抬一档） */
.room-map > .room-btn:not(.arena)::after {
  content: '';
  position: absolute;
  inset: -12%;
  pointer-events: none;
  opacity: 0.5;
  transition: opacity 0.28s ease;
}
.room-map > .room-btn:not(.arena):hover {
  transform: translateY(-1px) scale(1.012);
}
.room-map > .room-btn:not(.arena):hover::before {
  opacity: 0.9;
}
.room-map > .room-btn:not(.arena):hover::after {
  opacity: 0.92;
}
.room-map > .room-btn:not(.arena):active {
  transform: translateY(0) scale(0.99);
}
/* 名字仍是第一层：暖白，不纯白 */
.room-map > .room-btn:not(.arena) .rb-zh {
  color: #f4e6c6;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.62);
}
.room-map > .room-btn:not(.arena):hover .rb-zh {
  color: #fbf1d8;
}

/* ── 二、进场：一摞牌子依次浮出来（纯 CSS，animation-delay 按先后） ── */
@keyframes locPlateIn {
  from { opacity: 0; transform: translateY(7px); }
  to { opacity: 1; transform: translateY(0); }
}
.room-map > .room-btn:not(.arena) {
  animation: locPlateIn 0.42s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.room-map > .room-btn:not(.arena):nth-child(2) { animation-delay: 0.06s; }
.room-map > .room-btn:not(.arena):nth-child(3) { animation-delay: 0.12s; }
.room-map > .room-btn:not(.arena):nth-child(4) { animation-delay: 0.18s; }
.room-map > .room-btn:not(.arena):nth-child(n + 5) { animation-delay: 0.24s; }

/* ── 三、大礼堂【吃饭】（与黑湖边【钓鱼】共用这一身）：浮在半空的烛火 ── */
@keyframes locCandle {
  0%, 100% { opacity: 0.42; transform: translate3d(0, 0, 0) scale(1); }
  38% { opacity: 0.6; transform: translate3d(0, -1.5px, 0) scale(1.025); }
  64% { opacity: 0.46; transform: translate3d(0, 1px, 0) scale(0.99); }
}
.room-map > .room-btn.market:only-child {
  --loc-corner: rgba(240, 202, 122, 0.55);
  background-color: rgba(66, 44, 20, 0.76);
  background-image:
    radial-gradient(120% 150% at 50% -30%, rgba(240, 202, 122, 0.16), transparent 58%),
    repeating-linear-gradient(90deg, rgba(240, 202, 122, 0.05) 0 1px, transparent 1px 34px),
    linear-gradient(180deg, rgba(255, 236, 196, 0.05), transparent 42%);
  border-color: rgba(224, 178, 100, 0.44);
}
.room-map > .room-btn.market:only-child::after {
  background:
    radial-gradient(circle at 18% 26%, rgba(255, 226, 158, 0.5) 0 1.5px, transparent 4px),
    radial-gradient(circle at 34% 18%, rgba(255, 226, 158, 0.4) 0 1.5px, transparent 4px),
    radial-gradient(circle at 52% 28%, rgba(255, 226, 158, 0.44) 0 1.5px, transparent 4px),
    radial-gradient(circle at 68% 16%, rgba(255, 226, 158, 0.36) 0 1.5px, transparent 4px),
    radial-gradient(circle at 84% 27%, rgba(255, 226, 158, 0.42) 0 1.5px, transparent 4px),
    radial-gradient(70% 120% at 50% 0%, rgba(232, 176, 96, 0.14), transparent 66%);
  animation: locCandle 7.5s ease-in-out infinite;
}
.room-map > .room-btn.market:only-child:hover {
  border-color: rgba(246, 214, 148, 0.78);
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.52), 0 0 26px rgba(226, 172, 88, 0.16),
    inset 0 1px 0 rgba(255, 240, 206, 0.12);
}

/* ── 四、海格的小摊：麻布 + 木板 + 一棵蹭到边上的植物 ── */
@keyframes locCloth {
  0%, 100% { opacity: 0.5; transform: translate3d(0, 0, 0); }
  50% { opacity: 0.62; transform: translate3d(-2px, 1px, 0); }
}
.room-map > .room-btn.market:not(:only-child) {
  --loc-corner: rgba(214, 182, 122, 0.5);
  background-color: rgba(74, 58, 32, 0.78);
  background-image:
    linear-gradient(90deg, rgba(176, 140, 84, 0.7) 0 3px, transparent 3px),
    repeating-linear-gradient(0deg, rgba(226, 202, 158, 0.055) 0 1px, transparent 1px 4px),
    repeating-linear-gradient(90deg, rgba(226, 202, 158, 0.045) 0 1px, transparent 1px 4px),
    linear-gradient(180deg, rgba(255, 232, 180, 0.06), transparent 46%);
  border-color: rgba(196, 160, 100, 0.36);
}
.room-map > .room-btn.market:not(:only-child)::after {
  background:
    radial-gradient(26% 40% at 92% 88%, rgba(150, 190, 116, 0.2), transparent 70%),
    radial-gradient(18% 30% at 98% 70%, rgba(150, 190, 116, 0.14), transparent 72%),
    radial-gradient(60% 90% at 12% 0%, rgba(255, 226, 160, 0.12), transparent 64%);
  animation: locCloth 11s ease-in-out infinite;
}
.room-map > .room-btn.market:not(:only-child):hover {
  border-color: rgba(232, 198, 132, 0.66);
}

/* ── 五、大煮锅：铁、炉火、一缕将散未散的热气 ── */
@keyframes locHearth {
  0%, 100% { opacity: 0.5; transform: translate3d(0, 0, 0) scale(1); }
  42% { opacity: 0.72; transform: translate3d(0, -1px, 0) scale(1.035); }
  70% { opacity: 0.56; transform: translate3d(1px, 0, 0) scale(1.01); }
}
.room-map > .room-btn.steel:first-child {
  --loc-corner: rgba(226, 158, 78, 0.5);
  background-color: rgba(38, 34, 32, 0.82);
  background-image:
    linear-gradient(180deg, rgba(150, 150, 156, 0.08), transparent 34%),
    repeating-linear-gradient(90deg, rgba(160, 150, 142, 0.05) 0 1px, transparent 1px 22px),
    radial-gradient(80% 130% at 50% 128%, rgba(226, 132, 48, 0.3), transparent 62%);
  border-color: rgba(150, 140, 130, 0.34);
}
.room-map > .room-btn.steel:first-child::after {
  background:
    radial-gradient(ellipse 46% 60% at 50% 116%, rgba(255, 158, 62, 0.34), transparent 66%),
    radial-gradient(ellipse 22% 44% at 36% 104%, rgba(255, 196, 120, 0.16), transparent 70%),
    radial-gradient(ellipse 16% 40% at 64% 96%, rgba(255, 226, 190, 0.07), transparent 74%);
  animation: locHearth 4.6s ease-in-out infinite;
}
.room-map > .room-btn.steel:first-child:hover {
  border-color: rgba(232, 168, 96, 0.6);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.54), 0 0 22px rgba(226, 132, 48, 0.14),
    inset 0 1px 0 rgba(255, 236, 200, 0.1);
}

/* ── 六、兽栏：木栅栏、草地、头顶漏下来的一点圆光 ── */
@keyframes locGrass {
  0%, 100% { opacity: 0.46; transform: translate3d(0, 0, 0) rotate(0deg); }
  50% { opacity: 0.6; transform: translate3d(2px, -1px, 0) rotate(0.5deg); }
}
.room-map > .room-btn.steel:last-child {
  --loc-corner: rgba(168, 202, 132, 0.5);
  background-color: rgba(30, 44, 30, 0.8);
  background-image:
    linear-gradient(90deg, rgba(140, 112, 72, 0.55) 0 3px, transparent 3px),
    repeating-linear-gradient(90deg, rgba(122, 96, 60, 0.16) 0 2px, transparent 2px 26px),
    linear-gradient(180deg, transparent 62%, rgba(58, 92, 52, 0.34) 88%, rgba(44, 76, 42, 0.44) 100%),
    radial-gradient(30% 22% at 78% 12%, rgba(214, 234, 176, 0.1), transparent 70%);
  border-color: rgba(140, 168, 112, 0.32);
}
.room-map > .room-btn.steel:last-child::after {
  background:
    repeating-linear-gradient(74deg, rgba(176, 214, 148, 0.1) 0 1px, transparent 1px 9px),
    radial-gradient(24% 30% at 90% 84%, rgba(176, 214, 148, 0.18), transparent 72%),
    radial-gradient(16% 22% at 70% 92%, rgba(176, 214, 148, 0.12), transparent 74%);
  animation: locGrass 12s ease-in-out infinite;
}
.room-map > .room-btn.steel:last-child:hover {
  border-color: rgba(176, 214, 148, 0.6);
}

/* ── 七、医疗翼那两枚：洗白的布、银与一点暗红，治疗的光在慢慢走 ── */
@keyframes locHeal {
  0% { opacity: 0.34; transform: translate3d(-28%, 0, 0); }
  55% { opacity: 0.6; }
  100% { opacity: 0.34; transform: translate3d(28%, 0, 0); }
}
.room-map > .room-btn:not(.arena):has(.rb-tag):not(:has(.rb-sub)) {
  --loc-corner: rgba(226, 220, 208, 0.5);
  background-color: rgba(52, 48, 44, 0.78);
  background-image:
    linear-gradient(180deg, rgba(238, 234, 224, 0.1), rgba(238, 234, 224, 0.02) 40%, transparent 62%),
    repeating-linear-gradient(0deg, rgba(232, 228, 218, 0.04) 0 1px, transparent 1px 6px),
    radial-gradient(70% 120% at 88% 120%, rgba(150, 44, 40, 0.18), transparent 62%);
  border-color: rgba(226, 220, 208, 0.3);
}
.room-map > .room-btn:not(.arena):has(.rb-tag):not(:has(.rb-sub))::after {
  background:
    linear-gradient(100deg, transparent 34%, rgba(244, 238, 226, 0.14) 46%, rgba(196, 92, 80, 0.1) 54%, transparent 66%),
    radial-gradient(40% 70% at 50% 0%, rgba(238, 232, 220, 0.08), transparent 70%);
  animation: locHeal 9.5s ease-in-out infinite;
}
.room-map > .room-btn:not(.arena):has(.rb-tag):not(:has(.rb-sub)) .rb-zh { color: #f2efe6; }
.room-map > .room-btn:not(.arena):has(.rb-tag):not(:has(.rb-sub)) .rb-tag { color: rgba(226, 190, 176, 0.92); }
.room-map > .room-btn:not(.arena):has(.rb-tag):not(:has(.rb-sub)):hover {
  border-color: rgba(244, 240, 232, 0.6);
  box-shadow: 0 12px 26px rgba(0, 0, 0, 0.48), 0 0 20px rgba(160, 60, 52, 0.12),
    inset 0 1px 0 rgba(248, 246, 240, 0.14);
}

/* ── 八、这一类屏的抬头与退出口 ── */
.view:has(.room-map) .scene .label {
  letter-spacing: 0.24em;
}
.view:has(.room-map) .scene .name {
  color: #f6e9cb;
  text-shadow: 0 2px 14px rgba(0, 0, 0, 0.62);
}
.view:has(.room-map) .scene .note {
  color: rgba(226, 206, 168, 0.72);
}
.view:has(.room-map) .btn.exit {
  background: linear-gradient(180deg, rgba(74, 52, 32, 0.92), rgba(38, 26, 16, 0.94));
  border: 1px solid rgba(224, 178, 100, 0.4);
  border-radius: 2px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(246, 222, 172, 0.16);
  transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
.view:has(.room-map) .btn.exit:hover {
  background: linear-gradient(180deg, rgba(92, 66, 38, 0.96), rgba(50, 34, 20, 0.96));
  border-color: rgba(240, 206, 150, 0.72);
  box-shadow: 0 5px 14px rgba(0, 0, 0, 0.54), inset 0 1px 0 rgba(246, 222, 172, 0.22);
}

/* ── 九、省电与减弱动效：这些呼吸一律停掉（牌面回到静止的那一色） ── */
@media (prefers-reduced-motion: reduce) {
  .room-map > .room-btn:not(.arena),
  .room-map > .room-btn:not(.arena)::after {
    animation: none;
  }
}

/* ============================================================
   53 · 信件顶上那两栏（信件 / 相片）与一张摊开的相纸
   ------------------------------------------------------------
   ui/letters.js：状态栏那枚小信封进来的一张纸，多了一排分类。
   相片**不另起一本账**——它就是随信来的那一封（engine/letters.js 里那个 photo 字段），
   所以这一节只画两样新东西：那一排的条、那一张相纸。
   行、星标、墨点、翻纸、红点全部沿用第 33 节那一套（创作者要的就是"和信件一样"）。

   条子的类名另起一套（.ltr-tabs），**不复用 .mail-tabs**：
   【通讯】那身羊皮纸皮挂在 `.sheet:has(.mail-tabs)` 上（第 49 节），
   借过来会把信件这张纸整个换装——那不是这次要的事。
   ============================================================ */

/* 两栏的条：手感照 .mail-tabs（同一位作者的两排分类，读起来该是一路东西） */
.ltr-tabs {
  display: flex;
  gap: var(--sp-1);
  margin: 2px 0 var(--sp-3);
  padding-bottom: 6px;
  border-bottom: 1px solid rgba(64, 48, 31, 0.28);
}
.ltr-tab {
  position: relative;
  flex: 1 1 0;
  padding: 4px 2px 5px;
  font-family: var(--font-body);
  font-size: var(--fs-s);
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  background: transparent;
  border: 0;
  border-radius: var(--r);
  cursor: pointer;
  transition: color 0.14s ease, background 0.14s ease;
}
.ltr-tab:hover { color: var(--ink); background: rgba(255, 255, 255, 0.35); }
.ltr-tab.on { color: var(--red); font-weight: 700; background: rgba(140, 47, 47, 0.09); }
/* 这一栏里还有没读的：栏名右上角一颗，与信封上那颗（.hud-mail-dot）同一个画法、同一个色 */
.ltr-tab-dot {
  position: absolute;
  top: 1px;
  right: 7px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--red);
  box-shadow: 0 0 0 1.5px rgba(248, 241, 227, 0.9);
  pointer-events: none;
}

/* 名单里那一枚小缩略图：竖幅相片在 34×34 的格子里由 CSS 裁一角（不另出素材） */
.ph-thumb {
  flex: 0 0 34px;
  width: 34px;
  height: 34px;
  overflow: hidden;
  border-radius: 2px;
  background: rgba(255, 252, 240, 0.72);
  box-shadow:
    inset 0 0 0 1px rgba(64, 48, 31, 0.26),
    0 1px 2px rgba(0, 0, 0, 0.16);
}
.ph-thumb-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* 人脸在上半张（那几张照片都是），盯 26% 那条线才不会裁出半张脸 */
  object-position: 50% 26%;
}

/*
 * 摊开的那张相片：一圈相纸白边 + 落在纸上的那道影子。
 * 白边是 CSS 画的（下面留得比三面宽一点，那是相纸压手指的那条边），
 * 图本身**不裁、不铺满、不变形**——一张被裁成方形的相片就不是那张相片了。
 * 信件纸与图鉴那张详情纸共用这一块（.ph-print 在两处都是同一个名）。
 */
.ph-print {
  margin: var(--sp-4) auto 2px;
  padding: 9px 9px 10px;
  max-width: 400px;
  background: #fdf9ef;
  border: 1px solid rgba(64, 48, 31, 0.22);
  border-radius: 2px;
  box-shadow:
    0 7px 18px rgba(0, 0, 0, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.7);
}
.ph-print-img { display: block; width: 100%; height: auto; }
/* 题注走相纸下面那一行（他给了 note 才摆这一行，见 ui/letters.js 与 ui/codex.js） */
.ph-print-cap {
  margin-top: 7px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  line-height: 1.4;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
  text-align: center;
}
.ph-print-none {
  display: block;
  padding: 46px 0;
  font-size: var(--fs-s);
  color: var(--ink-soft);
  text-align: center;
}

/* 图鉴那一册里的相片格：图铺满整格（裁一角），底下那条字照别册一样压在半透明纸上 */
.codex-grid .tile-photo .tile-art { align-items: stretch; }
.codex-grid .tile-photo .tile-art-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 26%;
  border: 1px solid rgba(255, 252, 240, 0.85);
  border-radius: 2px;
}
/* 没收到过的那一格还是灰剪影（别册那一条在这儿同样成立），只是白边跟着淡掉 */
.codex-grid .tile.dark.tile-photo .tile-art-img { border-color: rgba(64, 48, 31, 0.16); }

/* 开始前那张纸上的一行要求：左边写要什么、右边写现在几点（够了转绿） */
.ch-gate {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: 8px 10px;
  border: 1px dashed rgba(64, 48, 31, 0.34);
  border-radius: var(--r);
  background: rgba(255, 252, 240, 0.5);
}
.cg-tx { font-size: var(--fs-s); letter-spacing: 0.04em; color: var(--ink); }
.cg-n { font-family: var(--font-hand); font-size: var(--fs-m); letter-spacing: 0.06em; color: var(--red); }
.cg-n.ok { color: #2f6b45; }

/* ============================================================
   54 · 外界地图那颗钮：整屏只它一颗会发光（ui/worldmap.js）
   ============================================================ */

/*
 * 满屏的地点牌都是「钉在照片上的一张羊皮纸」（.place）。
 * 这一颗要去的是外面的世界，不是又一间屋子，所以换了另一族长相：
 * 暗底描金的牌 + 一圈沿边流动的金光 + 往上飘的火星 + 整颗在呼吸。
 *
 * 那圈「沿边流光」要两层节点：外层是一整块转动的锥形光，
 * 内层那块暗牌盖在它上面、只让出一圈边——一层做不出来（遮罩会把光一起遮掉）。
 */
.map-row.wmap-row { margin: 2px 0 -2px; }

.wmap-btn {
  position: relative;
  display: inline-block;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  /* 呼吸走 filter，不碰 transform：碰了会和 hover 那一下的位移抢同一根轴 */
  animation: wmapBreath 3.6s ease-in-out infinite;
}

@keyframes wmapBreath {
  0%, 100% { filter: drop-shadow(0 0 5px rgba(224, 178, 100, 0.35)); }
  50%      { filter: drop-shadow(0 0 15px rgba(224, 178, 100, 0.75)); }
}

/*
 * 沿边那一圈光：**两层，各干一件事**。
 *   · .wmb-ring 贴着框（比牌子大 2px），自己**不转**，只管"挖空中间、留一条 2px 的边"；
 *   · 里面那个 <i> 比框大一圈（inset:-60%），是它在那儿慢慢转的锥形光。
 * 遮罩连整棵子树一起裁，所以只有那条边上透得出光 —— 看着就是"光顺着框爬"。
 * ⚠️ 这两件事不能并到同一层上：转的是矩形自己，那 2px 的边会跟着一起转，
 *    甩出来的就是一个比牌子大得多的方框（创作者 2026-10-11 第二次点的就是这个）。
 */
.wmb-ring {
  position: absolute;
  inset: -2px;
  padding: 2px;
  border-radius: calc(var(--r) + 2px);
  overflow: hidden;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}

.wmb-ring > i {
  position: absolute;
  inset: -60%;
  background: conic-gradient(from 0deg,
    rgba(224, 178, 100, 0) 0 46%,
    rgba(224, 178, 100, 0.55) 62%,
    #fff2cf 76%,
    rgba(224, 178, 100, 0.55) 86%,
    rgba(224, 178, 100, 0) 96%);
  animation: wmapSpin 4.2s linear infinite;
}

@keyframes wmapSpin { to { transform: rotate(1turn); } }

.wmb-face {
  position: relative;
  display: block;
  padding: var(--sp-2) var(--sp-5);
  border: 1px solid rgba(224, 178, 100, 0.5);
  border-radius: var(--r);
  background: radial-gradient(130% 150% at 50% -10%, #3a2a17, #150e07 72%);
  box-shadow: inset 0 1px 0 rgba(255, 238, 198, 0.18), 0 8px 22px rgba(0, 0, 0, 0.62);
  text-align: center;
  transition: transform 0.16s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.16s ease;
}

.wmap-btn:hover .wmb-face {
  border-color: rgba(255, 232, 178, 0.85);
  transform: translateY(-2px);
}
.wmap-btn:active .wmb-face { transform: translateY(0) scale(0.98); }

.wmb-zh {
  display: block;
  font-family: var(--font-body);
  font-size: var(--fs-m);
  letter-spacing: 0.16em;
  color: #f6e3b8;
}
/* 中英双语那套排法：中文一行紧跟英文一行，英文小很多、不折行 */
.wmb-en {
  display: block;
  margin-top: 1px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.22em;
  color: rgba(224, 178, 100, 0.8);
  white-space: nowrap;
}

/* 四粒火星：同一份动效，靠各自的 left 与 delay 错开 */
.wmb-spark {
  position: absolute;
  left: 50%;
  /* 钉在牌顶那条边上往上飘：原来锚在底边、只升 26 像素，全程都在牌子肚子里，看不见 */
  top: 0;

  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: #ffe9bb;
  box-shadow: 0 0 6px 2px rgba(224, 178, 100, 0.6);
  opacity: 0;
  pointer-events: none;
  animation: wmapFloat 3.4s ease-out infinite;
}
.wmb-spark.s2 { left: 26%; animation-delay: 0.9s; }
.wmb-spark.s3 { left: 74%; animation-delay: 1.7s; }
.wmb-spark.s4 { left: 40%; animation-delay: 2.6s; }

@keyframes wmapFloat {
  0%   { transform: translateY(2px) scale(0.5); opacity: 0; }
  22%  { opacity: 0.95; }
  100% { transform: translateY(-24px) scale(1.1); opacity: 0; }
}

/*
 * 那张大地图：整张手绘羊皮卷**直接浮在暗底上**。
 * 这张 PNG 自己是透明底（四角 alpha=0，画身在图自己的 5%–96% 那一块），
 * 所以既不在底下垫纸、也不做 multiply 混色——垫了纸反倒像"一张照片贴在信纸上"。
 */

/*
 * 'bare' 那一档皮：openSheet 那张羊皮纸整个撤掉（纸图、投影、内边距都清）。
 * 抬头那行 h2 留空、不占位——名字挪到图上的左上角去发光了（.wmp-cap）。
 * 右上角那颗 × 照旧留着：这张纸还是只有一个关的口子。
 */
.sheet.bare { background-image: none; box-shadow: none; padding: 0; border-radius: 0; }
.sheet.bare .sheet-head { margin: 0; padding: 0 2px; border-bottom: 0; box-shadow: none; }

/*
 * 那张纸默认是**按内容收宽**的（#overlay 是 flex 居中，纸是里头那根 shrink-wrap 的条）。
 * 一张 width:100% 的图给不出内容宽——不给纸定宽，整张地图会缩成一团。
 */
.sheet:has(.wmap-print) { width: 100%; }

.wmap-print { position: relative; }

/* 图与图钉共用这一层：那两处百分比是照它的宽高算的（见 data/worldmap.js 顶上那段） */
.wmp-field { position: relative; }

.wmp-img {
  display: block;
  width: 100%;
  height: auto;
  -webkit-user-drag: none;
}

/*
 * 抬头那两行字：站在图**上面**，**没有底、没有框**，靠一层暖金的辉光立住。
 * 它排在正常流里、底下那圈负边距把羊皮卷往上提 14 像素——于是字的下沿正好压在
 * 图顶上那条透明带里（画身在图自己的 6%–94%），看着就是"落在地图的边缘上"，
 * 而不是压在画上（创作者 2026-10-11 点过：先别盖住手绘）。
 * pointer-events 关掉：它是名字，不是一颗钮。
 */
.wmp-cap {
  position: relative;
  z-index: 3;
  width: max-content;
  margin: 0 0 -14px 4%;
  pointer-events: none;
}
.wc-zh {
  display: block;
  font-family: var(--font-body);
  font-size: var(--fs-l);
  letter-spacing: 0.16em;
  color: #fff3d8;
  text-shadow: 0 0 6px rgba(255, 226, 160, 0.9), 0 0 18px rgba(224, 178, 100, 0.6), 0 2px 5px rgba(0, 0, 0, 0.7);
  animation: wmpGlow 4.2s ease-in-out infinite;
}
/* 中英双语那套排法：中文一行紧跟英文一行，英文小很多、不折行 */
.wc-en {
  display: block;
  margin-top: 2px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.26em;
  color: rgba(255, 236, 196, 0.82);
  white-space: nowrap;
  text-shadow: 0 0 8px rgba(224, 178, 100, 0.7), 0 1px 3px rgba(0, 0, 0, 0.8);
}

@keyframes wmpGlow {
  0%, 100% { text-shadow: 0 0 5px rgba(255, 226, 160, 0.75), 0 0 14px rgba(224, 178, 100, 0.45), 0 2px 5px rgba(0, 0, 0, 0.7); }
  50%      { text-shadow: 0 0 9px rgba(255, 236, 190, 1), 0 0 26px rgba(224, 178, 100, 0.8), 0 2px 5px rgba(0, 0, 0, 0.7); }
}

/*
 * 一枚图钉：位置是**这张图自己的百分比**（数在 data/worldmap.js 的 x / y），
 * 所以纸宽一变（手机 / 桌面）针还钉在同一处地方，不会飘到别的故事上去。
 * 整颗按中心落点摆（translate -50%），针在上、字牌在下，读起来就是"这儿有个地方"。
 */
.wmp-spot {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

/* 针脚：一颗描金的小圆钉，底下拖一道短影，整颗在轻轻呼吸 */
.wms-pin {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  border: 1px solid rgba(58, 40, 22, 0.66);
  background: radial-gradient(circle at 34% 28%, #ffeec6, #c8904a 62%, #7d5223);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
  animation: wmsPulse 2.8s ease-in-out infinite;
}

@keyframes wmsPulse {
  0%, 100% { transform: scale(1); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45), 0 0 0 0 rgba(224, 178, 100, 0.5); }
  50%      { transform: scale(1.14); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45), 0 0 0 5px rgba(224, 178, 100, 0); }
}

/* 字牌：地图上一小块没画东西的地方垫一张小纸片，中文一行紧跟极小的英文一行 */
.wms-tx {
  padding: 2px var(--sp-2) 3px;
  border: 1px solid rgba(64, 48, 31, 0.4);
  border-radius: var(--r);
  background: rgba(255, 250, 236, 0.86);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.28);
  text-align: center;
  transition: transform 0.16s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.16s ease;
}

.wms-zh {
  display: block;
  font-family: var(--font-body);
  font-size: var(--fs-s);
  letter-spacing: 0.06em;
  color: var(--ink);
  white-space: nowrap;
}
.wms-en {
  display: block;
  font-family: var(--font-hand);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--ink-soft);
  white-space: nowrap;
}

.wmp-spot:hover .wms-tx {
  border-color: var(--red);
  transform: translateY(-2px);
}
.wmp-spot:active .wms-tx { transform: translateY(0) scale(0.97); }

@media (prefers-reduced-motion: reduce) {
  .wmap-btn, .wmb-ring, .wmb-spark, .wms-pin, .wc-zh { animation: none; }
  .wmb-spark { opacity: 0; }
}

/* ============================================================
   55 · 学院杯：礼堂那四根沙漏柱 + 详情那一屏（ui/housecup.js）
   ============================================================ */

/*
 * 规格第八节要的是"英伦魔法"，不是排行榜网页：
 * 石墙、羊皮纸、黄铜铭牌、学院旗帜、烛光与微尘。
 * 全部纯 CSS 画（不新增一张图），学院色与辅色由 JS 挂 --house / --accent
 * （色号唯一的来源是 data/houses.js 与 data/housecup.js，这里不抄第二份）。
 */

/* ───────── 一、大礼堂那一块：四根柱 ───────── */

.cup {
  position: relative;
  margin: 0 0 var(--sp-3);
  padding: var(--sp-3) var(--sp-4) var(--sp-4);
  border: 1px solid rgba(224, 178, 100, 0.28);
  border-radius: var(--r);
  background:
    radial-gradient(120% 80% at 50% -10%, rgba(255, 226, 160, 0.10), transparent 62%),
    linear-gradient(180deg, rgba(20, 14, 9, 0.82), rgba(9, 7, 5, 0.9));
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 236, 190, 0.12);
}

.cup-cap { text-align: center; margin-bottom: var(--sp-3); }
.cc-zh {
  display: block;
  font-family: var(--font-hand);
  font-size: var(--fs-l);
  letter-spacing: 0.2em;
  color: var(--cine-gold);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.8), 0 0 14px rgba(224, 178, 100, 0.35);
}
.cc-en {
  display: block;
  margin-top: 2px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.3em;
  color: rgba(224, 178, 100, 0.62);
}
/* 标题下面那道金线：两头淡出去，别切成两条硬边 */
.cup-cap::after {
  content: '';
  display: block;
  width: 72%;
  height: 1px;
  margin: var(--sp-2) auto 0;
  background: linear-gradient(90deg, transparent, rgba(224, 178, 100, 0.65), transparent);
}

.cup-cols {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: var(--sp-3);
}

.cup-col {
  position: relative;
  width: 21%;
  max-width: 96px;
  text-align: center;
}

.cu-crest {
  display: block;
  width: 30px;
  height: 30px;
  margin: 0 auto var(--sp-1);
  object-fit: contain;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.7));
}

/*
 * 沙漏管：一根竖着的暗玻璃管，分数从底下往上灌。
 * 高度用 --v（0–1，由 ui/housecup.js 现算）——第一名留一点头，不顶到天花板上。
 */
.cu-tube {
  position: relative;
  height: 96px;
  margin: 0 auto;
  border: 1px solid rgba(224, 178, 100, 0.22);
  border-radius: var(--r);
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.22));
  box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.7);
  overflow: hidden;
}

.cu-fill {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: calc(var(--v, 0) * 100%);
  background: linear-gradient(180deg, var(--house), rgba(0, 0, 0, 0.35));
  transition: height 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}
/* 液面那一道亮边：看着才像灌在管里的东西，不是一根色条 */
.cu-fill::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 2px;
  background: var(--accent, var(--cine-gold));
  opacity: 0.85;
  box-shadow: 0 0 8px var(--accent, var(--cine-gold));
}

.cu-pts {
  margin-top: var(--sp-1);
  font-family: var(--font-hand);
  font-size: var(--fs-l);
  letter-spacing: 0.04em;
  color: #f4e6c4;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
}
.cu-name {
  margin-top: 1px;
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  color: rgba(244, 230, 196, 0.8);
  white-space: nowrap;
}
.cu-week {
  margin-top: 2px;
  font-size: 10px;
  letter-spacing: 0.04em;
  color: rgba(213, 192, 154, 0.62);
  white-space: nowrap;
}
.cu-week.up { color: #8fd39a; }
.cu-week.down { color: #d98a86; }

/* 四院各自的气质（同一根管，各长各的脸色） */
.cup-col.gryffindor .cu-fill { box-shadow: 0 0 16px rgba(201, 162, 39, 0.35); }
.cup-col.hufflepuff .cu-tube { background: linear-gradient(180deg, rgba(60, 44, 16, 0.5), rgba(0, 0, 0, 0.2)); }
.cup-col.hufflepuff .cu-fill { box-shadow: 0 0 12px rgba(201, 162, 39, 0.28); }
/* 鹰院：管壁上几点慢慢闪的星（多枚径向渐变当背景，动的是 background-position） */
.cup-col.ravenclaw .cu-tube::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    radial-gradient(1.5px 1.5px at 20% 18%, rgba(220, 232, 255, 0.9), transparent 60%),
    radial-gradient(1px 1px at 68% 42%, rgba(220, 232, 255, 0.75), transparent 60%),
    radial-gradient(1.5px 1.5px at 42% 72%, rgba(220, 232, 255, 0.7), transparent 60%);
  animation: cupStars 5.5s ease-in-out infinite alternate;
}
@keyframes cupStars { from { opacity: 0.25; } to { opacity: 0.9; } }
/* 蛇院：管底一层慢慢呼吸的绿雾 */
.cup-col.slytherin .cu-tube::after {
  content: '';
  position: absolute;
  left: -20%;
  right: -20%;
  bottom: -10%;
  height: 60%;
  background: radial-gradient(60% 100% at 50% 100%, rgba(80, 190, 130, 0.32), transparent 70%);
  filter: blur(6px);
  animation: cupMist 6.5s ease-in-out infinite alternate;
}
@keyframes cupMist { from { opacity: 0.35; transform: translateY(4px); } to { opacity: 0.85; transform: translateY(-2px); } }

/* 第一名：古金铭牌 + 呼吸微光 + 一粒往上飘的金尘（规格第八节第 4 条） */
.cup-col.lead .cu-pts { color: #ffe9b6; }
.cup-col.lead::before {
  content: '';
  position: absolute;
  inset: -6px -4px 26px;
  border: 1px solid rgba(224, 178, 100, 0.5);
  border-radius: var(--r);
  animation: cupLead 3.6s ease-in-out infinite;
  pointer-events: none;
}
@keyframes cupLead {
  0%, 100% { box-shadow: 0 0 6px rgba(224, 178, 100, 0.25); opacity: 0.6; }
  50%      { box-shadow: 0 0 18px rgba(224, 178, 100, 0.6); opacity: 1; }
}
.cup-col.lead::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 46px;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  background: #ffe9b6;
  box-shadow: 0 0 6px 2px rgba(224, 178, 100, 0.7);
  opacity: 0;
  animation: cupDust 4.4s ease-out infinite;
  pointer-events: none;
}
@keyframes cupDust {
  0%   { transform: translateY(0) scale(0.6); opacity: 0; }
  18%  { opacity: 0.95; }
  100% { transform: translateY(-30px) scale(1.1); opacity: 0; }
}

.cup-foot { display: flex; justify-content: center; margin-top: var(--sp-3); }

/* ───────── 二、详情那一屏 ───────── */

.cup-board { margin-top: var(--sp-2); }

.cup-title { text-align: center; margin-bottom: var(--sp-3); }
.ct-zh {
  display: block;
  font-family: var(--font-hand);
  font-size: var(--fs-xl);
  letter-spacing: 0.18em;
  color: var(--cine-gold);
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.85);
}
.ct-en {
  display: block;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.3em;
  color: rgba(224, 178, 100, 0.6);
}
.ct-week {
  display: block;
  margin-top: var(--sp-1);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  color: rgba(213, 192, 154, 0.7);
}

.cup-cards { display: flex; flex-direction: column; gap: var(--sp-2); }

.cup-card {
  --house: var(--red);
  --accent: var(--cine-gold);
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid rgba(64, 48, 31, 0.4);
  border-left: 4px solid var(--house);
  border-radius: var(--r);
  background: linear-gradient(180deg, #fbf4e4, #eddfc0);
  box-shadow: 0 3px 0 rgba(50, 37, 24, 0.4), 0 12px 26px rgba(0, 0, 0, 0.45);
}
/* 学院那一点脸色淡淡铺在左边（用一层蒙版上 --house + 透明度，不依赖 color-mix） */
.cup-card > .ck-tint {
  position: absolute;
  inset: 0;
  border-radius: var(--r);
  background: linear-gradient(90deg, var(--house), transparent 55%);
  opacity: 0.12;
  pointer-events: none;
}
.ck-crest { width: 40px; height: 40px; object-fit: contain; flex: none; }
.ck-body { flex: 1 1 auto; min-width: 0; }
.ck-name { display: flex; align-items: baseline; gap: var(--sp-2); }
.ck-zh { font-size: var(--fs-m); font-weight: 700; letter-spacing: 0.06em; color: var(--ink); }
.ck-en { font-family: var(--font-hand); font-size: 10px; letter-spacing: 0.14em; color: var(--ink-soft); white-space: nowrap; }
.ck-line { display: flex; align-items: baseline; gap: var(--sp-2); margin-top: 2px; }
.ck-pts { font-family: var(--font-hand); font-size: var(--fs-xl); color: var(--ink); line-height: 1; }
.ck-rank {
  padding: 1px 6px;
  border: 1px solid rgba(64, 48, 31, 0.35);
  border-radius: var(--r);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  background: rgba(255, 252, 240, 0.6);
}
.ck-rank.tie { border-style: dashed; }
.ck-week { font-size: var(--fs-xs); letter-spacing: 0.04em; color: var(--ink-soft); }
.ck-week.up { color: #2f6b45; }
.ck-week.down { color: var(--red); }
.ck-why { margin-top: 3px; font-size: var(--fs-s); line-height: 1.6; color: var(--ink-soft); }

/* 第一名那一块：铭牌边上多一道金，慢慢呼吸 */
.cup-card.lead { border-color: rgba(201, 162, 39, 0.7); }
.cup-card.lead::after {
  content: '';
  position: absolute;
  inset: -1px;
  border: 1px solid rgba(201, 162, 39, 0.55);
  border-radius: var(--r);
  pointer-events: none;
  animation: cupLead 3.6s ease-in-out infinite;
}

.cup-sec { margin-top: var(--sp-4); }
.cs-cap {
  font-family: var(--font-hand);
  font-size: var(--fs-m);
  letter-spacing: 0.14em;
  color: var(--cine-gold);
  margin-bottom: var(--sp-2);
}

/* 本周那四条：一条一张小羊皮便签，各自歪一点点（和地图上那排牌子同一个手感） */
.cup-notes { display: flex; flex-direction: column; gap: var(--sp-2); }
.cup-note {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid rgba(64, 48, 31, 0.4);
  border-left: 3px solid var(--house, var(--red));
  border-radius: var(--r);
  background: linear-gradient(180deg, #fbf4e4, #eddfc0);
  box-shadow: 0 2px 0 rgba(50, 37, 24, 0.35), 0 8px 18px rgba(0, 0, 0, 0.4);
}
.cn-crest { width: 22px; height: 22px; object-fit: contain; flex: none; }
.cn-body { flex: 1 1 auto; min-width: 0; }
.cn-cap { font-size: var(--fs-xs); letter-spacing: 0.08em; color: var(--ink-soft); }
.cn-line { font-size: var(--fs-s); line-height: 1.55; color: var(--ink); }
.cn-pts { font-family: var(--font-hand); font-size: var(--fs-l); color: var(--ink-soft); flex: none; }
.cn-pts.up { color: #2f6b45; }
.cn-pts.down { color: var(--red); }

/* 往期：默认折叠，展开走 max-height + 淡入（不引动画库） */
.cup-hist { margin-top: var(--sp-4); }
.hc-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid rgba(224, 178, 100, 0.3);
  border-radius: var(--r);
  background: linear-gradient(180deg, rgba(30, 22, 14, 0.9), rgba(14, 10, 7, 0.92));
  cursor: pointer;
}
.hct-tx { font-family: var(--font-hand); font-size: var(--fs-m); letter-spacing: 0.12em; color: var(--cine-gold); }
.hct-ar { font-size: var(--fs-xs); letter-spacing: 0.08em; color: rgba(213, 192, 154, 0.75); }
.hc-body { max-height: 0; opacity: 0; overflow: hidden; transition: max-height 0.34s ease, opacity 0.28s ease; }
.cup-hist.open .hc-body { max-height: 340px; opacity: 1; overflow-y: auto; }
.hc-rows { display: flex; flex-direction: column; gap: 1px; padding-top: var(--sp-2); }
.hc-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-2);
  border-bottom: 1px dashed rgba(64, 48, 31, 0.22);
  font-size: var(--fs-xs);
  color: var(--ink-soft);
}
.hcr-week { flex: none; width: 5.4em; letter-spacing: 0.04em; }
.hcr-cell { flex: 1 1 0; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hcr-cell.up { color: #2f6b45; }
.hcr-cell.down { color: var(--red); }

.cup-none { font-size: var(--fs-s); color: var(--ink-soft); }

/*
 * 结算演出（规格第九节）：数据早就落好了，这里只是**这周第一次打开**时
 * 四张卡依次淡入、数字弹一下、便签随后。playedKey 在 ui/housecup.js 里记着，
 * 同一周关掉再打开不会再播第二遍。
 */
.cup-board.settling .cup-card { animation: cupCardIn 0.52s cubic-bezier(0.16, 1, 0.3, 1) backwards; animation-delay: calc(var(--i, 0) * 150ms); }
.cup-board.settling .ck-pts { animation: cupPop 0.5s ease-out backwards; animation-delay: calc(var(--i, 0) * 150ms + 120ms); }
.cup-board.settling .cup-note { animation: cupCardIn 0.5s ease-out backwards; animation-delay: calc(var(--i, 0) * 120ms + 620ms); }

@keyframes cupCardIn {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes cupPop {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.22); }
  100% { transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .cup-col.lead::before, .cup-col.lead::after,
  .cup-col.ravenclaw .cu-tube::after, .cup-col.slytherin .cu-tube::after,
  .cup-card.lead::after,
  .cup-board.settling .cup-card, .cup-board.settling .ck-pts, .cup-board.settling .cup-note { animation: none; }
  .cup-col.lead::after { opacity: 0; }
  .cu-fill { transition: none; }
}

/* ============================================================
   56 · 校报：一摊在信纸上的魔法报纸（ui/newspaper.js）
   ============================================================ */

/*
 * 规格第六节要的是"能翻阅的魔法报纸"，不是新闻门户：
 * 报头双细线、不对称头版、首字下沉、纸条歪着贴、蜡封、手写批注。
 * 全部纯 CSS（不新增一张图）；纸皮本身是 openSheet 那层 .sheet.letter 给的。
 *
 * ⚠️ 所有文字都是 textContent 上来的（h() 的 text），
 *    模型就算返回 <script> 也只会印成七个字符——这里没有任何一处把 AI 文本当 HTML。
 */

/* ───────── 栏顶那两行：开关与状态 ───────── */


.np-status {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  margin: 0 0 var(--sp-3);
  padding: var(--sp-1) var(--sp-2);
  border-top: 1px solid rgba(64, 48, 31, 0.22);
  border-bottom: 1px solid rgba(64, 48, 31, 0.22);
  font-size: var(--fs-xs);
  color: var(--ink-soft);
  text-align: center;
}
/* 没印出来那一行：暗红批注 + 一颗重试（创作者要的就是"把 API 原因原样说出来"） */
.np-status.bad { color: #7d2f28; border-color: rgba(140, 47, 47, 0.4); background: rgba(140, 47, 47, 0.06); }
.np-st-tx { min-width: 0; overflow-wrap: anywhere; }

.np-none { text-align: center; font-size: var(--fs-s); color: var(--ink-soft); padding: var(--sp-4) 0; }

/* 往期那几行 */
.np-hist { margin-top: var(--sp-4); }
.np-hist-cap {
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  letter-spacing: 0.16em;
  color: var(--ink-soft);
  border-bottom: 1px solid rgba(64, 48, 31, 0.28);
  margin-bottom: var(--sp-1);
}
.np-hist-row {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  width: 100%;
  padding: var(--sp-1) var(--sp-2);
  border: 0;
  border-bottom: 1px dashed rgba(64, 48, 31, 0.22);
  background: none;
  cursor: pointer;
  text-align: left;
  color: var(--ink);
  font-size: var(--fs-s);
}
.np-hist-row:hover { background: rgba(255, 250, 235, 0.7); }
.nhr-week { flex: none; width: 6.4em; font-family: var(--font-hand); font-size: var(--fs-xs); color: var(--ink-soft); }
.nhr-tx { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ───────── 报纸本体 ───────── */

.np { color: var(--ink); animation: npOpen 0.42s cubic-bezier(0.16, 1, 0.3, 1) backwards; }
/* 摊开那一下：从中间"展开"一张纸（不是淡入，淡入像弹窗） */
@keyframes npOpen {
  from { opacity: 0; transform: perspective(900px) rotateX(-7deg) translateY(6px); }
  to   { opacity: 1; transform: none; }
}

.np-mast { text-align: center; padding: var(--sp-1) 0 var(--sp-2); border-top: 3px double rgba(64, 48, 31, 0.6); border-bottom: 3px double rgba(64, 48, 31, 0.6); }
.np-name {
  display: block;
  font-family: var(--font-hand);
  font-size: var(--fs-2xl);
  letter-spacing: 0.16em;
  color: #2a1d10;
  text-shadow: 0 1px 0 rgba(255, 250, 235, 0.7);
}
.np-en {
  display: block;
  margin-top: 1px;
  font-family: var(--font-hand);
  font-size: var(--fs-xs);
  letter-spacing: 0.34em;
  color: var(--ink-soft);
}
.np-strip {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-2);
  margin-top: var(--sp-1);
  padding-top: 3px;
  border-top: 1px solid rgba(64, 48, 31, 0.3);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
}

.np-front { text-align: center; margin: var(--sp-3) 0; }
.np-kicker {
  display: inline-block;
  padding: 1px var(--sp-2);
  border: 1px solid rgba(140, 47, 47, 0.5);
  color: #7d2f28;
  font-size: 10px;
  letter-spacing: 0.2em;
}
.np-title {
  margin: var(--sp-1) 0 2px;
  font-family: var(--font-hand);
  font-size: var(--fs-2xl);
  line-height: 1.24;
  letter-spacing: 0.04em;
  color: #241809;
}
.np-sub { font-size: var(--fs-s); color: var(--ink-soft); }

/* 不对称两栏：左大右小，中间一道竖分栏线（报纸那味儿全靠它） */
.np-cols {
  display: grid;
  grid-template-columns: 1.62fr 1fr;
  gap: var(--sp-4);
  padding-top: var(--sp-3);
  border-top: 1px solid rgba(64, 48, 31, 0.34);
}
.np-side { border-left: 1px solid rgba(64, 48, 31, 0.28); padding-left: var(--sp-4); }

.np-h {
  margin: 0 0 var(--sp-1);
  font-family: var(--font-hand);
  font-size: var(--fs-m);
  letter-spacing: 0.08em;
  color: #2a1d10;
  border-bottom: 1px solid rgba(64, 48, 31, 0.26);
  padding-bottom: 2px;
}
.np-h.wide { text-align: center; }
.np-p { margin: 0 0 var(--sp-2); font-size: var(--fs-s); line-height: 1.75; }
.np-by { display: block; margin-bottom: var(--sp-3); font-size: 10px; color: var(--ink-soft); text-align: right; }

/* 主编导语：首字下沉那一枚 */
.np-intro { position: relative; margin: 0 0 var(--sp-4); font-size: var(--fs-s); line-height: 1.8; }
.np-drop {
  float: left;
  margin: 2px 6px 0 0;
  font-family: var(--font-hand);
  font-size: 34px;
  line-height: 0.9;
  color: #7d2f28;
}

/* ───────── 学院杯专题 ───────── */

.np-cup { margin-top: var(--sp-3); }
.np-cup-sum { margin: 0 0 var(--sp-2); font-size: var(--fs-s); line-height: 1.7; color: var(--ink-soft); }
.np-cup-row {
  --house: var(--red);
  display: grid;
  grid-template-columns: 26px 1fr auto;
  align-items: start;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-2) var(--sp-2) var(--sp-1);
  border-left: 3px solid var(--house);
  border-bottom: 1px solid rgba(64, 48, 31, 0.18);
}
.ncr-crest { width: 26px; height: 26px; object-fit: contain; }
.ncr-name { font-size: var(--fs-m); letter-spacing: 0.06em; }
.ncr-note { margin-top: 2px; font-size: var(--fs-xs); line-height: 1.6; color: var(--ink); }
.ncr-why { margin-top: 2px; font-size: 10px; color: var(--ink-soft); }
.ncr-num { text-align: right; white-space: nowrap; }
.ncr-delta { display: block; font-family: var(--font-hand); font-size: var(--fs-l); line-height: 1; }
.ncr-from { display: block; margin-top: 2px; font-size: 10px; color: var(--ink-soft); }
/* 加分：墨迹往上浮一点 + 绿；扣分：暗红 + 一道斜批；零：中性灰 */
.np-cup-row.up .ncr-delta { color: #2f6b45; text-shadow: 0 0 8px rgba(47, 107, 69, 0.22); }
.np-cup-row.down .ncr-delta { color: #7d2f28; text-decoration: underline wavy rgba(140, 47, 47, 0.55); text-underline-offset: 3px; }
.np-cup-row.flat .ncr-delta { color: var(--ink-soft); }
.np-cup-row.up { background: linear-gradient(90deg, rgba(224, 178, 100, 0.12), transparent 46%); }

/* ───────── 匿名爆料 / 金句 / 下周预告 ───────── */

.np-tip { position: relative; margin-bottom: var(--sp-3); padding-right: 26px; }
.np-tip .np-p { font-style: italic; }
.np-wax {
  position: absolute;
  right: 0;
  top: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #b0463d, #6d1f1a 68%);
  box-shadow: inset 0 -1px 2px rgba(0, 0, 0, 0.5), 0 1px 3px rgba(0, 0, 0, 0.35);
  animation: npWax 5.5s ease-in-out infinite alternate;
}
@keyframes npWax { from { filter: brightness(0.94); } to { filter: brightness(1.08); } }
.np-tip.humor .np-wax { background: radial-gradient(circle at 34% 30%, #d9b25a, #8a6a1c 68%); }
.np-tip.unconfirmed .np-wax { background: radial-gradient(circle at 34% 30%, #8f8f8a, #4c4a46 68%); }

.np-quote { position: relative; margin: 0 0 var(--sp-3); padding: var(--sp-2) var(--sp-2) 0 var(--sp-4); border-top: 1px solid rgba(64, 48, 31, 0.26); }
.np-q-mark { position: absolute; left: -2px; top: 2px; font-family: var(--font-hand); font-size: 34px; color: rgba(140, 47, 47, 0.5); }
.np-q-tx { margin: 0; font-family: var(--font-hand); font-size: var(--fs-m); line-height: 1.5; }
.np-q-by { display: block; margin-top: 2px; font-size: 10px; color: var(--ink-soft); text-align: right; }

.np-next { padding: var(--sp-2); border: 1px dashed rgba(64, 48, 31, 0.42); background: rgba(255, 250, 235, 0.5); }
.np-next .np-h { border-bottom: 0; }

/* ───────── 学生论坛：一张张歪着贴的纸条 ───────── */

.np-forum { margin-top: var(--sp-4); padding-top: var(--sp-3); border-top: 3px double rgba(64, 48, 31, 0.5); }
.np-post {
  --house: rgba(64, 48, 31, 0.5);
  position: relative;
  margin: 0 0 var(--sp-3);
  padding: var(--sp-3) var(--sp-3) var(--sp-2);
  border: 1px solid rgba(64, 48, 31, 0.34);
  border-radius: 1px;
  background: linear-gradient(180deg, #fdf7e8, #f0e3c6);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.22);
  transform: rotate(var(--tilt, -0.5deg));
}
/* 每张纸条歪得不一样（四张一轮换个方向），看着才像随手贴上去的 */
.np-post:nth-child(4n + 1) { --tilt: -0.7deg; }
.np-post:nth-child(4n + 2) { --tilt: 0.55deg; }
.np-post:nth-child(4n + 3) { --tilt: -0.3deg; }
.np-post:nth-child(4n + 4) { --tilt: 0.9deg; }
/* 顶上一条胶带（半透明、边缘毛一点） */
.np-tape {
  position: absolute;
  left: 50%;
  top: -7px;
  width: 54px;
  height: 14px;
  transform: translateX(-50%) rotate(-2deg);
  background: rgba(224, 196, 140, 0.55);
  border-left: 1px dashed rgba(255, 255, 255, 0.5);
  border-right: 1px dashed rgba(255, 255, 255, 0.5);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
}
.np-post-head { display: flex; align-items: center; gap: var(--sp-1); flex-wrap: wrap; font-size: var(--fs-xs); }
.np-author { font-weight: 700; color: #2a1d10; }
.np-house {
  padding: 0 5px;
  border: 1px solid var(--house);
  border-radius: 2px;
  font-size: 10px;
  color: var(--ink-soft);
}
.np-mood { font-size: 12px; }
.np-mood.m-excited { color: #b4863c; }
.np-mood.m-angry { color: #8c2f2f; }
.np-mood.m-confused { transform: rotate(12deg); display: inline-block; color: var(--ink-soft); }
.np-mood.m-amused { color: #6a7d4a; }
.np-likes { margin-left: auto; font-size: 10px; color: var(--ink-soft); }
.np-post-title { margin-top: 4px; font-family: var(--font-hand); font-size: var(--fs-m); }
.np-post-body { margin: 3px 0 0; font-size: var(--fs-s); line-height: 1.7; }
/* 楼中楼：往里缩一层、纸色暗一档、左边一道手写线 */
.np-reply {
  display: flex;
  align-items: baseline;
  gap: var(--sp-1);
  margin: var(--sp-1) 0 0 50%;
  padding-left: var(--sp-2);
  border-left: 2px solid rgba(64, 48, 31, 0.3);
  font-size: var(--fs-xs);
  line-height: 1.6;
  color: var(--ink-soft);
}
.np-reply .np-author { color: var(--ink); }
.np-reply-tx { min-width: 0; overflow-wrap: anywhere; }

/* ───────── 窄屏：两栏收成一列，缩进收一档 ───────── */

@media (max-width: 620px) {
  .np-cols { grid-template-columns: 1fr; }
  .np-side { border-left: 0; padding-left: 0; border-top: 1px solid rgba(64, 48, 31, 0.28); padding-top: var(--sp-3); }
  .np-reply { margin-left: var(--sp-3); }
  .np-title, .np-name { font-size: var(--fs-xl); }
}

@media (prefers-reduced-motion: reduce) {
  .np, .np-wax { animation: none; }
}

/* ============================================================
   57 · 斗兽场挑战那六关（ui/challenge.js）
   ============================================================ */

/*
 * 一块石台一行的名单：左边那颗头（各自物种的闪耀立绘）、中间四行字、右边那颗动作。
 * 手感跟着龙斗场那一屏走（暗石 + 铁牌），但**已通关的那一行要压暗一档**、
 * 没解锁的那一行要说清差哪一关——不摆一颗按不动的钮（那只会让人以为它坏了）。
 */

.chg-progress {
  margin: 0 0 var(--sp-2);
  font-family: var(--font-hand);
  font-size: var(--fs-s);
  letter-spacing: 0.14em;
  color: var(--cine-gold);
}

.chg-list { display: flex; flex-direction: column; gap: var(--sp-2); }

.chg-row {
  display: grid;
  grid-template-columns: 46px 1fr auto;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid rgba(224, 178, 100, 0.22);
  border-radius: var(--r);
  background: linear-gradient(180deg, rgba(38, 28, 18, 0.9), rgba(16, 12, 8, 0.92));
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 236, 190, 0.08);
}
/* 打通了的那一行压暗一档、边上留一道金（留着的证据，不是"这行坏了"） */
.chg-row.cleared { opacity: 0.86; border-color: rgba(224, 178, 100, 0.45); }
.chg-row.cleared .beast-art { filter: saturate(0.8); }
/* 还没解锁的那一行：整行沉下去，右边那句说清差哪一关 */
.chg-row.locked { opacity: 0.5; }
.chg-row.locked .beast-art { filter: grayscale(0.7); }

/* 那颗头：beastArtNode 挂的是 .beast-art（不是 .chg-face），所以按那个类来定尺寸 */
.chg-row .beast-art {
  width: 46px;
  height: 46px;
  object-fit: cover;
  object-position: 50% 22%;
  border-radius: 50%;
  border: 1px solid rgba(224, 178, 100, 0.4);
}

.chg-body { min-width: 0; }
.chg-cap { display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; }
.chg-no { font-family: var(--font-hand); font-size: var(--fs-xs); letter-spacing: 0.16em; color: var(--cine-gold); }
.chg-tier {
  padding: 0 5px;
  border: 1px solid rgba(224, 178, 100, 0.35);
  border-radius: 2px;
  font-size: 10px;
  color: rgba(224, 178, 100, 0.85);
}
.chg-zh { font-size: var(--fs-m); font-weight: 700; letter-spacing: 0.06em; color: #f4e6c4; }
.chg-en { font-family: var(--font-hand); font-size: 10px; letter-spacing: 0.14em; color: rgba(213, 192, 154, 0.6); }

.chg-stats { margin-top: 2px; font-size: var(--fs-xs); letter-spacing: 0.04em; color: rgba(213, 192, 154, 0.8); }
.chg-abil { margin-top: 2px; font-size: var(--fs-xs); line-height: 1.6; color: #e8cfa0; }
.chg-goal { margin-top: 2px; font-size: 10px; line-height: 1.6; color: rgba(213, 192, 154, 0.62); }
.chg-reward { margin-top: 2px; font-size: var(--fs-xs); color: #9dbf8a; }

.chg-act { flex: none; display: flex; align-items: center; }
.chg-lock { font-size: 10px; letter-spacing: 0.04em; color: rgba(213, 192, 154, 0.6); text-align: right; max-width: 6em; }

.chg-foot { margin-top: var(--sp-3); font-size: var(--fs-xs); color: rgba(213, 192, 154, 0.62); text-align: center; }

/* 那块铁牌现在点得动了：给一点"能进"的暗示（呼吸的一道光），但别抢每周节目那张节目单 */
.arena-gate.challenge.live { cursor: pointer; }
.arena-gate.challenge.live::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(224, 178, 100, 0.3);
  animation: chgPlate 4.6s ease-in-out infinite alternate;
  pointer-events: none;
}
@keyframes chgPlate { from { opacity: 0.25; } to { opacity: 0.9; } }

@media (prefers-reduced-motion: reduce) {
  .arena-gate.challenge.live::after { animation: none; }
}


/* ───────── 正在写：按了【立刻获取】之后那几十秒要有东西看 ───────── */

/*
 * 那一趟请求最长能跑 90 秒（ai/local.js 的 TIMEOUT_MS）。这段空白里如果纸上什么都不动，
 * 玩家分不清"在写"和"卡死了"——所以：一根斜着抖的羽毛笔 + 三颗呼吸的点，
 * 加上一句直述（"请求已经发出去，等接口回稿"）。纯 CSS，不引库。
 */
.np-writing {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0 0 var(--sp-3);
  padding: var(--sp-3);
  border: 1px solid rgba(224, 178, 100, 0.35);
  border-radius: var(--r);
  background: linear-gradient(180deg, rgba(255, 250, 235, 0.72), rgba(240, 228, 202, 0.6));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4);
}

.npw-quill {
  flex: none;
  font-size: 26px;
  line-height: 1;
  color: #40301f;
  transform-origin: 50% 90%;
  animation: npwWrite 1.1s ease-in-out infinite;
}
@keyframes npwWrite {
  0%, 100% { transform: rotate(-8deg) translateY(0); }
  50%      { transform: rotate(6deg) translateY(-2px); }
}

.npw-body { flex: 1 1 auto; min-width: 0; }
.npw-tx { font-size: var(--fs-m); letter-spacing: 0.06em; color: var(--ink); }
.npw-sub { margin-top: 2px; font-size: var(--fs-xs); line-height: 1.6; color: var(--ink-soft); }

/* 三颗点：一颗一颗亮过去（不是一起闪——一起闪看着像报错） */
.npw-dots { flex: none; display: inline-flex; gap: 4px; }
.npw-dots i {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: #8a6a1c;
  opacity: 0.25;
  animation: npwDot 1.2s ease-in-out infinite;
}
.npw-dots i:nth-child(2) { animation-delay: 0.2s; }
.npw-dots i:nth-child(3) { animation-delay: 0.4s; }
@keyframes npwDot { 0%, 100% { opacity: 0.2; } 40% { opacity: 1; } }


@media (prefers-reduced-motion: reduce) {
  .npw-quill, .npw-dots i { animation: none; }
  .npw-dots i { opacity: 0.7; }
}

/* ───────── 校报到了：那张小窗的封面 ───────── */

.npa { text-align: center; padding: var(--sp-2) 0 var(--sp-1); }
.npa-from {
  display: block;
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  color: #8a6a1c;
}
.npa-title {
  margin: var(--sp-1) 0 2px;
  font-family: var(--font-hand);
  font-size: var(--fs-xl);
  line-height: 1.3;
  letter-spacing: 0.04em;
  color: #241809;
}
.npa-title::after {
  content: '';
  display: block;
  width: 68%;
  height: 1px;
  margin: var(--sp-2) auto 0;
  background: linear-gradient(90deg, transparent, rgba(224, 178, 100, 0.75), transparent);
}
.npa-sub { margin: 6px 0 0; font-size: var(--fs-s); color: var(--ink-soft); }
.npa-hint { margin: var(--sp-2) 0 0; font-size: var(--fs-xs); color: var(--ink-soft); }

/*
 * 展开那一期的那一下（点开报纸 / 从小窗【展开这一期】进来）：
 * 从顶上"放下来"展开 + 两头同时往中间收，像把一张报纸从折缝里抖开；
 * 顶上那道双线顺势"画"出来。整段 0.5 秒，不挡阅读（.np 上的动画）。
 */
.np { transform-origin: top center; animation: npOpen 0.5s cubic-bezier(0.16, 1, 0.3, 1) backwards, npSpread 0.5s cubic-bezier(0.16, 1, 0.3, 1) backwards; }
@keyframes npSpread {
  from { clip-path: inset(0 42% 0 42%); opacity: 0.35; }
  to   { clip-path: inset(0 0 0 0); opacity: 1; }
}
.np-mast { animation: npRule 0.6s ease-out backwards; }
@keyframes npRule {
  from { border-top-width: 0; border-bottom-width: 0; }
  to   { border-top-width: 3px; border-bottom-width: 3px; }
}

@media (prefers-reduced-motion: reduce) {
  .np, .np-mast { animation: none; }
}
/* ============================================================
   58 · 魔法来信：拆一封信的那一整套场面（ui/envelope.js）
   ============================================================ */

/*
 * 一层暗幕 + 一只信封 + 一封羊皮纸。整段流程由根节点上的 `data-phase` 驱动：
 *   idle → arriving → sealed → breaking → opening → rising → unfolding → reading
 * 每一步只让该动的那几样动（见 ui/envelope.js 顶上那个状态机）。
 *
 * 时长一律走这一排自定义属性，改速度只改这一处；少动效那一档把它们压到 1ms
 * （JS 那边也会走缩短的时序，两边对得上）。
 * 颜色取创作者那份规格里那七个，按现有主题微调过（低饱和、复古）。
 */
.env-layer {
  --t-in: 1100ms;
  --t-break: 520ms;
  --t-open: 680ms;
  --t-rise: 660ms;
  --t-unfold: 780ms;
  --parch: #f1e4c5;
  --parch-deep: #e2ceA0;
  --env-paper: #e8d4a8;
  --env-deep: #4a3025;
  --ink: #30251e;
  --wax: #76252b;
  --brass: #b89a61;
  --moss: #263b31;

  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  overflow: hidden;
}

/* 暗幕：不是纯黑，是烛光下那种暖褐（规格第二节：背景不能比信抢眼） */
.env-scrim {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 90% at 50% 40%, rgba(24, 17, 11, 0.72), rgba(10, 7, 5, 0.9) 72%),
    rgba(12, 9, 6, 0.55);
  backdrop-filter: blur(2px);
}

.env-stage {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-4);
}

.env-x {
  position: absolute;
  right: 0;
  top: -6px;
  width: 34px;
  height: 34px;
  border: 1px solid rgba(184, 154, 97, 0.4);
  border-radius: var(--r);
  background: rgba(24, 17, 11, 0.75);
  color: rgba(241, 228, 197, 0.85);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
.env-x:focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; }

/* ───────── 一、信封 ───────── */

.env-letter {
  position: relative;
  width: min(86vw, 420px);
  aspect-ratio: 1.62;
  perspective: 1200px;
  transform-style: preserve-3d;
  filter: drop-shadow(0 26px 40px rgba(0, 0, 0, 0.62));
  transition: height var(--t-unfold) cubic-bezier(0.22, 1, 0.3, 1);
}

/*
 * 展开之后，这一块要从"一只信封的尺寸"长成"一页读信的尺寸"（手机上尤其要紧：
 * 信封那比例只有 249px 高，一封长信在那儿读是要命的）。
 * 同时把信封那几片淡掉——纸已经盖在上面了，留着只会在边缘露出一圈信封的边。
 */
.env-layer[data-phase='unfolding'] .env-letter,
.env-layer[data-phase='reading'] .env-letter {
  aspect-ratio: auto;
  height: min(74vh, 600px);
}
.env-layer[data-phase='reading'] .env-back,
.env-layer[data-phase='reading'] .env-pocket,
.env-layer[data-phase='reading'] .env-pocket-front,
.env-layer[data-phase='reading'] .env-flap {
  opacity: 0;
  transition: opacity 320ms ease;
}

/* 信封本体（背板）：旧纸色 + 一点纤维纹理（两道极淡的斜纹） */
.env-back {
  position: absolute;
  inset: 0;
  border-radius: 6px;
  background:
    repeating-linear-gradient(102deg, rgba(120, 90, 50, 0.05) 0 3px, transparent 3px 7px),
    linear-gradient(160deg, var(--env-paper), #d9c295 62%, #c9b083);
  box-shadow: inset 0 1px 0 rgba(255, 250, 235, 0.5), inset 0 -10px 20px rgba(74, 48, 37, 0.18);
}

/* 信封内部的深色夹层（翻盖掀开之后露出来的那一片暗） */
.env-pocket {
  position: absolute;
  left: 4%;
  right: 4%;
  top: 12%;
  bottom: 6%;
  border-radius: 4px;
  background: linear-gradient(180deg, #3a271c, #241811 70%);
  box-shadow: inset 0 8px 18px rgba(0, 0, 0, 0.55);
  /* 封口还盖着的时候里面不该露出来——否则一只封好的信封上先横着一道黑 V */
  opacity: 0;
  transition: opacity var(--t-open) ease;
}

/* 信封前侧折面：底下那两片折角（左右各一，靠 clip-path 切出来） */
.env-pocket-front {
  position: absolute;
  inset: 0;
  border-radius: 6px;
  background: linear-gradient(180deg, rgba(232, 212, 168, 0.15), transparent 40%),
    linear-gradient(200deg, #e3cfa5 0%, #d7c093 40%, #cbb083 100%);
  background-color: transparent;
  clip-path: polygon(0 100%, 0 26%, 50% 62%, 100% 26%, 100% 100%);
  box-shadow: inset 0 -1px 0 rgba(255, 250, 235, 0.35);
}

/* 上面的翻盖：绕**顶边**翻过去（transform-origin: top） */
.env-flap {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 58%;
  transform-origin: top center;
  transform: rotateX(0deg);
  backface-visibility: hidden;
  background: linear-gradient(180deg, #e6d3a9, #d5bd8f 70%, #c4a97c);
  clip-path: polygon(0 0, 100% 0, 50% 100%);
  box-shadow: 0 6px 14px rgba(74, 48, 37, 0.22);
  transition: transform var(--t-open) cubic-bezier(0.3, 1.3, 0.4, 1);
}

/* ───────── 二、封蜡 ───────── */

.env-seal {
  position: absolute;
  left: 50%;
  top: 46%;
  width: 76px;
  height: 76px;
  margin: -38px 0 0 -38px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  transform-style: preserve-3d;
}
.env-seal:focus-visible .es-wax { outline: 2px solid var(--brass); outline-offset: 4px; }
.env-seal[disabled] { cursor: default; }

/* 蜡体：不规则圆（border-radius 四个角各不相同）+ 厚度（内阴影 + 投影） */
.es-wax {
  position: absolute;
  inset: 6px;
  border-radius: 48% 52% 55% 45% / 52% 46% 54% 48%;
  background:
    radial-gradient(circle at 34% 30%, #a13a3f, var(--wax) 58%, #4e161b 100%);
  box-shadow:
    inset 0 2px 3px rgba(255, 190, 190, 0.28),
    inset 0 -4px 8px rgba(0, 0, 0, 0.5),
    0 4px 10px rgba(0, 0, 0, 0.5);
  transition: transform 160ms ease, filter 200ms ease;
}
/* 中心那枚徽章：盾形轮廓 + 一道横档（原创风格化纹章，不抄任何官方徽章） */
.es-crest {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 40%;
  height: 44%;
  margin: -22% 0 0 -20%;
  clip-path: polygon(50% 0, 100% 14%, 100% 58%, 50% 100%, 0 58%, 0 14%);
  background: linear-gradient(180deg, #e7cf9c, var(--brass) 55%, #8a6f3f);
  box-shadow: inset 0 1px 0 rgba(255, 250, 235, 0.6), inset 0 -2px 3px rgba(74, 48, 37, 0.5);
  opacity: 0.92;
}
.es-crest::after {
  content: '';
  position: absolute;
  left: 12%;
  right: 12%;
  top: 44%;
  height: 2px;
  background: rgba(74, 48, 37, 0.55);
}

/* 悬停 / 聚焦：轻轻浮起一点 + 镀一层金（克制，不抖） */
.env-layer[data-phase='sealed'] .env-seal:hover .es-wax,
.env-layer[data-phase='sealed'] .env-seal:focus-visible .es-wax {
  transform: translateY(-2px) scale(1.04);
  filter: drop-shadow(0 0 10px rgba(184, 154, 97, 0.55));
}

/* 裂纹：两条交叉的细缝，点下去之后从中间长开 */
.es-crack {
  position: absolute;
  inset: 12px;
  border-radius: 50%;
  background:
    linear-gradient(112deg, transparent 48.4%, rgba(24, 10, 10, 0.85) 48.8% 49.6%, transparent 50%),
    linear-gradient(58deg, transparent 47.6%, rgba(24, 10, 10, 0.7) 48% 48.9%, transparent 49.4%);
  opacity: 0;
  transform: scale(0.3);
  transition: opacity 160ms ease, transform var(--t-break) ease-out;
}

/* 蜡片碎屑：四片小三角，点下去那一刻往外轻散 */
.es-dust { position: absolute; inset: 0; opacity: 0; transition: opacity var(--t-break) ease; }
.es-dust i {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 8px;
  height: 6px;
  background: var(--wax);
  clip-path: polygon(0 0, 100% 34%, 22% 100%);
}
.es-dust i:nth-child(1) { --dx: -26px; --dy: -20px; --dr: -140deg; }
.es-dust i:nth-child(2) { --dx: 24px; --dy: -24px; --dr: 120deg; }
.es-dust i:nth-child(3) { --dx: -30px; --dy: 14px; --dr: 90deg; }
.es-dust i:nth-child(4) { --dx: 28px; --dy: 18px; --dr: -110deg; }

/* ───────── 三、羊皮纸 ───────── */

.env-parch {
  position: absolute;
  left: 6%;
  right: 6%;
  top: 16%;
  height: 78%;
  overflow: hidden;
  border-radius: 4px;
  opacity: 0;
  /*
   * ⚠️ 还盖着的时候这一层**必须不吃点击**：它是最后挂上去的（DOM 在封蜡之后），
   *    opacity 0 照样会挡住底下的东西——实测就是"封蜡看得见、点不动"
   *    （2026-10-12 创作者报的：这封信打不开）。翻到 reading 那一步再交回点击，
   *    因为那时候纸里要能滚。
   */
  pointer-events: none;
  transform: translateY(14%) scale(0.96);
  transition: transform var(--t-rise) cubic-bezier(0.22, 1, 0.3, 1), opacity var(--t-rise) ease,
    top var(--t-unfold) cubic-bezier(0.22, 1, 0.3, 1), height var(--t-unfold) cubic-bezier(0.22, 1, 0.3, 1),
    left var(--t-unfold) ease, right var(--t-unfold) ease;
}
/* 摊开成阅读区之后，这张纸自己要能滚 */
.env-layer[data-phase='reading'] .env-parch { pointer-events: auto; }

/* 卷着的那一截：展开时往上收掉（模拟卷纸打开，不是整张一起 scale） */
.ep-scroll {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 62%;
  background: linear-gradient(180deg, rgba(48, 37, 30, 0.28), rgba(48, 37, 30, 0.55));
  transition: height var(--t-unfold) cubic-bezier(0.22, 1, 0.3, 1);
  pointer-events: none;
}

.ep-paper {
  position: relative;
  height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--sp-4) clamp(14px, 5%, 34px) var(--sp-4);
  background:
    radial-gradient(120% 60% at 50% 0%, rgba(255, 250, 235, 0.55), transparent 60%),
    repeating-linear-gradient(94deg, rgba(120, 90, 50, 0.035) 0 3px, transparent 3px 8px),
    linear-gradient(180deg, var(--parch), var(--parch-deep));
  box-shadow: inset 0 0 60px rgba(120, 90, 50, 0.22), inset 0 2px 0 rgba(255, 250, 235, 0.5);
  color: var(--ink);
}

.ep-crest {
  width: 26px;
  height: 30px;
  margin: 0 auto var(--sp-2);
  clip-path: polygon(50% 0, 100% 14%, 100% 58%, 50% 100%, 0 58%, 0 14%);
  background: linear-gradient(180deg, #d8bf8b, var(--brass));
  opacity: 0.85;
}
.ep-school {
  text-align: center;
  font-family: var(--font-hand);
  font-size: var(--fs-m);
  letter-spacing: 0.2em;
  color: #5a4227;
}
.ep-title {
  text-align: center;
  margin-top: 4px;
  font-family: var(--font-hand);
  font-size: var(--fs-xl);
  letter-spacing: 0.1em;
  color: #3f2d1a;
}
.ep-rule {
  height: 1px;
  margin: var(--sp-2) auto var(--sp-3);
  width: 76%;
  background: linear-gradient(90deg, transparent, rgba(90, 66, 39, 0.6), transparent);
}
.ep-hello { margin: 0 0 var(--sp-2); font-size: var(--fs-m); letter-spacing: 0.04em; }
.ep-body p { margin: 0 0 var(--sp-3); font-size: var(--fs-s); line-height: 1.95; text-indent: 2em; }
.ep-sign { margin-top: var(--sp-4); text-align: right; }
.ep-sign-name {
  display: block;
  font-family: var(--font-hand);
  font-size: var(--fs-l);
  letter-spacing: 0.06em;
  color: #3f2d1a;
}
.ep-sign-title, .ep-sign-date { display: block; font-size: var(--fs-xs); color: #6b543a; }

/* 正文字段一格一格淡入（不逐字打字——长信那样读起来是要命的） */
.env-layer[data-phase='reading'] .ep-school,
.env-layer[data-phase='reading'] .ep-title,
.env-layer[data-phase='reading'] .ep-rule,
.env-layer[data-phase='reading'] .ep-hello,
.env-layer[data-phase='reading'] .ep-body p,
.env-layer[data-phase='reading'] .ep-sign {
  animation: envIn 420ms ease backwards;
}
.env-layer[data-phase='reading'] .ep-title { animation-delay: 60ms; }
.env-layer[data-phase='reading'] .ep-rule { animation-delay: 110ms; }
.env-layer[data-phase='reading'] .ep-hello { animation-delay: 160ms; }
.env-layer[data-phase='reading'] .ep-body p:nth-child(1) { animation-delay: 220ms; }
.env-layer[data-phase='reading'] .ep-body p:nth-child(2) { animation-delay: 280ms; }
.env-layer[data-phase='reading'] .ep-body p:nth-child(3) { animation-delay: 340ms; }
.env-layer[data-phase='reading'] .ep-body p:nth-child(4) { animation-delay: 400ms; }
.env-layer[data-phase='reading'] .ep-body p:nth-child(n + 5) { animation-delay: 460ms; }
.env-layer[data-phase='reading'] .ep-sign { animation-delay: 540ms; }
@keyframes envIn {
  from { opacity: 0; transform: translateY(5px); }
  to   { opacity: 1; transform: none; }
}

/* ───────── 四、按 phase 走的那一整条线 ───────── */

/* idle：还没飞进来（信封在画面外，略小略斜） */
.env-layer[data-phase='idle'] .env-letter {
  opacity: 0;
  transform: translate(-64vw, 44vh) rotate(-14deg) scale(0.8);
}
.env-layer[data-phase='idle'] .env-hint,
.env-layer[data-phase='idle'] .env-done { opacity: 0; }

/* arriving：从边上飞进来，慢慢减到停（带一点回弹） */
.env-layer[data-phase='arriving'] .env-letter {
  animation: envFlyIn var(--t-in) cubic-bezier(0.18, 0.9, 0.22, 1) both;
}
@keyframes envFlyIn {
  0%   { opacity: 0; transform: translate(-64vw, 44vh) rotate(-14deg) scale(0.78); }
  62%  { opacity: 1; transform: translate(0, 0) rotate(1.4deg) scale(1.03); }
  80%  { transform: translate(0, -1.4%) rotate(-0.6deg) scale(0.995); }
  100% { opacity: 1; transform: none; }
}
.env-layer[data-phase='arriving'] .env-hint { animation: envIn 300ms ease 600ms backwards; }

/* sealed：停稳了，封蜡开始呼吸（提示"这儿能点"） */
.env-layer[data-phase='sealed'] .env-hint { opacity: 1; }
.env-layer[data-phase='sealed'] .es-wax,
.env-layer[data-phase='sealed'] .es-crest { animation: envSeal 2.8s ease-in-out infinite; }
@keyframes envSeal {
  0%, 100% { filter: drop-shadow(0 0 0 rgba(184, 154, 97, 0)); }
  50%      { filter: drop-shadow(0 0 12px rgba(184, 154, 97, 0.6)); }
}

/* breaking：裂纹长开 + 一闪 + 蜡片散掉 */
.env-layer[data-phase='breaking'] .es-wax,
.env-layer[data-phase='opening'] .es-wax,
.env-layer[data-phase='rising'] .es-wax,
.env-layer[data-phase='unfolding'] .es-wax,
.env-layer[data-phase='reading'] .es-wax { opacity: 0; transform: scale(0.86); transition: opacity 280ms ease 160ms, transform 320ms ease; }
.env-layer[data-phase='breaking'] .es-crack { opacity: 1; transform: scale(1); }
.env-layer[data-phase='breaking'] .es-dust,
.env-layer[data-phase='opening'] .es-dust { opacity: 1; }
.env-layer[data-phase='breaking'] .es-dust i,
.env-layer[data-phase='opening'] .es-dust i {
  animation: envDust var(--t-break) cubic-bezier(0.2, 0.9, 0.3, 1) both;
}
.env-layer[data-phase='breaking'] .env-seal::after {
  content: '';
  position: absolute;
  inset: 4px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 240, 200, 0.85), transparent 62%);
  animation: envFlash 300ms ease-out both;
}
@keyframes envDust {
  from { transform: translate(0, 0) rotate(0) scale(1); opacity: 0.95; }
  to   { transform: translate(var(--dx), var(--dy)) rotate(var(--dr)) scale(0.7); opacity: 0; }
}
@keyframes envFlash {
  from { opacity: 0.9; transform: scale(0.6); }
  to   { opacity: 0; transform: scale(1.5); }
}

/* opening：翻盖绕顶边翻过去，内部那层暗露出来 */
.env-layer[data-phase='opening'] .env-flap,
.env-layer[data-phase='rising'] .env-flap,
.env-layer[data-phase='unfolding'] .env-flap,
.env-layer[data-phase='reading'] .env-flap {
  transform: rotateX(-168deg);
  filter: brightness(0.86);
}
.env-layer[data-phase='opening'] .env-pocket,
.env-layer[data-phase='rising'] .env-pocket,
.env-layer[data-phase='unfolding'] .env-pocket,
.env-layer[data-phase='reading'] .env-pocket { opacity: 1; }

/* rising：羊皮纸从信封口探出来、往上走一截（先露一小部分，再长上来） */
.env-layer[data-phase='rising'] .env-parch {
  opacity: 1;
  transform: translateY(-6%) scale(0.99) rotate(-1.2deg);
}
.env-layer[data-phase='unfolding'] .env-parch,
.env-layer[data-phase='reading'] .env-parch {
  opacity: 1;
  transform: none;
}

/* unfolding：整张纸铺开（顶上去、左右撑开），卷着那一截收掉 */
.env-layer[data-phase='unfolding'] .env-parch,
.env-layer[data-phase='reading'] .env-parch {
  left: 0;
  right: 0;
  top: 2%;
  height: 96%;
}
.env-layer[data-phase='unfolding'] .ep-scroll,
.env-layer[data-phase='reading'] .ep-scroll { height: 0; }

/* reading：提示收掉、两颗钮出来 */
.env-layer[data-phase='reading'] .env-hint { opacity: 0; }
.env-layer[data-phase='reading'] .env-done {
  opacity: 1;
  animation: envIn 360ms ease 620ms backwards;
}
.env-done { display: flex; gap: var(--sp-2); opacity: 0; }

.env-hint {
  margin: 0;
  font-size: var(--fs-s);
  letter-spacing: 0.08em;
  color: rgba(241, 228, 197, 0.82);
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.8);
  transition: opacity 240ms ease;
  text-align: center;
}

/* 收场：轻轻收起来 */
.env-out { opacity: 0; transition: opacity 220ms ease; }
.env-layer:not(.env-out) { animation: envIn 200ms ease both; }

/* ───────── 五、窄屏 / 少动效 ───────── */

@media (max-width: 560px) {
  .env-letter { width: min(92vw, 420px); }
  .env-seal { width: 68px; height: 68px; margin: -34px 0 0 -34px; }
  .env-x { right: -4px; top: -10px; width: 40px; height: 40px; }
  .ep-body p { line-height: 1.85; }
}

@media (prefers-reduced-motion: reduce) {
  .env-layer {
    --t-in: 1ms;
    --t-break: 1ms;
    --t-open: 1ms;
    --t-rise: 1ms;
    --t-unfold: 1ms;
  }
  .env-layer[data-phase='sealed'] .es-wax,
  .env-layer[data-phase='sealed'] .es-crest,
  .env-layer[data-phase='breaking'] .es-dust i { animation: none; }
  .env-layer[data-phase='reading'] .ep-school,
  .env-layer[data-phase='reading'] .ep-title,
  .env-layer[data-phase='reading'] .ep-rule,
  .env-layer[data-phase='reading'] .ep-hello,
  .env-layer[data-phase='reading'] .ep-body p,
  .env-layer[data-phase='reading'] .ep-sign,
  .env-layer[data-phase='reading'] .env-done { animation: none; opacity: 1; }
}

/* ───────── 栏顶那一排：滑动开关 + 齿轮（2026-10-12 改版） ───────── */

.np-bar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-2);
}

/* 开关那一格：轨道在上面，那行小字压在它下面 */
.np-sw { display: flex; flex-direction: column; gap: 4px; }
.np-sw-note { font-size: var(--fs-xs); letter-spacing: 0.04em; color: var(--ink-soft); }

/*
 * 一颗自己画的滑动开关：一条槽 + 会滑过去的小圆钮。
 * 是真 <button role="switch">，键盘 Tab / Enter / Space 都能拨（无障碍那条要求）。
 */
.np-sw-track {
  position: relative;
  width: 46px;
  height: 24px;
  padding: 0;
  border: 1px solid rgba(64, 48, 31, 0.45);
  border-radius: 12px;
  background: linear-gradient(180deg, #e9dcbe, #d9c9a3);
  box-shadow: inset 0 1px 3px rgba(74, 48, 37, 0.25);
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.np-sw-knob {
  position: absolute;
  left: 2px;
  top: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #fffaf0, #d8c295 72%);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  transition: transform 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}
.np-sw-track.on {
  background: linear-gradient(180deg, #b4863c, #8a6a1c);
  border-color: rgba(90, 66, 39, 0.65);
}
.np-sw-track.on .np-sw-knob { transform: translateX(22px); }
.np-sw-track:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }

/* 右边那颗齿轮 */
.np-gear {
  flex: none;
  width: 34px;
  height: 34px;
  border: 1px solid rgba(64, 48, 31, 0.35);
  border-radius: var(--r);
  background: rgba(255, 250, 235, 0.62);
  color: var(--ink);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
}
.np-gear:hover { border-color: var(--red); }
.np-gear:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }

/* 齿轮那张纸上的两行：左边一句话，右边一颗钮 / 一颗开关 */
.np-setrow {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) 0;
  border-bottom: 1px solid rgba(64, 48, 31, 0.18);
}
.np-setrow:last-child { border-bottom: 0; }
.np-setbody { flex: 1 1 auto; min-width: 0; }
.np-sett { font-size: var(--fs-m); color: var(--ink); }
.np-setn { margin-top: 2px; font-size: var(--fs-xs); line-height: 1.6; color: var(--ink-soft); }

@media (prefers-reduced-motion: reduce) {
  .np-sw-knob, .np-sw-track { transition: none; }
}

/* ───────── 手抄本里那两格：学院杯四行 + 学科概况（2026-10-12 改版） ───────── */

/*
 * 学院杯那一摞：**四家各一行**，各挂自己的徽章、自己的学院色。
 * 底色用 --house 淡淡铺在左边一道竖条上（不是整块染色——四行染透了像四个色卡）。
 * 领先那家多一层微光 + 一枚小星；并列时两家都带。
 */
.cup-strip { display: flex; flex-direction: column; gap: 2px; }

.cs-row {
  display: grid;
  grid-template-columns: 26px 1fr auto auto 14px;
  align-items: center;
  gap: var(--sp-2);
  width: 100%;
  padding: var(--sp-1) var(--sp-2);
  border: 1px solid rgba(64, 48, 31, 0.2);
  border-left: 3px solid var(--house);
  border-radius: var(--r);
  background: linear-gradient(90deg, rgba(255, 250, 235, 0.5), rgba(240, 228, 202, 0.34) 40%);
  cursor: pointer;
  text-align: left;
  transition: transform 0.14s ease, border-color 0.14s ease;
}
.cs-row:hover { transform: translateX(2px); border-color: var(--accent); }
.cs-row.lead {
  background:
    radial-gradient(120% 140% at 0% 50%, rgba(255, 236, 190, 0.5), transparent 62%),
    linear-gradient(90deg, rgba(255, 250, 235, 0.6), rgba(240, 228, 202, 0.34) 40%);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.cs-crest { width: 26px; height: 26px; object-fit: contain; }
.cs-name { font-size: var(--fs-s); letter-spacing: 0.04em; color: var(--ink); }
.cs-pts { font-family: var(--font-hand); font-size: var(--fs-m); color: var(--ink); }
.cs-week { font-size: var(--fs-xs); color: var(--ink-soft); min-width: 2.2em; text-align: right; }
.cs-week.up { color: #2f6b45; }
.cs-week.down { color: var(--red); }
.cs-star { color: var(--accent); font-size: 11px; }

/*
 * 学科概况：一行一门课。
 * 左边那道 3px 的颜色是**档位**（金=最好、往下渐暗到红）——只当提示，不写字母也不写分数；
 * 正文那句才是给人读的（创作者要的就是"用文字形容"）。
 */
.subj-box { padding: var(--sp-1) 0; }
.subj-row {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border-left: 3px solid rgba(64, 48, 31, 0.3);
  border-bottom: 1px dashed rgba(64, 48, 31, 0.16);
}
.subj-row:last-child { border-bottom: 0; }
.subj-row.g-O { border-left-color: #b4863c; }
.subj-row.g-E { border-left-color: #5c7a52; }
.subj-row.g-A { border-left-color: #6b543a; }
.subj-row.g-P { border-left-color: #9a7a3a; opacity: 0.92; }
.subj-row.g-D { border-left-color: #8c2f2f; opacity: 0.88; }
.subj-row.g-T { border-left-color: #8c2f2f; opacity: 0.82; }
.subj-name {
  flex: none;
  font-family: var(--font-hand);
  font-size: var(--fs-m);
  letter-spacing: 0.06em;
  color: #4a3025;
}
.subj-tx { font-size: var(--fs-s); line-height: 1.7; color: var(--ink); }

@media (max-width: 560px) {
  .subj-row { flex-direction: column; gap: 2px; }
}

/* ============================================================
   60 · 自由剧情那一屏：一章一个气泡 + 重写本章 + 章节翻页
   账在 engine/mainline.js（每一章留几稿、"定稿"是哪一版），界面在 ui/story-screen.js。
   创作者 2026-10-12 定的三件事：
     · 她那一步 = 一颗气泡（灰绿、半透明、靠右）；它写的一整章 = 另一颗（靠左）；
     · 那一章里角色一旦开口，那句台词的字 = 灰黄；
     · 【重写本章】添一稿、不覆盖旧稿，旁边 ‹ 1/2 › 切的就是"这一章定稿用哪版"。
   ============================================================ */

/* 一轮 = 上下两颗气泡（从前是一串散段落直接铺在玻璃上，分不清哪段是谁的） */
.doc-turn {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

/* 一颗气泡。圆角里那一个"尖角"朝着说话的那一方，不是装饰 */
.doc-b {
  padding: 9px 13px 10px;
  border-radius: 14px 14px 14px 4px;
  overflow-wrap: anywhere;
}
/* 她那一步那一颗照旧收着（一句话不该铺满一整屏） */
.doc-b.you {
  max-width: min(88%, 42rem);
}

/* 她走的那一步：灰绿、半透明、靠右 */
.doc-b.you {
  align-self: flex-end;
  border-radius: 14px 14px 4px 14px;
  background: rgba(122, 152, 132, 0.2);
  border: 1px solid rgba(150, 184, 158, 0.3);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.22);
}
.doc-b.you .doc-line {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--fs-s);
  line-height: 1.75;
  color: #d5e3d6;
}

/* 它写的那一整章：所有段落都装在同一颗里（"一章节回复为一个气泡"）。
   ⚠️ 这颗**横宽几乎占满**（创作者 2026-10-12 点的）：一章八九百字，
   窄窄一颗就变成手机上刷不到头的泡泡，读起来不像一篇文章。stretch 是它占满那一列的宽。 */
.doc-b.ai {
  align-self: stretch;
  background: rgba(16, 13, 10, 0.34);
  border: 1px solid rgba(226, 202, 160, 0.14);
  box-shadow: 0 3px 14px rgba(0, 0, 0, 0.26);
}
.doc-b.ai > :first-child { margin-top: 0; }
.doc-b.ai > :last-child { margin-bottom: 0; }

/* 「正在写」那一颗里只有墨迹那一行，别撑成一片空玻璃 */
.doc-b.ai .doc-ink { margin: 0; }

/* 角色一开口，那句的字就是灰黄（旁白照旧暖白，两种字一眼分得开） */
.doc-say .line { color: #cfc497; }

/* 落定中的那一轮、重写浮出来的那一稿：整颗气泡轻轻进来 */
.doc-turn.pending .doc-b,
.doc-turn.rewrite .doc-b.ai { animation: docIn 0.42s ease both; }

/* ---------- 输入框上面那一行：多出来的两颗 ---------- */

/* ‹ 1/2 › 是一伙的，挨着站、别被 justify-content 拉散 */
.doc-ver {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.doc-ver-n {
  min-width: 3.2em;
  text-align: center;
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  color: var(--cine-text);
}
/* 那两颗箭头是小方块：和旁边两颗文字钮一样高，但窄得多 */
.doc-meta .btn.dot {
  width: 26px;
  padding: 3px 0 4px;
  font-size: var(--fs-s);
  line-height: 1.4;
}

/* ---------- 羊皮纸上（【之前章节】点开那一章）：气泡跟着换成墨色 ---------- */

.sheet .doc-b.you {
  background: rgba(122, 152, 132, 0.16);
  border-color: rgba(88, 116, 92, 0.3);
}
.sheet .doc-b.you .doc-line { color: #35543c; }
.sheet .doc-b.ai {
  background: rgba(64, 48, 31, 0.05);
  border-color: rgba(64, 48, 31, 0.16);
  box-shadow: none;
}

/* ---------- 【之前章节】底下那一行翻页 ---------- */

.hist-pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px dotted rgba(64, 48, 31, 0.22);
}
/* 那两颗箭头是小方块（不挂 .panel：【固定剧情 · 主线】那一栏的翻页长在纸上） */
.hist-pager .btn {
  flex: none;
  width: 26px;
  min-width: 0;
  padding: 3px 0;
  font-size: var(--fs-s);
  line-height: 1.4;
  text-align: center;
}
.hist-pager-n {
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}

@media (prefers-reduced-motion: reduce) {
  .doc-turn.pending .doc-b,
  .doc-turn.rewrite .doc-b.ai { animation: none; }
}

/* ============================================================
   61 · 校报那个「大家怎么看这位学生」专栏（ui/newspaper.js 的 voicesBlock）
   创作者 2026-10-12 点的：让同学、老师专门发表对主角的看法。
   排版走"报纸上那一列引语"——和上面论坛那些歪便签分开：那是网上吵的，这一栏是印在纸上的。
   ============================================================ */

.np-voices {
  margin-top: var(--sp-4);
  padding-top: var(--sp-3);
  border-top: 3px double rgba(64, 48, 31, 0.5);
}
.np-voices-note {
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-xs);
  font-style: italic;
  color: var(--ink-soft);
}

/* 一人一段：左边那道竖线是他/她所属学院的颜色（不是学生的就用墨色） */
.np-voice {
  margin-bottom: var(--sp-3);
  padding: 6px 10px 7px 12px;
  border-left: 3px solid var(--house, rgba(64, 48, 31, 0.45));
  background: rgba(255, 250, 235, 0.42);
}
/* 左右各让一点，整栏看起来是排过的，不是一条直线下来的清单 */
.np-voice:nth-of-type(odd) { margin-right: 7%; }
.np-voice:nth-of-type(even) { margin-left: 7%; }

.nv-say {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--fs-s);
  line-height: 1.75;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.nv-by {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-top: 3px;
  font-size: var(--fs-xs);
  color: var(--ink-soft);
}
.nv-name { font-weight: 700; letter-spacing: 0.02em; color: var(--ink); }
.nv-role {
  padding: 0 5px;
  border: 1px solid rgba(64, 48, 31, 0.28);
  border-radius: 2px;
  white-space: nowrap;
}
.nv-house { color: var(--house, var(--ink-soft)); }

@media (max-width: 560px) {
  /* 窄屏上那 7% 的让位会把一行字挤成三行，不如齐着排 */
  .np-voice:nth-of-type(odd),
  .np-voice:nth-of-type(even) { margin-left: 0; margin-right: 0; }
}

/* ============================================================
   62 · 地图上的深蓝图钉（翻倒巷）：字牌跟着坡上那块牌子一起变冷色
   入口 2026-10-12 从城堡外挪进了【外界地图】，皮不能跟着挪丢——
   色号取 data/locations.js 那处地点自己的 tint（和 .place.tint 同一个出处）。
   ============================================================ */

.wmp-spot.tint .wms-tx {
  background: linear-gradient(180deg, var(--tint, #1d2b4d), #121a2e);
  border-color: rgba(224, 178, 100, 0.5);
  box-shadow: 0 2px 7px rgba(0, 0, 0, 0.5);
}
.wmp-spot.tint .wms-zh { color: #f1e7d0; }
.wmp-spot.tint .wms-en { color: rgba(224, 178, 100, 0.85); }

/* ============================================================
   63 · 固定剧情那张纸顶上两栏：【主线】/【支线】（ui/story-menu.js）
   手感照【设置】【信件】那两排分类条（.set-tabs / .mail-tabs），
   类名另起一套：.mail-tabs 那一套的羊皮纸皮挂在 `.sheet:has(.mail-tabs)` 上，
   照抄会把这张纸整个换装（信件那头踩过一次，注释就写在那儿）。
   ============================================================ */

.story-tabs {
  display: flex;
  gap: var(--sp-2);
  margin: 2px 0 var(--sp-3);
  padding-bottom: 6px;
  border-bottom: 1px solid rgba(64, 48, 31, 0.28);
}
.story-tab {
  flex: 1 1 0;
  padding: 4px 2px 5px;
  font: inherit;
  font-size: var(--fs-s);
  letter-spacing: 0.1em;
  color: var(--ink-soft);
  background: none;
  border: 0;
  border-radius: var(--r);
  cursor: pointer;
  transition: color 0.14s ease, background 0.14s ease;
}
.story-tab:hover { color: var(--ink); background: rgba(255, 255, 255, 0.35); }
.story-tab.on { color: var(--red); font-weight: 700; background: rgba(140, 47, 47, 0.09); }

/* ============================================================
   64 · 地图下面那两颗门：【迷离幻境】白雾 / 【外界地图】暗棕描金（ui/worldmap.js）
   右边那颗直接挂第 54 节的 .wmap-btn 那一个类（创作者要的就是"和原先那颗一样"），
   这一节只管两件事：两颗一左一右各贴一角，和给左边那颗现做一副白雾的皮。
   尺码照创作者 2026-10-13 二稿：他画的就是图上那两块字牌那么大，不是两颗大钮——
   所以字与内边距全部照 .wms-tx（图钉那块字牌）那一套，宽度由字自己撑。
   ============================================================ */

.wm-doors {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-top: var(--sp-3);
}
.wm-door { position: relative; flex: 0 0 auto; padding: 0; border: 0; background: none; cursor: pointer; }
/* 只剩一颗的时候（迷离幻境藏着）它仍钉右边——创作者画的就是那一角，别跳到左边去 */
.wm-doors > .wm-door:only-child { margin-left: auto; }
.wm-door .wmb-face { display: block; padding: 2px var(--sp-2) 3px; }
/* 字牌那一号的字：中文小一号、英文 10 像素，两行都不许折 */
.wm-door .wmb-zh { font-size: var(--fs-s); letter-spacing: 0.06em; }
.wm-door .wmb-en { font-size: 10px; letter-spacing: 0.1em; }

/* ───────── 白雾那一颗：半透的白玻璃 + 两缕横着飘的雾 ───────── */

/* 呼吸走 filter，和暗棕那颗同一套（不碰 transform，免得和 hover 那一下抢同一根轴） */
.wm-door.mist { animation: mistBreath 4.6s ease-in-out infinite; }
@keyframes mistBreath {
  0%, 100% { filter: drop-shadow(0 0 6px rgba(226, 238, 255, 0.42)); }
  50%      { filter: drop-shadow(0 0 17px rgba(226, 238, 255, 0.8)); }
}

.wm-door.mist .wmb-face {
  position: relative;
  overflow: hidden;
  border-color: rgba(255, 255, 255, 0.62);
  background: linear-gradient(180deg,
    rgba(255, 255, 255, 0.95), rgba(241, 247, 255, 0.82) 54%, rgba(223, 235, 252, 0.74));
  -webkit-backdrop-filter: blur(4px) saturate(1.12);
  backdrop-filter: blur(4px) saturate(1.12);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.92),
    inset 0 -11px 19px rgba(150, 176, 216, 0.34),
    0 8px 22px rgba(0, 0, 0, 0.5);
}
.wm-door.mist:hover .wmb-face { border-color: #fff; transform: translateY(-2px); }
.wm-door.mist:active .wmb-face { transform: translateY(0) scale(0.98); }

/* 白玻璃上要用冷墨，不是暗底那套米白字 */
.wm-door.mist .wmb-zh { color: #2b3a55; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); }
.wm-door.mist .wmb-en { color: rgba(58, 80, 118, 0.82); }
/* 字要压在雾上面：雾是 ::before/::after，不抬层的话 ::after 会糊在字上 */
.wm-door.mist .wmb-zh, .wm-door.mist .wmb-en { position: relative; z-index: 1; }

/* 沿边那一圈流光：同一副两层结构，只把锥形光从金换成银白 */
.wm-door.mist .wmb-ring > i {
  background: conic-gradient(from 0deg,
    rgba(226, 238, 255, 0) 0 46%,
    rgba(226, 238, 255, 0.5) 62%,
    #ffffff 76%,
    rgba(226, 238, 255, 0.5) 86%,
    rgba(226, 238, 255, 0) 96%);
}

/* 两缕雾：同一份动效，靠各自的时长与 delay 错开，从左边生、往右边散掉 */
.wm-door.mist .wmb-face::before,
.wm-door.mist .wmb-face::after {
  content: '';
  position: absolute;
  left: -46%;
  width: 74%;
  height: 130%;
  z-index: 0;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0));
  filter: blur(5px);
  pointer-events: none;
  animation: mistDrift 9s linear infinite;
}
.wm-door.mist .wmb-face::after { height: 92%; animation-duration: 13.5s; animation-delay: -5.5s; }

@keyframes mistDrift {
  from { transform: translateX(-25%); opacity: 0.2; }
  50%  { opacity: 0.72; }
  to   { transform: translateX(200%); opacity: 0.2; }
}

@media (prefers-reduced-motion: reduce) {
  .wm-door.mist, .wm-door.mist .wmb-face::before, .wm-door.mist .wmb-face::after { animation: none; }
  .wm-door.mist .wmb-face::before, .wm-door.mist .wmb-face::after { opacity: 0.4; }
}

/* ============================================================
   65 · 地图上"人就站在这儿"的那一颗：镀金、按不动（ui/worldmap.js 的 spot）
   判据只有 main.js 的 C.herePin 那一个口（认模式，不认状态栏那个名字），画的时候给那一颗挂 .here：
   在城堡里 → 「城堡内」金，在禁林 → 「禁林」金，摊子前 → 「云游商人」金。
   ============================================================ */

.wmp-spot.here { pointer-events: none; cursor: default; }

/* 针尖镀金：亮金一颗，外圈加一道暖光（走 filter，不碰 box-shadow——那根轴被 wmsPulse 占着） */
.wmp-spot.here .wms-pin {
  border-color: rgba(122, 84, 22, 0.9);
  background: radial-gradient(circle at 34% 28%, #fff6da, #f0c063 58%, #a9702a);
  filter: drop-shadow(0 0 8px rgba(255, 214, 120, 0.85));
}

/* 字牌整个镀一层金 */
.wmp-spot.here .wms-tx {
  border-color: rgba(122, 84, 22, 0.75);
  background: linear-gradient(180deg, #f7dc9a, #dcae55);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45), 0 0 14px rgba(224, 178, 100, 0.5);
  transform: none;
}
.wmp-spot.here .wms-zh { color: #4a3210; }
.wmp-spot.here .wms-en { color: rgba(74, 50, 16, 0.72); }
/* 深蓝那块（翻倒巷）也镀，但走压暗的暖金，不跟它的冷色底撞 */
.wmp-spot.here.tint .wms-tx { background: linear-gradient(180deg, #6d5527, #3a2c12); }
.wmp-spot.here.tint .wms-zh { color: #f3dfae; }
.wmp-spot.here.tint .wms-en { color: rgba(243, 223, 174, 0.82); }

/* ============================================================
   66 · 【迷离幻境】那张图：一块取景框 + 底下一句话（ui/mist.js）
   纸皮和外界地图是同一副（openSheet 的 skin:'bare'、左上角那两行借 .wmp-cap），
   区别只有一条：那张图上钉着地方，这一张上什么都没有——玩家已经站在这儿了。
   ============================================================ */

.sheet:has(.mist-print) { width: 100%; }

.mist-print { position: relative; }

/*
 * 六张场景图都是竖构图（约 1006×1564），照原比例摊开会比整屏还高，
 * 所以裁成一块框（cover）——这一张上没有图钉，裁掉哪一格都不影响。
 */
.mist-view {
  position: relative;
  overflow: hidden;
  height: min(62vh, 74vw);
  border: 1px solid rgba(240, 226, 196, 0.42);
  border-radius: var(--r);
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
  /* 底下那圈是"这块地方在发光"：暖金一层、月白一层，都往外散，不是描边 */
  box-shadow:
    inset 0 0 70px rgba(8, 10, 18, 0.55),
    0 0 26px rgba(255, 241, 208, 0.3),
    0 0 70px rgba(214, 232, 255, 0.22),
    0 10px 30px rgba(0, 0, 0, 0.6);
}

/*
 * 画面正中那团慢慢亮起来又落下去的光：走**暖金**，不走白——
 * 这六张场景图本身就是白花花的（创作者那批"幻境"是褪过色的），
 * 再叠一层白等于没叠（第一版就是栽在这儿），暖金才提得出"在发光"。
 */
.mist-glow {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(48% 36% at 50% 42%,
    rgba(255, 236, 188, 0.34), rgba(255, 226, 164, 0.12) 55%, rgba(255, 255, 255, 0) 78%);
  mix-blend-mode: screen;
  animation: mistGlow 7.5s ease-in-out infinite;
}

@keyframes mistGlow {
  0%, 100% { opacity: 0.4; }
  50%      { opacity: 0.9; }
}

/*
 * 从底边升上去、越升越淡的光点（16 粒，序号错开，见 ui/mist.js 的 motes）。
 * --sz / --dx 由每一粒自己带：大小、往哪边飘一点都不一样；
 * 上升走 ease-out——刚离地快、往上越来越慢，收在"淡没"而不是"到顶"。
 */
.mist-motes {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.mist-motes > i {
  position: absolute;
  bottom: -10px;
  width: var(--sz, 5px);
  height: var(--sz, 5px);
  border-radius: 50%;
  /* 芯是白的、外圈是金的：这一景本身就白，纯白的光点会陷进去看不见 */
  background: radial-gradient(circle, #fff 0 26%, #ffe3a0 46%, rgba(226, 166, 66, 0.62) 64%, rgba(255, 255, 255, 0) 78%);
  box-shadow: 0 0 9px rgba(255, 216, 138, 0.95), 0 0 22px rgba(238, 182, 96, 0.5);
  opacity: 0;
  animation: mistRise 12s ease-out infinite;
}

@keyframes mistRise {
  0%   { transform: translate3d(0, 0, 0) scale(0.5); opacity: 0; }
  12%  { opacity: 0.95; }
  62%  { opacity: 0.5; }
  100% { transform: translate3d(var(--dx, 0), -58vh, 0) scale(1.05); opacity: 0; }
}

/* 那一句话：图上没有别的东西，它就站在取景框底下正中 */
.mist-line {
  margin: var(--sp-3) 3% 0;
  font-family: var(--font-body);
  font-size: var(--fs-s);
  line-height: 1.8;
  letter-spacing: 0.04em;
  text-align: center;
  color: #f2e6cd;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9), 0 0 16px rgba(226, 238, 255, 0.22);
}

/* 不要动效的时候：光停在半空、白光停在中档，这两层还在，只是不再飘不再呼吸 */
@media (prefers-reduced-motion: reduce) {
  .mist-glow, .mist-motes > i { animation: none; }
  .mist-glow { opacity: 0.8; }
  .mist-motes > i { opacity: 0.7; transform: translate3d(0, -34vh, 0); }
}

/* ============================================================
   67 · 黑巫师营地：地图上那颗黑紫图钉 + 营地那一屏（ui/camp.js）
   创作者 2026-10-16 定的：这一处「挂在禁林按钮下」，按钮与界面一律走黑紫，
   和别处那片米黄纸牌 / 深棕描金不是一族。
   ============================================================ */

/* ── 一、地图上那颗图钉：**挂在禁林那块牌底下**的一截垂链 + 一颗暗核 + 小字牌 ──
   （创作者 2026-10-16 第二稿：不要英文、压缩小一点、要有"依附于禁林按钮"的感觉、加黑暗粒子）
   DOM 见 ui/worldmap.js 的 campSpot：.camp-attach（垂链 + .camp-orb 暗核）→ .wms-tx（只写中文）
   → .camp-motes（飘着的暗粒子，绝对定位，不参与布局）。 */
.wmp-spot.camp { gap: 0; }

/* 连着禁林那块牌的一小截垂链（上虚下实，看着像从牌子底下垂出来的） */
.wmp-spot.camp .camp-attach {
  position: relative;
  width: 2px;
  height: 12px;
  background: linear-gradient(180deg, rgba(150, 110, 230, 0) 0 20%, rgba(122, 86, 198, 0.55) 48%, rgba(172, 132, 250, 0.92));
  box-shadow: 0 0 5px rgba(140, 100, 220, 0.5);
}

/* 尾端那颗会呼吸的暗核：黑紫一颗，外圈一道紫光 */
.wmp-spot.camp .camp-orb {
  position: absolute;
  left: 50%;
  bottom: -3px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  transform: translateX(-50%);
  background: radial-gradient(circle at 34% 30%, #e2d4ff, #6b45b8 56%, #150a22);
  box-shadow: 0 0 9px rgba(150, 110, 230, 0.9), 0 0 0 1px rgba(20, 10, 36, 0.85);
  animation: campOrb 2.6s ease-in-out infinite;
}
@keyframes campOrb {
  0%, 100% { transform: translateX(-50%) scale(1);    box-shadow: 0 0 6px rgba(150, 110, 230, 0.72), 0 0 0 1px rgba(20, 10, 36, 0.85); }
  50%      { transform: translateX(-50%) scale(1.24); box-shadow: 0 0 15px rgba(172, 132, 248, 1),   0 0 0 1px rgba(20, 10, 36, 0.85); }
}

/* 假期那一颗（伦敦地图右上）：没有禁林牌子可挂，暗核独立摆在字牌上方 */
.wmp-spot.camp.alone { gap: 3px; }
.wmp-spot.camp.alone .camp-orb {
  position: relative;
  left: auto;
  bottom: auto;
  width: 14px;
  height: 14px;
  margin: 0 auto;
  transform: none;
  animation: campOrbAlone 2.6s ease-in-out infinite;
}
@keyframes campOrbAlone {
  0%, 100% { transform: scale(1);    box-shadow: 0 0 6px rgba(150, 110, 230, 0.72), 0 0 0 1px rgba(20, 10, 36, 0.85); }
  50%      { transform: scale(1.24); box-shadow: 0 0 15px rgba(172, 132, 248, 1),   0 0 0 1px rgba(20, 10, 36, 0.85); }
}

/* 小字牌：只有中文一行，比别处那几颗小一档 */
.wmp-spot.camp .wms-tx {
  padding: 3px 10px 4px;
  background: linear-gradient(180deg, #452a66, #170d24);
  border-color: rgba(170, 130, 240, 0.62);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.62), 0 0 14px rgba(120, 80, 200, 0.5);
}
.wmp-spot.camp .wms-zh {
  font-size: var(--fs-xs);
  letter-spacing: 0.12em;
  color: #efe7ff;
}

/* 黑暗粒子：一小团会呼吸的暗紫光 + 几粒往上飘、慢慢消失的暗紫屑 */
.wmp-spot.camp .camp-motes {
  position: absolute;
  inset: -24px -26px -14px;
  overflow: hidden;
  pointer-events: none;
}
.wmp-spot.camp .camp-motes::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 50% 68%, rgba(146, 96, 236, 0.42), rgba(84, 42, 152, 0.14) 52%, rgba(0, 0, 0, 0) 74%);
  animation: campHalo 3.4s ease-in-out infinite;
}
@keyframes campHalo {
  0%, 100% { opacity: 0.6; transform: scale(0.95); }
  50%      { opacity: 1;   transform: scale(1.07); }
}
.wmp-spot.camp .camp-motes > i {
  position: absolute;
  bottom: 8px;
  left: var(--x, 50%);
  width: var(--sz, 4px);
  height: var(--sz, 4px);
  border-radius: 50%;
  /* 芯是近黑的暗紫、外圈一道紫光：画在浅色雪地上才看得出"黑暗"又不会糊掉 */
  background: radial-gradient(circle, #2a1546 0 30%, #6b3fb8 58%, rgba(120, 70, 200, 0.3) 72%, rgba(0, 0, 0, 0) 82%);
  box-shadow: 0 0 8px rgba(150, 100, 235, 0.75);
  opacity: 0;
  animation: campMote 5.6s ease-out infinite;
}
.wmp-spot.camp .camp-motes > i:nth-child(1) { --x: 20%; --sz: 5px; animation-delay: 0s;   animation-duration: 5.2s; }
.wmp-spot.camp .camp-motes > i:nth-child(2) { --x: 44%; --sz: 3px; animation-delay: 1.1s; animation-duration: 6.4s; }
.wmp-spot.camp .camp-motes > i:nth-child(3) { --x: 66%; --sz: 4px; animation-delay: 2.3s; animation-duration: 5.8s; }
.wmp-spot.camp .camp-motes > i:nth-child(4) { --x: 32%; --sz: 3px; animation-delay: 3.4s; animation-duration: 6.8s; }
.wmp-spot.camp .camp-motes > i:nth-child(5) { --x: 78%; --sz: 4px; animation-delay: 4.2s; animation-duration: 5.4s; }
@keyframes campMote {
  0%   { transform: translate3d(0, 0, 0) scale(0.4);      opacity: 0; }
  18%  { opacity: 0.92; }
  100% { transform: translate3d(6px, -34px, 0) scale(1.06); opacity: 0; }
}

/* 站在营地里那一颗仍按第 65 节镀金（.here 的判据在 main.js 的 C.herePin） */
.wmp-spot.camp.here .camp-attach {
  background: linear-gradient(180deg, rgba(224, 178, 100, 0) 0 20%, rgba(200, 150, 60, 0.6) 48%, rgba(240, 192, 99, 0.95));
  box-shadow: 0 0 5px rgba(224, 178, 100, 0.5);
}
.wmp-spot.camp.here .camp-orb {
  background: radial-gradient(circle at 34% 30%, #fff6da, #f0c063 58%, #a9702a);
  box-shadow: 0 0 9px rgba(255, 214, 120, 0.9), 0 0 0 1px rgba(90, 62, 18, 0.8);
  animation: none;
}
.wmp-spot.camp.here .wms-tx {
  border-color: rgba(122, 84, 22, 0.75);
  background: linear-gradient(180deg, #f7dc9a, #dcae55);
}
.wmp-spot.camp.here .wms-zh { color: #4a3210; }


/* ── 二、营地那一屏：箱子 + 守箱人名牌 + 那几句 ── */
.camp-stage {
  /* 这一屏的字压在深色林子上——--ink 那套是给羊皮纸的深棕，在这儿整行看不见 */
  --ink: #f5ecd6;
  --ink-soft: #d3c2a3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  max-width: 560px;
  margin: 4px auto 0;
  padding: 2px 12px 6px;
  text-align: center;
}

/* 那个箱子：纯 CSS 的三块（箱身 / 盖 / 锁）+ 背后一团紫光 */
.camp-chest {
  position: relative;
  width: 250px;
  max-width: 76vw;
  aspect-ratio: 2720 / 1520;
  margin: 8px 0 0;
}
.camp-chest .cc-glow {
  position: absolute;
  left: 50%;
  top: 56%;
  width: 230px;
  height: 190px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(150, 100, 235, 0.44), rgba(90, 50, 160, 0.14) 55%, transparent 72%);
  filter: blur(3px);
  animation: campGlow 3.4s ease-in-out infinite;
  pointer-events: none;
}
@keyframes campGlow {
  0%, 100% { opacity: 0.7; }
  50%      { opacity: 1; }
}
/* 箱子本体就是那张照片（透明底，2720×1520），不再拿三块 CSS 拼盖子 / 箱身 / 锁 */
.camp-chest .cc-art {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 10px 16px rgba(0, 0, 0, 0.6));
  user-select: none; -webkit-user-drag: none;
}

.camp-line {
  margin: 4px 0 0;
  font-family: var(--font-body);
  font-size: var(--fs-m);
  color: var(--ink);
  line-height: 1.7;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.92), 0 0 16px rgba(0, 0, 0, 0.7);
}
.camp-guard {
  margin: 8px 0 0;
  font-family: var(--font-body);
  font-size: var(--fs-s);
  color: var(--ink-soft);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.92), 0 0 14px rgba(0, 0, 0, 0.66);
}
/* 守箱人的名牌：一块黑紫描边的小牌，中间写上【名字】 */
.camp-nameplate {
  display: inline-block;
  margin: 2px 0;
  padding: 7px 20px;
  border-radius: var(--r);
  background: linear-gradient(180deg, #4a2f6d, #1c1029);
  border: 1px solid rgba(170, 130, 240, 0.6);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5), 0 0 16px rgba(120, 80, 200, 0.45);
  font-family: var(--font-body);
  font-size: var(--fs-m);
  letter-spacing: 0.12em;
  color: #ece2ff;
}
.camp-ask {
  margin: 12px 0 0;
  font-family: var(--font-body);
  font-size: var(--fs-m);
  color: var(--ink);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.92), 0 0 16px rgba(0, 0, 0, 0.7);
}

.camp-acts {
  display: flex;
  justify-content: center;
  gap: var(--sp-2);
  margin: 16px auto 2px;
}
/* 这一屏那两颗按钮也走黑紫（创作者点名「按钮…用黑紫色」）：
   只改营地这一屏里的，不动 duel.css 里那套 gold 皮的全局定义 */
.camp-acts .btn.gold,
.camp-acts .btn.gold.strong {
  color: #ece2ff;
  background: linear-gradient(180deg, rgba(74, 47, 109, 0.9), rgba(28, 16, 41, 0.94));
  border-color: rgba(170, 130, 240, 0.6);
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.45), inset 0 0 0 1px rgba(170, 130, 240, 0.16), 0 0 14px rgba(120, 80, 200, 0.28);
}
.camp-acts .btn.gold:hover,
.camp-acts .btn.gold.strong:hover {
  color: #fff;
  border-color: rgba(202, 172, 255, 0.95);
  background: linear-gradient(180deg, rgba(96, 62, 140, 0.95), rgba(40, 22, 58, 0.96));
}
.camp-warn {
  margin: 14px auto 2px;
  max-width: 460px;
  font-size: var(--fs-s);
  color: #f0d9a0;
  text-align: center;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
}
.camp-loot {
  margin: 6px 0 2px;
  font-family: var(--font-body);
  font-size: var(--fs-m);
  text-align: center;
  color: var(--ink);
}

@media (prefers-reduced-motion: reduce) {
  .wmp-spot.camp .camp-orb, .wmp-spot.camp .camp-motes::before, .wmp-spot.camp .camp-motes > i, .camp-chest .cc-glow { animation: none; }
}

/* ============================================================
   54. 潜水寻宝（ui/dive.js + ui/fishing.js 左下角那颗入口）
   ------------------------------------------------------------
   创作者 2026-10-17：黑湖与禁林溪流水池都要有一个**深蓝色、带气泡往上冒**的入口，
   点进去是一局 45 秒的「黄金矿工」式下潜。这一节的规矩只有一条：
   **整屏都泡在同一片深蓝里**——牌子、水面、钩子、HUD 全用这一族蓝，
   不要在中途插进羊皮纸那种奶油色（入口与里面要是两套皮，看着像两件事）。
   ============================================================ */

/* ── 水边那两颗并排的钮（黑湖边 / 禁林溪流水池）：钓鱼 + 潜水寻宝 ──
   创作者 2026-10-17：不要从前那条满宽的奶油色牌子，也不要只挂一颗；
   两颗同族深蓝、比满宽牌子小一号、并排放。钓鱼那颗有一尾小鱼游过去，潜水那颗冒气泡。 */
.water-acts { display: flex; gap: 10px; margin-top: 6px; }
.water-act {
  position: relative; overflow: hidden;
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  padding: 9px 14px 10px;
  border-radius: 8px;
  color: #dcefff;
  background:
    radial-gradient(120% 90% at 18% 0%, rgba(96, 178, 255, 0.42), transparent 62%),
    linear-gradient(180deg, rgba(15, 56, 102, 0.95), rgba(6, 24, 48, 0.97));
  border: 1px solid rgba(120, 185, 255, 0.55);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(200, 235, 255, 0.22);
  cursor: pointer;
  text-align: left;
  transition: border-color 0.16s ease, box-shadow 0.16s ease, transform 0.12s ease;
}
.water-act:hover { border-color: rgba(170, 215, 255, 0.9); box-shadow: 0 8px 22px rgba(0, 0, 0, 0.5), 0 0 18px rgba(90, 170, 255, 0.28); }
.water-act:active { transform: translateY(1px); }
.wa-zh { position: relative; z-index: 1; font-size: var(--fs-s); font-weight: 700; letter-spacing: 0.1em; text-shadow: 0 1px 4px rgba(0, 0, 0, 0.7); }
.wa-cost { position: relative; z-index: 1; font-size: var(--fs-xs); color: rgba(182, 216, 255, 0.88); }

/* 钓鱼那颗：一尾小鱼横着游过去（一高一低两条、错开时间；overflow: hidden 兜着） */
.wa-fish { position: absolute; inset: 0; pointer-events: none; }
.wa-fish i {
  position: absolute; left: -20px; top: 30%;
  font-size: 12px; font-style: normal; line-height: 1;
  opacity: 0;
  animation: waFishSwim 5.8s linear infinite;
}
.wa-fish i:nth-child(2) { top: 64%; font-size: 10px; animation-delay: 2.9s; }
@keyframes waFishSwim {
  0%   { transform: translateX(0) translateY(0); opacity: 0; }
  12%  { opacity: 0.7; }
  50%  { transform: translateX(120px) translateY(-2px); opacity: 0.75; }
  88%  { opacity: 0.6; }
  100% { transform: translateX(240px) translateY(1px); opacity: 0; }
}

/* 潜水那颗：几颗小气泡往上冒 */
.wa-bub { position: absolute; inset: 0; pointer-events: none; }
.wa-bub i {
  position: absolute; bottom: -7px;
  width: 5px; height: 5px; border-radius: 50%;
  background: rgba(190, 230, 255, 0.5);
  border: 1px solid rgba(225, 245, 255, 0.7);
  animation: waBubUp 3.6s linear infinite;
}
.wa-bub i:nth-child(1) { left: 14%; animation-delay: 0s; width: 4px; height: 4px; }
.wa-bub i:nth-child(2) { left: 38%; animation-delay: 1.2s; }
.wa-bub i:nth-child(3) { left: 62%; animation-delay: 0.6s; width: 3px; height: 3px; }
.wa-bub i:nth-child(4) { left: 84%; animation-delay: 1.9s; }
@keyframes waBubUp {
  0%   { transform: translateY(0) scale(0.6); opacity: 0; }
  18%  { opacity: 0.85; }
  100% { transform: translateY(-56px) scale(1); opacity: 0; }
}
/* ── 水底那一屏 ── */
.dive-stage { position: relative; margin-top: var(--sp-3); }
.dive-water {
  position: relative;
  overflow: hidden;
  min-height: 58vh;
  border-radius: 10px;
  /* 底下那张「潜水底图」（湖底沉箱的照片）垫在最下层，**深灰绿**渐变仍旧盖在上头——
     创作者 2026-10-17 后来点名的：这一屏不要那层亮蓝，要和黑湖岸上那张底图同族的深灰绿。
     不透明度压低让照片透出来，色相全换到绿这一侧，水色仍是主调。 */
  background:
    radial-gradient(66% 42% at 50% -8%, rgba(158, 196, 172, 0.2), transparent 64%),
    linear-gradient(180deg, rgba(34, 52, 44, 0.72) 0%, rgba(24, 39, 33, 0.78) 40%, rgba(12, 22, 17, 0.86) 100%),
    var(--dive-panel-bg, none) center / cover no-repeat;
  border: 1px solid rgba(150, 185, 165, 0.35);
  box-shadow: inset 0 0 70px rgba(0, 0, 0, 0.62);
  touch-action: manipulation;
  cursor: crosshair;
}
/* 水面那道亮光（纯装饰、不吃点击） */
.dive-water::after {
  content: '';
  position: absolute; inset: 0 0 auto; height: 16%;
  background: linear-gradient(180deg, rgba(200, 225, 205, 0.16), transparent);
  pointer-events: none;
}
.dive-bubbles { position: absolute; inset: 0; pointer-events: none; }
.dive-bubbles i {
  position: absolute; left: var(--x); top: var(--y);
  width: 6px; height: 6px; border-radius: 50%;
  background: rgba(200, 222, 208, 0.12);
  border: 1px solid rgba(205, 228, 212, 0.38);
  animation: diveDrift 7s linear infinite;
  animation-delay: var(--d);
}
@keyframes diveDrift {
  0%   { transform: translateY(0) scale(0.85); opacity: 0; }
  14%  { opacity: 0.75; }
  100% { transform: translateY(-64px) scale(1.1); opacity: 0; }
}

/* 水里的东西：位置与个头都由 ui/dive.js 写成 CSS 变量 */
.dive-obj {
  position: absolute; left: var(--x); top: var(--y);
  width: var(--w); height: var(--w);
  display: flex; align-items: center; justify-content: center;
  transform: translate(-50%, -50%);
  z-index: 2;
  pointer-events: none;              /* 命中判定在 JS 里做，别让 DOM 抢走指针 */
  filter: drop-shadow(0 5px 7px rgba(0, 0, 0, 0.5));
  transition: filter 0.15s ease;
}
/* 白底那几张（石头、木头，以及灶台那些食物照）：multiply 会把白底压进水色里 */
.dive-obj .dive-art {
  max-width: 100%; max-height: 100%;
  object-fit: contain;
  mix-blend-mode: multiply;
  user-select: none; -webkit-user-drag: none;
}
/* 图底下那行名字（创作者 2026-10-17）：小小字、绝对定位——不占命中框，也不会把图挤歪 */
.dive-obj .dive-name {
  position: absolute;
  left: 50%; top: calc(100% + 1px);
  transform: translateX(-50%);
  max-width: 96px;
  font-family: var(--font-body);
  font-size: 9px;
  line-height: 1.3;
  letter-spacing: 0.02em;
  color: rgba(232, 240, 234, 0.9);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.95), 0 0 6px rgba(0, 0, 0, 0.7);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  pointer-events: none;
}
/* B 池那几件：沉在深处、边上一圈很淡的绿光，让人一眼知道"这个值钱但难" */
.dive-obj.deep { filter: drop-shadow(0 0 7px rgba(150, 205, 175, 0.5)) drop-shadow(0 5px 7px rgba(0, 0, 0, 0.5)); }
/*
 * 特别池那八枚（创作者 2026-10-17）：在水里要**四周发光**，一眼就知道这东西不一般。
 * 光做在 ::before 的一团暖金晕上（img 那层带着 multiply，光不能画在它上面，会一起被压暗），
 * 再加一圈慢呼吸——和旁边那群普通鱼、石头完全两样。
 */
.dive-obj.special { z-index: 3; }
.dive-obj.special::before {
  content: '';
  position: absolute; inset: -16%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(255, 205, 120, 0.42), rgba(255, 180, 90, 0.14) 56%, transparent 76%);
  animation: diveSpecialPulse 2.2s ease-in-out infinite;
  pointer-events: none;
}
.dive-obj.special .dive-art {
  filter: drop-shadow(0 0 5px rgba(255, 214, 140, 0.9)) drop-shadow(0 0 14px rgba(255, 170, 80, 0.55)) drop-shadow(0 5px 7px rgba(0, 0, 0, 0.5));
}
@keyframes diveSpecialPulse {
  0%, 100% { transform: scale(0.86); opacity: 0.55; }
  50%      { transform: scale(1.12); opacity: 0.95; }
}
/* 被钩住、正往上游的那一件 */
.dive-obj.held { z-index: 5; filter: drop-shadow(0 0 10px rgba(215, 240, 220, 0.75)); }
/*
 * 拖出水面那一下（创作者 2026-10-17 要的"沉甸甸抖一下"）：
 *   · 整屏往下沉一沉再弹回来；
 *   · 钩爪单独往下一坠（.dive-arm 自己不挂动画——它的 rotate 是 JS 每帧写的，
 *     挂上 animation 会把旋转盖掉）；
 *   · 物件在钩尖上先胀一下、再缩着淡掉（260ms 后才从 DOM 摘掉，见 ui/dive.js 的 take）。
 */
@keyframes diveThud {
  0%   { transform: translateY(0); }
  20%  { transform: translateY(3px); }
  55%  { transform: translateY(-2px); }
  100% { transform: translateY(0); }
}
.dive-stage.thud { animation: diveThud 0.28s ease; }
@keyframes diveClawThud {
  0%   { transform: translateY(0); }
  30%  { transform: translateY(6px); }
  100% { transform: translateY(0); }
}
.dive-arm.thud .dive-claw { animation: diveClawThud 0.3s ease; }
@keyframes diveLanding {
  0%   { transform: translate(-50%, -50%) scale(1); opacity: 1; }
  35%  { transform: translate(-50%, -50%) scale(1.16) translateY(-4px); opacity: 1; }
  100% { transform: translate(-50%, -50%) scale(0.7) translateY(-12px); opacity: 0; }
}
.dive-obj.landing { z-index: 7; animation: diveLanding 0.28s ease-out forwards; }

/* 钩子：从左上的转轴（水面正中）转下来；线长与角度每帧由 JS 改 */
.dive-arm {
  position: absolute; left: 50%; top: 0;
  width: 0; height: 0;
  transform-origin: 50% 0;
  z-index: 4;
}
.dive-line {
  position: absolute; left: -1px; top: 0;
  width: 2px; height: 6px;
  background: linear-gradient(180deg, rgba(230, 242, 232, 0.95), rgba(160, 195, 175, 0.6));
  box-shadow: 0 0 6px rgba(160, 205, 180, 0.5);
}
.dive-claw {
  position: absolute; left: -9px; top: 6px;
  width: 18px; height: 15px;
  /* 开口朝下的一只爪：上边封口、下边敞开 */
  border: 2px solid rgba(230, 242, 232, 0.9);
  border-bottom: 0;
  border-radius: 10px 10px 0 0;
  box-shadow: 0 0 8px rgba(160, 205, 180, 0.45);
}

/* 底下那条读数：剩余 / 抓到 / 下钩 */
.dive-hud { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.dive-hud-lb { font-size: var(--fs-xs); letter-spacing: 0.08em; color: var(--ink-soft); }
.dive-time {
  min-width: 2.2em;
  font-family: var(--font-hand);
  font-size: var(--fs-l);
  color: #dcebe2;
  text-shadow: 0 1px 5px rgba(0, 0, 0, 0.7);
}
.dive-count { font-family: var(--font-hand); font-size: var(--fs-m); color: #a9cbb7; }
/*
 * 塞德里克在岸上守着那一颗小气泡（创作者 2026-10-17）：
 * 摆在读数那一行**底下、靠左**（他圈的红框位置），小头像 + 一句"为什么多了 15 秒"。
 * 只有他真来帮忙的那一局才出现。
 */
.dive-help {
  display: flex;
  align-items: center;
  gap: 7px;
  width: fit-content;
  max-width: 100%;
  margin: 10px 0 0;
  padding: 4px 12px 4px 4px;
  border-radius: 999px;
  color: #e8f3ec;
  background:
    radial-gradient(120% 140% at 18% 0%, rgba(140, 190, 160, 0.38), transparent 64%),
    linear-gradient(180deg, rgba(34, 56, 45, 0.94), rgba(14, 26, 20, 0.96));
  border: 1px solid rgba(165, 200, 180, 0.55);
  box-shadow: 0 5px 14px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(225, 242, 232, 0.2);
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  line-height: 1.3;
}
.dive-help-face {
  width: 24px; height: 24px;
  flex: none;
  border-radius: 50%;
  object-fit: cover;
  object-position: 50% 12%;
  background: rgba(225, 242, 232, 0.1);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
}
.dive-help-txt { white-space: nowrap; }
/*
 * 【下钩】与【直接结算】（创作者 2026-10-17）：两颗都**按内容宽**，不许再拉成一条长杠。
 * 默认 .btn 是 width:100%，摆进这条 flex 里就会吃掉整行——所以两颗都写明 flex:none / width:auto。
 * 下钩靠右（margin-left:auto 推开前面那几个读数），直接结算挨在它右边、再小一号。
 */
.btn.dive-go {
  flex: none;
  width: auto;
  min-width: 0;
  margin-left: auto;
  padding: 6px 16px 7px;
  font-size: var(--fs-s);
  line-height: 1.2;
  color: #eef5f0;
  background: linear-gradient(180deg, rgba(60, 86, 72, 0.96), rgba(24, 39, 32, 0.98));
  border: 1px solid rgba(155, 190, 168, 0.7);
  box-shadow: 0 3px 0 rgba(8, 16, 12, 0.7), 0 8px 18px rgba(0, 0, 0, 0.45);
}
.btn.dive-go:hover { border-color: rgba(200, 230, 210, 0.95); }
.btn.dive-go:disabled { opacity: 0.55; }
.btn.dive-end {
  flex: none;
  width: auto;
  min-width: 0;
  padding: 6px 12px 7px;
  font-size: var(--fs-s);
  line-height: 1.2;
  letter-spacing: 0.02em;
  color: rgba(220, 236, 226, 0.92);
  background: linear-gradient(180deg, rgba(42, 62, 52, 0.9), rgba(16, 27, 22, 0.95));
  border: 1px solid rgba(150, 185, 165, 0.42);
  box-shadow: 0 3px 0 rgba(8, 16, 12, 0.6), 0 6px 14px rgba(0, 0, 0, 0.4);
}
.btn.dive-end:hover { border-color: rgba(200, 230, 210, 0.85); color: #f0f7f2; }
.btn.dive-end:disabled { opacity: 0.5; }

/* 结算那张纸：**深灰绿的一张**（和这一屏同一族），东西一格一格摆（和背包同一个读法）。
   创作者 2026-10-17：不要一行一行的列表，也不要奶油色那层皮；后来又把亮蓝换成了湖岸的深灰绿。 */
.sheet.dive-result {
  /* 这一张整张换色：纸上所有文字都读这套变量，不逐条挑着改（漏一条就是深底深字） */
  --fg: #dfeae2;
  --fg-dim: #aec2b4;
  --fg-accent: #a9cbb7;
  --fg-danger: #ffb4a4;
  color: #dfeae2;
  background:
    radial-gradient(120% 60% at 50% 0%, rgba(110, 160, 135, 0.28), transparent 62%),
    linear-gradient(180deg, #1d2e26 0%, #14211b 46%, #0a120e 100%);
  border: 1px solid rgba(150, 185, 165, 0.45);
  box-shadow: 0 22px 54px rgba(0, 0, 0, 0.62), inset 0 1px 0 rgba(210, 235, 220, 0.14);
}
.sheet.dive-result .sheet-head h2 { color: #dcebe2; }
.sheet.dive-result .x-btn { filter: invert(1) brightness(1.5); opacity: 0.9; }
.sheet.dive-result .dive-res-line { margin: 2px 2px 12px; font-size: var(--fs-m); color: #d5e5da; }
.sheet.dive-result .dive-res-none { margin: 10px 2px; color: rgba(190, 210, 198, 0.85); }
/*
 * 这张纸底下那颗【回岸边】也穿同一套深灰绿（创作者 2026-10-17 点的：
 * 纸上全是这一族颜色，就它还是一块奶油色的纸牌，看着像从别的界面掉进来的）。悬停亮边、按下沉一沉。
 */
.sheet.dive-result .acts .btn {
  color: #eef5f0;
  background: linear-gradient(180deg, rgba(60, 86, 72, 0.96), rgba(24, 39, 32, 0.98));
  border: 1px solid rgba(155, 190, 168, 0.7);
  box-shadow: 0 3px 0 rgba(8, 16, 12, 0.7), 0 8px 18px rgba(0, 0, 0, 0.45);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}
.sheet.dive-result .acts .btn:hover {
  color: #f4faf6;
  border-color: rgba(200, 230, 210, 0.95);
  background: linear-gradient(180deg, rgba(72, 102, 86, 0.97), rgba(30, 48, 39, 0.98));
}
.sheet.dive-result .acts .btn:active { transform: translateY(1px); }

.dive-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(76px, 1fr)); gap: 8px; margin: 4px 0 6px; }
.dive-cell {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 9px 6px 7px;
  border-radius: 8px;
  background: linear-gradient(180deg, rgba(48, 72, 59, 0.55), rgba(16, 28, 22, 0.62));
  border: 1px solid rgba(150, 185, 165, 0.38);
  box-shadow: inset 0 1px 0 rgba(210, 235, 220, 0.14);
}
.dive-cell-art { width: 38px; height: 38px; object-fit: contain; filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.45)); }
.dive-cell-nm { font-size: 10.5px; line-height: 1.2; text-align: center; color: #dfeae2; }
.dive-cell-n { position: absolute; right: 5px; top: 3px; font-family: var(--font-hand); font-size: var(--fs-xs); color: #a9cbb7; }
/*
 * 特别池那一件在结算纸上（创作者 2026-10-17）：**框要发光**，格子底色也和别的两样——
 * 暖金的一格，边上一圈光慢慢呼吸；名字压成奶油金，图也带一层柔光。
 */
.dive-cell.special {
  border-color: rgba(255, 208, 130, 0.85);
  background:
    radial-gradient(120% 120% at 50% 0%, rgba(255, 205, 120, 0.35), transparent 66%),
    linear-gradient(180deg, rgba(84, 62, 24, 0.78), rgba(30, 22, 10, 0.88));
  animation: diveCellGlow 2.4s ease-in-out infinite;
}
.dive-cell.special .dive-cell-nm { color: #ffe9b8; }
.dive-cell.special .dive-cell-n { color: #ffd88f; }
.dive-cell.special .dive-cell-art { filter: drop-shadow(0 0 6px rgba(255, 210, 130, 0.75)); }
@keyframes diveCellGlow {
  0%, 100% { box-shadow: 0 0 10px rgba(255, 190, 90, 0.35), inset 0 0 10px rgba(255, 220, 150, 0.12); }
  50%      { box-shadow: 0 0 22px rgba(255, 200, 110, 0.7), inset 0 0 16px rgba(255, 225, 160, 0.24); }
}

/* 捞到东西那朵小气泡：水面左上角，冒出来停一下再淡掉（连抓两件会从头再放一遍） */
.dive-bubble {
  position: absolute; left: 14px; top: 12px; z-index: 6;
  max-width: 66%;
  padding: 8px 13px 9px;
  border-radius: 14px;
  font-size: var(--fs-s);
  line-height: 1.4;
  color: #e8f3ec;
  background:
    radial-gradient(120% 120% at 20% 0%, rgba(140, 190, 160, 0.4), transparent 60%),
    linear-gradient(180deg, rgba(34, 56, 45, 0.94), rgba(14, 26, 20, 0.96));
  border: 1px solid rgba(165, 200, 180, 0.6);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.45), 0 0 16px rgba(120, 180, 145, 0.22), inset 0 1px 0 rgba(225, 242, 232, 0.25);
  pointer-events: none;
  opacity: 0;
}
.dive-bubble[data-kind="junk"] { color: rgba(216, 227, 237, 0.92); border-color: rgba(160, 185, 210, 0.45); }
/* 特别池那件：气也亮一档（暖金边 + 一圈金晕），和普通东西分开 */
.dive-bubble[data-kind="special"] {
  color: #fff2cf;
  border-color: rgba(255, 210, 130, 0.85);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.45), 0 0 22px rgba(255, 190, 90, 0.4), inset 0 1px 0 rgba(255, 240, 200, 0.3);
}
.dive-bubble::after {
  content: ''; position: absolute; left: 16px; bottom: -6px;
  width: 10px; height: 10px;
  background: rgba(14, 26, 20, 0.96);
  border-right: 1px solid rgba(165, 200, 180, 0.6);
  border-bottom: 1px solid rgba(165, 200, 180, 0.6);
  transform: rotate(45deg);
}
.dive-bubble.on { animation: diveBubblePop 2.4s ease forwards; }
@keyframes diveBubblePop {
  0%   { opacity: 0; transform: translateY(8px) scale(0.92); }
  10%  { opacity: 1; transform: none; }
  78%  { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translateY(-6px); }
}

/* ── 跳进水里的那一下（main.js 的 takeDive 调 ui/dive.js 的 playDiveIntro） ──
 * 整屏压一层深灰绿的暗角，同时一片气泡从底下浮上来；两秒多一起散掉，露出水底那一屏。
 * 挂在 body 上、不吃点击，所以换屏重铺也盖得住。 */
.dive-intro {
  position: fixed; inset: 0; z-index: 70;
  overflow: hidden; pointer-events: none;
  background: radial-gradient(110% 85% at 50% 42%,
    rgba(8, 26, 18, 0) 26%, rgba(5, 18, 13, 0.55) 62%, rgba(2, 9, 6, 0.9) 100%);
  animation: diveIntroDim 2.2s ease forwards;
}
@keyframes diveIntroDim {
  0%   { opacity: 0; }
  12%  { opacity: 1; }
  55%  { opacity: 0.9; }
  100% { opacity: 0; }
}
.dive-intro i {
  position: absolute; bottom: -24px; left: var(--x);
  width: var(--s); height: var(--s); border-radius: 50%;
  background: rgba(200, 230, 212, 0.26);
  border: 1px solid rgba(220, 242, 228, 0.6);
  opacity: 0;
  animation: diveIntroRise var(--d) ease-in var(--delay) forwards;
}
@keyframes diveIntroRise {
  0%   { transform: translate(0, 0) scale(0.7); opacity: 0; }
  15%  { opacity: 0.85; }
  100% { transform: translate(var(--drift), -105vh) scale(1.05); opacity: 0; }
}

/* ============================================================
   67. 假期：家 · 伦敦 · 鱼塘（创作者 2026-10-18）
   ------------------------------------------------------------
   主屋入口照 2026-10-19 红框版式图：全部换成带底图的大卡（和国王十字 / 农庄同一身）：
     上排：餐厅（左）/ 卧室（右）
     左下：农庄（高，一整列）
     右列：壁炉 / 沙包 / 地窖（地窖从农庄挪回屋里）
   位置与大小在 data/home.js 的 HOME_ROOM_CARDS（百分比）。
   餐厅 / 卧室 / 复习课本各自是一张单独的地图（见 ui/home.js 的三张 screen）。
   ============================================================ */
.home-card-map {
  position: relative;
  height: clamp(320px, 56vh, 620px);
  margin-top: var(--sp-2);
}
.home-card-map .home-room-card {
  position: absolute;
  height: auto;
  min-height: 0;
  padding: 8px 5px;
  border-radius: 10px;
}
.home-card-map .home-room-card .kx-zh { font-size: var(--fs-m); }
.home-card-map .home-room-card .kx-tag { font-size: 9px; }
@media (max-width: 560px) {
  .home-card-map { height: clamp(300px, 54vh, 520px); }
  .home-card-map .home-room-card { padding: 6px 3px; border-radius: 8px; }
  .home-card-map .home-room-card .kx-zh { font-size: var(--fs-s); }
  .home-card-map .home-room-card .kx-en { font-size: 8px; }
  .home-card-map .home-room-card .kx-tag { font-size: 8px; }
}

/*
 * 家里所有弹页共用的皮：深棕 + 古铜金，不铺羊皮纸。
 * 字一律走暖白（底子深了，墨色看不见），按钮也跟着换成木牌那身。
 */
.sheet.home-sheet {
  background:
    radial-gradient(80% 46% at 50% 0%, rgba(224, 178, 100, 0.13), transparent 68%),
    linear-gradient(180deg, rgba(56, 39, 22, 0.97), rgba(24, 16, 9, 0.98));
  border: 1px solid rgba(224, 178, 100, 0.46);
  box-shadow: 0 28px 70px rgba(0, 0, 0, 0.72), inset 0 1px 0 rgba(246, 222, 172, 0.08);
  color: #f1e2c2;
}
.sheet.home-sheet .sheet-head {
  border-bottom-color: rgba(224, 178, 100, 0.34);
  box-shadow: 0 1px 0 rgba(224, 178, 100, 0.12);
}
.sheet.home-sheet .sheet-head h2 { color: #f5e7c8; }
.sheet.home-sheet .narr,
.sheet.home-sheet .small,
.sheet.home-sheet .tiny { color: #e6d3ae; }
.sheet.home-sheet .muted { color: rgba(230, 211, 174, 0.66); }
.sheet.home-sheet .btn {
  color: #f1e2c2;
  background:
    radial-gradient(80% 80% at 18% 0%, rgba(224, 178, 100, 0.13), transparent 62%),
    linear-gradient(180deg, rgba(82, 58, 32, 0.96), rgba(38, 26, 14, 0.98));
  border: 1px solid rgba(220, 184, 116, 0.5);
  box-shadow: 0 3px 0 rgba(12, 8, 5, 0.55), 0 8px 18px rgba(0, 0, 0, 0.4);
}
.sheet.home-sheet .btn:hover {
  color: #fff4d6;
  border-color: rgba(246, 214, 148, 0.9);
  background:
    radial-gradient(80% 80% at 18% 0%, rgba(240, 202, 126, 0.18), transparent 62%),
    linear-gradient(180deg, rgba(102, 72, 40, 0.98), rgba(50, 34, 18, 0.99));
}
.sheet.home-sheet .btn.primary { border-left: 4px solid rgba(232, 195, 122, 0.9); }
.sheet.home-sheet .btn:disabled { opacity: 0.42; }

/* 家里那几张纸不摆「小羊皮方牌」的叉：只留一个金色 × */
.sheet.home-sheet .x-btn .x-img,
.sheet.home-dummy .x-btn .x-img { display: none; }
.sheet.home-sheet .x-btn,
.sheet.home-dummy .x-btn {
  position: relative;
  border: 1px solid rgba(210, 174, 106, 0.5);
  border-radius: 4px;
  background: linear-gradient(180deg, rgba(104, 76, 40, 0.9), rgba(44, 30, 16, 0.94));
  box-shadow: inset 0 1px 0 rgba(255, 240, 200, 0.16), 0 2px 8px rgba(0, 0, 0, 0.45);
}
.sheet.home-sheet .x-btn::before,
.sheet.home-dummy .x-btn::before {
  content: '×';
  font-size: 21px;
  line-height: 1;
  color: #e8cb91;
}
.sheet.home-sheet .x-btn:hover,
.sheet.home-dummy .x-btn:hover { border-color: rgba(244, 214, 152, 0.9); }

/*
 * 复习课本七门课一屏排完：比教室那一摞压得更紧一点（主屏的散点布局不受影响）。
 */
.view:has(.home-review-list) .home-review-list { gap: 4px; margin-top: 4px; }
.view:has(.home-review-list) .subject {
  padding-top: 5px;
  padding-bottom: 5px;
}

.home-rows { display: flex; flex-direction: column; gap: 6px; }
.home-row {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 8px 11px;
  border-radius: 8px; text-align: left;
  color: #f0e1bf;
  background: linear-gradient(180deg, rgba(70, 50, 26, 0.7), rgba(34, 23, 12, 0.78));
  border: 1px solid rgba(212, 176, 108, 0.42);
  cursor: pointer;
}
.home-row:hover { border-color: rgba(240, 206, 142, 0.8); background: linear-gradient(180deg, rgba(86, 62, 32, 0.78), rgba(42, 28, 14, 0.82)); }
.home-row .hr-zh { flex: 1 1 auto; font-size: var(--fs-s); }
.home-row .hr-sub { font-size: var(--fs-xs); color: rgba(225, 204, 162, 0.72); }
.home-row .hr-go { font-size: var(--fs-xs); color: #e6c98f; }

/* 装修：4 列小方块，图直接铺满格子，名字压在图上下缘 */
.home-decor-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px;
  margin-top: 10px;
}
.home-decor-tile {
  position: relative;
  overflow: hidden;
  aspect-ratio: 1 / 1;
  padding: 0;
  border-radius: 6px;
  border: 1px solid rgba(210, 174, 106, 0.42);
  background-color: rgba(0, 0, 0, 0.28);
  background-size: cover;
  background-position: center;
  cursor: pointer;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.38);
  transition: transform 0.16s ease, border-color 0.16s ease, box-shadow 0.16s ease;
}
.home-decor-tile::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(8, 6, 4, 0) 46%, rgba(8, 6, 4, 0.58) 70%, rgba(8, 6, 4, 0.92) 100%);
}
.home-decor-tile:hover {
  border-color: rgba(245, 212, 146, 0.9);
  transform: translateY(-2px);
  box-shadow: 0 7px 16px rgba(0, 0, 0, 0.5), 0 0 12px rgba(220, 170, 85, 0.2);
}
.home-decor-tile.on {
  border-color: rgba(245, 212, 146, 0.98);
  box-shadow: 0 0 12px rgba(220, 170, 85, 0.34), inset 0 0 0 1px rgba(245, 212, 146, 0.22);
}
.home-decor-tile .hd-overlay {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 1;
  padding: 0 3px 4px;
  text-align: center;
}
.home-decor-tile .hd-name {
  display: block;
  font-size: 10px;
  line-height: 1.25;
  letter-spacing: 0.02em;
  color: #f4e4c2;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
}
.home-decor-tile .hd-from {
  display: block;
  margin-top: 1px;
  font-size: 8px;
  line-height: 1.2;
  color: rgba(230, 208, 168, 0.78);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9);
}
.home-decor-tile .hd-state {
  position: absolute;
  top: 3px; right: 4px;
  z-index: 2;
  padding: 0 4px;
  font-size: 8px;
  line-height: 1.5;
  letter-spacing: 0.08em;
  color: #2a1b0b;
  background: rgba(246, 220, 162, 0.94);
  border-radius: 3px;
}

/* 升级那颗：金底、亮边、压一层同色光，和旁边那些木牌分开 */
.sheet.home-sheet .btn.home-upgrade {
  color: #2a1b0b;
  background:
    radial-gradient(90% 120% at 20% 0%, rgba(255, 248, 214, 0.9), transparent 58%),
    linear-gradient(180deg, #f3d99a, #c79a4e 72%, #a97a34);
  border: 1px solid #f7e4b0;
  box-shadow: 0 3px 0 #6b4a1a, 0 8px 22px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.62);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-shadow: 0 1px 0 rgba(255, 248, 214, 0.55);
}
.sheet.home-sheet .btn.home-upgrade:hover {
  color: #1f1206;
  border-color: #fff1c6;
  background:
    radial-gradient(90% 120% at 20% 0%, rgba(255, 252, 232, 1), transparent 60%),
    linear-gradient(180deg, #ffe9b4, #d6ab5e 72%, #b4843b);
  box-shadow: 0 4px 0 #6b4a1a, 0 10px 26px rgba(0, 0, 0, 0.52), 0 0 18px rgba(240, 206, 138, 0.28);
}

/* 花园 / 农田 / 农庄 */
.home-farm { margin-top: var(--sp-2); display: flex; flex-direction: column; gap: 12px; }
.home-plot-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(64px, 1fr)); gap: 6px; }
.home-plot {
  min-height: 62px; padding: 5px 4px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  border-radius: 8px; color: #ecdcb8;
  background: linear-gradient(180deg, rgba(64, 46, 24, 0.66), rgba(32, 22, 12, 0.76));
  border: 1px solid rgba(208, 170, 104, 0.4);
  cursor: pointer;
}
.home-plot.empty { border-style: dashed; color: rgba(225, 205, 166, 0.62); }
.home-plot.ripe { border-color: rgba(240, 208, 138, 0.86); box-shadow: 0 0 10px rgba(214, 168, 82, 0.24); }
.home-plot .hp-name { font-size: var(--fs-s); }
.home-plot .hp-sub { font-size: 10px; color: rgba(228, 208, 168, 0.76); }
.home-plot .hp-mark { font-size: 22px; line-height: 1; color: rgba(230, 206, 160, 0.5); }
.home-pens { display: flex; gap: 10px; flex-wrap: wrap; }
.home-pen {
  flex: 1 1 240px; padding: 9px 11px; border-radius: 9px;
  background: rgba(44, 31, 16, 0.7); border: 1px solid rgba(206, 168, 100, 0.38);
}
.home-pen .hpn-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.home-pen .hpn-name { font-size: var(--fs-s); color: #f0dfb8; letter-spacing: 0.1em; }
.home-pen .hpn-sub { font-size: 10px; color: rgba(228, 208, 168, 0.72); }
.home-pen .hpn-acts { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 7px; }
/* 兽栏里那几颗动作钮：走棕金小条，不要全局那颗满宽的奶油色大按钮 */
.home-pen .hpn-acts .btn {
  width: auto; flex: 0 0 auto;
  padding: 6px 10px;
  font-size: var(--fs-xs);
  color: #f0e1bf;
  background: linear-gradient(180deg, rgba(78, 56, 30, 0.92), rgba(38, 26, 14, 0.96));
  border: 1px solid rgba(212, 176, 108, 0.52);
  box-shadow: none;
}
.home-pen .hpn-acts .btn:hover { color: #fff3d2; border-color: rgba(242, 210, 146, 0.9); }
.home-pen .hpn-acts .btn:disabled { opacity: 0.45; }
.home-hunt .btn {
  width: auto;
  color: #f0e1bf;
  background: linear-gradient(180deg, rgba(78, 56, 30, 0.92), rgba(38, 26, 14, 0.96));
  border: 1px solid rgba(212, 176, 108, 0.52);
  box-shadow: none;
}
.pond-head .btn {
  width: auto;
  color: #f1e2c2;
  background:
    radial-gradient(80% 80% at 18% 0%, rgba(224, 178, 100, 0.13), transparent 62%),
    linear-gradient(180deg, rgba(82, 58, 32, 0.96), rgba(38, 26, 14, 0.98));
  border: 1px solid rgba(220, 184, 116, 0.56);
  box-shadow: 0 3px 0 rgba(12, 8, 5, 0.55), 0 8px 18px rgba(0, 0, 0, 0.4);
}
.pond-head .btn:hover { color: #fff4d6; border-color: rgba(246, 214, 148, 0.9); }
.home-hunt { display: flex; justify-content: center; }

/* 国王十字车站：四张竖着的大卡片，各自铺目的地的底图 */
.kx-grid {
  display: grid;
  /* 2026-10-20 多了阿兹卡班那一站：五张并一排，窄屏再折成两列 */
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 8px;
  margin-top: 10px;
}
/* 开学后站台上只剩霍格沃茨一张（2026-10-21）：别占满一整幅宽，给一张横幅 */
.kx-grid.single {
  grid-template-columns: minmax(0, 360px);
  justify-content: center;
}
.kx-grid.single .kx-card { height: clamp(180px, 30vh, 260px); }
.kx-card {
  position: relative;
  overflow: hidden;
  height: clamp(220px, 46vh, 360px);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 12px 6px;
  border-radius: 8px;
  border: 1px solid rgba(224, 178, 100, 0.55);
  background-size: cover;
  background-position: center;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
  cursor: pointer;
  transition: transform 0.16s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.16s ease, box-shadow 0.16s ease;
}
.kx-card::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(8, 6, 4, 0.04) 22%, rgba(8, 6, 4, 0.58) 66%, rgba(8, 6, 4, 0.94) 100%);
}
.kx-card:hover {
  border-color: rgba(245, 215, 154, 0.96);
  transform: translateY(-3px);
  box-shadow: 0 12px 26px rgba(0, 0, 0, 0.56), 0 0 16px rgba(224, 178, 100, 0.2);
}
.kx-card:active { transform: translateY(0) scale(0.99); }
.kx-tx { position: relative; z-index: 1; width: 100%; text-align: center; }
.kx-zh {
  display: block;
  font-size: var(--fs-s);
  letter-spacing: 0.1em;
  line-height: 1.4;
  color: #f7e9c9;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.8);
}
.kx-en {
  display: block;
  margin-top: 3px;
  font-family: var(--font-hand);
  font-size: 9px;
  letter-spacing: 0.05em;
  line-height: 1.35;
  color: rgba(243, 224, 180, 0.82);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
  overflow-wrap: anywhere;
}
@media (max-width: 560px) {
  /*
   * 竖屏：五张竖图并排会挤成一条条（每张不到 90px），所以折成两列、压低卡片；
   * 开学只剩一张时用 single 那套占一整行，做成一条横幅。
   */
  .kx-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; }
  .kx-grid.single { grid-template-columns: minmax(0, 1fr); }
  .kx-card { height: clamp(96px, 13vh, 132px); padding: 8px 6px; }
  .kx-grid.single .kx-card { height: clamp(140px, 20vh, 190px); }
  .kx-zh { font-size: var(--fs-s); }
  .kx-en { font-size: 8px; }
}

/* 鱼塘：木框里一池水；UI 的框、字、按钮都走家里的棕金色 */
.home-pond {
  position: relative; margin-top: var(--sp-2); padding: 12px;
  border-radius: 12px;
  background:
    radial-gradient(80% 60% at 50% 0%, rgba(224, 178, 100, 0.12), transparent 66%),
    linear-gradient(180deg, rgba(52, 37, 21, 0.94), rgba(24, 16, 9, 0.96));
  border: 1px solid rgba(220, 184, 116, 0.46);
  box-shadow: inset 0 0 50px rgba(0, 0, 0, 0.5);
}
.pond-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; color: #e9d6ad; font-size: var(--fs-s); margin-bottom: 9px; }

/* 水族箱：一池水，鱼在里面自由游 */
.pond-tank {
  position: relative;
  height: clamp(280px, 52vh, 460px);
  border-radius: 10px;
  overflow: hidden;
  background:
    radial-gradient(90% 60% at 50% -10%, rgba(120, 230, 220, 0.18), transparent 62%),
    radial-gradient(80% 60% at 20% 100%, rgba(40, 120, 140, 0.25), transparent 70%),
    linear-gradient(180deg, rgba(10, 48, 54, 0.96), rgba(3, 20, 28, 0.98));
  border: 1px solid rgba(110, 210, 210, 0.38);
  box-shadow: inset 0 0 70px rgba(0, 0, 0, 0.55), 0 8px 20px rgba(0, 0, 0, 0.4);
}
/* 塘底垫的那张湖底图（潜水那一屏同一张）：压到 60%，只当底、不抢鱼 */
.pond-bed {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  opacity: 0.6;
  background: center / cover no-repeat;
}
.pond-tank::before {
  content: '';
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  opacity: 0.34;
  background:
    repeating-linear-gradient(115deg, rgba(180, 255, 245, 0.05) 0 2px, transparent 2px 26px),
    radial-gradient(80% 40% at 50% 0, rgba(170, 255, 245, 0.12), transparent 70%);
  animation: pondCaustic 7s ease-in-out infinite alternate;
}
@keyframes pondCaustic {
  from { transform: translateX(-8px) skewX(-2deg); opacity: 0.22; }
  to   { transform: translateX(10px) skewX(2deg); opacity: 0.4; }
}
.pond-tank::after {
  content: '';
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.45);
}
.pond-empty {
  position: absolute; inset: 0; z-index: 2;
  display: grid; place-items: center;
  margin: 0; padding: 0 24px;
  text-align: center;
  color: rgba(205, 240, 238, 0.72);
  font-size: var(--fs-s);
}
/* 往上飘的水泡 */
.pond-air {
  position: absolute; bottom: -12px; left: var(--x); z-index: 0;
  width: var(--s); height: var(--s);
  border-radius: 50%;
  border: 1px solid rgba(200, 245, 255, 0.45);
  background: rgba(180, 235, 255, 0.1);
  pointer-events: none;
  animation: pondAirRise var(--d) linear infinite;
  animation-delay: var(--delay);
}
@keyframes pondAirRise {
  0%   { transform: translateY(0) scale(0.7); opacity: 0; }
  15%  { opacity: 0.8; }
  100% { transform: translateY(-540px) scale(1.1); opacity: 0; }
}
/* 一条鱼：位置与游速由行内变量给 */
.pond-fish {
  position: absolute;
  left: var(--x); top: var(--y); z-index: 2;
  padding: 0; border: 0; background: none;
  cursor: pointer;
  animation: pondSwim var(--dur) ease-in-out infinite alternate;
  animation-delay: var(--delay);
  filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.4));
  will-change: transform;
}
.pond-fish:hover {
  filter: drop-shadow(0 0 8px rgba(180, 245, 255, 0.55)) drop-shadow(0 4px 6px rgba(0, 0, 0, 0.4));
}
.pf-img {
  display: block;
  width: auto; height: auto;
  max-width: 64px; max-height: 52px;
  object-fit: contain;
  user-select: none; -webkit-user-drag: none;
}
@keyframes pondSwim {
  0%   { transform: translate(-50%, -50%) translate(0, 0) scaleX(var(--flip)); }
  35%  { transform: translate(-50%, -50%) translate(18px, -10px) scaleX(var(--flip)); }
  70%  { transform: translate(-50%, -50%) translate(-12px, 12px) scaleX(var(--flip)); }
  100% { transform: translate(-50%, -50%) translate(-20px, -6px) scaleX(var(--flip)); }
}
/* 捞鱼气泡：点下去先从鱼身上冒一串 */
.pf-bubbles { position: absolute; left: 50%; top: 50%; width: 0; height: 0; pointer-events: none; }
.pf-bubbles i {
  position: absolute; left: 0; bottom: 0;
  width: 5px; height: 5px; border-radius: 50%;
  background: rgba(220, 250, 255, 0.88);
  box-shadow: 0 0 6px 2px rgba(170, 240, 255, 0.5);
  opacity: 0;
}
.pond-fish.caught { pointer-events: none; }
.pond-tank.catching .pond-fish { pointer-events: none; }
.pond-fish.caught .pf-bubbles i { animation: pondCatchBubble 0.55s ease-out forwards; }
.pond-fish.caught .pf-bubbles i:nth-child(1) { --bx: -14px; animation-delay: 0s; }
.pond-fish.caught .pf-bubbles i:nth-child(2) { --bx: 4px;  animation-delay: 0.06s; }
.pond-fish.caught .pf-bubbles i:nth-child(3) { --bx: 18px; animation-delay: 0.12s; }
.pond-fish.caught .pf-bubbles i:nth-child(4) { --bx: -4px; animation-delay: 0.18s; }
@keyframes pondCatchBubble {
  0%   { opacity: 0; transform: translate(0, 0) scale(0.4); }
  25%  { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--bx), -46px) scale(1.35); }
}
.pond-fish.caught .pf-img { animation: pondFishCaught 0.5s ease forwards; }
@keyframes pondFishCaught {
  from { transform: scale(1); opacity: 1; }
  to   { transform: scale(0.7) translateY(-10px); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .pond-air, .pond-fish, .pond-tank::before { animation: none; }
  .pond-fish { transform: translate(-50%, -50%) scaleX(var(--flip)); }
}
.pond-summary { margin-top: 9px; display: flex; flex-direction: column; gap: 2px; color: rgba(233, 214, 173, 0.82); }

/* ═══════════════ 动物园（小型庄园起） ═══════════════
 * 深棕色皮：生产五格 + 繁育两对。界面结构在 ui/zoo.js，
 * 底图仍是农庄那几张（牧场），这一层压一块几乎不透明的深棕木台。
 */
.view:has(.zoo-scene) { position: relative; isolation: isolate; }
.view:has(.zoo-scene)::before {
  content: ''; position: absolute; inset: -22px -26px; z-index: -2; pointer-events: none;
  background:
    radial-gradient(42% 26% at 50% 8%, rgba(224, 178, 100, .14), transparent 72%),
    linear-gradient(180deg, rgba(24, 16, 9, .82), rgba(9, 6, 3, .93));
  box-shadow: inset 0 0 88px rgba(0, 0, 0, .62);
}
.zoo-scene {
  position: relative; margin: 0 -4px; padding: 14px 14px 16px;
  border: 1px solid rgba(196, 150, 84, .38); border-radius: 10px;
  background:
    repeating-linear-gradient(90deg, rgba(255, 226, 163, .02) 0 1px, transparent 1px 14px),
    linear-gradient(180deg, rgba(48, 32, 19, .96), rgba(22, 14, 8, .98));
  box-shadow: inset 0 1px 0 rgba(255, 232, 183, .08), 0 14px 30px rgba(0, 0, 0, .5);
}
.zoo-bar {
  display: flex; align-items: center; gap: 10px;
  padding: 0 2px 10px; margin-bottom: 12px;
  border-bottom: 1px solid rgba(196, 150, 84, .22);
}
.zoo-bar.breed { margin-top: 18px; }
.zoo-bar .zb-zh {
  font-family: var(--font-hand); font-size: 14px; letter-spacing: .16em; color: #f2d59b;
}
.zoo-bar .zb-sub { flex: 1; min-width: 0; font-size: 10px; color: rgba(218, 197, 155, .66); }
.zoo-bar .btn.small { flex: none; font-size: 11px; padding: 4px 10px; }

.zoo-make { display: grid; grid-template-columns: repeat(var(--cols, 5), minmax(0, 1fr)); gap: 10px; }
.zoo-card {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 10px 8px; border: 1px solid rgba(190, 151, 82, .35); border-radius: 8px;
  background: linear-gradient(180deg, rgba(57, 38, 22, .9), rgba(25, 17, 10, .95));
  box-shadow: inset 0 1px 0 rgba(255, 236, 194, .06);
}
.zoo-card.empty { border-style: dashed; background: rgba(18, 12, 7, .45); }
.zc-head { width: 100%; display: flex; justify-content: space-between; align-items: center; gap: 4px; }
.zc-no { font-family: var(--font-hand); font-size: 10px; letter-spacing: .18em; color: rgba(219, 190, 137, .78); }
.zc-status {
  font-size: 9px; padding: 1px 6px; border-radius: 999px;
  border: 1px solid rgba(222, 178, 91, .4); color: #f0d49a; background: rgba(154, 112, 54, .18);
}
.zoo-face {
  width: 100%; aspect-ratio: 1; padding: 0; overflow: hidden; cursor: pointer;
  border: 1px solid rgba(210, 168, 96, .36); border-radius: 6px;
  background: radial-gradient(circle at 50% 40%, rgba(236, 205, 148, .14), rgba(18, 12, 7, .72));
  display: grid; place-items: center;
}
.zoo-face:hover { border-color: rgba(238, 196, 118, .72); filter: brightness(1.04); }
.zoo-face .beast-art { width: 94%; height: 94%; object-fit: contain; }
.zc-q { font-size: 30px; color: rgba(232, 203, 150, .6); }
.zc-name {
  font-family: var(--font-hand); font-size: 13px; color: #f3dca8; line-height: 1.2;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.zc-sp { font-size: 10px; color: rgba(206, 187, 145, .7); }
.zc-line { width: 100%; display: flex; justify-content: space-between; font-size: 10px; color: rgba(226, 206, 166, .7); }
.zc-v { color: #eed49b; }
.zc-bar { width: 100%; height: 4px; border-radius: 2px; background: rgba(0, 0, 0, .45); overflow: hidden; }
.zc-bar i { display: block; height: 100%; background: linear-gradient(90deg, #9f7a38, #e9c67e); }
.zc-none { font-size: 10px; color: rgba(218, 195, 150, .55); text-align: center; line-height: 1.4; }
.zc-acts { display: flex; gap: 4px; width: 100%; }
/* 卡上这三颗（详情 / 喂食 / 卸下）与顶上那条栏里的【动物仓库】同一身深棕——
   原来它们就是通用 .btn.small 那张米黄纸，摆在整片深棕的动物园里三颗白块很跳。
   ⚠️ 只改这两处，别去动 .btn.small：全工程的米黄小钮都归它。 */
.zc-acts .btn,
.zoo-bar .btn {
  flex: 1; min-width: 0; padding: 4px 2px; font-size: 11px;
  text-align: center;
  border: 1px solid rgba(222, 178, 91, .5); border-radius: 6px;
  background: linear-gradient(180deg, rgba(112, 79, 40, .9), rgba(48, 33, 20, .96));
  color: #f4dda8;
  box-shadow: none; text-shadow: none;
}
.zc-acts .btn:hover,
.zoo-bar .btn:hover {
  border-color: rgba(238, 196, 118, .78);
  filter: brightness(1.1);
  transform: none;
  box-shadow: 0 0 10px rgba(201, 152, 70, .22);
}
.zc-acts .btn:active,
.zoo-bar .btn:active { transform: translateY(1px); box-shadow: none; }
.zc-acts .btn:disabled,
.zoo-bar .btn:disabled { opacity: .5; }
.zoo-add {
  width: 100%; aspect-ratio: 1; cursor: pointer;
  border: 1px dashed rgba(222, 178, 91, .4); border-radius: 6px;
  background: rgba(0, 0, 0, .18); color: rgba(238, 209, 155, .78);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
}
.zoo-add:hover { border-color: rgba(238, 196, 118, .7); background: rgba(154, 112, 54, .14); }
.za-plus { font-size: 26px; line-height: 1; }
.za-tx { font-size: 10px; letter-spacing: .08em; }
.zoo-harvest {
  width: 100%; padding: 5px 4px; cursor: pointer;
  display: flex; justify-content: center; gap: 6px;
  border: 1px solid rgba(222, 178, 91, .5); border-radius: 6px;
  background: linear-gradient(180deg, rgba(112, 79, 40, .9), rgba(48, 33, 20, .96));
  color: #f4dda8; font-size: 11px;
}
.zoo-harvest:hover { filter: brightness(1.08); }
.zoo-harvest.has { box-shadow: 0 0 0 1px rgba(238, 196, 118, .2), 0 0 12px rgba(201, 152, 70, .18); }
.zh-tx { letter-spacing: .08em; }
.zh-n { background: #d8ad61; color: #2a1a0c; border-radius: 999px; padding: 0 5px; font-size: 10px; font-weight: 600; }

.zoo-breeds { display: grid; grid-template-columns: repeat(var(--cols, 2), minmax(0, 1fr)); gap: 12px; }
.zoo-breed {
  padding: 10px 12px; border: 1px solid rgba(190, 151, 82, .35); border-radius: 8px;
  background: linear-gradient(180deg, rgba(57, 38, 22, .9), rgba(25, 17, 10, .95));
}
.zoo-breed.busy { border-color: rgba(231, 180, 92, .6); box-shadow: inset 0 0 24px rgba(219, 166, 78, .08); }
.zoo-bar.inner { border-bottom: 0; padding: 0 0 8px; margin-bottom: 6px; }
.zoo-pair { display: flex; align-items: center; justify-content: center; gap: 12px; }
.zoo-pslot {
  width: 88px; aspect-ratio: 1; padding: 4px; cursor: pointer; overflow: hidden;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  border: 1px solid rgba(210, 168, 96, .4); border-radius: 7px;
  background: radial-gradient(circle at 50% 40%, rgba(236, 205, 148, .1), rgba(18, 12, 7, .66));
  color: rgba(232, 203, 150, .7);
}
.zoo-pslot.empty { border-style: dashed; background: rgba(0, 0, 0, .2); }
.zoo-pslot:hover { border-color: rgba(238, 196, 118, .66); }
.zoo-breed.busy .zoo-pslot { cursor: default; }
.zps-face { width: 100%; flex: 1; min-height: 0; display: grid; place-items: center; overflow: hidden; border-radius: 4px; }
.zps-face .beast-art { width: 100%; height: 100%; object-fit: contain; }
.zps-q { font-size: 24px; color: rgba(232, 203, 150, .6); }
.zps-plus { font-size: 22px; line-height: 1; }
.zps-name {
  max-width: 100%; font-size: 10px; color: #f0d7a4;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.zoo-heart { color: rgba(224, 150, 130, .8); font-size: 14px; }
.zoo-start {
  width: 100%; margin-top: 10px; padding: 8px; cursor: pointer;
  border: 1px solid rgba(210, 165, 88, .4); border-radius: 6px;
  background: rgba(0, 0, 0, .25); color: rgba(232, 209, 164, .72);
  font-size: 12px; letter-spacing: .08em;
}
.zoo-start.ready {
  background: linear-gradient(180deg, rgba(126, 88, 42, .95), rgba(57, 38, 21, .98));
  color: #f6dfa8; border-color: rgba(232, 188, 104, .64);
}
.zoo-start:hover:not(:disabled) { filter: brightness(1.08); }
.zoo-start:disabled { opacity: .55; cursor: default; filter: grayscale(.4); }
.zoo-foot { margin: 14px 2px 0; font-size: 10px; color: rgba(212, 192, 154, .6); }

/* 仓库 / 挑动物的弹页（皮借 home-sheet，深棕那套） */
.zoo-sheet-title { display: flex; align-items: baseline; gap: 8px; margin: 2px 0 12px; }
.zst-zh { font-family: var(--font-hand); font-size: 18px; letter-spacing: .12em; color: #f3dca8; }
.zst-en { font-size: 10px; color: rgba(222, 198, 152, .6); }
.zoo-wgrid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px 10px; }
.zoo-wcard {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 8px 6px; border: 1px solid rgba(190, 151, 82, .3); border-radius: 8px;
  background: rgba(18, 12, 7, .38);
}
.zoo-wcard.pick { cursor: pointer; }
.zoo-wcard.pick:hover { border-color: rgba(238, 196, 118, .6); background: rgba(88, 61, 32, .3); }
.zoo-wcard.off { opacity: .42; filter: grayscale(.7); pointer-events: none; }
.zoo-wcard.sel { outline: 2px solid #d8ad61; outline-offset: 2px; }
.zw-head { width: 100%; display: flex; flex-direction: column; align-items: flex-start; gap: 1px; }
.zw-status { font-size: 9px; color: #e5c477; }
.zw-where { font-size: 9px; color: rgba(210, 188, 145, .55); }
.zw-face {
  width: 100%; aspect-ratio: 1; overflow: hidden;
  display: grid; place-items: center;
  border: 1px solid rgba(210, 168, 96, .26); border-radius: 6px;
  background: radial-gradient(circle at 50% 40%, rgba(236, 205, 148, .12), rgba(18, 12, 7, .6));
}
.zw-face .beast-art { width: 92%; height: 92%; object-fit: contain; }
.zw-q { font-size: 26px; color: rgba(232, 203, 150, .6); }
.zw-name {
  max-width: 100%; font-size: 12px; color: #f3dca8;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.zw-sp { font-size: 10px; color: rgba(206, 187, 145, .7); }
.zw-acts { display: flex; width: 100%; gap: 3px; margin-top: 2px; }
.zw-btn {
  flex: 1; min-width: 0; padding: 3px 0; cursor: pointer;
  border: 1px solid rgba(200, 156, 84, .35); border-radius: 4px;
  background: rgba(0, 0, 0, .25); color: #e8d3aa; font-size: 10px;
}
.zw-btn:hover { background: rgba(112, 79, 40, .4); }
.zw-btn.danger { color: #e4b3a3; border-color: rgba(190, 112, 90, .35); }

@media (max-width: 720px) {
  .zoo-make { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .zoo-breeds { grid-template-columns: 1fr; }
  .zoo-wgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 460px) {
  .zoo-make { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* 深棕底上不能用 multiply：那会把动物压成一块黑影（立绘头像本身就是透明底） */
.zoo-scene .beast-art,
.zoo-wcard .beast-art { mix-blend-mode: normal; }

/* ── 制种机（中型庄园起，田地屏） ── */
.seedm-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 10px; }
.seedm-card {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 10px 8px; border: 1px solid rgba(190, 151, 82, .38); border-radius: 8px;
  background: linear-gradient(180deg, rgba(57, 38, 22, .9), rgba(25, 17, 10, .95));
  box-shadow: inset 0 1px 0 rgba(255, 236, 194, .06);
}
.seedm-card.empty { border-style: dashed; background: rgba(18, 12, 7, .45); }
.seedm-card.ready {
  border-color: rgba(232, 188, 104, .78);
  box-shadow: inset 0 0 24px rgba(219, 166, 78, .1), 0 0 14px rgba(201, 152, 70, .16);
}
.sm-head { width: 100%; display: flex; justify-content: space-between; align-items: center; gap: 4px; }
.sm-no { font-family: var(--font-hand); font-size: 10px; letter-spacing: .16em; color: rgba(219, 190, 137, .8); }
.sm-state { font-size: 9px; color: #e5c477; }
.seedm-card.ready .sm-state { color: #f6dfa8; }
.sm-art {
  width: 54px; height: 54px; display: grid; place-items: center; overflow: hidden;
  border: 1px solid rgba(210, 168, 96, .34); border-radius: 7px;
  background: radial-gradient(circle at 50% 40%, rgba(236, 205, 148, .14), rgba(18, 12, 7, .7));
}
.sm-art-img { width: 88%; height: 88%; object-fit: contain; }
.sm-name {
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: var(--font-hand); font-size: 12px; color: #f3dca8;
}
.sm-sub { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 9px; color: rgba(210, 188, 145, .66); }
.seedm-card > .btn { width: 100%; margin-top: 2px; }
.sm-add {
  width: 100%; aspect-ratio: 1.5; cursor: pointer;
  border: 1px dashed rgba(222, 178, 91, .42); border-radius: 6px;
  background: rgba(0, 0, 0, .18); color: rgba(238, 209, 155, .78);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
}
.sm-add:hover { border-color: rgba(238, 196, 118, .7); background: rgba(154, 112, 54, .14); }
.sm-plus { font-size: 24px; line-height: 1; }
.sm-add-tx { font-size: 10px; letter-spacing: .08em; }

.seedm-picks { display: flex; flex-direction: column; gap: 6px; }
.seedm-row {
  display: grid; grid-template-columns: 38px minmax(0, 1fr) auto auto; align-items: center; gap: 8px;
  padding: 7px 8px; border: 1px solid rgba(190, 151, 82, .28); border-radius: 7px;
  background: rgba(18, 12, 7, .38);
}
.smr-art {
  width: 36px; height: 36px; display: grid; place-items: center; overflow: hidden;
  border: 1px solid rgba(210, 168, 96, .28); border-radius: 6px; background: rgba(0, 0, 0, .2);
}
.smr-art-img { width: 88%; height: 88%; object-fit: contain; }
.smr-tx { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.smr-name { font-size: 12px; color: #f3dca8; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.smr-sub { font-size: 10px; color: rgba(210, 188, 145, .66); }
.smr-n { font-size: 11px; color: #eed49b; }
.seedm-row .btn { flex: none; }
/* 一行两颗（放入 / 一键放入）：并排贴右边，窄屏时整行挪到下面 */
.smr-acts { display: flex; align-items: center; gap: 6px; flex: none; }
@media (max-width: 460px) {
  .seedm-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .seedm-row { grid-template-columns: 32px minmax(0, 1fr) auto; }
  .seedm-row .smr-acts { grid-column: 1 / -1; }
  .seedm-row .smr-acts .btn { flex: 1; }
}

/* ── 休息 / 睡觉 / 复习：带底图的大动作卡（与国王十字同一身） ── */
.action-cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  margin-top: 10px;
}
.action-cards.two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* 公共休息室那两张在 .room-map 里，别被「一摞牌子」的 22rem 宽度夹住 */
.room-map > .action-cards { width: min(100%, 40rem); }
.action-card .kx-tx { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.action-card .kx-tag {
  display: block; margin-top: 3px;
  font-size: 10px; letter-spacing: .04em; line-height: 1.35;
  color: rgba(246, 228, 187, .94); text-shadow: 0 1px 4px rgba(0, 0, 0, .85);
}
.action-card .kx-sub {
  display: block; margin-top: 2px; max-width: 96%;
  font-size: 9px; line-height: 1.5;
  color: rgba(226, 209, 175, .8); text-shadow: 0 1px 4px rgba(0, 0, 0, .85);
}
.action-card:disabled {
  opacity: .55; filter: grayscale(.35);
  cursor: not-allowed;
}
.action-card:disabled:hover {
  transform: none;
  border-color: rgba(224, 178, 100, .55);
  box-shadow: 0 8px 20px rgba(0, 0, 0, .5);
}
@media (max-width: 560px) {
  .action-cards, .action-cards.two { gap: 6px; }
  .action-card .kx-tag { font-size: 9px; }
  .action-card .kx-sub { font-size: 8px; }
}

/* 农庄五张卡比休息/餐厅那张矮一档：两排摆下来不至于挤满整屏 */
.action-cards.farm-cards .action-card {
  height: clamp(190px, 34vh, 280px);
}
@media (max-width: 560px) {
  .action-cards.farm-cards .action-card {
    height: clamp(180px, 32vh, 250px);
  }
}

/* ═══════════════ 果园（小型庄园起） ═══════════════
 * 照开局选魔杖那种一沓卡片的做法：所有树叠在舞台中间，能左右拖，
 * 前面那棵最大、两边各露半棵。名称与状态直接悬在树下（没有底板）。
 * 摇下来的果子挂在树按钮里的 .orchard-drop-layer 上，由 ui/orchard.js 的 rAF 做重力与弹地。
 */
.orchard-scene { margin-top: var(--sp-2); }
.orchard-stage {
  position: relative;
  min-height: clamp(340px, 46vh, 480px);
  touch-action: pan-y;
  cursor: grab;
  user-select: none;
}
.orchard-stage:active { cursor: grabbing; }
.orchard-panel {
  position: absolute;
  left: 50%;
  top: 48%;
  width: min(84%, 440px);
  display: flex;
  flex-direction: column;
  align-items: center;
  transform: translate(-50%, -50%);
  transition: transform 0.34s cubic-bezier(0.22, 0.85, 0.25, 1), opacity 0.34s ease;
  will-change: transform, opacity;
}
.orchard-panel.ghost { pointer-events: none; }
.orchard-tree {
  position: relative;
  width: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.orchard-tree.empty { min-height: clamp(230px, 38vh, 380px); align-items: center; }
.orchard-tree-art {
  display: block;
  width: 100%;
  height: clamp(210px, 34vh, 340px);
  background: center / contain no-repeat;
  filter: drop-shadow(0 18px 22px rgba(0, 0, 0, 0.42));
  transition: transform 0.18s cubic-bezier(0.16, 1, 0.3, 1);
  transform-origin: 50% 92%;
}
.orchard-tree:hover .orchard-tree-art { transform: scale(1.02); }
.orchard-tree-art.is-shaking { animation: orchardShake 0.58s ease-in-out; }
@keyframes orchardShake {
  0%, 100% { transform: rotate(0deg); }
  12% { transform: rotate(-2.6deg); }
  34% { transform: rotate(2.4deg); }
  56% { transform: rotate(-1.6deg); }
  78% { transform: rotate(1.1deg); }
}
/* 空地：一个虚线圆圈加号 */
.orchard-tree.empty .empty-art {
  width: clamp(120px, 34vw, 180px);
  height: clamp(120px, 34vw, 180px);
  display: grid;
  place-items: center;
  font-size: 56px;
  font-weight: 300;
  color: rgba(205, 233, 190, 0.5);
  border: 2px dashed rgba(158, 200, 146, 0.42);
  border-radius: 50%;
  background: rgba(20, 34, 18, 0.2);
}
/* 名称 / 铲除 / 状态：悬空在树下，不垫底板 */
.orchard-name {
  margin-top: 4px;
  font-size: var(--fs-m);
  letter-spacing: 0.08em;
  color: #f5ecd8;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.88), 0 0 18px rgba(0, 0, 0, 0.6);
}
.btn.small.orchard-del {
  margin-top: 6px;
  padding: 3px 18px 4px;
  font-size: var(--fs-xs);
  letter-spacing: 0.1em;
  color: #f6ddd3;
  background: rgba(92, 26, 20, 0.82);
  border: 1px solid rgba(206, 112, 90, 0.6);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
}
.btn.small.orchard-del:hover { background: rgba(116, 34, 26, 0.9); border-color: rgba(230, 140, 112, 0.8); }
.orchard-status {
  margin-top: 5px;
  font-size: var(--fs-xs);
  color: #cfe3c2;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.9);
}
.orchard-status.growing { color: #b6d6a4; }
.orchard-status.mature { color: #e9d6ad; }
.orchard-status.harvest { color: #f0d78f; }
/* 树上攒着的果子数 */
.orchard-fruit-badge {
  position: absolute;
  right: 16%;
  top: 24%;
  min-width: 34px;
  padding: 2px 7px;
  border-radius: 999px;
  font-size: var(--fs-xs);
  text-align: center;
  color: #1f2a16;
  background: linear-gradient(180deg, #f0d78f, #d8b45f);
  border: 1px solid rgba(255, 240, 190, 0.8);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
  pointer-events: none;
}
/* 摇下来的果子：绝对定位、rAF 改 transform */
.orchard-drop-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: visible;
  z-index: 3;
}
.orchard-fruit-fall {
  position: absolute;
  width: 30px;
  height: 30px;
  background-image: var(--fruit-img);
  background-size: contain;
  background-repeat: no-repeat;
  transform: translate(-50%, -50%);
  will-change: transform, opacity;
  filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.45));
}
/* 底下的左右箭头与计数（和选魔杖同一副小件） */
.orchard-counter { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 2px; }
/* 抬头那一行里的翻页：靠右和【全部收获】排在一起，不再占树底下那一截 */
.plots-bulk .orchard-counter { gap: 8px; margin-top: 0; }
.plots-bulk .orchard-count { min-width: 46px; }
.orchard-arrow {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  font-size: 18px;
  line-height: 1;
  color: #e9d6ad;
  background: rgba(26, 20, 12, 0.55);
  border: 1px solid rgba(224, 178, 100, 0.4);
  cursor: pointer;
}
.orchard-arrow:hover { border-color: rgba(224, 178, 100, 0.75); }
.orchard-arrow:disabled { opacity: 0.3; cursor: not-allowed; }
.orchard-count { min-width: 58px; text-align: center; font-size: var(--fs-xs); color: var(--cine-dim); }
/* 种什么：一格格树种 */
.orchard-tree-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: var(--sp-2);
}
.orchard-seed {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 2px;
  min-height: 150px;
  padding: 8px 6px 10px;
  border-radius: var(--r);
  border: 1px solid rgba(196, 155, 96, 0.34);
  background: rgba(30, 48, 32, 0.68);
  color: var(--cine-text);
  cursor: pointer;
  transition: border-color 0.14s ease, transform 0.14s ease;
}
.orchard-seed:hover { border-color: rgba(214, 180, 110, 0.7); transform: translateY(-2px); }
.orchard-seed.is-off { opacity: 0.42; cursor: not-allowed; transform: none; }
.orchard-seed-art { width: 72px; height: 82px; background: center / contain no-repeat; filter: drop-shadow(0 6px 8px rgba(0, 0, 0, 0.4)); }
.orchard-seed-name { font-size: var(--fs-s); }
.orchard-seed-info { font-size: var(--fs-xs); color: #b6d6a4; }
.orchard-seed-price { font-size: var(--fs-xs); color: #d9c08a; }

/* ═══════════════ 地窖（花园小楼起） ═══════════════ */
.cellar-grid {
  display: grid;
  grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr));
  gap: 10px;
  margin-top: var(--sp-2);
}
.cellar-barrel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 10px 8px 9px;
  border-radius: 12px;
  border: 1px solid rgba(196, 155, 96, 0.4);
  background:
    radial-gradient(80% 60% at 50% 0%, rgba(224, 178, 100, 0.1), transparent 66%),
    linear-gradient(180deg, rgba(44, 32, 20, 0.9), rgba(22, 16, 10, 0.94));
  box-shadow: inset 0 0 26px rgba(0, 0, 0, 0.5);
  color: #efe2c4;
  cursor: pointer;
  transition: border-color 0.16s ease, transform 0.16s ease;
}
.cellar-barrel:hover { border-color: rgba(224, 178, 100, 0.7); transform: translateY(-2px); }
/* 空桶：不垫棕色底板，只留透明底的木桶和下面两行字 */
.cellar-barrel.is-empty {
  background: none;
  border-color: transparent;
  box-shadow: none;
}
.cellar-barrel.is-empty:hover {
  background: rgba(224, 178, 100, 0.07);
  border-color: rgba(224, 178, 100, 0.28);
}
.cellar-barrel.is-ready {
  border-color: rgba(230, 196, 112, 0.9);
  box-shadow: 0 0 18px rgba(230, 190, 100, 0.34), inset 0 0 26px rgba(0, 0, 0, 0.5);
  animation: cellarReady 2.4s ease-in-out infinite;
}
@keyframes cellarReady {
  0%, 100% { box-shadow: 0 0 12px rgba(230, 190, 100, 0.24), inset 0 0 26px rgba(0, 0, 0, 0.5); }
  50% { box-shadow: 0 0 24px rgba(240, 205, 125, 0.5), inset 0 0 26px rgba(0, 0, 0, 0.5); }
}
.cellar-barrel-art {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  max-height: 150px;
  background: center / contain no-repeat;
  display: grid;
  place-items: center;
}
.cellar-wine-art {
  width: 40%;
  height: 54%;
  background: center / contain no-repeat;
  transform: translateY(-4%);
  filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.45));
}
.cellar-bubble {
  position: absolute;
  inset: 6% 18%;
  border-radius: 50%;
  border: 1px solid rgba(255, 235, 170, 0.45);
  animation: cellarBubble 2.8s ease-out infinite;
  pointer-events: none;
}
@keyframes cellarBubble {
  0% { opacity: 0; transform: scale(0.6) translateY(10px); }
  30% { opacity: 0.85; }
  100% { opacity: 0; transform: scale(1.15) translateY(-16px); }
}
.cellar-barrel-plate { display: flex; flex-direction: column; align-items: center; gap: 1px; text-align: center; }
.cellar-barrel-name { font-size: var(--fs-s); letter-spacing: 0.04em; }
.cellar-barrel-state { font-size: var(--fs-xs); color: #c9ad7d; }
.cellar-barrel.is-ready .cellar-barrel-state { color: #f0d78f; }
/* 配方纸 */
.cellar-recipes { display: flex; flex-direction: column; gap: 6px; max-height: 58vh; overflow-y: auto; }
.cellar-recipe {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid rgba(196, 155, 96, 0.3);
  background: rgba(38, 28, 18, 0.62);
  color: #efe2c4;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.14s ease, background 0.14s ease;
}
.cellar-recipe:hover { border-color: rgba(224, 178, 100, 0.65); background: rgba(52, 38, 24, 0.72); }
.cellar-recipe.is-off { opacity: 0.42; cursor: not-allowed; }
.cellar-recipe.is-off:hover { border-color: rgba(196, 155, 96, 0.3); background: rgba(38, 28, 18, 0.62); }
.cellar-recipe.is-locked .cellar-recipe-meta { color: #e0b46a; }
.cellar-recipe-art { flex: none; width: 40px; height: 54px; background: center / contain no-repeat; }
.cellar-recipe-tx { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.cellar-recipe-name { font-size: var(--fs-s); }
.cellar-recipe-need { font-size: var(--fs-xs); color: #b9c9a8; }
.cellar-recipe-meta { font-size: var(--fs-xs); color: #c9ad7d; }

@media (max-width: 560px) {
  /*
   * 手机：树收一档，整屏（抬头 + 树 + 状态）要能落在底部托盘上面；
   * 树太高时底部那行「可收获」会被 sticky 托盘盖住（创作者 2026-10-21 报的）。
   */
  .orchard-stage { min-height: clamp(280px, 38vh, 380px); }
  .orchard-panel { width: 78%; top: 46%; }
  .orchard-tree-art { height: clamp(180px, 27vh, 280px); }
  .orchard-tree.empty { min-height: clamp(180px, 27vh, 280px); }
  .orchard-tree.empty .empty-art { width: clamp(96px, 24vw, 150px); height: clamp(96px, 24vw, 150px); font-size: 44px; }
  .cellar-grid { gap: 7px; }
  .cellar-barrel { padding: 8px 5px 7px; }
}
@media (prefers-reduced-motion: reduce) {
  .orchard-tree-art.is-shaking { animation: none; }
  .cellar-barrel.is-ready, .cellar-bubble { animation: none; }
}


/* ═══════════════ 外出打工（巫师劳动部 · 2026-10-19） ═══════════════
 * 三处门脸 = 一行小方格；底下那一段是岗位列表与那张羊皮纸里的账行。
 * 类名一律带 job- 前缀：main.css 两万多行，短名（.row / .pay 这种）迟早撞别人家容器。
 */
/* 三处门脸：小方格（2026-10-19 改版——原来是占满整屏的竖版大牌，他说"换成小方框不要这么大"）。
   格子自己排成一行，左边对齐，不铺满整幅。 */
.job-place-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
}
.job-place-tile {
  width: 112px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 0 0 7px;
  border: 1px solid rgba(196, 155, 96, 0.42);
  border-radius: 8px;
  background: rgba(30, 22, 14, 0.66);
  cursor: pointer;
  overflow: hidden;
  transition: border-color 0.14s ease, transform 0.14s ease, box-shadow 0.14s ease;
}
.job-place-tile:hover {
  border-color: rgba(245, 215, 154, 0.9);
  transform: translateY(-2px);
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.5);
}
.job-place-tile:active { transform: translateY(0) scale(0.99); }
.jpt-art {
  width: 100%;
  aspect-ratio: 1 / 1;
  background: center / cover no-repeat;
}
.jpt-name { font-size: var(--fs-s); letter-spacing: 0.06em; color: #f7e9c9; }
.jpt-en {
  max-width: 100%;
  font-family: var(--font-hand);
  font-size: 8px;
  letter-spacing: 0.04em;
  color: rgba(243, 224, 180, 0.6);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.job-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
}

/* 一条岗位 = 左边名字与那行代价，右边工钱。上不了的灰着，但照样点得进去看缺什么。 */
.job-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 12px;
  border-radius: 8px;
  border: 1px solid rgba(196, 155, 96, 0.3);
  background: rgba(30, 22, 14, 0.66);
  color: #efe2c4;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.14s ease, background 0.14s ease, transform 0.14s ease;
}
.job-row:hover {
  border-color: rgba(224, 178, 100, 0.68);
  background: rgba(46, 34, 20, 0.76);
  transform: translateY(-1px);
}
.job-row:active { transform: translateY(0) scale(0.995); }
.job-row.is-off { filter: saturate(0.32); opacity: 0.66; }
.job-row.is-off:hover { transform: none; border-color: rgba(196, 155, 96, 0.34); }

.job-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.job-name { font-size: var(--fs-s); letter-spacing: 0.06em; }
.job-en {
  font-family: var(--font-hand);
  font-size: 9px;
  letter-spacing: 0.04em;
  color: rgba(243, 224, 180, 0.6);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.job-need { font-size: var(--fs-xs); color: #b9c9a8; }

/* 工钱那一列：上面两个字极小，下面是数（细条框的右边一律走这个排法） */
.job-pay { flex: none; text-align: right; display: flex; flex-direction: column; gap: 1px; }
.job-pay-label { font-size: 9px; letter-spacing: 0.14em; opacity: 0.55; }
.job-pay-num { font-size: var(--fs-s); color: #e0b46a; white-space: nowrap; }

/* 羊皮纸里那几行账（报酬 / 时间 / 精力 / 门槛）。
   字色不写死：这张纸穿的是 .sheet.gringotts 那身浅纸，深字由那身皮自己给。 */
.job-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 3px 0;
  border-bottom: 1px dashed rgba(120, 96, 60, 0.22);
  font-size: var(--fs-s);
}
.job-line:last-of-type { border-bottom: none; }
.job-label { flex: none; opacity: 0.6; letter-spacing: 0.08em; }
.job-value { text-align: right; font-weight: 600; }

@media (max-width: 620px) {
  .job-row { gap: 8px; padding: 8px 10px; }
  .job-need { white-space: normal; }
}

/* ── 地图上那颗【外出打工】：淡蓝 + 一串店铺彩旗（2026-10-19 创作者：要和别的钮不一样）
   DOM 见 ui/london.js 的 mapPin / bunting：针脚照旧，彩旗压在字牌里面顶上那一条。
   ⚠️ 类名挂的是 .wmp-spot.is-shop（不是裸 .shop）：main.css 里 .shop-sub / .shop-note 那一族已经占着"店"这个字。 */

/* 针尖换淡蓝（那颗呼吸的金色是别处六颗共用的，这里整条动画换成蓝圈） */
.wmp-spot.is-shop .wms-pin {
  border-color: rgba(30, 62, 86, 0.72);
  background: radial-gradient(circle at 34% 28%, #f2fbff, #7fb6dc 60%, #2f6a95);
  animation: wmsPulseBlue 2.8s ease-in-out infinite;
}
@keyframes wmsPulseBlue {
  0%, 100% { transform: scale(1); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45), 0 0 0 0 rgba(150, 205, 235, 0.62); }
  50%      { transform: scale(1.14); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45), 0 0 0 5px rgba(150, 205, 235, 0); }
}

/* 淡蓝字牌：底浅字深（渐变最深那一端上量的：名 8.28:1、英文 6.71:1）。
   顶上多让出 12px 给那一串彩旗——彩旗是**压在牌里**的，见下面 .wms-bunting。 */
.wmp-spot.is-shop .wms-tx {
  position: relative;
  padding-top: 14px;
  background: linear-gradient(180deg, #e7f3fb, #b3d2e8);
  border-color: rgba(58, 106, 140, 0.8);
  box-shadow: 0 2px 7px rgba(0, 0, 0, 0.34), inset 0 1px 0 rgba(255, 255, 255, 0.62);
}
.wmp-spot.is-shop .wms-zh { color: #16334a; }
.wmp-spot.is-shop .wms-en { color: rgba(28, 66, 92, 0.78); }
.wmp-spot.is-shop:hover .wms-tx { border-color: #1d5f8c; }

/* 那一串彩旗：一根绳 + 七面三角小旗，各按各的拍子摆（纯 CSS，不引新图）。
   它**在字牌里面**（DOM 是 .wms-tx 的第一个孩子），绝对定位压在牌子顶上那一条，
   左右各让 3px 不出边；上面那条 padding-top: 12px 就是给它让的位置。 */
.wms-bunting {
  position: absolute;
  left: 3px;
  right: 3px;
  top: 2px;
  display: flex;
  justify-content: space-between;
  height: 10px;
  pointer-events: none;
}
.wms-bunting::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 1px;
  height: 2px;
  border-radius: 50%;
  background: linear-gradient(180deg, #eaf5fc, #8fb9d4);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}
.wms-bunting i {
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 8px solid #cfe6f5;
  transform-origin: 50% 0;
  animation: wmsFlag 2.4s ease-in-out infinite;
}
.wms-bunting i.bf1 { border-top-color: #f2e2b8; }
.wms-bunting i.bf2 { border-top-color: #9cc6e8; }
.wms-bunting i.bf3 { border-top-color: #e9bfae; }
.wms-bunting i:nth-child(2n) { animation-delay: -0.6s; }
.wms-bunting i:nth-child(3n) { animation-delay: -1.2s; }
@keyframes wmsFlag {
  0%, 100% { transform: rotate(-6deg); }
  50%      { transform: rotate(6deg); }
}

/* 人正站在这处：字牌照全地图那套"到此一游"镀金（具体度抬到 0,4,0 才压得住上面那几条），彩旗停住 */
.wmp-spot.is-shop.here .wms-tx {
  background: linear-gradient(180deg, #f7dc9a, #dcae55);
  border-color: rgba(122, 84, 22, 0.75);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45), 0 0 14px rgba(224, 178, 100, 0.5);
}
.wmp-spot.is-shop.here .wms-zh { color: #4a3210; }
.wmp-spot.is-shop.here .wms-en { color: rgba(74, 50, 16, 0.72); }
.wmp-spot.is-shop.here .wms-pin {
  border-color: rgba(122, 84, 22, 0.9);
  background: radial-gradient(circle at 34% 28%, #fff6da, #f0c063 58%, #a9702a);
  filter: drop-shadow(0 0 8px rgba(255, 214, 120, 0.85));
  animation: none;
}
.wmp-spot.is-shop.here .wms-bunting i { animation: none; }

@media (prefers-reduced-motion: reduce) {
  .wmp-spot.is-shop .wms-pin, .wms-bunting i { animation: none; }
}

/* 纸上那几颗钮（上工 / 解锁 / 算了 / 知道了）——2026-10-19 创作者：「按钮换成类似背景的格式」。
   不再用那身浮在纸上的米黄 3D 钮，改成**从这张纸上压出来的一块**：
   同一道斜纹纸纹 + 一圈金棕细边 + 一点内凹；主那颗（lead）只是描边重些、纸色深一档。
   只圈在 .sheet 里 + 自带 job-btn 类，别处那三张屏的钮一个字不动。 */
.sheet .btn.job-btn {
  display: inline-block;
  width: auto;
  min-width: 78px;
  padding: 6px 16px 7px;
  border: 1px solid rgba(150, 118, 66, 0.42);
  border-radius: 4px;
  background:
    repeating-linear-gradient(112deg, rgba(255, 255, 255, 0.5) 0 2px, transparent 2px 9px),
    linear-gradient(180deg, #f7f1e3, #ece0c8);
  color: #3a2c18;
  font-size: var(--fs-s);
  letter-spacing: 0.1em;
  text-align: center;
  text-shadow: none;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.75), 0 1px 2px rgba(90, 66, 34, 0.22);
  transition: border-color 0.14s ease, box-shadow 0.14s ease, transform 0.14s ease;
}
.sheet .btn.job-btn:hover {
  border-color: rgba(120, 88, 40, 0.8);
  transform: translateY(-1px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.75), 0 3px 7px rgba(90, 66, 34, 0.26);
}
.sheet .btn.job-btn:active {
  transform: translateY(1px);
  box-shadow: inset 0 1px 3px rgba(90, 66, 34, 0.3);
}
.sheet .btn.job-btn:disabled {
  opacity: 0.5;
  transform: none;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6);
}
.sheet .btn.job-btn.job-btn-lead {
  border-color: rgba(184, 147, 63, 0.85);
  background:
    repeating-linear-gradient(112deg, rgba(255, 255, 255, 0.42) 0 2px, transparent 2px 9px),
    linear-gradient(180deg, #f3e7cd, #e4d2ae);
  font-weight: 600;
}

/* ═══════════════ 巫师商会 + 开店（2026-10-19） ═══════════════
 * 两身衣服：
 *   · 商会大厅 = 古灵阁那身（复用 .gb-card 的大理石 + 金线 + 印章），只补一条引言与租位行；
 *   · 店铺那一屏 = 示意图那身深棕 + 金：深棕底、金细边、奶白字，格子一律无边框。
 * 纯 CSS，不引新图（背景是 assets 里那张摊位图，见 stageBg）。
 */

/* ── 一、伦敦地图上那颗【巫师商会】：金币印章 + 深绿金边，和别的钉不一样 ── */
.wmp-spot.is-guild .wms-pin {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 1px solid rgba(96, 68, 20, 0.9);
  background:
    radial-gradient(circle at 34% 28%, #fff6d8, #d9a83f 58%, #7d5216);
  box-shadow: 0 0 0 2px rgba(28, 52, 34, 0.9), 0 0 10px rgba(226, 184, 92, 0.55);
  animation: wmsPulseGold 3s ease-in-out infinite;
}
@keyframes wmsPulseGold {
  0%, 100% { transform: scale(1); box-shadow: 0 0 0 2px rgba(28, 52, 34, 0.9), 0 0 8px rgba(226, 184, 92, 0.45); }
  50%      { transform: scale(1.12); box-shadow: 0 0 0 2px rgba(28, 52, 34, 0.9), 0 0 14px rgba(226, 184, 92, 0.85); }
}
.wmp-spot.is-guild .wms-tx {
  background: linear-gradient(180deg, #26402c, #14251a);
  border-color: rgba(214, 176, 96, 0.8);
  box-shadow: 0 3px 9px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(246, 222, 172, 0.14);
}
.wmp-spot.is-guild .wms-zh { color: #f4e3b6; letter-spacing: 0.1em; }
.wmp-spot.is-guild .wms-en { color: rgba(228, 198, 130, 0.8); }
.wmp-spot.is-guild:hover .wms-tx { border-color: #f0cf86; }
/* 站在商会里：字牌照例镀金（0,4,0 才压得住上面那几条） */
.wmp-spot.is-guild.here .wms-tx {
  background: linear-gradient(180deg, #f7dc9a, #dcae55);
  border-color: rgba(122, 84, 22, 0.75);
}
.wmp-spot.is-guild.here .wms-zh { color: #4a3210; }
.wmp-spot.is-guild.here .wms-en { color: rgba(74, 50, 16, 0.72); }
@media (prefers-reduced-motion: reduce) {
  .wmp-spot.is-guild .wms-pin { animation: none; }
}

/* ── 二、商会大厅（古灵阁那身）：大理石卡里多一句引言 + 一颗租赁 ── */
.guild-card { width: min(94vw, 30rem); }
.guild-line {
  margin: 10px 2px 14px;
  font-size: var(--fs-s);
  line-height: 1.7;
  color: #3a3128;
}
.guild-card .gb-acts { margin-top: 12px; }
.guild-card .gb-acts .btn { width: 100%; }

/* 租位那四条（租赁纸上）：一条一行，左文右键 */
.guild-spots { display: flex; flex-direction: column; gap: 8px; }
.guild-spot {
  padding: 9px 10px;
  border: 1px solid rgba(184, 147, 63, 0.36);
  border-radius: 6px;
  background: rgba(255, 252, 244, 0.55);
}
.gs-head { display: flex; align-items: baseline; gap: 8px; }
.gs-name { font-size: var(--fs-s); color: #2b241a; letter-spacing: 0.06em; }
.gs-en { font-family: var(--font-hand); font-size: 9px; color: rgba(90, 74, 48, 0.7); }
.gs-line { margin: 4px 0 6px; font-size: var(--fs-xs); line-height: 1.6; color: #4a3f30; }
.gs-meta { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 6px; font-size: 10px; color: #6b583a; }

/* ── 三、店铺那一屏（照示意图那身：深棕 + 金线，中间开一窗透出底图） ──
 *
 * 整屏一张竖卡。卡自己**不铺底**：抬头 / 交货台 / 背包 / 底栏各是一块深色面板，
 * 中间那一格（.shop-scene）留空——于是立绘背后透的就是脚下那张摊位底图（创作者 2026-10-19 定的）。
 * 这一屏没有状态栏、也没有底下那排门牌（main.js 的 noHud + 本屏的 setTray），
 * 所以卡按 100dvh 撑满，背包那一格 flex 长出来吃剩下的高度。
 *
 * 层序（从下往上）：底图 → 立绘 → 预算卡 / 气泡 → 交货台与背包那两块面板。
 */
.shop-card {
  width: 100%;
  max-width: 44rem;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  min-height: calc(100dvh - var(--sp-4) - var(--sp-5) - env(safe-area-inset-top) - env(safe-area-inset-bottom));
  overflow: hidden;
  border: 1px solid rgba(201, 160, 99, 0.52);
  border-radius: 0;
  background: none;
  box-shadow: 0 18px 46px rgba(0, 0, 0, 0.66);
  color: #f0e2c2;
}

/* 抬头：英文小字在上、店名大字、副标题；右边那颗金币框 */
.shop-head {
  flex: none;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 14px 8px;
  background: linear-gradient(180deg, rgba(50, 34, 19, 0.96), rgba(28, 20, 12, 0.94));
  border-bottom: 1px solid rgba(201, 160, 99, 0.34);
}
.sh-title { display: flex; flex-direction: column; min-width: 0; }
.sh-en {
  font-family: var(--font-hand);
  font-size: 9px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: rgba(226, 196, 140, 0.6);
}
.sh-zh {
  font-size: 22px;
  line-height: 1.4;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: #f6e6c2;
  text-shadow: 0 2px 7px rgba(0, 0, 0, 0.55);
}
.sh-sub { font-size: 10px; letter-spacing: 0.14em; color: rgba(226, 196, 140, 0.66); }
.sh-money {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  padding: 6px 9px;
  border: 1px solid rgba(201, 160, 99, 0.42);
  border-radius: 0;
  background: rgba(14, 9, 5, 0.5);
}
.sh-gold { display: flex; align-items: baseline; gap: 4px; font-size: var(--fs-s); letter-spacing: 0.04em; color: #f0d79a; }
.sh-dia { font-style: normal; font-size: 9px; color: #d8ac55; }
.sh-earn { font-size: 9px; letter-spacing: 0.1em; color: rgba(226, 196, 140, 0.6); }
.sh-today { font-size: 9px; color: rgba(226, 196, 140, 0.46); }

/* 中间那一窗：透明，人物站在底图上。
   它是整屏唯一会长的一格——上下几块面板加起来多高都不该让卡超出视口（要占满、不滚动）。 */
.shop-scene {
  position: relative;
  flex: 1 1 auto;
  min-height: clamp(200px, 34vh, 480px);
  overflow: hidden;
  background: none;
  border-bottom: 1px solid rgba(201, 160, 99, 0.34);
}
.shop-stage {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0 6px;
}
/*
 * 立绘：**按窗口的高度定大小**（创作者 2026-10-19 图3）。
 * 原来写的是 max-height，于是原图本来就小的立绘在这格里只有一小坨、原图大的又顶满，
 * 一位一个样。改成 height:100% 之后每一位都站满这一格，只有过宽的按宽度收（max-width）。
 */
.shop-npc {
  display: block;
  height: 100%;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  transform-origin: 50% 100%;
  filter: drop-shadow(0 10px 18px rgba(0, 0, 0, 0.6));
}
.shop-stage.in .shop-npc { animation: shopWalkIn 0.62s ease-out backwards; }
.shop-stage.out .shop-npc { animation: shopWalkOut 0.3s ease-in forwards; }
@keyframes shopWalkIn {
  0%   { opacity: 0; transform: translate(-6px, 10px) scale(0.985); }
  25%  { opacity: 0.55; transform: translate(3px, 6px) scale(1); }
  45%  { transform: translate(-2px, 3px); }
  65%  { transform: translate(1px, 1px); }
  100% { opacity: 1; transform: none; }
}
@keyframes shopWalkOut {
  0%   { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translate(8px, 6px); }
}
.shop-npc-none {
  margin: auto;
  padding: 0 16px;
  text-align: center;
  font-size: var(--fs-xs);
  color: rgba(226, 196, 140, 0.62);
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.8);
}

/* 预算卡：压在窗口左上角（示意图就摆在这儿） */
.shop-budget {
  position: absolute;
  z-index: 3;
  left: 12px;
  top: 12px;
  width: min(58%, 12.5rem);
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 9px 11px 10px;
  border: 1px solid rgba(214, 176, 96, 0.62);
  border-radius: 0;
  background: rgba(13, 9, 5, 0.84);
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.5);
}
.sbg-label { display: flex; align-items: center; gap: 5px; font-size: 10px; letter-spacing: 0.1em; color: rgba(226, 196, 140, 0.78); }
.sbg-dia { font-style: normal; font-size: 9px; color: #d8ac55; }
/* 预算那一格写全三档（x加隆x西可x纳特，创作者 2026-10-19）：字比原来小一号才塞得下 */
.sbg-num { display: flex; align-items: baseline; gap: 4px; flex-wrap: wrap; }
.sbg-val { font-size: 19px; line-height: 1.35; letter-spacing: 0.02em; color: #f6e0a6; text-shadow: 0 2px 8px rgba(0, 0, 0, 0.6); }
.shop-budget-note { margin: 3px 0 0; font-size: 9.5px; line-height: 1.55; color: rgba(226, 196, 140, 0.6); }

/* 窗口右上角那一格：这位只点一个类别（与左上的预算卡对称；深棕小方框 + 金细边 + 直角） */
.shop-need {
  position: absolute;
  z-index: 3;
  right: 12px;
  top: 12px;
  max-width: 46%;
  padding: 5px 10px 6px;
  border: 1px solid rgba(201, 160, 99, 0.42);
  border-radius: 0;
  background: rgba(13, 9, 5, 0.84);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.45);
  font-size: 10px;
  line-height: 1.5;
  letter-spacing: 0.06em;
  white-space: nowrap;
}
.sneed-label { color: rgba(226, 196, 140, 0.72); }
.sneed-cat { color: #f7dd9f; font-weight: 700; letter-spacing: 0.1em; }
/* 特殊客人猜需求：那一格只写「？？？」，别把类别漏出来 */
.shop-need.is-hidden .sneed-cat { color: #e8cb91; letter-spacing: 0.28em; }
/* 特殊客人那颗金框小牌：压在「客人需求」正下方（2026-10-19 他圈的位置） */
.shop-special-tag {
  position: absolute;
  z-index: 3;
  right: 12px;
  top: 42px;
  padding: 3px 9px 4px;
  border: 1px solid rgba(222, 178, 96, 0.85);
  border-radius: 0;
  background: rgba(26, 18, 10, 0.9);
  box-shadow: 0 5px 14px rgba(0, 0, 0, 0.45), inset 0 0 0 1px rgba(222, 178, 96, 0.18);
  font-size: 10px;
  line-height: 1.4;
  letter-spacing: 0.18em;
  color: #f7dd9f;
  pointer-events: none;
}

/* 气泡：照图4 那一张——浅羊皮纸、直角、左边一个小尖角指着说话的人 */
.shop-bubble {
  position: absolute;
  z-index: 4;
  right: 10px;
  bottom: 10px;
  max-width: min(56%, 18rem);
  padding: 7px 10px 8px;
  border: 1px solid rgba(112, 84, 40, 0.62);
  border-radius: 0;
  background: linear-gradient(180deg, rgba(216, 198, 154, 0.96), rgba(184, 164, 118, 0.96));
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.55);
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.32s ease, transform 0.32s ease;
  pointer-events: none;
}
/* 那根尖角：贴在左边框外，跟人物同侧（示意图里气泡在右下，人站在中间偏左） */
.shop-bubble::before {
  content: '';
  position: absolute;
  left: -8px;
  top: 34%;
  width: 0;
  height: 0;
  border-top: 6px solid transparent;
  border-bottom: 6px solid transparent;
  border-right: 8px solid rgba(112, 84, 40, 0.62);
}
.shop-bubble::after {
  content: '';
  position: absolute;
  left: -7px;
  top: 34%;
  width: 0;
  height: 0;
  border-top: 6px solid transparent;
  border-bottom: 6px solid transparent;
  border-right: 7px solid rgba(208, 189, 144, 0.96);
}
.shop-bubble.show { opacity: 1; transform: none; }
.sb-who { display: block; margin-bottom: 2px; font-size: 9px; letter-spacing: 0.14em; color: rgba(58, 45, 23, 0.72); }
.sb-text { display: block; font-size: 11px; line-height: 1.6; color: #33280f; }
.sb-ask { display: block; margin-top: 3px; font-size: 11px; line-height: 1.6; color: #46371a; }
.sb-item { color: #1c5f9e; text-decoration: underline; text-underline-offset: 2px; }

/* 特殊客人那四种气泡（2026-10-19）：颜色照着手札里对应那一栏来，别用默认的米黄。
 *   常驻 = 浅蓝 + 斜纹；学院 = 荣誉册那一页羊皮；黑街 = 地下声望那一栏深绿；声望 = 巫师界那一栏深蓝。
 * 左边那个小尖角两半（::before 描边 / ::after 底色）也得跟着换——
 * 深色气泡旁边挂一块米黄，比没换还显眼。 */
.shop-bubble.special .sb-who { opacity: 0.7; }
.shop-bubble.sb-reg {
  border-color: #6fa8cf;
  background:
    repeating-linear-gradient(135deg, rgba(255, 255, 255, 0.45) 0 7px, rgba(255, 255, 255, 0) 7px 15px),
    linear-gradient(180deg, #d9efff, #b6d9f2);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.55), inset 0 0 0 1px rgba(255, 255, 255, 0.55);
  color: #123a55;
}
.shop-bubble.sb-reg::before { border-right-color: #6fa8cf; }
.shop-bubble.sb-reg::after { border-right-color: #d5ecfc; }
.shop-bubble.sb-reg .sb-who,
.shop-bubble.sb-reg .sb-text { color: #123a55; }

.shop-bubble.sb-house {
  border-color: rgba(176, 138, 74, 0.75);
  background:
    repeating-linear-gradient(0deg, rgba(176, 138, 74, 0.06) 0 1px, transparent 1px 5px),
    linear-gradient(155deg, #f8eed6 0%, #f1e2c1 44%, #e5cfa3 100%);
  color: #55391a;
}
.shop-bubble.sb-house::before { border-right-color: rgba(176, 138, 74, 0.75); }
.shop-bubble.sb-house::after { border-right-color: #f6ecd4; }
.shop-bubble.sb-house .sb-who,
.shop-bubble.sb-house .sb-text { color: #55391a; }

.shop-bubble.sb-under {
  border-color: rgba(126, 190, 126, 0.55);
  background:
    repeating-linear-gradient(115deg, rgba(126, 190, 126, 0.12) 0 6px, transparent 6px 15px),
    linear-gradient(180deg, #3f6c46, #1f3a26 62%, #132718);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.6), inset 0 0 0 1px rgba(150, 220, 150, 0.18);
  color: #d9efd4;
}
.shop-bubble.sb-under::before { border-right-color: rgba(126, 190, 126, 0.55); }
.shop-bubble.sb-under::after { border-right-color: #33603d; }
.shop-bubble.sb-under .sb-who,
.shop-bubble.sb-under .sb-text { color: #d9efd4; }

.shop-bubble.sb-wiz {
  border-color: rgba(206, 170, 96, 0.62);
  background:
    radial-gradient(120% 150% at 92% -12%, rgba(96, 140, 190, 0.35), transparent 58%),
    linear-gradient(165deg, #1d3047, #16263b 55%, #101b2c);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.6), inset 0 0 0 1px rgba(120, 164, 214, 0.28);
  color: #dce7f4;
}
.shop-bubble.sb-wiz::before { border-right-color: rgba(206, 170, 96, 0.62); }
.shop-bubble.sb-wiz::after { border-right-color: #1a2b41; }
.shop-bubble.sb-wiz .sb-who,
.shop-bubble.sb-wiz .sb-text { color: #dce7f4; }

/* 交货台那一整块 */
.shop-counter {
  flex: none;
  padding: 10px 12px 12px;
  background: rgba(26, 18, 10, 0.95);
  border-bottom: 1px solid rgba(201, 160, 99, 0.34);
}
.shop-counter-head {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 3px 10px;
  margin-bottom: 8px;
}
.sch-zh { display: flex; align-items: center; gap: 6px; font-size: var(--fs-s); line-height: 1.4; font-weight: 700; letter-spacing: 0.12em; color: #f0dcb0; }
.sch-mark { font-style: normal; font-size: 11px; color: #d8ac55; }
/* 今日词条就摆在抬头这一行（创作者 2026-10-19 圈的那个位置） */
.sch-buff { flex: 1 1 11rem; min-width: 0; font-size: 10px; line-height: 1.5; color: #e6c07a; }
.sch-right { margin-left: auto; display: flex; flex-direction: column; align-items: flex-end; }
.sch-en { font-family: var(--font-hand); font-size: 9px; letter-spacing: 0.2em; color: rgba(226, 196, 140, 0.55); }
.sch-hint { font-size: 9px; color: rgba(226, 196, 140, 0.42); }

/* 虚线那一格：空台子时里面是「＋ / 拖到这里 / 点一下也行」三行 */
.shop-counter-drop {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  justify-content: center;
  gap: 10px;
  min-height: 88px;
  padding: 10px;
  border: 1px dashed rgba(201, 160, 99, 0.5);
  border-radius: 0;
  background: rgba(10, 7, 4, 0.42);
}
.shop-counter-drop.has { justify-content: flex-start; }
.sc-plus { display: block; width: 100%; text-align: center; font-size: 20px; line-height: 1.3; color: rgba(226, 196, 140, 0.45); }
.sc-hint { display: block; width: 100%; text-align: center; font-size: 10px; line-height: 1.7; color: rgba(226, 196, 140, 0.45); }
.sc-faint { font-size: 9px; color: rgba(226, 196, 140, 0.32); }

/* 交货台右边切出一小块：客人自己带的货（2026-10-20）。
 * 眼下所有客人都空着手，这一格只是把位置留出来；以后以物易物的客人往这儿摆货。 */
.shop-counter-row { display: flex; align-items: stretch; gap: 10px; }
.shop-counter-row .shop-counter-drop { flex: 1 1 auto; min-width: 0; }
.shop-cust-goods {
  flex: 0 0 108px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px 8px 9px;
  border: 1px solid rgba(201, 160, 99, 0.34);
  background: rgba(10, 7, 4, 0.35);
}
.scg-title { font-size: 10px; line-height: 1.45; letter-spacing: 0.08em; color: #e6c07a; }
.scg-empty { flex: 1 1 auto; display: grid; place-items: center; font-size: var(--fs-xs); color: rgba(226, 196, 140, 0.45); }
/* 以物易物的客人带来的货：小蛋 + 名字 + ×1，底下写交换条件 */
.scg-goods { display: flex; align-items: center; gap: 4px; padding: 3px 4px; border: 1px solid rgba(201, 160, 99, 0.28); background: rgba(10, 7, 4, 0.4); }
.scg-art { font-size: 20px; line-height: 1; }
.scg-name { min-width: 0; font-size: 10px; line-height: 1.3; color: #f0dcb0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.scg-n { margin-left: auto; font-size: 9px; color: #e2bd77; }
.scg-want { margin-top: auto; font-size: 9px; line-height: 1.45; color: rgba(240, 215, 154, 0.85); }

/* 结算那一框：左边大字价值，右边客人预算 / 剩余预算两行 */
.shop-tally {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 8px;
  padding: 5px 11px;
  border: 1px solid rgba(201, 160, 99, 0.34);
  border-radius: 0;
  background: rgba(10, 7, 4, 0.5);
}
.st-left { display: flex; flex-direction: column; gap: 0; min-width: 0; }
.st-label { font-size: 10px; line-height: 1.4; letter-spacing: 0.1em; color: rgba(226, 196, 140, 0.62); }
.st-val { font-size: 22px; line-height: 1.3; color: #f6e0a6; letter-spacing: 0.02em; }
/* 台面价右边那一格：今天的词条（与客人点名）把这一台货抬起来 / 压下去多少。
   它是结算框里的第三块（左边台面价、中间这一格、右边客人预算），自己占一行摆在中线上。 */
.st-impact {
  flex: 0 1 auto;
  min-width: 0;
  margin-left: 4px;
  padding: 2px 8px 3px;
  border: 1px solid rgba(201, 160, 99, 0.34);
  border-radius: 0;
  font-size: 10px;
  line-height: 1.5;
  letter-spacing: 0.06em;
}
.st-impact.is-none { display: none; }
/* 被抬起来的镀金，被压下去的压深紫（跟营地那身黑紫一个族） */
.st-impact.is-up {
  color: #f7dd9f;
  border-color: rgba(226, 184, 92, 0.72);
  background: linear-gradient(180deg, rgba(84, 58, 22, 0.9), rgba(46, 31, 12, 0.94));
  box-shadow: inset 0 0 0 1px rgba(226, 184, 92, 0.16);
}
.st-impact.is-down {
  color: #ddc9ff;
  border-color: rgba(150, 112, 214, 0.72);
  background: linear-gradient(180deg, rgba(58, 36, 86, 0.92), rgba(28, 16, 42, 0.95));
  box-shadow: inset 0 0 0 1px rgba(160, 122, 224, 0.18);
}
.st-right { display: flex; flex-direction: column; align-items: flex-end; gap: 1px; flex: none; }
.st-l { font-size: 10px; line-height: 1.4; color: rgba(226, 196, 140, 0.72); white-space: nowrap; }

/* 两颗动作键：示意图里都是深底金边，确认交货那一颗描得更亮 */
.shop-acts { display: flex; gap: 10px; margin-top: 10px; }
.shop-acts .btn {
  flex: 1 1 0;
  min-width: 0;
  width: auto;
  text-align: center;
  white-space: nowrap;
  padding: 7px 10px;
  font-size: var(--fs-xs);
  line-height: 1.45;
  letter-spacing: 0.1em;
  color: #ecd9ae;
  background: linear-gradient(180deg, rgba(48, 33, 19, 0.96), rgba(28, 20, 12, 0.98));
  border: 1px solid rgba(201, 160, 99, 0.38);
  border-left-width: 1px;
  border-radius: 0;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.45);
}
.shop-acts .btn:hover { border-color: rgba(240, 207, 134, 0.8); transform: translateY(-1px); box-shadow: 0 5px 14px rgba(0, 0, 0, 0.5); }
.shop-acts .btn:active { transform: translateY(0) scale(0.99); box-shadow: 0 1px 5px rgba(0, 0, 0, 0.4); }
.shop-acts .btn:disabled { opacity: 0.4; transform: none; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35); }
.shop-acts .btn.primary {
  color: #f9ecd0;
  border-color: rgba(222, 178, 96, 0.9);
  background: linear-gradient(180deg, rgba(84, 58, 25, 0.98), rgba(48, 32, 15, 0.98));
  box-shadow: inset 0 0 0 1px rgba(222, 178, 96, 0.22), 0 4px 14px rgba(0, 0, 0, 0.5);
}
.shop-acts .btn.primary:hover { border-color: #f6dd9f; }

/* 随身背包那一整块（抬头 + 横着拖的那条带子在一个框里） */
.shop-bagbox {
  flex: none;
  display: flex;
  flex-direction: column;
  padding: 10px 12px 12px;
  background: rgba(22, 15, 9, 0.95);
  border-bottom: 1px solid rgba(201, 160, 99, 0.34);
}
.shop-bag-head { display: flex; align-items: flex-start; flex-wrap: wrap; gap: 3px 10px; margin-bottom: 7px; }
.sbh-zh { display: flex; align-items: center; gap: 6px; font-size: var(--fs-s); line-height: 1.4; font-weight: 700; letter-spacing: 0.12em; color: #f0dcb0; }
.sbh-mark { font-style: normal; font-size: 11px; color: #d8ac55; }
.sbh-right { margin-left: auto; display: flex; flex-direction: column; align-items: flex-end; }
.sbh-en { font-family: var(--font-hand); font-size: 9px; letter-spacing: 0.2em; color: rgba(226, 196, 140, 0.55); }
.sbh-hint { font-size: 9px; color: rgba(226, 196, 140, 0.42); }

/* 背包：一条横向拖动的带子，格子无边框（只有图 + 名 + 价） */
.shop-bag {
  flex: none;
  display: flex;
  align-content: flex-start;
  flex-wrap: nowrap;
  gap: 10px;
  height: 112px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  padding: 8px 6px;
  border: 1px solid rgba(201, 160, 99, 0.22);
  border-radius: 0;
  background: rgba(10, 7, 4, 0.45);
  scrollbar-width: thin;
  scrollbar-color: rgba(122, 88, 45, 0.95) rgba(10, 7, 4, 0.6);
}
/* 那条横着挪的滑块：深棕小条，跟这一屏一个料子（浏览器的默认白条太跳） */
.shop-bag::-webkit-scrollbar { height: 8px; }
.shop-bag::-webkit-scrollbar-track {
  background: rgba(10, 7, 4, 0.6);
  border-top: 1px solid rgba(201, 160, 99, 0.18);
}
.shop-bag::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, #7a582f, #4a3419);
  border: 1px solid rgba(201, 160, 99, 0.4);
}
.shop-bag::-webkit-scrollbar-thumb:hover { background: linear-gradient(180deg, #96692f, #5b3f1d); }
.shop-bag-empty { margin: auto; padding: 0 8px; font-size: var(--fs-xs); color: rgba(226, 196, 140, 0.5); }
.shop-item {
  position: relative;
  flex: 0 0 auto;
  width: 92px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 6px 4px 7px;
  border: 0;
  background: none;
  color: #f0e2c2;
  cursor: grab;
  touch-action: pan-x;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
.shop-item:hover { background: rgba(255, 236, 200, 0.07); border-radius: 0; }
.shop-item:active { cursor: grabbing; }
/* 格子里的四行（图 / 名 / 价，数量钉在右上角）都不许被压扁：
 * 背包那条带子是定高的，flex 一缩就把「名字」这一行挤成 2px（2026-10-19 他报的）。 */
.si-art { flex: 0 0 auto; height: 38px; display: grid; place-items: center; font-size: 26px; }
.si-img { max-height: 38px; width: auto; max-width: 64px; object-fit: contain; }
.si-name {
  flex: 0 0 auto;
  max-width: 100%;
  font-size: 11px;
  line-height: 1.3;
  text-align: center;
  color: #f6e6c2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.si-price { flex: 0 0 auto; font-size: 9px; line-height: 1.4; color: #e2bd77; white-space: nowrap; }
.si-n { position: absolute; top: 4px; right: 6px; font-size: 9px; color: #e2bd77; }
.shop-item.on-counter { opacity: 0.96; }

/* 拖着走的那张小牌子 */
.shop-ghost {
  position: fixed;
  z-index: 9999;
  transform: translate(-50%, -50%);
  padding: 4px 8px;
  border: 1px solid rgba(214, 176, 96, 0.8);
  border-radius: 0;
  background: rgba(30, 20, 12, 0.94);
  color: #f3e2bd;
  font-size: 10px;
  pointer-events: none;
}

/* 底下那排：左边摇铃那张卡，右边闭店 */
.shop-foot {
  flex: none;
  display: flex;
  align-items: stretch;
  gap: 10px;
  padding: 8px 12px;
  background: rgba(30, 21, 12, 0.96);
}
.shop-bell {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  padding: 7px 12px 8px;
  border: 1px solid rgba(214, 176, 96, 0.62);
  border-radius: 0;
  background: linear-gradient(180deg, rgba(62, 44, 24, 0.98), rgba(34, 24, 14, 0.98));
  color: #f3e2bd;
  cursor: pointer;
  font-family: var(--font-body);
  text-align: left;
  transition: border-color 0.14s ease, transform 0.14s cubic-bezier(0.16, 1, 0.3, 1);
}
.shop-bell:hover { border-color: #f0cf86; transform: translateY(-1px); }
.shop-bell:disabled { opacity: 0.45; cursor: default; transform: none; }
.sbell-zh { display: flex; align-items: center; gap: 6px; font-size: var(--fs-xs); letter-spacing: 0.12em; }
.sbell-mark { font-style: normal; font-size: 12px; color: #d8ac55; }
.sbell-left { font-size: 9px; color: rgba(226, 196, 140, 0.62); }
.shop-foot .btn.exit {
  flex: 0 0 auto;
  width: auto;
  padding: 0 16px;
  border-radius: 0;
  border-color: rgba(201, 160, 99, 0.45);
  background: linear-gradient(180deg, rgba(48, 33, 19, 0.96), rgba(28, 20, 12, 0.98));
}

/* 示意图最底下那行英文（纯装饰） */
.shop-motto {
  flex: none;
  margin: 0;
  padding: 5px 12px 7px;
  text-align: center;
  font-family: var(--font-hand);
  font-size: 8.5px;
  letter-spacing: 0.2em;
  color: rgba(226, 196, 140, 0.42);
  background: rgba(20, 14, 8, 0.96);
}

@media (max-width: 560px) {
  .shop-card { max-width: none; }
  .shop-scene { min-height: 200px; }
  /* 窄屏：预算卡收一点，给右上角那格【客人需求】让出位置（两处都是 12px 边距，别叠上） */
  .shop-budget { width: min(50%, 10rem); }
  .shop-need { right: 8px; top: 8px; padding: 4px 8px 5px; }
  .shop-special-tag { right: 8px; }
  .sbg-val { font-size: 17px; }
  .sh-zh { font-size: 19px; }
  .shop-bubble { max-width: 62%; right: 8px; bottom: 8px; }
  .st-l { font-size: 9px; }
  /* 窄屏：交货台右边那一小块也收一收，别把拖放区挤没 */
  .shop-cust-goods { flex-basis: 92px; padding: 7px 6px 8px; }
}
@media (prefers-reduced-motion: reduce) {
  .shop-stage.in .shop-npc, .shop-stage.out .shop-npc { animation: none; }
  .shop-bubble { transition: none; }
  .shop-bell:hover, .shop-acts .btn:hover { transform: none; }
}

/* ═══════════════ 店铺那两张纸（shop-sheet · 2026-10-19） ═══════════════
 * 「钱不够」的二次确认与「今日收摊」的结算纸都开在店铺那一屏上，
 * 所以穿的是那一屏的皮：深棕 + 金细线 + **直角**，里面每一颗键、那个 ×、
 * 那块读数框一律同一套（挂在皮肤作用域上，以后从店铺开的新纸自动继承）。
 */
.sheet.shop-sheet {
  max-width: 30rem;
  background-color: #1b1309;
  background-image: linear-gradient(180deg, rgba(46, 32, 18, 0.98), rgba(23, 16, 10, 0.99));
  background-size: auto;
  border: 1px solid rgba(201, 160, 99, 0.52);
  border-radius: 0;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.72), inset 0 1px 0 rgba(246, 222, 172, 0.08);
  color: #f0e2c2;
}
.sheet.shop-sheet .sheet-head {
  border-bottom: 1px solid rgba(201, 160, 99, 0.34);
  box-shadow: none;
}
.sheet.shop-sheet .sheet-head h2 { color: #f6e6c2; letter-spacing: 0.12em; }
.sheet.shop-sheet p { color: #f0e2c2; }
.sheet.shop-sheet .muted,
.sheet.shop-sheet .small,
.sheet.shop-sheet .tiny { color: rgba(226, 196, 140, 0.72); }
.sheet.shop-sheet .x-btn {
  position: relative;
  border: 1px solid rgba(201, 160, 99, 0.5);
  border-radius: 0;
  background: linear-gradient(180deg, rgba(62, 44, 24, 0.98), rgba(34, 24, 14, 0.98));
  box-shadow: inset 0 1px 0 rgba(255, 240, 200, 0.14), 0 2px 8px rgba(0, 0, 0, 0.45);
}
/* 那张「小羊皮方牌」的叉是浅色的，压在这张深棕纸上就是一块白斑：换成一个金 × */
.sheet.shop-sheet .x-btn .x-img { display: none; }
.sheet.shop-sheet .x-btn::before { content: '×'; font-size: 21px; line-height: 1; color: #e8cb91; }
.sheet.shop-sheet .x-btn:hover { border-color: #f0cf86; }

/* 纸上的读数框（结算那四条）：同一块深棕，别摆一张浅色纸 */
.sheet.shop-sheet .card {
  color: #f0e2c2;
  background: rgba(10, 7, 4, 0.5);
  background-image: none;
  border: 1px solid rgba(201, 160, 99, 0.34);
  border-radius: 0;
  box-shadow: none;
}
.sheet.shop-sheet .row + .row { border-top: 1px solid rgba(201, 160, 99, 0.2); }
.sheet.shop-sheet .val { color: #f0d79a; }

/* 纸上的每一颗键：深棕底 + 金细边 + 直角；【成交】那颗描得亮一档 */
.sheet.shop-sheet .acts { gap: 10px; }
.sheet.shop-sheet .acts .btn {
  width: auto;
  min-width: 92px;
  text-align: center;
  white-space: nowrap;
  padding: 8px 14px;
  font-size: var(--fs-xs);
  line-height: 1.45;
  letter-spacing: 0.1em;
  color: #ecd9ae;
  background: linear-gradient(180deg, rgba(62, 44, 24, 0.98), rgba(30, 21, 12, 0.99));
  background-image: linear-gradient(180deg, rgba(62, 44, 24, 0.98), rgba(30, 21, 12, 0.99));
  border: 1px solid rgba(201, 160, 99, 0.42);
  border-left-width: 1px;
  border-left-color: rgba(201, 160, 99, 0.42);
  border-radius: 0;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.45);
  text-shadow: none;
}
.sheet.shop-sheet .acts .btn:hover {
  color: #f9ecd0;
  border-color: rgba(240, 207, 134, 0.85);
  transform: translateY(-1px);
  box-shadow: 0 5px 14px rgba(0, 0, 0, 0.5);
}
.sheet.shop-sheet .acts .btn:active { transform: translateY(0) scale(0.99); box-shadow: 0 1px 5px rgba(0, 0, 0, 0.4); }
.sheet.shop-sheet .acts .btn:disabled { opacity: 0.42; transform: none; }
.sheet.shop-sheet .acts .btn.primary {
  color: #f9ecd0;
  border-color: rgba(222, 178, 96, 0.9);
  background-image: linear-gradient(180deg, rgba(84, 58, 25, 0.98), rgba(48, 32, 15, 0.98));
  box-shadow: inset 0 0 0 1px rgba(222, 178, 96, 0.22), 0 4px 14px rgba(0, 0, 0, 0.5);
}

/* 长按背包那一格 →「加入交货台」那张件数纸（2026-10-19）：
 * 同一身深棕 + 金线 + 直角。件数框与那几颗小键不在 .acts 里，
 * 所以皮也得在这张皮肤的作用域里给它们补上——别让深色纸上冒出一块白。 */
.sheet.shop-sheet.compact { max-width: 23rem; }
.sheet.shop-sheet .sq-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid rgba(201, 160, 99, 0.34);
  background: rgba(10, 7, 4, 0.5);
}
.sheet.shop-sheet .sq-art {
  flex: 0 0 auto;
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  font-size: 30px;
}
.sheet.shop-sheet .sq-art img { max-width: 100%; max-height: 100%; object-fit: contain; }
.sheet.shop-sheet .sq-meta { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.sheet.shop-sheet .sq-name { color: #f0e2c2; font-size: var(--fs-m); letter-spacing: 0.06em; }
.sheet.shop-sheet .sq-sub { color: rgba(226, 196, 140, 0.72); font-size: var(--fs-xs); }
.sheet.shop-sheet .sq-qty { display: flex; align-items: center; gap: 8px; margin-top: 12px; }
.sheet.shop-sheet .sq-step,
.sheet.shop-sheet .sq-all {
  height: 40px;
  min-width: 40px;
  padding: 0 10px;
  border: 1px solid rgba(201, 160, 99, 0.42);
  border-radius: 0;
  background: linear-gradient(180deg, rgba(62, 44, 24, 0.98), rgba(30, 21, 12, 0.99));
  color: #ecd9ae;
  font: inherit;
  font-size: var(--fs-s);
  letter-spacing: 0.08em;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.45);
}
.sheet.shop-sheet .sq-step:hover,
.sheet.shop-sheet .sq-all:hover { color: #f9ecd0; border-color: rgba(240, 207, 134, 0.85); }
.sheet.shop-sheet .sq-step:active { transform: translateY(1px); }
.sheet.shop-sheet .sq-num {
  width: 5.5rem;
  height: 40px;
  border: 1px solid rgba(201, 160, 99, 0.42);
  border-radius: 0;
  background: rgba(10, 7, 4, 0.72);
  color: #f6e6c2;
  font: inherit;
  font-size: var(--fs-m);
  letter-spacing: 0.08em;
  text-align: center;
  -moz-appearance: textfield;
  appearance: textfield;
}
.sheet.shop-sheet .sq-num::-webkit-outer-spin-button,
.sheet.shop-sheet .sq-num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.sheet.shop-sheet .sq-num:focus {
  outline: none;
  border-color: rgba(240, 207, 134, 0.85);
  box-shadow: 0 0 0 1px rgba(222, 178, 96, 0.35);
}
.sheet.shop-sheet .sq-all { margin-left: auto; }

/* ═══════════════ 产业地图（2026-10-19 产业系统） ═══════════════
 * 整屏地面就是那张产业图（main.js 的 stageBg 里 `industry` 那一格），上面只摆一颗大卡【店铺】。
 * 摆法沿用家里那套绝对定位（.home-card-map 一个路子），卡与家里那几张同一种长相。
 */
.industry-map {
  position: relative;
  height: clamp(320px, 58vh, 640px);
  margin-top: var(--sp-2);
}
.industry-map .industry-card {
  position: absolute;
  height: auto;
  min-height: 0;
  padding: 10px 8px;
  border-radius: 12px;
}
.industry-map .industry-card .kx-zh { font-size: var(--fs-l); }
.industry-map .industry-card .kx-en { font-size: 10px; }
.industry-map .industry-card .kx-tag { font-size: 10px; }
@media (max-width: 560px) {
  .industry-map { height: clamp(300px, 52vh, 520px); }
  .industry-map .industry-card { padding: 7px 5px; border-radius: 10px; }
  .industry-map .industry-card .kx-zh { font-size: var(--fs-m); }
}

/* ═══════════════ 寄售橱窗（2026-10-20） ═══════════════
 * 屏是药房那面木柜（.apo + .mkt-cabinet + .mkt-cell.glass），
 * 纸上那几张弹页穿店铺那身深棕金线（shop-sheet）——同一副木柜色，别在深木屏上顶一张米黄纸。
 */

/* 右上角那两颗：账单 / 回商会。深木底 + 金细线，和柜子同一身 */
.consign-top { display: flex; align-items: center; gap: 8px; }
.consign-top .btn {
  min-width: 0;
  padding: 5px 12px 6px;
  font-size: var(--fs-xs);
  letter-spacing: 0.12em;
  color: #f0dcb0;
  background: linear-gradient(180deg, rgba(79, 54, 32, 0.98), rgba(46, 31, 19, 0.99));
  background-image: linear-gradient(180deg, rgba(79, 54, 32, 0.98), rgba(46, 31, 19, 0.99));
  border: 1px solid rgba(201, 160, 99, 0.5);
  border-left-width: 1px;
  border-left-color: rgba(201, 160, 99, 0.5);
  border-radius: 0;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.5);
  text-shadow: none;
}
.consign-top .btn:hover { color: #f9ecd0; border-color: rgba(240, 207, 134, 0.85); }
.consign-top .btn:active { transform: translateY(1px); box-shadow: 0 1px 5px rgba(0, 0, 0, 0.4); }
.consign-top .btn.primary {
  color: #f9ecd0;
  border-color: rgba(222, 178, 96, 0.9);
  background-image: linear-gradient(180deg, rgba(96, 66, 30, 0.98), rgba(55, 37, 18, 0.98));
}

/* 六格：两列三行，和药房那面柜子同一个分栏 */
.consign-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.consign-cabinet::before { opacity: 0.9; }
.consign-cell { min-height: 128px; justify-content: flex-start; }
.consign-cell.is-empty {
  justify-content: center;
  gap: 3px;
  background:
    repeating-linear-gradient(135deg, rgba(255, 255, 255, 0.03) 0 6px, transparent 6px 13px),
    linear-gradient(180deg, rgba(44, 34, 25, 0.5), rgba(24, 18, 13, 0.66));
  border-style: dashed;
}
.consign-empty-mark { font-size: 26px; line-height: 1; color: rgba(240, 220, 176, 0.5); }
.consign-empty-zh { font-size: var(--fs-s); letter-spacing: 0.12em; color: #f0dcb0; }
.consign-empty-note { font-size: var(--fs-xs); color: rgba(226, 196, 140, 0.5); }
.consign-qty {
  position: absolute;
  top: 6px;
  right: 8px;
  font-size: 10px;
  letter-spacing: 0.06em;
  color: #f7dd9f;
}
.consign-cell .mkt-name { font-size: var(--fs-s); }

/* 选货那几行：图 / 名 / 有几件 / 标价 */
.consign-pick { padding: 4px; }
.consign-pick-row {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 8px;
  border: 0;
  border-bottom: 1px solid rgba(201, 160, 99, 0.18);
  background: none;
  color: #f0e2c2;
  font: inherit;
  font-size: var(--fs-s);
  text-align: left;
  cursor: pointer;
}
.consign-pick-row:last-child { border-bottom: 0; }
.consign-pick-row:hover { background: rgba(201, 160, 99, 0.1); }
.cp-art { width: 34px; height: 34px; display: grid; place-items: center; overflow: hidden; }
.cp-art img { max-width: 100%; max-height: 100%; object-fit: contain; }
.cp-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cp-qty { color: rgba(226, 196, 140, 0.75); font-size: var(--fs-xs); }

/* 账单里那一摞出售记录 */
.consign-history { max-height: 34vh; overflow-y: auto; padding: 2px 10px 6px; }
.consign-day { padding: 7px 0 8px; border-bottom: 1px solid rgba(201, 160, 99, 0.18); }
.consign-day:last-child { border-bottom: 0; }
.cd-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.cd-day { font-size: var(--fs-s); letter-spacing: 0.1em; color: #f0dcb0; }
.cd-total { font-size: var(--fs-xs); color: #f0d79a; }
.cd-line { margin-top: 3px; font-size: var(--fs-xs); line-height: 1.5; color: rgba(240, 226, 194, 0.78); }

/* 纸上那两排小步进器（数量 / 价格增幅）——深棕金线，和 shop-sheet 里那一套一致 */
.sheet.shop-sheet .cs-item { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border: 1px solid rgba(201, 160, 99, 0.34); background: rgba(10, 7, 4, 0.5); }
.sheet.shop-sheet .cs-art { flex: 0 0 auto; width: 46px; height: 46px; display: grid; place-items: center; overflow: hidden; }
.sheet.shop-sheet .cs-art img { max-width: 100%; max-height: 100%; object-fit: contain; }
.sheet.shop-sheet .cs-meta { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.sheet.shop-sheet .cs-name { color: #f0e2c2; font-size: var(--fs-m); letter-spacing: 0.06em; }
.sheet.shop-sheet .cs-sub { color: rgba(226, 196, 140, 0.72); font-size: var(--fs-xs); }
.sheet.shop-sheet .cs-qty { display: inline-flex; align-items: center; gap: 6px; }
.sheet.shop-sheet .cs-step {
  width: 34px;
  height: 32px;
  min-width: 34px;
  padding: 0;
  border: 1px solid rgba(201, 160, 99, 0.42);
  border-radius: 0;
  background: linear-gradient(180deg, rgba(62, 44, 24, 0.98), rgba(30, 21, 12, 0.99));
  color: #ecd9ae;
  font: inherit;
  font-size: var(--fs-s);
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.45);
}
.sheet.shop-sheet .cs-step:hover { border-color: rgba(240, 207, 134, 0.85); color: #f9ecd0; }
.sheet.shop-sheet .cs-step:active { transform: translateY(1px); }
.sheet.shop-sheet .cs-num {
  width: 4.6rem;
  height: 32px;
  border: 1px solid rgba(201, 160, 99, 0.42);
  border-radius: 0;
  background: rgba(10, 7, 4, 0.72);
  color: #f6e6c2;
  font: inherit;
  font-size: var(--fs-s);
  text-align: center;
  -moz-appearance: textfield;
  appearance: textfield;
}
/* 价格增幅是个百分数：% 垫在数字右边（输入框里放不下字面符号，用一层绝对定位的尾巴） */
.sheet.shop-sheet .cs-num-wrap { position: relative; display: inline-flex; }
.sheet.shop-sheet .cs-num-wrap .cs-num { padding-right: 1.3rem; text-align: right; }
.sheet.shop-sheet .cs-pct {
  position: absolute;
  right: 0.5rem;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
  color: rgba(226, 196, 140, 0.72);
  font-size: var(--fs-xs);
  line-height: 1;
}
.sheet.shop-sheet .cs-num::-webkit-outer-spin-button,
.sheet.shop-sheet .cs-num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.sheet.shop-sheet .cs-num:focus { outline: none; border-color: rgba(240, 207, 134, 0.85); box-shadow: 0 0 0 1px rgba(222, 178, 96, 0.35); }
.sheet.shop-sheet .cs-money { width: 7rem; }
.sheet.shop-sheet .cs-read { display: block; margin-top: 8px; font-size: var(--fs-xs); color: rgba(226, 196, 140, 0.72); }
