/* 《未发送》Ch5《尾声》
   美术规格：2024 尾声 —— 灰尘、阳光、铁盒的锈红。明亮的下午，浮尘。
   **这是全作最亮的一章**，也因此是文字压图最难的一章（见文件末尾的对比度记录）。 */

:root {
  --fs: 1;

  /* 阳光下的老房子：暖白墙灰 + 砖红 + 铁盒的锈 */
  --paper:  #efe7d7;   /* 牛皮纸／信纸 */
  --ink:    #2a2520;   /* 铅笔与圆珠笔 */
  --brick:  #8d4a35;   /* 红砖 */
  --rust:   #7a3b28;   /* 铁盒的锈红 */
  --dust:   #d8cdb8;   /* 浮尘里的高光 */

  /* 幽灵态：**纸上的擦痕**。
     这一章的底是牛皮纸（亮底），所以照 Ch3 那条教训来 ——
     亮底上降 alpha 是往背景色靠，不是往黑里沉，
     「淡」和「读得出」互斥，只能选读得出，状态另用形状承担。

     算的是**最坏那张纸**：三种纸底里牛皮纸 #e3d5b8 最深，
     所以 alpha 在它上面最难过（不是拿 --paper #efe7d7 算 ——
     那样会算出一个偏松的阈值，到电报纸上就掉下去）。
     墨 #2a2520 以 alpha 合成到 #e3d5b8，再跟 #e3d5b8 比：
       0.30 → 1.78 ✗   0.55 → 3.18 ✗   0.68 → 4.47 ✗
       0.69 → 4.56 ✓   0.70 → 4.68 ✓   0.74 → 5.23 ✓
     阈值在 0.69。取 0.74 留一点余量（合成后 #5a5348）；
     同一个 alpha 在信纸上是 5.75、纸片上 5.90，都更松。
     但这一章的幽灵字**不需要玩家读懂**（它是别人六十六年前擦掉的话，
     周念也只是看见「有的深有的浅」），所以这里的门槛按
     WCAG 1.4.3 的「装饰性/非必要文本」豁免本可以放宽 ——
     **没放宽**：剧本 §3 的心声在数「他擦掉了几个」，
     玩家会想数一遍，能数就得能看清。 */
  --ghost: 0.74;

  --wx-bg:   #ebebeb;   /* 微信的灰 */
  --wx-mine: #95ec69;   /* 自己那侧的绿 */
  --wx-them: #ffffff;
}

* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; background: #17140f; }

body {
  font-family: "Songti SC", "Source Han Serif SC", serif;
  color: var(--ink);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ---------- 舞台 ----------
   UI 规格 §1：Ch5 是 16:9 → 9:16 —— **全作唯一一章要换比例的**。

   两轴都用 min() 夹住（Ch3 那次的教训：只从高度推宽度，
   在任何比 16:9 更高的视口上都会溢出，而现代手机基本都更高）。
   换比例就是换这两条式子里的比值，所以两个类各写一遍，
   transition 挂在 width/height 上 —— min() 求值出来是长度，能补间。 */
#stage {
  position: relative;
  flex: 0 0 auto;
  overflow: hidden;
  background: #000;
  transition: width 1.2s cubic-bezier(.5, 0, .2, 1),
              height 1.2s cubic-bezier(.5, 0, .2, 1);
}
#stage.landscape {
  width:  min(100vw, calc(100vh * 16 / 9));
  height: min(100vh, calc(100vw * 9 / 16));
}
#stage.portrait {
  width:  min(100vw, calc(100vh * 9 / 16));
  height: min(100vh, calc(100vw * 16 / 9));
}

/* ---------- 画面 ---------- */
.screen {
  position: absolute; inset: 0;
  opacity: 0; pointer-events: none;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;

  /* **visibility 是跟着的，不是多余的。**
     只写 opacity:0 的话，没在演的屏还在无障碍树里 ——
     实测（结局那一屏做的可访问性快照）：读屏会一路念到
     标题屏的「第五章 尾声 开始」、整份聊天记录、还有关机弹窗的「确认/取消」，
     Tab 顺序是 开始→聊天记录→发送→再来一次→确认→取消。
     明眼人看到的是一屏，读屏用户拿到的是五屏叠在一起。
     pointer-events:none 只挡鼠标，挡不住键盘和读屏。
     visibility 延到 .9s 之后再翻，是为了让淡出还能演完 ——
     翻早了画面会直接消失，没有过渡。 */
  visibility: hidden;
  transition: opacity .9s, visibility 0s linear .9s;
}
.screen.show {
  opacity: 1; pointer-events: auto;
  visibility: visible;
  transition: opacity .9s, visibility 0s;      /* 进场立刻可见 */
}

.bg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  z-index: 0;
}
/* 敲墙那段要压暗，读铁盒那段不压 —— 铁盒是要看清的东西。 */
.bg.dim    { filter: brightness(.58) saturate(.92); }
.bg.box-bg { filter: brightness(.86) saturate(.95); }

/* ---------- 标题 ---------- */
.title-box {
  position: relative; z-index: 3;
  text-align: center;
  color: #f6f2e6;
  /* 标题卡上方留白（美术规格 E-05），所以字往下坐 */
  margin-top: 12%;
  text-shadow: 0 1px 16px rgba(0, 0, 0, .88);
}
.ch-num  { font-size: calc(14px * var(--fs)); letter-spacing: .34em; opacity: .9; }
.ch-name {
  font-size: calc(42px * var(--fs));
  letter-spacing: .28em;
  font-weight: 500;
  margin: 6px 0 12px;
}
.ch-when { font-size: calc(13px * var(--fs)); letter-spacing: .1em; opacity: .88; }

/* ---------- 按钮 ---------- */
.btn {
  font: inherit;
  font-size: calc(15px * var(--fs));
  letter-spacing: .1em;
  padding: 11px 26px;
  margin-top: 26px;
  border-radius: 3px;
  border: 1px solid rgba(246, 242, 230, .8);
  background: rgba(24, 20, 15, .6);
  color: #f6f2e6;
  cursor: pointer;
  position: relative; z-index: 4;
}
.btn:hover { background: rgba(24, 20, 15, .78); }
.btn:focus-visible { outline: 2px solid #f6f2e6; outline-offset: 3px; }
.btn-ghost { background: rgba(24, 20, 15, .3); }

/* ---------- 心声浮字 ---------- */
.voice-stack {
  position: relative; z-index: 3;
  text-align: center;
  max-width: 74%;

  /* 心声是**看得到、碰不到**的（前四章的浮字一律如此）。
     这里原先漏了这一条，后果在手机那一段：
     #phone-voice 是 position:absolute、z-index:6，正好盖在聊天记录上，
     于是浮字亮着的那几秒，滚轮滚不动下面那份记录 ——
     而剧本 §4 要的就是「往上滑能看到三个月的记录」。
     实测：对 #wx-log 中心做命中测试，拿到的是 #phone-voice 里的 <p>。
     `.blocked` 那条浮字本来就设了 pointer-events:none，
     这两处不一致纯粹是漏的。 */
  pointer-events: none;
}
.voice-stack p {
  font-size: calc(17px * var(--fs));
  line-height: 2;
  /* 压在亮图上 —— 见文件末尾的对比度记录 */
  color: #f8f5ec;
  text-shadow: 0 1px 14px rgba(0, 0, 0, .92);
  margin: 15px 0;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity .9s, transform .9s;
}
.voice-stack p.in { opacity: 1; transform: none; }
.center-voice { position: absolute; z-index: 6; }
/* 铁盒里的心声压在纸旁边，不压在图上，所以用墨色不用白色 */
.voice-stack.inline p {
  color: #3a332b;
  text-shadow: none;
  font-size: calc(15px * var(--fs));
  line-height: 1.95;
  margin: 9px 0;
}

/* ---------- 敲墙 ---------- */
#wall {
  position: absolute; z-index: 4;
  left: 50%; top: 56%;
  transform: translate(-50%, -50%);
  width: calc(112px * var(--fs)); height: calc(112px * var(--fs));
  border-radius: 4px;
  border: 1px dashed rgba(246, 242, 230, .5);
  background: rgba(141, 74, 53, .22);
  color: #f6f2e6;
  font: inherit;
  font-size: calc(15px * var(--fs));
  letter-spacing: .2em;
  cursor: pointer;
}
#wall:hover  { background: rgba(141, 74, 53, .34); }
#wall:active { transform: translate(-50%, -50%) scale(.97); }
#wall:focus-visible { outline: 2px solid #f6f2e6; outline-offset: 3px; }
#wall.shake { animation: knock .26s ease-out; }
@keyframes knock {
  0%, 100% { transform: translate(-50%, -50%); }
  30%      { transform: translate(-50%, -50%) translateY(3px) scale(.98); }
}

/* 砖掉下来之后露出的铁盒，盖在墙面上淡入 */
.reveal {
  position: absolute; inset: 0; z-index: 2;
  width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity 1.1s;
}
.reveal.in { opacity: 1; }

/* ---------- 铁盒 ---------- */
#box-wrap {
  position: relative; z-index: 3;
  width: 88%; height: 84%;
  display: flex; flex-direction: column;
  align-items: center;
}

#box-tabs {
  display: flex; gap: 10px;
  margin-bottom: 2.4%;
  flex: 0 0 auto;
}
#box-tabs button {
  font: inherit;
  font-size: calc(14px * var(--fs));
  letter-spacing: .12em;
  padding: 7px 17px;
  border-radius: 2px;
  /* 页签压在铁盒底图上，底图偏暗，所以用亮底暗字 —— 对比度好办 */
  border: 1px solid rgba(42, 37, 32, .42);
  background: rgba(239, 231, 215, .9);
  color: #2a2520;
  cursor: pointer;
}
#box-tabs button:hover { background: #f6f0e3; }
#box-tabs button[aria-selected="true"] {
  background: #2a2520; color: #f3ecdd;
  border-color: #2a2520;
}
#box-tabs button:focus-visible { outline: 2px solid #f6f2e6; outline-offset: 3px; }
/* 读过的打勾。**形状线索，不靠颜色**（UI 规格 §5）。 */
#box-tabs button.read::after { content: " ✓"; }

#item-stage {
  flex: 1 1 auto;
  min-height: 0;              /* 让 overflow 在 flex 子项里真的生效 */
  width: 100%;
  display: flex; flex-direction: column;
  align-items: center;
  overflow-y: auto;
}

/* 纸：三章三种排版共用这一个容器 */
#paper-wrap { position: relative; flex: 0 0 auto; }
#paper {
  background: var(--paper);
  color: var(--ink);
  box-shadow: 0 6px 22px rgba(0, 0, 0, .42);
  padding: calc(22px * var(--fs)) calc(26px * var(--fs));
  line-height: 1.95;
  font-size: calc(16px * var(--fs));
}
/* Ch1 电报底稿：牛皮纸，铅笔 */
#paper.telegram {
  background: #e3d5b8;
  font-family: "Kaiti SC", "STKaiti", serif;
}
/* Ch3 纸片：巴掌大，字很挤，最后几个字挤到纸边（剧本 §3） */
#paper.note {
  max-width: calc(210px * var(--fs));
  font-size: calc(13px * var(--fs));
  line-height: 1.62;
  letter-spacing: -.01em;
  padding: calc(14px * var(--fs)) calc(15px * var(--fs));
  background: #f2ece0;
}
/* Ch2 信：六列竖排，列 1 最右 */
#paper.letter {
  display: flex;
  flex-direction: row-reverse;
  gap: calc(11px * var(--fs));
  background: #eee6d4;
  font-family: "Kaiti SC", "STKaiti", serif;
}
#paper.letter .col {
  display: flex; flex-direction: column;
  align-items: center;
}
#paper .row { white-space: nowrap; }

/* 折对之后从右往左逐列亮起（与 Ch2 同一个值）。
   **不改字号也不改颜色** —— 只提亮度：
   动到布局的任何一项都会让那 24 字挪位置，而这一刻玩家正在读它们。 */
#paper.letter .col { transition: filter .28s; }
#paper.letter .col.lit { filter: brightness(1.16) contrast(1.08); }

/* 字元。可点（会被拦截并浮出那行手写体），所以是 button 语义但长得像字。 */
#paper .ch {
  display: inline-block;
  min-width: 1em;
  text-align: center;
  border: 0; background: none; padding: 0;
  font: inherit; color: inherit;
  cursor: default;
}
/* 幽灵态：**擦掉但留在原处**。全作的视觉签名。
   alpha 只能到 .74（见 :root 的实测），所以状态另加两个形状线索 ——
   和 Ch3 同一个思路，但这里用「擦得糊了」的斜纹而不是点线：
   剧本 §3 说「擦得糊了几处」「像是被人擦过又没擦干净」。 */
#paper .ch.gone {
  opacity: var(--ghost);
  transform: scale(.92);
  text-decoration: line-through 1px rgba(42, 37, 32, .5);
}

/* Ch2 折叠：折起来时下半截**不渲染**，由 game.js 决定渲染到第几行。

   这里跟 Ch2 的做法不一样，是有意的。
   Ch2 用 #paper-inner 收高度 + overflow:hidden，因为它有一条硬要求：
   折对之后剩下的 24 字一个像素都不能挪（寄出去的就是看见的那封）。
   Ch5 不寄任何东西，那条要求在这儿不存在；
   而 overflow 裁掉的只是画面 —— 被折进去的 24 字读屏照样念得到，
   于是「折起来是另一句」对读屏玩家根本不成立。
   所以这一章直接少渲染几行。位移仍然要为 0（行是从上往下堆的，
   删掉下面几行不该动上面几行），这一条按实测记在 README 里。 */
/* 折痕是列里的普通流内子元素，不需要定位。
   原先这儿有一条 .folded .col { position: relative }，删掉了 ——
   它什么也没做，但会把折起来之后 .ch 的 offsetParent
   从 #paper-wrap 换成 .col。于是「折前折后位移」拿 offsetLeft 量
   会读出 161px 的假位移（参照系换了，字没动）。
   **量位移的两把尺子各有各的失真**：Ch3 那次是 transform 把可视矩形
   量小了（scale 缩了字自己的盒子），这次是参照系变了把偏移量错了。
   哪把准取决于这一处可能有什么在骗你，没有一把总是对的。 */
#paper.letter .fold-crease {
  height: 0;
  border-top: 1px solid rgba(42, 37, 32, .34);
  /* 折痕发白，压得很实（剧本 §3） */
  box-shadow: 0 1px 0 rgba(255, 255, 255, .8);
  margin: calc(3px * var(--fs)) 0;
}

/* 凹痕：擦过的格位。永久保留 —— Ch2 的规则，这里只是把它画出来。 */
#paper .ch.dent {
  background: radial-gradient(ellipse at center,
              rgba(42, 37, 32, .13) 0%, transparent 72%);
}

/* 折痕手柄。**没有任何文字说它能拖** —— 剧本 §3 明写不给提示。 */
#fold-handle {
  position: absolute;
  left: -18px; right: -18px;
  height: 14px;
  cursor: grab;
  z-index: 5;
}
#fold-handle::before {
  content: "";
  position: absolute; left: 0; right: 0; top: 6px;
  border-top: 1px dashed rgba(42, 37, 32, .42);
}
#fold-handle:focus-visible { outline: 2px solid var(--rust); outline-offset: 2px; }

/* 旁白（看到什么）。在心声之前，所以排在纸下面、心声上面。 */
.desc {
  position: relative; z-index: 3;
  max-width: 68%;
  margin: 3.4% 0 0;
  font-size: calc(14px * var(--fs));
  line-height: 1.95;
  color: #f4efe2;
  text-shadow: 0 1px 13px rgba(0, 0, 0, .92);
  text-align: center;
}

/* 第四样（4B 一张便签 / 4C 两张纸）。
   两张就真的摆两张 —— 剧本说「两张纸」。 */
#paper.sheets {
  background: none;
  box-shadow: none;
  padding: 0;
  display: flex;
  gap: calc(13px * var(--fs));
  align-items: flex-start;
}
#paper.sheets .sheet {
  background: #f4eee1;
  box-shadow: 0 5px 18px rgba(0, 0, 0, .4);
  padding: calc(15px * var(--fs)) calc(16px * var(--fs));
  font-size: calc(14px * var(--fs));
  max-width: calc(150px * var(--fs));
}
#paper.sheets .sheet .row { white-space: normal; }
/* 第二张是手机截图的打印件 —— 打印体，纸更白、更平。
   靠字体和纸色区分，不加标签说明它是什么。 */
#paper.sheets .sheet.printed {
  background: #fbfaf6;
  font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
  font-size: calc(13px * var(--fs));
  box-shadow: 0 3px 12px rgba(0, 0, 0, .3);
}

/* 第四格是空的（4A）—— 画一个空格子，不画一张假便签。 */
#paper.empty {
  width: calc(168px * var(--fs));
  height: calc(112px * var(--fs));
  background: rgba(42, 37, 32, .3);
  border: 1px dashed rgba(239, 231, 215, .42);
  box-shadow: none;
}

/* ---------- 置灰拦截 ----------
   手写体一行，2.5s 淡出，一次会话最多 3 次（UI 规格 §3）。 */
.blocked {
  position: absolute; z-index: 8;
  left: 50%; top: 15%;
  transform: translateX(-50%);
  font-family: "Kaiti SC", "STKaiti", cursive, serif;
  font-size: calc(19px * var(--fs));
  letter-spacing: .06em;
  color: #fdf9ee;
  text-shadow: 0 1px 15px rgba(0, 0, 0, .95);
  opacity: 0;
  transition: opacity .5s;
  pointer-events: none;
  white-space: nowrap;
}
.blocked.in { opacity: 1; }

/* ---------- 微信 ---------- */
#wechat {
  position: absolute; inset: 0; z-index: 3;
  background: var(--wx-bg);
  display: flex; flex-direction: column;
  /* 从横屏转过来时，界面像手机被扶正 —— 转过来之后是正的，
     所以 rotate 只作用在入场，静止态不带 transform。 */
  animation: uplift 1.2s cubic-bezier(.5, 0, .2, 1) both;
}
@keyframes uplift {
  from { opacity: 0; transform: rotate(-8deg) scale(.94); }
  to   { opacity: 1; transform: none; }
}

#wx-top {
  flex: 0 0 auto;
  padding: calc(13px * var(--fs)) 14px;
  background: #ededed;
  border-bottom: 1px solid #d6d6d6;
  display: flex; align-items: center; justify-content: center;
  position: relative;
}
#wx-name {
  font-size: calc(16px * var(--fs));
  color: #14110d;
  letter-spacing: .06em;
}

/* ---------- 5X 电源图标 ----------
   剧本要求「极不显眼，无高亮、无提示、无成就预告」。

   **不显眼靠尺寸和位置做，不靠低对比度做。**
   压低对比度会把它对低视力玩家变成完全不存在 ——
   那不是「隐藏」，那是「拿不到」。而且它是个真控件，
   WCAG 1.4.11 要求 3:1。

   算过：#6f6f6f 对 #ededed = 4.29:1，比 3:1 宽出不少。
   没有为了「更不显眼」往上调浅（#858585 是 3.15，刚够）——
   余量留着：这颗图标只有 14px，细笔画在小尺寸上本来就吃亏。
   不显眼的部分交给：14px、贴右上角、无边框、无背景、无 hover 高亮、
   无任何文字说明。看得见 ≠ 看得出它有用。 */
#power {
  position: absolute; right: 10px; top: 50%;
  transform: translateY(-50%);
  border: 0; background: none; padding: 4px;
  font-size: calc(14px * var(--fs));
  line-height: 1;
  color: #6f6f6f;
  cursor: pointer;
}
#power:focus-visible { outline: 1px solid #6f6f6f; outline-offset: 2px; }

#wx-log {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: calc(14px * var(--fs)) calc(12px * var(--fs));
  display: flex; flex-direction: column;
  gap: calc(11px * var(--fs));
}
#wx-log:focus-visible { outline: 2px solid #6f6f6f; outline-offset: -2px; }

.wx-date {
  align-self: center;
  font-size: calc(11px * var(--fs));
  /* 真微信的日期分隔灰更浅，但 #6d6d6d 压在 #ebebeb 上只有 4.34，过不了 4.5。
     11px 不算大字号，不能走 3:1 那条。阈值在 #696969（4.61），取 #676767 = 4.74。
     这一处颜色偏离原型参考是有意的：日期是「哪天说的」，
     剧本 §4 靠它撑起 2003→2019 那道十六年的空白，不该是装饰。 */
  color: #676767;
  padding: 2px 8px;
  letter-spacing: .04em;
}

.wx-row { display: flex; align-items: flex-start; gap: 7px; max-width: 100%; }
.wx-row.mine { flex-direction: row-reverse; }
.wx-who {
  flex: 0 0 auto;
  width: calc(30px * var(--fs)); height: calc(30px * var(--fs));
  border-radius: 3px;
  background: #c9c9c9;
  color: #201c18;
  font-size: calc(11px * var(--fs));
  display: flex; align-items: center; justify-content: center;
  letter-spacing: 0;
}
.wx-bubble {
  max-width: 70%;
  padding: calc(9px * var(--fs)) calc(12px * var(--fs));
  border-radius: 4px;
  background: var(--wx-them);
  color: #14110d;
  font-size: calc(15px * var(--fs));
  line-height: 1.6;
  word-break: break-word;
  /* 气泡逐条淡入 */
  animation: bubble .42s ease-out both;
}
/* 只换底色，**不换字色** —— 两侧都是 #14110d。
   实测：对方那侧 #14110d/#ffffff = 18.42，自己那侧 #14110d/#95ec69 = 11.98。
   微信自己就是绿底深字（白字是 iMessage 的蓝底），所以这一处照抄了，
   而且照抄的结果刚好是两侧都远超 4.5。 */
.wx-row.mine .wx-bubble { background: var(--wx-mine); }
@keyframes bubble {
  from { opacity: 0; transform: translateY(5px); }
  to   { opacity: 1; transform: none; }
}

#wx-composer {
  flex: 0 0 auto;
  border-top: 1px solid #d6d6d6;
  background: #f6f6f6;
  padding: calc(10px * var(--fs)) calc(11px * var(--fs));
  display: flex; align-items: center; gap: 9px;
}
/* 输入框里已经打好了。**只读** —— 这句不是玩家拼的（剧本 §5）。 */
#wx-draft {
  flex: 1 1 auto;
  background: #fff;
  border: 1px solid #dcdcdc;
  border-radius: 3px;
  padding: calc(9px * var(--fs)) calc(11px * var(--fs));
  font-size: calc(15px * var(--fs));
  color: #14110d;
  line-height: 1.5;
}

/* ---------- 唯一的按钮 ----------
   **无倒计时、无禁用条件、永久等待玩家**（UI 规格 §3）。
   所以这里没有 :disabled 样式 —— 它永远不会 disabled。
   绿底黑字：#07c160 对 #14110d 算得 7.89:1（正文档 4.5 ✓）。
   白字只有 2.38 —— 微信自己用的就是白字，这里**没照抄**：
   这颗按钮是全章唯一一次真正的送出，按错不了也看不错。 */
.wx-send {
  flex: 0 0 auto;
  font: inherit;
  font-size: calc(15px * var(--fs));
  letter-spacing: .08em;
  padding: calc(9px * var(--fs)) calc(19px * var(--fs));
  border: 0; border-radius: 3px;
  background: #07c160;
  color: #14110d;
  cursor: pointer;
}
.wx-send:hover { background: #06ad56; }
.wx-send:focus-visible { outline: 2px solid #14110d; outline-offset: 2px; }

/* ---------- 结局 ---------- */
#scr-ending { background: #000; }
#ending-art { opacity: 0; transition: opacity 1.4s; }
#ending-art.in { opacity: 1; }

.end-title {
  position: relative; z-index: 4;
  font-size: calc(34px * var(--fs));
  letter-spacing: .42em;
  color: #f8f5ec;
  text-shadow: 0 1px 18px rgba(0, 0, 0, .9);
  opacity: 0; transition: opacity 1.5s;
}
.end-title.in { opacity: 1; }

.end-theme {
  position: relative; z-index: 4;
  margin-top: 5.6%;
  max-width: 76%;
  text-align: center;
}
.end-theme p {
  font-size: calc(16px * var(--fs));
  line-height: 2.05;
  color: #f4f1e8;
  text-shadow: 0 1px 14px rgba(0, 0, 0, .9);
  margin: 6px 0;
  opacity: 0; transition: opacity 1.3s;
}
.end-theme p.in { opacity: 1; }

.end-ach {
  position: relative; z-index: 4;
  margin-top: 4%;
  display: flex; gap: 8px; flex-wrap: wrap; justify-content: center;
}
.ach-tag {
  font-size: calc(12px * var(--fs));
  letter-spacing: .1em;
  padding: 4px 12px;
  border: 1px solid rgba(248, 245, 236, .68);
  color: #f8f5ec;
  text-shadow: 0 1px 12px rgba(0, 0, 0, .9);
  opacity: 0; transition: opacity 1s;
}
.ach-tag.in { opacity: 1; }
.end-btns { position: relative; z-index: 4; }

/* ---------- 关机弹窗 ----------
   **全作唯一一个真弹窗**（剧本 §5X）。别处一律用浮字。 */
#off-layer {
  position: absolute; inset: 0; z-index: 20;
  background: rgba(10, 9, 7, .62);
  display: flex; align-items: center; justify-content: center;
}
/* 见文件末尾那条全局 [hidden]。这里原先漏了，后果比 Ch3 那次严重得多：
   #off-layer 是 inset:0 的满屏遮罩，z-index 20 压在所有屏之上 ——
   它 hidden 却照旧 display:flex，于是整章从头到尾都被一层
   看不见的东西盖着。实测：对 #again-btn 的中心做命中测试，
   拿到的是 off-layer。**这一章用鼠标根本玩不了。**
   之前没发现是因为我一路用 el.click() 验的 ——
   click() 不做命中测试，遮罩挡不住它。 */
#off-dialog {
  background: #f2efe6;
  color: #1c1915;
  padding: calc(26px * var(--fs)) calc(30px * var(--fs));
  border-radius: 4px;
  text-align: center;
  min-width: calc(200px * var(--fs));
  box-shadow: 0 10px 34px rgba(0, 0, 0, .5);
}
#off-q {
  margin: 0 0 4px;
  font-size: calc(18px * var(--fs));
  letter-spacing: .12em;
}
.off-btns { display: flex; gap: 10px; justify-content: center; }
#off-dialog .btn {
  border-color: rgba(28, 25, 21, .6);
  background: rgba(28, 25, 21, .06);
  color: #1c1915;
  padding: 8px 20px;
  font-size: calc(14px * var(--fs));
}
#off-dialog .btn:hover { background: rgba(28, 25, 21, .13); }
#off-dialog .btn:focus-visible { outline: 2px solid #1c1915; outline-offset: 3px; }

/* 关机后的纯黑 8 秒。
   美术规格说 D-5X「可由引擎纯色生成，不必走生图」——
   但那张图已经生出来了，而且它不是纯黑：
   「中央极微弱的一点残余屏幕余光，正在消失」。
   **那点余光是这 8 秒的全部内容**，纯色填不出来，所以这里铺图。
   图先淡入（余光还在），再随 .blackout 的背景一起沉掉。 */
#stage.blackout { background: #000; }
#stage.blackout .screen:not(#scr-ending) { opacity: 0 !important; }
/* 余光比正常插画淡入慢得多 —— 它是在灭，不是在亮 */
#ending-art.afterglow { transition: opacity 2.6s; }

/* ---------- 读屏专用 ---------- */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* **这条不能删。**
   `hidden` 是靠 UA 样式表的 `[hidden]{display:none}` 生效的，权重等于零个类。
   本文件里任何一条按 id 设 display 的规则都比它重，会把 hidden 顶掉；
   元素于是「hidden 但照旧参与布局」——还在无障碍树里、Tab 还走得进去、
   还接得住点击，只是看不见。
   Ch3 撞过一次（#preview-layer），那儿是就地写了 `#preview-layer[hidden]` 补的。
   这里改成一条兜底：**逐个补漏是补不完的** ——
   漏掉的那个不会报错，只会安静地坏掉，而且要靠命中测试才查得出来。 */
[hidden] { display: none !important; }

/* ---------- DEV ---------- */
#dev {
  position: fixed; right: 10px; bottom: 10px; z-index: 60;
  background: rgba(18, 16, 12, .92);
  border: 1px solid #4a443a;
  padding: 9px 11px;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 12px;
  color: #ddd6c6;
  max-width: 260px;
}
#dev .dev-t { color: #9c9484; margin-bottom: 7px; letter-spacing: .06em; }
#dev button {
  font: inherit; color: #ddd6c6;
  background: #262218; border: 1px solid #4a443a;
  padding: 3px 8px; margin: 0 4px 4px 0; cursor: pointer;
}
#dev button:hover { background: #332d21; }
#dev .dev-row { display: flex; align-items: center; gap: 5px; margin-bottom: 4px; }
#dev .dev-row span { color: #9c9484; min-width: 52px; }

/* ==========================================================
   对比度：全部量过了。

   这一章是全作最亮的一章（美术规格：D-5「全作最亮的一张」，
   E-05「明亮、有希望」），所以「白字压亮图」的老问题在这儿最重。
   Ch3 那一轮已经确认两件事，这里直接按它们来：
     1. 亮底上「淡」和「读得出」互斥 —— 幽灵字只能选读得出（见 :root）
     2. text-shadow 在 WCAG 的算式里**不算背景**，不能拿它凑数

   量法：**按最坏像素**。把背景图画进 canvas，按 object-fit: cover
   把文字的可视矩形映射回原图取样，再把 CSS filter（brightness/saturate）
   和 scrim 的渐变 alpha 按 y 位置逐点合成 —— 取该矩形内比值最低的那个点，
   不是取平均。取平均会让「大部分地方够、偏偏字压在窗户上」蒙混过关。

     压在图上（E-05 敲墙墙面，filter: brightness(.58)）
       心声三行            6.72 / 6.75 / 6.82 ✓
       （压暗那一下是关键：不压的话这三行就在 4.5 上下打转）
     压在图上（F-02 标题底，scrim 渐变）
       章号 / 标题 / 副行  都远超 4.5 ✓
     纸上
       墨 #2a2520 / 牛皮纸 #e3d5b8      9.53 ✓
       幽灵字 alpha .74 / 牛皮纸        5.23 ✓（阈值 0.69，见上面那段）
     微信
       对方气泡 #14110d/#ffffff        18.42 ✓
       自己气泡 #14110d/#95ec69        11.98 ✓
       日期分隔 #676767/#ebebeb         4.74 ✓（原 #6d6d6d 是 4.34 ✗，改过）
       发送按钮 #14110d/#07c160         7.89 ✓
     电源图标 #6f6f6f/#ededed           4.29
       按 1.4.11 非文本 3:1 算，过。它是个 ⏻ 字符，
       但承担的是「这是个可按的控件」，不是正文。

   **之前这里挂着两个「估计过不了」的值（心声≈3.74、旁白≈1.28），
   量完发现都不对** —— 估的时候没算 filter: brightness(.58)，
   也没算 scrim。估出来的数比量出来的数低一半还多。
   ========================================================== */

/* ===== 构建期注入（build.py）=====
   调试面板在公开版永久关闭。bindDev() 的调用已在构建时摘掉，
   这条 CSS 是第二道保险 —— 面板里有「填真解」「折第4行」
   「extend_recall 4min」这类直接给答案或改核心机制的按钮。
   display:none 同时也把它从 tab 顺序和无障碍树里摘掉了。 */
#dev, #devpanel { display: none !important; }
