:root {
  --bg-top: #2b1f3d;
  --bg-bottom: #4a2a45;
  --panel: rgba(18, 12, 26, 0.72);
  --panel-soft: rgba(255, 255, 255, 0.08);
  --accent: #ff8fb1;
  --accent-strong: #ff5c8a;
  --text: #f5eef2;
  --text-dim: #d8c8d6;
  --purple: #8a5cf6;
  --gold: #ffe6a8;
  --gold-line: rgba(255, 209, 102, 0.75);
  --gold-fill: rgba(255, 209, 102, 0.16);
}

* { box-sizing: border-box; }

/* 背景グラデーションは html にだけ置く。body に置くと、body の背景が
   #bg-layer(z-index:-2)より後に描画されて背景画像を覆い隠してしまう */
html {
  background: linear-gradient(160deg, var(--bg-top), var(--bg-bottom));
  background-attachment: fixed;
}

html, body {
  margin: 0;
  padding: 0;
  min-height: 100%;
  color: var(--text);
  font-family: "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Yu Gothic", "Meiryo", sans-serif;
  overflow-x: hidden;
}

body { background: transparent; }

/* ---------------- 背景レイヤー ---------------- */

/* 背景は2枚を重ねてクロスフェードさせる。1枚で opacity 0 から出すと
   切り替わるたびに下地のグラデーションが見えて画面が瞬く */
.bg-layer {
  position: fixed;
  inset: 0;
  background-size: cover;
  background-position: center;
  z-index: -2;
  opacity: 0;
  transition: opacity 0.55s ease-out;
}
.bg-layer.bg-show { opacity: 1; }

/* 背景の上に敷く暗幕。本文の可読性は各パネルの背景で確保しているので、
   ここは背景写真が見える程度に留める(濃くしすぎると背景を入れた意味が消える) */
#bg-scrim {
  position: fixed;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(180deg, rgba(20, 12, 30, 0.28) 0%, rgba(20, 12, 30, 0.46) 55%, rgba(20, 12, 30, 0.62) 100%);
}

/* ---------------- 立ち絵 ---------------- */

/* 透過PNG由来の全身立ち絵。額装せず、画面下端に立たせる */
.sprite {
  position: fixed;
  display: none;
  bottom: 0;
  z-index: 1;
  pointer-events: none;
  width: auto;
  /* 背景となじませる影。box-shadow だと矩形に影が出るので filter を使う */
  filter: drop-shadow(0 6px 20px rgba(0, 0, 0, 0.55));
}
/* 彼女が最初に口を開いた時に、ふわっと現れる(場面の入りでは出さない)。
   0.5s だと「出た」ではなく「切り替わった」に見えるので、少し長く取る。
   下から迫り上がる動きは付けない(2026-09-05 本人指示)。透明度だけで出す */
.sprite.sprite-in { animation: spriteIn 0.85s ease-out; }
@keyframes spriteIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
/* タイトルでは入場のあと、ゆっくり呼吸させる(本編は表情の差し替えと干渉するので動かさない) */
body.is-title #sprite.sprite-in { animation: spriteIn 0.85s ease-out, breathe 4.6s ease-in-out 0.85s infinite; }
body.is-title #sprite { animation: breathe 4.6s ease-in-out infinite; }
@keyframes breathe {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-3px); }
}

/* 表情の切り替え用。裏のレイヤーに次の表情を載せて重ね、180msで溶かす。
   差分はすべて同じポーズ・同じシルエットなので、顔だけが変化して見える */
#sprite-b {
  opacity: 0;
  transition: opacity 0.18s ease-out;
  z-index: 1;
}
#sprite-b.is-shown { opacity: 1; }

/* 狭い画面も全身で立たせる。本文を1ブロックずつ送るようにしてパネルが小さく
   なったので、本文を画面下に寄せれば顔が隠れない(以前は全文を一度に出していて
   パネルが高く、顔が必ず裏に隠れるため円形アイコンに逃がしていた) */
@media (max-width: 999px) {
  /* 顔の位置は画面の「上端」から決める。下端から決めると画面の高さによって
     顔の高さが変わり、機種によっては本文パネルに隠れる。
     上端から決めると、立ち絵が小さい(＝画面が低い)ほど顔も上に来るので、
     どの高さでも顔は必ず上から 52px 〜 26% の間に収まる。
     大きさは画面の高さの 56%(2026-09-04 本人指示で 84% から縮小)。
     右端に寄せると小さくした時に画面の縁で切れて見えるので、中央に立たせる
     (img は置換要素なので、left/right: 0 + margin: auto で幅を伸ばさずに中央へ来る)。
     足元は本文パネルの後ろに隠れる = ADV の通常の見せ方。下端の
     フェード(mask-image)で、パネルとの境目が硬くならないようにしている */
  .sprite {
    /* 頭上の余白。低い機種は 52px(HUD のすぐ下)のままにしないと顔が枠に掛かる。
       背の高い機種は余裕があるので、画面の高さに比例して下げる */
    top: var(--sprite-top, 52px);
    bottom: auto;
    left: 0;
    right: 0;
    margin: 0 auto;
    height: min(56vh, 620px);
    height: min(56dvh, 620px);
    -webkit-mask-image: linear-gradient(to bottom, #000 84%, transparent 100%);
    mask-image: linear-gradient(to bottom, #000 84%, transparent 100%);
  }
  /* エンディングも本編と同じ高さに立たせる。HUD が無いぶん上げていたが、
     場面が切り替わった時に彼女の背だけが変わって見える(2026-09-05 本人指摘)。
     顔と本文パネルの間は、下の余白の詰め方で稼ぐ */
  /* 本文と選択肢は画面の下 68% までを使う。上は顔のために空ける。
     立ち絵を 56dvh に縮めた(2026-09-04)ぶん、あごは上から約 22% に来るので、
     68% でも顔には掛からない。60% のままだと 320x568 級で選択肢の最後が
     画面の下に 25px はみ出していた(スクロールバーは出さない作りなので気づけない)。
     収まらない時はこの中でスクロールさせ、顔の領域には絶対に食い込ませない */
  #screen-event,
  #screen-confession,
  #screen-ending {
    flex: 0 1 auto;
    margin-top: auto;
    max-height: 68dvh;
    overflow-y: auto;
    /* 名札が枠の上端から 13px はみ出す。スクロール領域の縁で刈られないよう上に余白を取る */
    padding-top: 16px;
    overscroll-behavior: contain;
    /* ここにスクロールバーが出ると、その幅ぶん本文の枠が縮んで
       左右に動いて見える。指で送るだけの画面なので出さない */
    scrollbar-width: none;
  }
  /* 中身は縮ませない(縮むと枠の高さが揺れる)。
     flex-end ではなく先頭の auto マージンで下に寄せる —— flex-end だと
     はみ出した時に上端がスクロールで出せなくなる */
  #screen-event > *,
  #screen-confession > *,
  #screen-ending > * {
    flex-shrink: 0;
  }
  #screen-event > :first-child,
  #screen-confession > :first-child,
  #screen-ending > :first-child {
    margin-top: auto;
  }
}

/* 頭上の余白は画面の高さの 14%。立ち絵は 56dvh、あごはその 35%(= 上から 19.6dvh)
   なので、14dvh 下げてもあごは 34dvh に収まり、本文の枠(上端は 32〜50dvh)に掛からない。
   ただし 320x568 級だけは本文と選択肢で画面がほぼ埋まり、枠の上端が 33dvh まで
   上がってくるので下げられない。そこだけ HUD のすぐ下(52px)に戻す */
@media (max-width: 999px) {
  .sprite { --sprite-top: 14dvh; }
}
@media (max-width: 999px) and (max-height: 620px) {
  .sprite { --sprite-top: 52px; }
}

/* 広い画面: 本文窓は画面下、立ち絵は右に等身大で(窓が脚に掛かるのは ADV の定石) */
@media (min-width: 1000px) {
  .sprite {
    right: max(40px, calc(50vw - 600px));
    height: min(94vh, 960px);
    height: min(94dvh, 960px);
  }
}

/* ---------------- レイアウト ---------------- */

#app {
  max-width: 640px;
  margin: 0 auto;
  /* ホーム画面から起動した時(standalone)に、上端が時計に掛からないようにする。
     ふつうのブラウザでは安全域が 0 なので、これまでと同じ 24px になる */
  padding: calc(24px + env(safe-area-inset-top, 0px)) 20px 48px;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  position: relative;
  z-index: 2;
}

/* 本文が下端に来るので、ホームインジケータに被らないぶんだけ空ける */
@media (max-width: 999px) {
  #app { padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px)); }
}

.screen { display: none; flex: 1; flex-direction: column; }
.screen.active { display: flex; }
/* イベント画面は overflow が効いている(縦に長い時に中でスクロールさせるため)。
   その切り取り箱が選択肢と同じ幅なので、枠から外へ出た光がボタンの縁で
   ぴったり断ち切られ、まっすぐな境界線に見えていた(2026-09-06 本人指摘)。
   左右を負の margin で広げ、同じ幅の padding で戻す —— 中身の位置は1pxも
   動かないまま、切り取り箱だけが左右に 20px ぶん広がる */
#screen-event {
  margin-left: -20px;
  margin-right: -20px;
  padding-left: 20px;
  padding-right: 20px;
}

/* ---------------- 進行度バー(プレイ中のみ) ---------------- */

.hud {
  display: none;
  margin-bottom: 4px;
}
.hud-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px 12px;
  margin-bottom: 6px;
  /* ボタンが3つあり、375px 幅では1行に収まらない。
     縮めずに折り返す(縮めると横スクロールが出る) */
  flex-wrap: wrap;
}
.hud-progress {
  /* 日付は場面の手がかりなので、他のHUD要素より一段強く出す(2026-09-09 本人指示)。
     メーター0.8rem・リンク0.78rem に対してはっきり主役にする */
  color: var(--text);
  font-size: 1.3rem;
  font-weight: bold;
  letter-spacing: 0.04em;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.9);
}
.link-btn {
  border: none;
  background: none;
  color: var(--text-dim);
  font-size: 0.78rem;
  cursor: pointer;
  padding: 4px 6px;
  border-radius: 10px;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.link-btn:hover { color: var(--accent); }
.link-btn[aria-pressed="true"] { color: var(--accent); }
.hud-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
  margin-left: auto;
}
.hud-bar {
  height: 3px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.16);
  overflow: hidden;
}
.hud-bar > span {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--accent), var(--purple));
  transition: width 0.45s ease-out;
}

/* ---------------- タイトル ---------------- */

/* タイトルは画像のロゴ。h1 のテキストは残さず alt に持たせている。
   図鑑を1列(7段)にしたぶん、余白を詰めて画面に収める */
.game-title {
  margin: 16px 0 18px;
  text-align: center;
}
.game-logo {
  display: block;
  width: min(88%, 460px);
  height: auto;
  margin: 0 auto;
  /* ロゴ自体が縁取りを持っているので、影は接地感を出す程度に留める */
  filter: drop-shadow(0 6px 22px rgba(0, 0, 0, 0.55));
  /* 復号が済むまで見せない(JS が is-ready を付ける)。読み込みの遅い回線で空白が先に出ないように */
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.8s ease, transform 0.8s ease;
}
.game-logo.is-ready { opacity: 1; transform: none; }
/* 音を鳴らすための1タップを待っている間だけ出す */
.tap-hint {
  display: none;
  text-align: center;
  color: var(--text-dim);
  font-size: 0.85rem;
  letter-spacing: 0.12em;
  margin: 28px 0 0;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.9);
  animation: tapPulse 2.2s ease-in-out infinite;
}
#screen-title.is-gated .tap-hint { display: block; }
#screen-title.is-gated .title-menu { display: none; }
@keyframes tapPulse {
  0%, 100% { opacity: 0.4; }
  50% { opacity: 1; }
}

.title-menu {
  display: flex;
  flex-direction: column;
}

/* 音のオン/オフはロゴの上の空きに逃がす(2026-09-05)。
   本文中の HUD と同じ「右上」なので、置き場所が1つに揃う。
   絶対配置なので縦の行を1つぶん(約36px)減らせる */
.title-sound {
  position: absolute;
  top: 0;
  right: 0;
  margin: 0;
  z-index: 3;
}
#screen-title { position: relative; }
/* タイトルの説明。ロゴの直下に置くので、主張しすぎない大きさにする */
.title-lead {
  text-align: center;
  color: var(--text-dim);
  font-size: 0.78rem;
  line-height: 1.75;
  margin: 0 auto 14px;
  max-width: 340px;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.9);
}
/* 1文で1行。<br> で折ると、狭い画面で折り返しと二重になって行が増える。
   折り返す時は行の長さを揃える —— 揃えないと最後の行に「です。」だけ残る */
.title-lead span { display: block; text-wrap: balance; }

/* 結末の画面だけに出る音の設定。#screen-ending の中に入れると、
   あの画面は縦に長い時スクロールするので一緒に流れてしまう。
   暗転(.fade は z-index 900)には隠れてほしいので、それより下に置く。

   置き場所は左上。右上は 41歳の「謝る / いじる」の一言(.adult-poke)が
   同じ座標に出ていて、文字が重なっていた(2026-09-07 本人報告)。
   あちらは「彼女の頭のあたりにセリフを出す」ための位置なので動かせない。
   左上はプロローグでも結末でも空いている —— 一枚絵(.cg)はいちばん上でも
   10% / 18dvh で、このボタンの下端(約38px)より下に来る */
#float-sound {
  display: none;
  position: fixed;
  top: calc(14px + env(safe-area-inset-top, 0px));
  left: 14px;
  z-index: 500;
}
body.is-ending #float-sound,
body.is-prologue #float-sound { display: block; }
/* 押した場所の近くで開く。プロローグと結末は左上にボタンがあるので、パネルも左へ寄せる */
body.is-ending .sound-panel,
body.is-prologue .sound-panel { left: 16px; right: auto; }
/* 早送り。押している間だけ既読の枠を送る。本文の枠の中、右下に置く
   (2026-09-08 本人指示)。送りマーク ▼ と同じ隅なので、▼ の方を左へ寄せる。
   長押しで文字が選択されたり、iOS で吹き出しが出たりしないようにする */
.skip-btn {
  position: absolute;
  right: 10px;
  bottom: 8px;
  z-index: 2;
  padding: 6px 14px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.26);
  background: rgba(0, 0, 0, 0.34);
  color: var(--text-dim);
  font-size: 0.76rem;
  cursor: pointer;
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
.skip-btn[hidden] { display: none; }
/* 送り先が無い枠では消す。結末の最後の枠で文字に重なるため */
.textbox.is-text-done .skip-btn { display: none; }
/* 押している間は塗る。未読に着いて止まると色が戻るので、
   「ここから先は読んでいない」が指を離さなくても分かる */
.skip-btn:active,
body.is-skipping .skip-btn {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
  color: #fff;
}
/* 反応を出している間は、上下どちらの枠にも出さない(2026-09-08 本人指示)。
   本文はもう送れず、反応には「つづける」がある。
   is-reacting は showReaction で付き、次の場面の頭(showEvent)で外れる */
#screen-event.is-reacting .skip-btn { display: none; }
/* 選択肢を選んでいる間も出さない。送る先が無く、押せても何も起きないため */
body.is-choosing .skip-btn { display: none; }
.credits {
  text-align: center;
  color: var(--text-dim);
  font-size: 0.72rem;
  margin: 18px 0 0;
}
.credits a { color: var(--text-dim); }

.name-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0 auto 22px;
  width: 100%;
  max-width: 320px;
}
#player-name-input {
  padding: 12px 14px;
  border-radius: 10px;
  border: 1px solid rgba(255,255,255,0.25);
  background: rgba(10, 6, 16, 0.6);
  color: var(--text);
  font-size: 1rem;
}
#player-name-input::placeholder { color: rgba(255,255,255,0.4); }
#player-name-input.invalid {
  border-color: #ff5c8a;
  box-shadow: 0 0 0 3px rgba(255, 92, 138, 0.25);
}
#player-name-input.shake { animation: nameShake 0.4s; }
@keyframes nameShake {
  0%, 100% { transform: translateX(0); }
  20%, 60% { transform: translateX(-6px); }
  40%, 80% { transform: translateX(6px); }
}
/* 出したり消したりで下のボタンが動かないよう、1行ぶんの高さを常に確保する */
.name-error {
  margin: 0;
  min-height: 1.2em;
  font-size: 0.8rem;
  line-height: 1.2;
  color: #ff8fa8;
}

.main-btn {
  display: block;
  margin: 0 auto 14px;
  padding: 14px 40px;
  border-radius: 999px;
  border: none;
  background: linear-gradient(135deg, var(--accent-strong), var(--purple));
  color: white;
  font-size: 1.05rem;
  font-weight: bold;
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(255, 92, 138, 0.35);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.main-btn:hover { transform: translateY(-2px); box-shadow: 0 10px 22px rgba(255, 92, 138, 0.45); }
.main-btn:active { transform: translateY(0); }
/* 名前が空の間は「はじめる」を沈ませて、押せないことを見た目でも伝える
   (押すこと自体は許す —— 押した時に理由を出したいので) */
.main-btn.is-off { opacity: 0.45; box-shadow: none; }
.main-btn.is-off:hover { transform: none; box-shadow: none; }
.main-btn.secondary {
  background: rgba(10, 6, 16, 0.55);
  border: 1px solid rgba(255,255,255,0.35);
  box-shadow: none;
}

/* ---------------- イベント画面 ---------------- */

/* 似顔絵の画面へ移るボタン(E8B)。既定は隠しておき、その場面でだけ出す */
#btn-draw-start { display: none; }

.event-title {
  font-size: 1.1rem;
  color: var(--accent);
  border-left: 4px solid var(--accent-strong);
  padding-left: 10px;
  margin: 20px 0 16px;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.9);
}
/* 本文の枠は2層。外側(.textbox)が箱の見た目と名札を持ち、内側(.event-text)が
   固定高さのスクロール領域。名札は箱の上端に掛けて出すので、外側は overflow を切らない
   (内側に付けると overflow: auto に名札ごと刈られる) */
.textbox {
  position: relative;
  background: var(--panel);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 14px;
  margin-bottom: 20px;
  /* 横幅を固定する。プロローグ・本編・告白・エンディングで同じ値を使い、
     画面が変わっても枠が広がったり縮んだりしないようにする。
     狭い画面では #app の内寸(640 - 左右 padding 40)＝600px で頭打ち */
  width: 100%;
  max-width: 600px;
  /* flex-shrink を切らないと、flex の子として潰されて高さが揺れる */
  flex-shrink: 0;
}
.event-text {
  /* 送りマーク(::after)の基準 */
  position: relative;
  padding: 20px;
  line-height: 1.95;
  font-size: 0.98rem;
  /* 高さを固定する。ブロックごとの文章量で枠が伸び縮みすると、
     読んでいる間ずっと画面が動いて酔う。1ブロックは最長52字 */
  height: calc(1.95em * 3 + 40px);
  overflow-y: auto;
  /* スクロールバーは出さない。出た瞬間にその幅ぶん中身が詰まって、
     枠が左右に伸び縮みして見える(酔いの原因) */
  scrollbar-width: none;
  white-space: normal;
  word-break: auto-phrase;
  line-break: strict;
}
/* プロローグは本編より少し大きく、行間を広く取って回想の距離感を出す。
   ただし横幅と左位置は本編と揃える —— ここを狭めて中央寄せにすると、
   広い画面でプロローグから本編に移った瞬間に枠の幅と位置が飛ぶ(その8) */
.prologue-box { margin: auto 0 28px; }
.prologue-text {
  font-size: 1.02rem;
  line-height: 2.15;
  letter-spacing: 0.02em;
}

/* 名札。話者のいる枠だけ、左上の縁に掛けて出す(地の文では出ない)。
   data-speaker / data-voice は typeText がブロックごとに付け替える。
   色は声と同じ区分: ハナエ=ピンク / 主人公=紫 / その他=灰 */
.textbox[data-speaker]::before {
  content: attr(data-speaker);
  position: absolute;
  top: -13px;
  left: 14px;
  z-index: 2;
  padding: 3px 14px;
  border-radius: 999px;
  background: var(--accent-strong);
  color: #fff;
  font-size: 0.78rem;
  font-weight: bold;
  letter-spacing: 0.06em;
  line-height: 1.4;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
  pointer-events: none;
}
.textbox[data-voice="3"]::before { background: var(--purple); }
.textbox[data-voice="2"]::before { background: #5e5876; }

.event-choices {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* 描画直後の誤タップ防止。1つずつ現れる間はクリックを受け付けない */
.event-choices.is-locked { pointer-events: none; }
/* 選んだ直後: 選んだ肢だけ残し、他は沈める */
.event-choices.is-decided .choice-btn { opacity: 0.28; transition: opacity 0.25s ease; }
.event-choices.is-decided .choice-btn.is-chosen {
  opacity: 1;
  background: rgba(255, 143, 177, 0.3);
  border-color: var(--accent);
  box-shadow: 0 0 0 1px rgba(255, 143, 177, 0.35), 0 6px 18px rgba(255, 92, 138, 0.25);
}
@keyframes choiceIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.choice-btn {
  animation: choiceIn 0.32s ease-out both;
  text-align: left;
  padding: 14px 16px;
  border-radius: 14px;
  border: 1px solid rgba(255,255,255,0.18);
  background: rgba(18, 12, 26, 0.7);
  color: var(--text);
  font-size: 0.95rem;
  line-height: 1.6;
  cursor: pointer;
  word-break: auto-phrase;
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}
.choice-btn:hover {
  background: rgba(255, 143, 177, 0.22);
  border-color: var(--accent);
  transform: translateX(2px);
}

/* パーフェクトの分かれ道の印。2周目以降だけ付く(game.js の showPerfectHint)。
   「【重要】」のような札は攻略情報に見えて作品から浮くので、枠を光らせるだけにする。

   ボタンの左右には 14px しか余白が無い(実測)。そこへ外向きの光を出すと、
   html の overflow-x: hidden に画面端で断ち切られ、まっすぐな境界線が出る
   (2026-09-06 本人指摘・2回)。擬似要素で内側から出す手も、その要素の縁が
   透けて二重枠に見えた。
   → **外へ出す光は捨てて、枠そのものを光らせる**。
      枠の色を明るくし、内側へ滲ませる(inset)。外向きは 6px までに抑え、
      14px の余白の中で消えきるようにする */
.event-choices.is-key .choice-btn {
  animation: choiceIn 0.32s ease-out both, keyGlow 2.8s ease-in-out 0.32s infinite;
}
@keyframes keyGlow {
  0%, 100% {
    border-color: rgba(255, 143, 177, 0.5);
    box-shadow:
      0 0 4px rgba(255, 143, 177, 0.16),
      0 0 10px rgba(255, 143, 177, 0.08),
      inset 0 0 8px rgba(255, 143, 177, 0.07);
  }
  50% {
    border-color: rgba(255, 226, 238, 0.98);
    box-shadow:
      0 0 5px rgba(255, 232, 242, 0.5),
      0 0 14px rgba(255, 143, 177, 0.34),
      inset 0 0 3px rgba(255, 232, 242, 0.3),
      inset 0 0 20px rgba(255, 143, 177, 0.32);
  }
}
/* 選んだ後は光を止める(反応を読んでいる間まで点滅していると落ち着かない) */
.event-choices.is-key.is-decided .choice-btn {
  animation: none;
  box-shadow: none;
}
/* 反応の枠と「つづける」を1つの入れ物にまとめる(.textbox の色違い)。
   ボタンを枠の外(下)に置くと、出た瞬間にその高さぶん枠が上へ動く。
   画面下端に寄せている作りなので、下に何かが増えると全部が持ち上がって酔う */
.reaction-wrap {
  display: none;
  margin-top: 20px;
  margin-bottom: 0;
  background: rgba(138, 92, 246, 0.28);
  border: 1px solid rgba(138, 92, 246, 0.5);
}
.reaction-wrap.is-shown { display: block; }

/* 反応の枠の内側。本文と同じくページ送りするので、高さも同じ理由で固定する */
.event-reaction {
  display: block;
  position: relative;
  padding: 18px;
  line-height: 1.85;
  font-size: 0.95rem;
  height: calc(1.85em * 3 + 36px);
  overflow-y: auto;
  scrollbar-width: none;
  word-break: auto-phrase;
  line-break: strict;
}
/* 次のブロックがある時だけ出す送りマーク */
.event-text.has-next::after,
.event-reaction.has-next::after {
  content: "▼";
  position: absolute;
  right: 12px;
  bottom: 6px;
  font-size: 0.65rem;
  color: var(--accent);
  animation: pagerBlink 1.1s ease-in-out infinite;
}
/* 早送りボタンが出ている間だけ、送りマークをボタンの左へ寄せる。
   1周目(ボタンを出さない)は隅のままにする */
body.has-skip .event-text.has-next::after,
body.has-skip .event-reaction.has-next::after { right: 92px; }
@keyframes pagerBlink {
  0%, 100% { opacity: 0.25; }
  50% { opacity: 1; }
}

/* 枠の中の右下に浮かせる。送りマーク ▼ と同じ隅なので、
   「次へ進む」の場所が最後まで一定になる(両方が同時に出ることはない)。
   枠の外に置いているのでスクロールしても動かず、本文の書き換えでも消えない */
.reaction-actions {
  display: none;
  position: absolute;
  right: 10px;
  bottom: 9px;
  align-items: center;
  gap: 14px;
  z-index: 2;
}
.reaction-actions.is-shown { display: flex; }
.next-btn {
  padding: 8px 20px;
  font-size: 0.85rem;
  border-radius: 999px;
  border: none;
  background: var(--accent-strong);
  color: white;
  font-weight: bold;
  cursor: pointer;
}

/* ---------------- 自由行動 ---------------- */

.free-remaining {
  text-align: center;
  color: var(--accent);
  font-weight: bold;
  margin-bottom: 4px;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.9);
}
.free-hint {
  text-align: center;
  color: var(--text-dim);
  font-size: 0.85rem;
  margin-bottom: 20px;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.9);
}
.free-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
/* display を持たせているので、hidden 属性だけでは消えない。UA の [hidden]{display:none}
   よりこちらが強いため明示する(.reveal-btn / .skip-btn と同じ手当て) */
.free-list[hidden] { display: none; }
.free-list.is-locked { pointer-events: none; }
.free-card {
  animation: choiceIn 0.32s ease-out both;
  padding: 16px 18px;
  border-radius: 14px;
  border: 1px solid rgba(255,255,255,0.2);
  background: rgba(18, 12, 26, 0.7);
  color: var(--text);
  font-size: 1rem;
  cursor: pointer;
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.free-card-title { font-weight: bold; }
/* 一行の状況説明。何が起きる日かは分かるが、選択肢の中身までは明かさない */
.free-blurb {
  font-size: 0.82rem;
  color: var(--text-dim);
  line-height: 1.5;
}
.free-card:hover {
  border-color: var(--accent);
  background: rgba(255, 143, 177, 0.2);
}

/* ---------------- 自由行動: 学校の地図 ---------------- */

/* 一覧を地図に置き換えた(2026-09-12 本人案)。狙いは「どこで起きる話なのか」を
   絵で分からせること。一覧(.free-list)は残してあり、#btn-free-view で行き来できる。
   絵は画面の向きで2枚を使い分ける(横 bg_campus / 縦 bg_campus_portrait)。
   ピンの位置は絵ごとに違うので、座標は game.js の FREE_MAP が持つ */

/* flex: none —— 縦に入りきらない時、flex に縮められると下限(260px)を割って
   ピンが重なる。縮めずにスクロールさせる */
.free-map { margin: 0 auto 10px; width: 100%; text-align: center; flex: none; }
.free-map[hidden] { display: none; }
/* 絵と同じ比率の箱。ピンは % で置くので、箱と絵がずれると位置も全部ずれる。
   「幅は 100%、高さは画面の◯%まで」と両方から縛ると大きさが決まらなくなる
   (実際に横向きで 293px まで縮んだ)ので、**高さの上限から幅を逆算**して決める */
.free-map-inner {
  position: relative;
  display: block;
  margin: 0 auto;
}
/* 縦画面。絵は 2:3。見出し・案内・下のボタンのぶんを引いた残りが高さの上限 */
@media (orientation: portrait) {
  /* 260px は「ピン同士が指で押し分けられる」下限。これ以上は縮めない ——
     縮めるとピンが重なって選べなくなる(実測で 155px まで縮んで破綻した) */
  .free-map-inner { aspect-ratio: 2 / 3; width: min(100%, max(260px, calc(min(70vh, 620px) * 2 / 3))); }
}
/* 背の低い縦画面(iPhone SE など)。同じ割合だと下にはみ出してスクロールが出る */
@media (orientation: portrait) and (max-height: 700px) {
  .free-map-inner { width: min(100%, max(260px, calc(62vh * 2 / 3))); }
}
/* 背の低い画面では、地図を縮める代わりに周りの文字を削る。
   日付は HUD に出ているので、長い見出しは無くても迷わない */
@media (max-height: 640px) {
  #screen-free.is-map .event-title { display: none; }
  #screen-free.is-map .free-hint { font-size: 0.78rem; margin-bottom: 10px; }
  #screen-free.is-map .free-remaining { margin-bottom: 2px; }
}
/* 横画面。絵は 3:2 */
@media (orientation: landscape) {
  .free-map-inner { aspect-ratio: 3 / 2; width: min(100%, 720px); }
}
.free-map-img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 14px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
  /* 読み込みが済むまでは白い箱が出ないように、薄く沈めておく */
  background: rgba(18, 12, 26, 0.6);
  opacity: 0;
  transition: opacity 0.35s ease;
}
.free-map-img.is-ready { opacity: 1; }

/* ピンの層。ラベルが絵の端から少しはみ出すので、切り取らない */
.free-pins { position: absolute; inset: 0; }
.free-pins.is-locked { pointer-events: none; }

/* 出現アニメは位置合わせの translate を含んだ専用のものを使う。
   choiceIn(transform を translateY だけに書き換える)を当てると、
   fill-mode: both のせいで translate(-50%,-50%) が消え、**全ピンが半個ぶんずれる**。
   2026-09-12 に実際に踏んだ —— 座標の方をいじって辻褄を合わせないこと */
@keyframes pinIn {
  from { opacity: 0; transform: translate(-50%, calc(-50% + 6px)); }
  to   { opacity: 1; transform: translate(-50%, -50%); }
}
.free-pin {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  animation: pinIn 0.32s ease-out both;
}
/* 絵の下端に近い場所は、名前を丸の上に出す(下にはみ出さないように) */
.free-pin.is-up { flex-direction: column-reverse; }

.free-pin-dot {
  position: relative;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 0.8rem;
  font-weight: bold;
  color: #fff;
  background: linear-gradient(160deg, #ff8fb1, #c86bd8);
  border: 2px solid rgba(255, 255, 255, 0.95);
  /* 夕方の明るい地図の上に置くので、白い縁の外に暗い線を1本入れて輪郭を立てる。
     さらに常時ほんのり光らせる —— 脈だけだと、消えている間は地図に埋もれる */
  box-shadow:
    0 0 0 1px rgba(20, 10, 30, 0.45),
    0 3px 10px rgba(0, 0, 0, 0.6),
    0 0 14px 3px rgba(255, 143, 177, 0.65);
}
/* 外へ広がる輪。丸そのものではなく擬似要素を動かす —— 丸の transform を
   アニメで書き換えると、位置合わせや hover の拡大を消してしまう(pinIn の件と同じ罠) */
.free-pin:not(.is-done) .free-pin-dot::after {
  content: "";
  position: absolute;
  inset: -5px;
  border-radius: 50%;
  border: 2px solid rgba(255, 214, 233, 0.95);
  box-shadow: 0 0 14px 4px rgba(255, 143, 177, 0.8);
  animation: pinRing 2s ease-out infinite;
  pointer-events: none;
}
@keyframes pinRing {
  0%   { transform: scale(0.72); opacity: 0; }
  25%  { opacity: 1; }
  75%  { transform: scale(1.55); opacity: 0; }
  100% { transform: scale(1.55); opacity: 0; }
}
/* 動きを減らす設定では輪を止める。常時のにじみは残るので、押せることは伝わる */
@media (prefers-reduced-motion: reduce) {
  .free-pin { animation: none; }
  .free-pin:not(.is-done) .free-pin-dot::after { animation: none; opacity: 0.85; }
}
/* 明るい地図の上に置くので、字は必ず暗い下地に載せる */
.free-pin-label {
  font-size: 0.72rem;
  line-height: 1.4;
  white-space: nowrap;
  color: #fff;
  background: rgba(14, 9, 20, 0.82);
  border-radius: 999px;
  padding: 1px 8px;
}
.free-pin:hover .free-pin-dot,
.free-pin:focus-visible .free-pin-dot { transform: scale(1.12); }
.free-pin:focus-visible { outline: 3px solid var(--accent); outline-offset: 4px; border-radius: 8px; }

/* 選び終えた場所。消すのではなく残す —— 何を選んだか思い出せるように */
.free-pin.is-done { cursor: pointer; }   /* 押すと「もう行った」と分かる札が出る */
.free-pin.is-done .free-pin-dot {
  background: rgba(90, 84, 100, 0.9);
  border-color: rgba(255, 255, 255, 0.5);
  box-shadow: 0 0 0 1px rgba(20, 10, 30, 0.4), 0 2px 6px rgba(0, 0, 0, 0.45);
}
.free-pin.is-done .free-pin-label { opacity: 0.65; }

/* 一覧と地図の行き来 */
.free-view-row { text-align: center; margin: 6px 0 0; }

/* 横向き(高さ 560px 以下)。縦に積むと地図が 220px ほどにしかならず、どこに何があるか
   読めない。本文の画面と同じ考え方で、左に文字・右に地図の2段組みにする */
@media (orientation: landscape) and (max-height: 560px) {
  #screen-free.is-map {
    display: grid;
    /* auto にすると、絵の元の幅(1536px)ぶんの列を取ろうとして文字の列が潰れる。
       割合で決め打ちする */
    grid-template-columns: minmax(0, 1fr) minmax(0, 62%);
    grid-template-rows: auto auto auto auto;
    column-gap: 18px;
    align-content: center;
  }
  #screen-free.is-map .event-title,
  #screen-free.is-map .free-remaining,
  #screen-free.is-map .free-hint,
  #screen-free.is-map .free-view-row { grid-column: 1; margin-bottom: 6px; }
  #screen-free.is-map .free-map {
    grid-column: 2;
    grid-row: 1 / -1;
    align-self: center;
    margin: 0;
  }
  /* 2段組みの右側。高さいっぱいを使えるので、幅はそこから逆算する */
  #screen-free.is-map .free-map-inner { width: min(100%, calc(82vh * 3 / 2)); }
}

/* ピンを押した時の札。狭い画面では下から、広い画面では中央に出す */
.free-panel {
  position: fixed;
  inset: 0;
  z-index: 1300;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 16px;
  background: rgba(10, 6, 16, 0.62);
}
.free-panel[hidden] { display: none; }
.free-panel-box {
  width: min(420px, 100%);
  padding: 16px 18px 18px;
  border-radius: 16px;
  border: 1px solid rgba(255, 143, 177, 0.4);
  background: rgba(24, 16, 32, 0.97);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.55);
  animation: endingAppear 0.24s ease-out both;
}
@media (prefers-reduced-motion: reduce) { .free-panel-box { animation: none; } }
@media (min-height: 620px) { .free-panel { align-items: center; } }
.free-panel-place {
  margin: 0 0 2px;
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  color: var(--accent);
}
.free-panel-title {
  margin: 0 0 8px;
  font-size: 1.05rem;
  line-height: 1.5;
}
.free-panel-blurb {
  margin: 0 0 16px;
  font-size: 0.88rem;
  line-height: 1.7;
  color: var(--text-dim);
}
.free-panel-btns { display: flex; gap: 10px; justify-content: center; }

/* ---------------- エンディング ---------------- */

.ending-title {
  text-align: center;
  font-size: 1.4rem;
  color: var(--accent);
  margin: 40px 0 20px;
  line-height: 1.6;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.9);
}
.ending-new {
  display: none;
  align-self: center;
  margin: 24px 0 0;
  padding: 6px 16px;
  border-radius: 999px;
  background: rgba(255, 92, 138, 0.9);
  color: #fff;
  font-size: 0.8rem;
  font-weight: bold;
  letter-spacing: 0.03em;
}

/* 41歳の結末だけのおまけ。押すと表情が上がったり下がったりする。
   表情の差し替えは本編と同じ 180ms のクロスフェード(setSprite に任せている)。
   セリフとボタンをまとめて画面の右上(彼女の頭のあたり)に固定する ——
   セリフ・表情・押すものが一箇所に集まっていないと視線が往復する
   (2026-09-05 本人指示。スマートフォンでの見え方を基準にしている) */
.adult-poke {
  position: fixed;
  top: max(10px, env(safe-area-inset-top, 0px));
  right: 10px;
  z-index: 3;
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
}
.adult-poke[hidden] { display: none; }
/* 段ごとの一言。状態の説明ではなく彼女のセリフなので、本文と同じ明るさで出す。
   画面の右上(彼女の頭のあたり)に置く —— セリフと表情が同時に目に入る位置が
   いちばん読みやすい。ボタンの上に置くと、顔とセリフで視線が上下に往復する
   (2026-09-05 本人指示。スマートフォンでの見え方を基準にしている) */
.poke-hint {
  /* 折り返さない。折り返すと1〜2文字だけ次の行に落ちて読みにくい
     (2026-09-05 本人指摘)。長さは4種とも決まっているので、幅は中身に任せる */
  white-space: nowrap;
  max-width: calc(100vw - 20px);
  margin: 0;
  padding: 8px 13px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 14px;
  background: rgba(18, 12, 24, 0.74);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
  font-size: 0.92rem;
  line-height: 1.45;
  text-align: right;
  color: var(--text);
}
.poke-row {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
}
.poke-btn {
  min-width: 96px;
  padding: 9px 20px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  color: var(--text);
  font-size: 0.94rem;
  cursor: pointer;
}
.poke-tease {
  border-color: rgba(255, 122, 158, 0.5);
  background: rgba(255, 92, 138, 0.16);
}
.poke-btn:disabled { opacity: 0.3; cursor: default; }
.poke-btn:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
/* 結末の画面では連打で拡大させない(2026-09-11 本人指摘)。「呼ぶ」「謝る」「いじる」は
   連打される。iPhone の Safari は同じ所を素早く2回たたくと画面を拡大する(viewport の
   user-scalable=no は無視される)。touch-action: manipulation はダブルタップ拡大だけを止め、
   指2本での拡大は残す。300ms の待ちも無くなるので、連打がそのまま数えられる。
   ボタンを外れて周りをたたいても拡大しないよう、結末の間は画面全体に掛ける
   (touch-action は継承しないので * にも書く。SUPER HANAE と同じ手当て)。
   古い iOS 向けの保険は game.js(.adult-poke の touchend) */
body.is-ending,
body.is-ending * {
  touch-action: manipulation;
}
/* Perfect の「呼ぶ」。一言は押すまで見せない —— 最初から出ていると、
   呼ぶ前に返事をしていることになる。ただし場所は取っておく(詰めると、最初に押した瞬間に
   「呼ぶ」が一言のぶん下へずれて、連打の2回目以降が吹き出しに当たって空振りする。2026-09-11 実測) */
.poke-hint.is-blank { visibility: hidden; }
.poke-call {
  border-color: rgba(255, 170, 200, 0.5);
  background: rgba(255, 140, 180, 0.14);
}
/* 一言が変わるたびに軽く浮かせる。同じ長さの一言が続くと、変わったことに気づきにくい */
.poke-hint.is-pop { animation: pokePop 0.28s ease-out; }
@keyframes pokePop {
  from { opacity: 0.2; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .poke-hint.is-pop { animation: none; }
}

/* 読み終わりに増える要素。本文の枠が滑らかに上がっていく間に、
   こちらは浮かび上がる。両方を同時に動かすと「増えた」ではなく
   「画面が組み変わった」ように見える(2026-09-06 本人指摘) */
.ending-appear {
  /* 枠の移動(game.js の ENDING_SLIDE_MS)と同じ長さにする。
     片方だけ速いと、増えた要素が先に決まって枠だけ遅れて見える */
  animation: endingAppear 0.8s cubic-bezier(0.22, 0.8, 0.28, 1) both;
}
@keyframes endingAppear {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

/* 結末の後に出す「今回の距離」。本編中は好感度を伏せたまま、
   終わってから7段階だけ見せる。数値は出さない */
.ending-hearts {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 2px;
  margin: 0 0 10px;
  font-size: 0.95rem;
  line-height: 1.4;
}
.rh-label {
  margin-right: 8px;
  font-size: 0.76rem;
  letter-spacing: 0.08em;
  color: var(--text-dim);
}
.rh {
  opacity: 0.18;
  filter: grayscale(1);
}
.rh.is-on {
  opacity: 1;
  filter: none;
  animation: rhPop 0.45s ease-out both;
}
@keyframes rhPop {
  from { opacity: 0; transform: scale(0.6); }
  to   { opacity: 1; transform: scale(1); }
}

/* ---------------- エンディング図鑑(タイトル画面) ---------------- */

.ending-gallery {
  margin: 14px auto 0;
  width: 100%;
  max-width: 420px;
}
.gallery-head {
  text-align: center;
  color: var(--accent);
  font-size: 0.85rem;
  font-weight: bold;
  margin: 0 0 6px;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.9);
}
/* 上から下へ「良い結末 → 悪い結末」の梯子。2列だと順位に見えないので1列にする。
   縦に伸びるぶん、1マスの余白と字を詰めて高さを抑える */
.gallery-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 4px;
  /* 中身は「名前(最長84px)＋間8px＋ハート80px＋左右の余白20px」で 192px。
     画面幅いっぱいに広げると中身に対して横長に見える(2026-09-06 本人指摘)ので、
     梯子として読める幅に絞って中央に置く */
  max-width: 216px;
  margin: 0 auto;
}
.gallery-cell {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 5px 10px;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(18, 12, 26, 0.6);
  color: var(--text-dim);
  font-size: 0.75rem;
  text-align: center;
  letter-spacing: 0.02em;
}
.gallery-cell.is-got {
  color: var(--text);
  border-color: rgba(255, 143, 177, 0.55);
  background: rgba(255, 143, 177, 0.14);
}
/* 到達済みのマスは押すと読み返せる。ボタンにしても見た目は変えず、
   押した時の沈み込みだけで「押せる」ことを伝える */
button.gallery-cell {
  font: inherit;
  font-size: 0.75rem;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  transition: transform 0.12s ease, background-color 0.12s ease;
}
button.gallery-cell:hover { background: rgba(255, 143, 177, 0.24); }
button.gallery-cell:active { transform: scale(0.97); }
/* 図鑑のハート。結末の後に出すものと同じ形にして、同じ物差しだと分かるようにする。
   本文より小さく、点いていないぶんは沈める */
.gallery-hearts {
  flex: none;
  font-size: 0.62rem;
  letter-spacing: -0.04em;
  line-height: 1;
}
.gh {
  opacity: 0.16;
  filter: grayscale(1);
}
.gh.is-on {
  opacity: 1;
  filter: none;
}
.gallery-name {
  flex: 1 1 auto;
  text-align: right;
}
/* 未到達のヒント(浦川)。到達済みの名前より弱く出して、
   「まだ見ていない」ことが見た目で分かるようにする */
.gallery-name.is-hint {
  opacity: 0.5;
  font-size: 0.86em;
  font-style: italic;
}
.gallery-cell .gallery-hearts { text-align: left; }

.gallery-done {
  text-align: center;
  color: var(--gold);
  font-size: 0.8rem;
  margin: 12px 0 0;
}
/* 見出しの行に並べる版(2026-09-11)。独立した行だと画面が伸びるため */
.gallery-head .gallery-done { margin: 0 0 0 8px; }
/* 図鑑から結末を読み返す時の確認。画用紙と同じ作りの覆い */
.confirm-overlay {
  position: fixed;
  inset: 0;
  z-index: 1300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(10, 6, 16, 0.86);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
.confirm-overlay[hidden] { display: none; }
.confirm-box {
  width: min(320px, 86vw);
  padding: 18px 16px 16px;
  border-radius: 16px;
  border: 1px solid rgba(255, 143, 177, 0.4);
  background: rgba(24, 16, 32, 0.96);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.55);
  text-align: center;
  animation: endingAppear 0.24s ease-out both;
}
.confirm-text {
  margin: 0 0 14px;
  font-size: 0.9rem;
  line-height: 1.7;
  color: var(--text);
}
.confirm-name {
  display: block;
  margin-bottom: 4px;
  color: var(--accent);
  font-weight: bold;
}
.confirm-btns {
  display: flex;
  gap: 10px;
  justify-content: center;
}
.confirm-btn {
  min-width: 96px;
  padding: 10px 16px;
  border-radius: 999px;
  border: none;
  font-size: 0.88rem;
  font-weight: bold;
  cursor: pointer;
  transition: transform 0.15s ease;
}
.confirm-btn:active { transform: scale(0.97); }
.confirm-btn.is-yes {
  background: linear-gradient(135deg, var(--accent-strong), var(--purple));
  color: white;
  box-shadow: 0 6px 16px rgba(255, 92, 138, 0.32);
}
.confirm-btn.is-no {
  background: rgba(10, 6, 16, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.35);
  color: var(--text-dim);
}

/* キーボード操作時のフォーカス表示。マウス操作では出さない */
.choice-btn:focus-visible,
.free-card:focus-visible,
.main-btn:focus-visible,
.next-btn:focus-visible,
.link-btn:focus-visible,
#player-name-input:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

/* ---------------- 拡大している間(body.is-zoomed) ----------------

   iPhone で拡大するとメモリが足りずにページが落ちる/カクつくため、
   拡大中だけ「拡大倍率で作り直しになるもの」を切る(game.js の initZoomGuard)。
   見た目は変わるが、指を離せば元に戻る。**新しい演出を足す時は、ここにも足すこと** */

/* 立ち絵とロゴの影。大きな絵の輪郭に沿った影は、拡大時に一番重い */
body.is-zoomed .sprite,
body.is-zoomed .game-logo { filter: none !important; }

/* 後ろの絵をぼかして持ち直す枠。本文枠は遊んでいる間ずっと出ている */
body.is-zoomed .textbox,
body.is-zoomed .poke-hint,
body.is-zoomed .sound-panel,
body.is-zoomed .confirm-overlay,
body.is-zoomed .log-overlay,
body.is-zoomed .draw-overlay {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

/* 動き続ける層。止めるだけでは拡大後の絵を持ち続けるので、置くのをやめる */
body.is-zoomed .rain-layer,
body.is-zoomed .ember-layer,
body.is-zoomed .petal { display: none !important; }

/* 小さく光り続けるもの。止めるだけでよい(層としては軽い) */
body.is-zoomed .free-pin-dot::after,
body.is-zoomed .reveal-btn,
body.is-zoomed .tap-hint,
body.is-zoomed .sprite { animation-play-state: paused !important; }

/* 出現アニメ(fill-mode: both)を「止める」と、拡大した瞬間に出てきたものが
   透明のまま固まってしまう。こちらは止めずに無くす —— 拡大中は演出なしで即座に出る */
body.is-zoomed .choice-btn,
body.is-zoomed .free-card,
body.is-zoomed .free-pin {
  animation: none !important;
  opacity: 1 !important;
}
/* .free-pin は位置合わせを transform に頼っているので、アニメを外しても消えないようにする */
body.is-zoomed .free-pin { transform: translate(-50%, -50%) !important; }

/* ---------------- ハート演出 ---------------- */

#heart-layer {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 999;
}
.heart {
  position: absolute;
  top: 55%;
  font-size: 1.6rem;
  opacity: 0;
}
/* 大きな上がり幅の時、顔の周りにひと呼吸の光 */
.heart-glow {
  position: absolute;
  width: 160px;
  height: 160px;
  margin: -80px 0 0 -80px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 170, 200, 0.55) 0%, rgba(255, 143, 177, 0.18) 45%, rgba(255, 143, 177, 0) 70%);
  animation: heartGlow 0.8s ease-out forwards;
  pointer-events: none;
}
@keyframes heartGlow {
  0% { opacity: 0; transform: scale(0.4); }
  30% { opacity: 1; }
  100% { opacity: 0; transform: scale(1.6); }
}
.heart-small { animation: floatSmall 1.2s ease-out forwards; }
.heart-big { font-size: 2.1rem; animation: floatBig 1.3s ease-out forwards; }
.heart-huge { font-size: 2.8rem; animation: floatHuge 1.4s ease-out forwards; }
.heart-shrink { font-size: 1.1rem; opacity: 0.6; animation: shrink 1s ease-out forwards; }
.heart-break { font-size: 1.8rem; animation: breakFall 1.2s ease-in forwards; }

@keyframes floatSmall {
  0% { opacity: 0; transform: translateY(0) scale(0.8); }
  30% { opacity: 1; }
  100% { opacity: 0; transform: translateY(-60px) scale(1); }
}
@keyframes floatBig {
  0% { opacity: 0; transform: translateY(0) scale(0.6); }
  25% { opacity: 1; }
  100% { opacity: 0; transform: translateY(-90px) scale(1.2); }
}
@keyframes floatHuge {
  0% { opacity: 0; transform: translateY(0) scale(0.5) rotate(-8deg); }
  20% { opacity: 1; }
  100% { opacity: 0; transform: translateY(-130px) scale(1.5) rotate(8deg); }
}
@keyframes shrink {
  0% { opacity: 0.7; transform: translateY(0) scale(1); }
  100% { opacity: 0; transform: translateY(-20px) scale(0.5); }
}
@keyframes breakFall {
  0% { opacity: 0.9; transform: translateY(0) rotate(0deg); }
  100% { opacity: 0; transform: translateY(40px) rotate(25deg); }
}

@media (prefers-reduced-motion: reduce) {
  .tap-hint { animation: none; opacity: 0.8; }
  .event-text.has-next::after,
  .event-reaction.has-next::after { animation: none; opacity: 0.9; }
  .bg-layer { transition: none; }
  .sprite.sprite-in { animation: none; }
  #sprite-b { transition: none; }
  .choice-btn { animation: none; }
  .event-choices.is-key .choice-btn {
    animation: none;
    border-color: rgba(255, 226, 238, 0.9);
    box-shadow: 0 0 5px rgba(255, 190, 214, 0.35), inset 0 0 16px rgba(255, 143, 177, 0.24);
  }
  .event-choices.is-decided .choice-btn { transition: none; }
  body.is-title #sprite, body.is-title #sprite.sprite-in { animation: none; }
  .game-logo { transition: none; opacity: 1; transform: none; }
  .telop { transition: none; }
  .fade { transition: none; }
  /* 花びらは動きが本体なので、動きを止めるくらいなら出さない。風も止める */
  .petals { animation: none; }
  .petal { display: none; }
  .rain-wind, .rain-haze, .rain-near i, .rain-mid i, .rain-far i { animation: none; }
  .ending-appear { animation: none; }
  .ember, .ember > i, .ember > i > b { animation: none; }
  body.is-demon .ember-layer { animation: none; }
  .sprite { transition: none; }
  .heart-glow { display: none; }
  /* 本作は好感度を非公開にしており、ハートが唯一のフィードバック。
     消すと選択の良し悪しを知る手段が無くなるので、動きだけ止めて静止表示する */
  .rh.is-on { animation: none; }
  .heart {
    animation: none;
    opacity: 1;
    top: 42%;
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.7));
  }
}

@media (max-width: 420px) {
  .game-title { margin-top: 20px; }
  /* padding の一括指定にすると、上で入れた padding-top: 100px を打ち消して
     立ち絵の円形アイコンが HUD に重なる。左右下だけを上書きする */
  #app { padding-left: 14px; padding-right: 14px; }
  .event-text { padding: 16px; }
}

/* ---------------- バックログ(これまでの話) ---------------- */

/* #app の外に置いてある。中に入れると、本文の早送りタップを拾ってしまう */
.log-overlay {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(12, 8, 18, 0.78);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  padding: 20px 16px;
}
.log-overlay.is-open { display: flex; }
/* 画用紙(z-index 1200)より前に出す。描いている途中で直前の会話を読み返せる */
body.is-drawing .log-overlay { z-index: 1300; }

.log-panel {
  margin: auto;
  width: 100%;
  max-width: 640px;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 14px;
  overflow: hidden;
}
.log-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 18px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  flex-shrink: 0;
}
.log-heading {
  margin: 0;
  font-size: 0.95rem;
  letter-spacing: 0.06em;
  color: var(--accent);
}
.log-body {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 4px 18px 18px;
}
.log-item {
  padding: 16px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.log-item:last-child { border-bottom: none; }
.log-title {
  margin: 0 0 8px;
  font-size: 0.82rem;
  letter-spacing: 0.06em;
  color: var(--text-dim);
}
/* 本文は textContent で入れているので、改行はここで折る */
.log-text,
.log-react {
  margin: 0;
  white-space: pre-wrap;
  line-height: 1.8;
  font-size: 0.9rem;
  word-break: auto-phrase;
}
.log-choice {
  margin: 12px 0 4px;
  font-size: 0.88rem;
  color: var(--accent);
}
.log-react {
  color: var(--text-dim);
}
.log-empty {
  margin: 0;
  padding: 24px 0;
  text-align: center;
  color: var(--text-dim);
  font-size: 0.88rem;
}

/* ==========================================================================
   狭い画面の本文まわり
   基本ルールより後ろに置くこと。同じ詳細度なので、前に置くと負けて効かない
   ========================================================================== */

@media (max-width: 999px) {
  /* パネルは4行ぶんで固定。1ブロックは最長52字で、390px 幅なら4行に収まる。
     画面が低い機種では 22dvh で頭打ちにする(高さは固定のまま) */
  .event-text { height: min(calc(1.95em * 4 + 40px), 22dvh); }
  .textbox { margin-bottom: 14px; }
  .event-reaction {
    height: min(calc(1.85em * 4 + 36px), 22dvh);
    padding: 14px 16px;
  }
  .reaction-wrap { margin-top: 14px; }
  /* 顔の領域を守るため、下に置くものは詰める */
  .event-title { margin: 0 0 12px; font-size: 1rem; }
  .choice-btn { padding: 12px 14px; }
  /* エンディングは要素が多い(バッジ・見出し・本文・ボタン・脚注)。
     既定の余白のままだと 60dvh に収まらず、はみ出したぶんが顔まで伸びる */
  .ending-new { margin-top: 0; }
  .ending-title { margin: 14px 0 14px; font-size: 1.05rem; }
  .credits { margin-top: 12px; }
}

/* 画面が低い機種はさらに詰める。選択肢4つ＋パネルが収まる境目がこのあたり */
@media (max-width: 999px) and (max-height: 800px) {
  .event-text { font-size: 0.9rem; line-height: 1.75; padding: 14px 16px; }
  /* 反応は本文より短い。低い機種では3行ぶんに詰めて、本文と2枚並んでも収める */
  .event-reaction {
    font-size: 0.88rem;
    line-height: 1.7;
    padding: 12px 14px;
    height: min(calc(1.85em * 3 + 30px), 20dvh);
  }
  .choice-btn { padding: 10px 14px; font-size: 0.88rem; }
  .event-title { font-size: 0.95rem; margin-bottom: 10px; }
  .event-choices { gap: 8px; }
  /* エンディングは脚注とボタンのぶんだけ背が高い。低い機種では 60dvh に入りきらず、
     入りきらないぶんが上に伸びて顔に掛かるので、ここでさらに詰める */
  .ending-new { font-size: 0.74rem; padding: 5px 12px; }
  .ending-title { margin: 10px 0; font-size: 0.98rem; }
  #btn-restart { padding: 11px 32px; font-size: 0.95rem; margin-bottom: 10px; }
  .credits { margin-top: 8px; }
}

/* 320px 級の狭い画面は1行に入る字数が減り、同じブロックでも行数が増える。
   枠の高さは 22dvh で頭打ちなので、行間と余白を詰めて4行を収める
   (実測: 320x568 で本文3件が 6px はみ出していた) */
@media (max-width: 360px) {
  .event-text { line-height: 1.7; padding: 12px 14px; }
  .event-reaction { line-height: 1.6; padding: 10px 12px; }
  /* 枠が2〜3行しか無い画面では、本文の最終行がボタンの高さまで届く。
     当たる面積を減らすためにボタンを小さくする */
  .next-btn { padding: 6px 14px; font-size: 0.78rem; }
  .reaction-actions { right: 8px; bottom: 7px; }
}

/* 横向きの携帯は高さが足りない。立ち絵を画面の中央に置くと、どう詰めても
   本文の枠があごに掛かる(実測: 844x390 で -4px)。横向きの時だけ
   「左に文章・右に立ち絵」の横並びにする —— 幅は余っているので、
   縦に積むのをやめれば顔の上下に余白が要らなくなる。
   広い画面(1000px 以上)の ADV 配置とは別物(あちらは本文を画面下に敷く) */
@media (max-width: 999px) and (max-height: 560px) {
  #app {
    max-width: none;
    margin: 0;
    padding: 8px 16px calc(8px + env(safe-area-inset-bottom, 0px));
  }
  /* 立ち絵は右端へ。縦に詰める必要が無くなるので、縦向きより大きく見せられる */
  .sprite {
    left: auto;
    right: max(8px, 3vw);
    margin: 0;
    height: min(94vh, 620px);
    height: min(94dvh, 620px);
    -webkit-mask-image: none;
    mask-image: none;
  }
  body.is-ending .sprite { top: 52px; }
  /* 文章は左 62% に収める。右は立ち絵のために空ける */
  #screen-event,
  #screen-confession,
  #screen-ending,
  #screen-free {
    max-height: none;
    width: min(62vw, 560px);
    /* 縦の制限が無くなったのでスクロール枠にしない。auto のままだと、
       枠の上に 13px はみ出す名札が縁で刈られる */
    overflow: visible;
    padding-top: 14px;
  }
  /* 横に並べたので縦は余る。反応を出す時に本文を畳む必要はない */
  #screen-event.is-reacting .textbox:not(.reaction-wrap) { display: block; }
  /* 横向きは本文の列が 62vw しかない。字を少し小さくして、3行の枠に
     最長ブロック(58字)が収まるようにする(667x375 で 6px はみ出していた) */
  .event-text {
    font-size: 0.85rem;
    line-height: 1.75;
    height: min(calc(1.75em * 3 + 28px), 26dvh);
  }
  .event-reaction {
    font-size: 0.84rem;
    line-height: 1.7;
    height: min(calc(1.7em * 3 + 26px), 24dvh);
  }
  .textbox { margin-bottom: 10px; }
  .reaction-wrap { margin-top: 10px; }
  .event-choices { gap: 6px; }
  .choice-btn { padding: 8px 12px; font-size: 0.85rem; }
  .next-btn { padding: 6px 14px; font-size: 0.78rem; }
  .reaction-actions { right: 8px; bottom: 7px; }
  .free-card { padding: 10px 14px; }
  .free-hint { margin-bottom: 10px; }
  .event-title { margin-bottom: 8px; }
}

/* スクロールバーを幅ごと消す(WebKit)。出入りのたびに枠が左右に動くのを防ぐ */
.event-text::-webkit-scrollbar,
.event-reaction::-webkit-scrollbar,
#screen-event::-webkit-scrollbar,
#screen-confession::-webkit-scrollbar,
#screen-ending::-webkit-scrollbar {
  width: 0;
  height: 0;
  display: none;
}

/* 場面の区切り(本文の「＿＿＿」)。中央に短い罫線 */
.text-rule {
  display: block;
  width: 120px;
  height: 1px;
  margin: 1.2em auto 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.55), transparent);
}

/* ---------------- 読み上げ専用 ---------------- */

.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;
}

/* ---------------- 音の設定 ---------------- */

.sound-panel {
  position: fixed;
  top: 52px;
  right: 16px;
  z-index: 1001;
  width: min(260px, calc(100vw - 32px));
  padding: 12px 16px 14px;
  border-radius: 14px;
  background: var(--panel);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
}
.sound-panel[hidden] { display: none; }
.sound-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 6px;
}
.sound-heading {
  font-size: 0.85rem;
  font-weight: bold;
  color: var(--accent);
  letter-spacing: 0.06em;
}
.sound-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 8px 0;
  font-size: 0.82rem;
  color: var(--text-dim);
  cursor: pointer;
}
.sound-label { width: 3.2em; flex-shrink: 0; }
.sound-row input[type="range"] {
  flex: 1;
  min-width: 0;
  accent-color: var(--accent-strong);
  cursor: pointer;
}
.sound-row output {
  width: 2.4em;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.sound-mute {
  display: block;
  width: 100%;
  margin-top: 8px;
  padding: 8px 12px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: rgba(10, 6, 16, 0.55);
  color: var(--text);
  font-size: 0.82rem;
  cursor: pointer;
}
.sound-mute:hover { border-color: var(--accent); color: var(--accent); }
.sound-mute:focus-visible,
.sound-row input:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
/* ネタバレ表示のスイッチ(2周遊んだ人だけ。2026-09-11 本人指示)。
   .sound-mute は display:block を持つので、hidden 属性だけでは消えない。明示する
   (.reveal-btn / .skip-btn と同じ手当て) */
.reveal-toggle[hidden] { display: none; }
.reveal-toggle[aria-pressed="true"] {
  border-color: rgba(255, 214, 110, 0.7);
  background: rgba(255, 196, 64, 0.16);
  color: var(--gold);
}
/* スイッチの下に添える「何が出るのか」の一行(2026-09-12)。
   スイッチと同じく hidden 属性で出し入れするので、display を明示する */
.reveal-note {
  margin: 5px 2px 0;
  font-size: 0.72rem;
  line-height: 1.4;
  color: var(--text-dim);
}
.reveal-note[hidden] { display: none; }

/* ==========================================================================
   広い画面(1000px 以上): 本文窓を画面下に敷く ADV の定石配置
   立ち絵は右、選択肢は窓の上の左側、HUD は画面上端。
   同じ詳細度の指定に勝たせるため、必ずファイルの末尾に置く
   ========================================================================== */

@media (min-width: 1000px) {
  #app { max-width: none; margin: 0; padding: 0; }

  /* タイトルと自由行動だけ従来の 640px カラム。タイトルは立ち絵がある時だけ左に寄せる */
  #screen-title,
  #screen-free {
    width: 100%;
    max-width: 640px;
    margin: 0 auto;
    padding: 24px 20px 48px;
  }
  /* 自由行動は HUD(固定)の下から */
  #screen-free { padding-top: 72px; }
  #app.col-left #screen-title { margin-left: max(24px, calc(50vw - 660px)); margin-right: 0; }

  /* 本編・プロローグ・告白・エンディング: 画面下端に寄せた1カラム */
  #screen-prologue,
  #screen-event,
  #screen-confession,
  #screen-ending {
    justify-content: flex-end;
    width: min(940px, 92vw);
    margin: 0 auto;
    padding: 72px 0 32px;
  }
  .textbox { max-width: none; border-radius: 16px; margin-bottom: 0; }
  .prologue-box { margin: auto 0 0; }
  .event-text,
  .event-reaction {
    padding: 22px 30px 24px;
    font-size: 1.04rem;
    line-height: 2;
    height: calc(2em * 3 + 46px);
  }
  .prologue-text { font-size: 1.08rem; line-height: 2.1; }
  .textbox[data-speaker]::before { left: 18px; font-size: 0.82rem; padding: 4px 16px; top: -15px; }

  /* 選択肢は窓の上・左側に積む。右は立ち絵のために空ける */
  #screen-event .textbox:not(.reaction-wrap) { order: 2; }
  /* 右 400px は立ち絵のために空ける(1024px 幅でも肘に掛からない) */
  .event-choices { order: 1; width: min(560px, calc(100% - 400px)); margin-bottom: 16px; }
  .reaction-wrap { order: 3; margin-top: 0; }
  /* 反応中は本文の窓を反応の窓に置き換える(同じ場所・同じ大きさ) */
  #screen-event.is-reacting .textbox:not(.reaction-wrap) { display: none; }
  .reaction-actions { right: 16px; bottom: 12px; }
  .next-btn { padding: 9px 22px; font-size: 0.9rem; }
  .choice-btn { padding: 14px 18px; font-size: 0.98rem; }
  /* 見出しの下に名札(枠の上 15px)が来るので、重ならないぶん空ける */
  .event-title { margin: 0 0 24px; }

  /* HUD は画面上端に固定 */
  .hud {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 3;
    margin: 0;
    padding: 12px 28px 0;
  }
  /* 基本(スマホ)は 1.3rem。PC は少しだけ大きく揃える(2026-09-09) */
  .hud-progress { font-size: 1.35rem; }
  .hud-bar { max-width: 420px; }

  /* 告白・エンディング */
  #btn-confess,
  #btn-restart { margin: 18px auto 0; }
  .ending-new { align-self: flex-start; margin: 0 0 10px; }
  .ending-title { text-align: left; margin: 0 0 24px; font-size: 1.3rem; }
  #ending-foot { margin-top: 10px; }
}

/* ---------------- 色調・雨 ---------------- */

/* 場面ごとの薄い色(夕方・雨・嵐)。背景の上、暗幕の下 */
#bg-tint {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.9s ease;
}
#bg-tint.is-on { opacity: 1; }

/* 雨。実際の雨の見え方に寄せてある。観察したのは7点:
   1. 見えているのは粒ではなく軌跡。終端速度(小粒 2m/s〜大粒 9m/s)で落ちる粒の
      残像が短い線になる。大きい粒ほど速く、線は長い
   2. 線はほぼ透明。水は無色で、見えるのは背景の光を屈折・反射したぶんだけ。
      暗い背景では明るく見えるので mix-blend-mode: screen で乗せる。
      不透明な白い線を流すと画面の引っかき傷に見える
   3. 傾きが一定ではない。小さい粒ほど終端速度が遅く、風に流されて傾きが大きい
      → 奥の層ほど寝かせ、列ごとにも 1〜2度ばらつかせている
   4. 遠近がある。手前は太く長く速くまばら、奥は細く短く遅く密。
      いちばん奥は線が分離せず「かすみ」になるので、線ではなく veil で置いている
   5. 落下は等速(すでに終端速度)。加速はさせない
   6. 降り方が一様ではない → 全体を数秒周期で横に振り、濃さも脈打たせる
   7. 空気中の水滴で遠景のコントラストが落ちる(空気遠近)→ 薄い veil を重ねる

   1本の帯が「間隔をあけた短い筋の列」になっている。帯ごと傾けて、その傾きの
   方向へ動かすので、筋の向きと動く向きが必ず一致する(別々に指定するとズレる) */
.rain-layer {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  opacity: 0;
  transition: opacity 0.9s ease;
  /* 背景の光を拾って見えるものなので、塗るのではなく足す(2) */
  mix-blend-mode: screen;
}
body.is-rain .rain-layer { opacity: 1; }
/* 雨の層は、降っている間と止んでいくフェード(0.9秒)の間だけ置く(2026-09-12)。
   以前は透明にしただけで置きっぱなしで、28本の筋・風・もやのアニメーションが
   雨の無い場面でもずっと回っていた(Perfect の結末で動いている 40 個のうち 30 個が見えない雨)。
   iPhone でピンチ拡大すると、この層と上に重なる立ち絵・枠まで高い倍率で描き直すことになり、
   メモリが足りずにページが落ちていた(2026-09-12 本人報告)。火の粉と同じく display で切る。
   付け外しは game.js の setWeather */
.rain-layer { display: none; }
.rain-layer.is-active { display: block; }

/* 突風。場全体にかかるので、容器ごと横に振って濃さも脈打たせる(6) */
.rain-wind {
  position: absolute;
  inset: -24% -14%;
  animation: rainGust 11.3s ease-in-out infinite;
}
@keyframes rainGust {
  0%, 100% { transform: translateX(0); opacity: 0.92; }
  27% { transform: translateX(9px); opacity: 1; }
  52% { transform: translateX(-6px); opacity: 0.8; }
  78% { transform: translateX(4px); opacity: 0.95; }
}

.rain-near i,
.rain-mid i,
.rain-far i {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--x);
  width: var(--w);
  /* 筋の形。上へ向かって消えていき、下端(粒そのもの)がいちばん明るい。
      落ちている粒の後ろに軌跡が伸びるので、明るい端は必ず下に来る(1) */
  background: repeating-linear-gradient(
    180deg,
    rgba(255, 255, 255, 0) 0,
    rgba(255, 255, 255, var(--a)) var(--len),
    rgba(255, 255, 255, 0) calc(var(--len) + 2px),
    rgba(255, 255, 255, 0) var(--period)
  );
  animation: rainFall var(--sp) linear var(--d, 0s) infinite;
}
/* ちょうど1周期ぶん動かすと、繋ぎ目が出ないまま延々と落ち続ける */
@keyframes rainFall {
  from { transform: rotate(var(--tilt)) translateY(0); }
  to   { transform: rotate(var(--tilt)) translateY(var(--loop)); }
}

/* 手前。大粒なので太く長く速く、風にはあまり流されない(3)(4) */
.rain-near i {
  --w: 2px;
  --len: 40px;
  --period: 170px;
  --loop: 850px;
  --sp: 0.66s;
  --a: 0.3;
}
/* 中景 */
.rain-mid i {
  --w: 1.4px;
  --len: 24px;
  --period: 130px;
  --loop: 780px;
  --sp: 0.95s;
  --a: 0.17;
}
/* 奥。細く短く遅く、密。1本ずつは読み取れず、全体で降っている感じだけが残る */
.rain-far i {
  --w: 1px;
  --len: 13px;
  --period: 74px;
  --loop: 592px;
  --sp: 1.45s;
  --a: 0.085;
}

/* 列ごとに位置・位相・傾きを変える。割り切れない値にして、同じ降り方の
   繰り返しに見えないようにしている(3) */
.rain-near i:nth-child(1) { --x: 7%; --d: 0s; --tilt: 5.4deg; --len: 46px; }
.rain-near i:nth-child(2) { --x: 23%; --d: -0.21s; --tilt: 6.8deg; --len: 34px; }
.rain-near i:nth-child(3) { --x: 38%; --d: -0.44s; --tilt: 6.1deg; --len: 41px; }
.rain-near i:nth-child(4) { --x: 55%; --d: -0.13s; --tilt: 7.3deg; --len: 30px; }
.rain-near i:nth-child(5) { --x: 71%; --d: -0.36s; --tilt: 5.9deg; --len: 44px; }
.rain-near i:nth-child(6) { --x: 89%; --d: -0.29s; --tilt: 6.5deg; --len: 37px; }
.rain-mid i:nth-child(1) { --x: 3%; --d: -0.05s; --tilt: 8.6deg; --len: 27px; }
.rain-mid i:nth-child(2) { --x: 12%; --d: -0.41s; --tilt: 9.4deg; --len: 21px; }
.rain-mid i:nth-child(3) { --x: 19%; --d: -0.62s; --tilt: 9.9deg; --len: 25px; }
.rain-mid i:nth-child(4) { --x: 27%; --d: -0.24s; --tilt: 8.2deg; --len: 30px; }
.rain-mid i:nth-child(5) { --x: 34%; --d: -0.79s; --tilt: 10.3deg; --len: 19px; }
.rain-mid i:nth-child(6) { --x: 44%; --d: -0.33s; --tilt: 9.1deg; --len: 26px; }
.rain-mid i:nth-child(7) { --x: 53%; --d: -0.55s; --tilt: 8.8deg; --len: 23px; }
.rain-mid i:nth-child(8) { --x: 63%; --d: -0.17s; --tilt: 10.0deg; --len: 28px; }
.rain-mid i:nth-child(9) { --x: 72%; --d: -0.71s; --tilt: 9.6deg; --len: 20px; }
.rain-mid i:nth-child(10) { --x: 82%; --d: -0.47s; --tilt: 8.4deg; --len: 24px; }
.rain-far i:nth-child(1) { --x: 1%; --d: -0.1s; --tilt: 12.2deg; --len: 15px; }
.rain-far i:nth-child(2) { --x: 9%; --d: -0.9s; --tilt: 13.1deg; --len: 11px; }
.rain-far i:nth-child(3) { --x: 16%; --d: -0.4s; --tilt: 11.8deg; --len: 14px; }
.rain-far i:nth-child(4) { --x: 24%; --d: -1.3s; --tilt: 12.7deg; --len: 10px; }
.rain-far i:nth-child(5) { --x: 31%; --d: -0.6s; --tilt: 13.4deg; --len: 16px; }
.rain-far i:nth-child(6) { --x: 39%; --d: -1.6s; --tilt: 12.0deg; --len: 12px; }
.rain-far i:nth-child(7) { --x: 47%; --d: -0.3s; --tilt: 12.9deg; --len: 13px; }
.rain-far i:nth-child(8) { --x: 56%; --d: -1.1s; --tilt: 11.6deg; --len: 15px; }
.rain-far i:nth-child(9) { --x: 64%; --d: -0.8s; --tilt: 13.2deg; --len: 11px; }
.rain-far i:nth-child(10) { --x: 73%; --d: -1.5s; --tilt: 12.4deg; --len: 14px; }
.rain-far i:nth-child(11) { --x: 81%; --d: -0.2s; --tilt: 12.8deg; --len: 12px; }
.rain-far i:nth-child(12) { --x: 92%; --d: -1.0s; --tilt: 11.9deg; --len: 16px; }

/* いちばん奥は線が分離せず、かすみの面になる(4)。あわせて空気遠近で
   遠景のコントラストが落ちるぶんの白み(7)。上ほど濃い */
.rain-haze {
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgba(198, 214, 236, 0.085), rgba(198, 214, 236, 0.03) 62%, rgba(198, 214, 236, 0.015));
  animation: rainHaze 8.7s ease-in-out infinite;
}
@keyframes rainHaze {
  0%, 100% { opacity: 0.85; }
  40% { opacity: 1; }
  70% { opacity: 0.78; }
}

/* ---------------- 火の粉(悪魔化した41歳の結末だけ) ---------------- */

/* 実際に舞い上がる燃え差しの見え方に寄せてある。観察したのは7点:
   1. 熱の対流に乗って上がるので、重力で減速せず、ほぼ一定の速さで上がり続ける
   2. まっすぐ上がらず乱流で蛇行する。しかも上に行くほど振れ幅が大きくなるので、
      全体として下が細く上が広い円錐になる → 揺れの keyframe で振幅を育てている
   3. 上がりながら冷える。下は黄白、上へ行くほど 橙 → 赤 → 暗くなって消える
   4. 大きさがばらばら。小さい粒ほど速く高く上がり、早く消える
   5. 回転しながら燃えるので、燃えている面の向きが変わってチカチカする
   6. 数が一定ではない(火が爆ぜると増える)→ 容器ごと濃さを脈打たせる
   7. 周囲より明るいので加算的に光る → mix-blend-mode: screen

   上昇・蛇行・燃え方は周期が別なので、1要素では合成できず3層に分けている */
.ember-layer {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  mix-blend-mode: screen;
}
/* 悪魔化した時だけ動かす。隠したまま28個×3層を回し続けるのは無駄なので display で切る。
   **層の見え方をアニメーションに預けない** —— 以前は出現のフェードイン(0→1)を
   層の opacity に掛けていたが、display: none から表示へ切り替えた瞬間に
   そのアニメーションが始まらないことがあり、層が opacity 0 のまま固まって
   火の粉が出ないことがあった(2026-09-05 本人報告)。
   いまは脈動だけを掛けてあり、いちばん薄い瞬間でも 0.72 で必ず見えている。
   粒ごとの出入りは emberRise 側で付けているので、演出としても足りている */
body.is-demon .ember-layer {
  display: block;
  animation: emberBurst 6.3s ease-in-out infinite;
}
/* 火が爆ぜて数が増える瞬間(6) */
@keyframes emberBurst {
  0%, 100% { opacity: 0.85; }
  22% { opacity: 1; }
  61% { opacity: 0.72; }
}

/* 1層目: 上昇(1)。等速で、画面の下から上へ抜ける */
.ember {
  position: absolute;
  left: var(--x);
  bottom: -8%;
  width: var(--w);
  height: var(--w);
  animation: emberRise var(--d) linear var(--delay) infinite;
}
@keyframes emberRise {
  0%   { transform: translateY(0); opacity: 0; }
  7%   { opacity: 1; }
  72%  { opacity: 0.9; }
  100% { transform: translateY(-118dvh); opacity: 0; }
}

/* 2層目: 乱流の蛇行(2)。上昇と同じ周期で振幅を育て、円錐に広がって見せる */
.ember > i {
  display: block;
  width: 100%;
  height: 100%;
  animation: emberSway var(--d) ease-in-out var(--delay) infinite;
}
@keyframes emberSway {
  0%   { transform: translateX(0); }
  25%  { transform: translateX(calc(var(--amp) * 0.3)); }
  50%  { transform: translateX(calc(var(--amp) * -0.55)); }
  75%  { transform: translateX(calc(var(--amp) * 0.85)); }
  100% { transform: translateX(calc(var(--amp) * -1)); }
}

/* 3層目: 粒そのもの。冷えて色が落ちる(3)のと、チカチカ(5)。
   色の変化と明滅は別々の要素に分けないと、opacity の指定が食い合う */
.ember > i > b {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  /* 色は emberBurn で動かすが、基底も持たせておく。keyframe だけに置くと
     アニメーションが動かない環境(動きを減らす設定など)で粒が消える */
  background: #ffc35a;
  box-shadow: 0 0 11px 2px rgba(255, 170, 70, 0.8);
  animation: emberBurn var(--d) linear var(--delay) infinite,
             emberFlicker var(--fd) ease-in-out var(--delay) infinite alternate;
}
@keyframes emberBurn {
  0%   { background: #fffbe8; box-shadow: 0 0 14px 4px rgba(255, 222, 150, 0.95); }
  30%  { background: #ffcf72; box-shadow: 0 0 12px 3px rgba(255, 170, 70, 0.8); }
  65%  { background: #ff8438; box-shadow: 0 0 9px 1px rgba(255, 104, 44, 0.6); }
  100% { background: #a8281a; box-shadow: 0 0 4px 0 rgba(200, 56, 30, 0.25); }
}
@keyframes emberFlicker {
  from { opacity: 0.45; }
  to   { opacity: 1; }
}

.ember:nth-child(1) { --x: 1.2%; --w: 3px; --d: 5.2s; --delay: -6.8s; --amp: 36px; --fd: 0.43s; }
.ember:nth-child(2) { --x: 6.8%; --w: 5px; --d: 5.1s; --delay: -6.4s; --amp: 42px; --fd: 0.18s; }
.ember:nth-child(3) { --x: 7.2%; --w: 6px; --d: 5.3s; --delay: -7.1s; --amp: 24px; --fd: 0.61s; }
.ember:nth-child(4) { --x: 11.2%; --w: 4px; --d: 5.2s; --delay: -4.7s; --amp: 18px; --fd: 0.23s; }
.ember:nth-child(5) { --x: 16.5%; --w: 4px; --d: 5.5s; --delay: -6.3s; --amp: 24px; --fd: 0.21s; }
.ember:nth-child(6) { --x: 18.4%; --w: 4px; --d: 4.4s; --delay: -5.0s; --amp: 18px; --fd: 0.22s; }
.ember:nth-child(7) { --x: 20.4%; --w: 3px; --d: 3.7s; --delay: -2.4s; --amp: 42px; --fd: 0.19s; }
.ember:nth-child(8) { --x: 26.9%; --w: 4px; --d: 5.6s; --delay: -6.3s; --amp: 30px; --fd: 0.36s; }
.ember:nth-child(9) { --x: 27.1%; --w: 3px; --d: 4.3s; --delay: -5.0s; --amp: 36px; --fd: 0.56s; }
.ember:nth-child(10) { --x: 31.4%; --w: 3px; --d: 3.8s; --delay: -3.4s; --amp: 48px; --fd: 0.36s; }
.ember:nth-child(11) { --x: 37.0%; --w: 4px; --d: 5.0s; --delay: -6.6s; --amp: 48px; --fd: 0.38s; }
.ember:nth-child(12) { --x: 38.0%; --w: 6px; --d: 5.4s; --delay: -0.3s; --amp: 48px; --fd: 0.56s; }
.ember:nth-child(13) { --x: 45.5%; --w: 3px; --d: 5.2s; --delay: -5.3s; --amp: 36px; --fd: 0.47s; }
.ember:nth-child(14) { --x: 49.0%; --w: 5px; --d: 5.5s; --delay: -4.5s; --amp: 18px; --fd: 0.6s; }
.ember:nth-child(15) { --x: 48.9%; --w: 4px; --d: 4.7s; --delay: -3.4s; --amp: 48px; --fd: 0.36s; }
.ember:nth-child(16) { --x: 53.9%; --w: 5px; --d: 5.1s; --delay: -1.6s; --amp: 48px; --fd: 0.29s; }
.ember:nth-child(17) { --x: 56.8%; --w: 5px; --d: 5.2s; --delay: -6.9s; --amp: 36px; --fd: 0.53s; }
.ember:nth-child(18) { --x: 59.7%; --w: 4px; --d: 5.3s; --delay: -1.6s; --amp: 24px; --fd: 0.27s; }
.ember:nth-child(19) { --x: 62.7%; --w: 4px; --d: 5.2s; --delay: -0.5s; --amp: 24px; --fd: 0.42s; }
.ember:nth-child(20) { --x: 69.0%; --w: 4px; --d: 4.7s; --delay: -1.7s; --amp: 48px; --fd: 0.5s; }
.ember:nth-child(21) { --x: 73.9%; --w: 4px; --d: 5.1s; --delay: -5.3s; --amp: 42px; --fd: 0.52s; }
.ember:nth-child(22) { --x: 74.4%; --w: 4px; --d: 5.6s; --delay: -6.2s; --amp: 18px; --fd: 0.26s; }
.ember:nth-child(23) { --x: 80.1%; --w: 3px; --d: 4.6s; --delay: -3.8s; --amp: 48px; --fd: 0.58s; }
.ember:nth-child(24) { --x: 81.5%; --w: 6px; --d: 6.8s; --delay: -0.3s; --amp: 48px; --fd: 0.44s; }
.ember:nth-child(25) { --x: 84.5%; --w: 5px; --d: 5.3s; --delay: -7.0s; --amp: 30px; --fd: 0.3s; }
.ember:nth-child(26) { --x: 87.3%; --w: 4px; --d: 5.1s; --delay: -6.2s; --amp: 48px; --fd: 0.3s; }
.ember:nth-child(27) { --x: 93.1%; --w: 4px; --d: 4.8s; --delay: -3.7s; --amp: 24px; --fd: 0.22s; }
.ember:nth-child(28) { --x: 95.8%; --w: 4px; --d: 4.5s; --delay: -1.4s; --amp: 36px; --fd: 0.31s; }
.ember:nth-child(3) { --x: 21%; --w: 5px; --d: 4.6s; --delay: -4.1s; --amp: 20px; --fd: 0.22s; }
.ember:nth-child(4) { --x: 29%; --w: 2px; --d: 8.2s; --delay: -1.2s; --amp: 44px; --fd: 0.53s; }
.ember:nth-child(5) { --x: 36%; --w: 4px; --d: 5.9s; --delay: -5.6s; --amp: 30px; --fd: 0.37s; }
.ember:nth-child(6) { --x: 44%; --w: 3px; --d: 7.1s; --delay: -3.4s; --amp: 38px; --fd: 0.41s; }
.ember:nth-child(7) { --x: 51%; --w: 2px; --d: 8.7s; --delay: -6.8s; --amp: 48px; --fd: 0.58s; }
.ember:nth-child(8) { --x: 58%; --w: 5px; --d: 4.4s; --delay: -0.7s; --amp: 18px; --fd: 0.19s; }
.ember:nth-child(9) { --x: 65%; --w: 3px; --d: 6.3s; --delay: -4.9s; --amp: 36px; --fd: 0.44s; }
.ember:nth-child(10) { --x: 72%; --w: 4px; --d: 5.1s; --delay: -2.8s; --amp: 24px; --fd: 0.28s; }
.ember:nth-child(11) { --x: 78%; --w: 2px; --d: 8.9s; --delay: -7.3s; --amp: 52px; --fd: 0.61s; }
.ember:nth-child(12) { --x: 84%; --w: 3px; --d: 6.6s; --delay: -1.9s; --amp: 32px; --fd: 0.35s; }
.ember:nth-child(13) { --x: 89%; --w: 5px; --d: 4.9s; --delay: -5.2s; --amp: 22px; --fd: 0.24s; }
.ember:nth-child(14) { --x: 93%; --w: 2px; --d: 7.8s; --delay: -3.9s; --amp: 42px; --fd: 0.51s; }
.ember:nth-child(15) { --x: 34%; --w: 3px; --d: 7.4s; --delay: -6.1s; --amp: 40px; --fd: 0.39s; }
.ember:nth-child(16) { --x: 62%; --w: 4px; --d: 5.7s; --delay: -0.4s; --amp: 28px; --fd: 0.33s; }

/* 悪魔は最終形態。小さいままだと見せ場にならないので、切り替わりと一緒に大きくする。
   起点を右上にすると、角を画面の外に出さずに下へ伸びる(中央や下起点だと角が切れる) */
/* 既定は 0.42秒(悪魔化の拡大はこの速さが合う)。紙が出る時の寄せだけ、
   --sprite-move で長くする。
   動かすのは transform だけにする —— height を動かすと毎フレーム画像を
   拡大し直すことになり、目に見えてカクつく(2026-09-06 本人指摘) */
.sprite {
  transition: transform var(--sprite-move, 0.42s) cubic-bezier(0.22, 0.8, 0.28, 1);
  transform-origin: top right;
}
@media (max-width: 999px) {
  body.is-demon .sprite {
    /* 右上へ寄せる(2026-09-05 本人指示)。右肩が画面の外に出るのは構わない
       —— 顔と角を上に持ってくるほうが迫力が出る。translate を先に書いて
       画面の単位で動かし、そのあと拡大する */
    transform: translate(8vw, -8dvh) scale(1.9);
    transform-origin: top right;
  }
}
@media (min-width: 1000px) {
  /* 広い画面ではもともと画面の高さいっぱいに立っているので、上げ幅は控えめに。
     起点を下にすると上へ伸びて角が切れるので、ここも上を固定して下へ伸ばす */
  body.is-demon .sprite {
    transform: scale(1.08);
    transform-origin: top right;
  }
}

/* ---------------- 場面転換のテロップ ---------------- */

.telop {
  position: fixed;
  inset: 0;
  z-index: 800;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background: rgba(8, 5, 12, 0.92);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.42s ease;
}
.telop.is-in { opacity: 1; transition-duration: 0.26s; }
.telop.is-out { opacity: 0; }
.telop-date {
  font-size: 1.6rem;
  letter-spacing: 0.14em;
  color: var(--text);
  font-weight: bold;
  text-shadow: 0 2px 16px rgba(255, 143, 177, 0.35);
}
.telop-place {
  font-size: 0.9rem;
  letter-spacing: 0.18em;
  color: var(--text-dim);
  padding-top: 10px;
  border-top: 1px solid rgba(255, 255, 255, 0.18);
  min-height: 1.4em;
}
.telop-place:empty { display: none; }

/* ---------------- 暗転 ---------------- */

.fade {
  position: fixed;
  inset: 0;
  z-index: 900;
  background: #06040a;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.6s ease;
}
.fade.is-dark { opacity: 1; }

/* ---------------- タイトルの花びら ---------------- */

/* 実際の桜の落ち方に寄せてある。要点は6つ:
   1. 落下は概ね等速(終端速度)。ただし滑空と失速で速い区間と遅い区間が交互に来る
   2. 平板なので左右交互に渦が剥がれ、ジグザグに揺れる(フラッター)
   3. 面が回るので正面 ↔ 真横 が交互に見え、真横では線になって消える
   4. 回転軸は画面の法線ではなく主に水平軸(rotateX)。rotateZ だけだと硬貨が回って見える
   5. 風は場全体にかかる → 容器ごと横に揺らして、全体が同じ方向へ流れる時間帯を作る
   6. 遠いものは小さく・遅く・淡い
   落下・横揺れ・回転は周期が別なので、1要素では合成できず3層に分けている */
.petals {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  display: none;
  overflow: hidden;
}
body.is-title .petals {
  display: block;
  /* 風。全部の花びらに同じ横流れをかける(5) */
  animation: petalWind 19s ease-in-out infinite;
}
@keyframes petalWind {
  0%, 100% { transform: translateX(0); }
  30% { transform: translateX(16px); }
  55% { transform: translateX(-11px); }
  80% { transform: translateX(7px); }
}

/* 1層目: 落下。滑空と失速で速さが変わる(1) */
.petal {
  position: absolute;
  top: -9vh;
  left: var(--x, 50%);
  width: var(--w, 11px);
  height: calc(var(--w, 11px) * 0.86);
  opacity: 0;
  animation: petalFall var(--d, 13s) linear var(--delay, 0s) infinite;
}
@keyframes petalFall {
  0%   { transform: translateY(0); opacity: 0; animation-timing-function: cubic-bezier(.35, 0, .75, .35); }
  7%   { opacity: var(--op, 0.85); }
  32%  { transform: translateY(34vh); animation-timing-function: cubic-bezier(.15, .55, .35, 1); }
  58%  { transform: translateY(56vh); animation-timing-function: cubic-bezier(.35, 0, .75, .35); }
  86%  { transform: translateY(92vh); opacity: var(--op, 0.85); animation-timing-function: linear; }
  100% { transform: translateY(116vh); opacity: 0; }
}

/* 2層目: 左右のジグザグ(2)。alternate で行って戻ってを繰り返す */
.petal > i {
  display: block;
  width: 100%;
  height: 100%;
  animation: petalSway var(--sd, 1.9s) ease-in-out var(--delay, 0s) infinite alternate;
}
@keyframes petalSway {
  from { transform: translateX(calc(var(--amp, 26px) * -1)); }
  to   { transform: translateX(var(--amp, 26px)); }
}

/* 3層目: 花びらそのものと、面の回転(3)(4)。
   rotateX(90deg) で高さが 0 になり、真横から見た線になる = 一瞬消える */
.petal > i > b {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 62% 62% 52% 52% / 78% 78% 34% 34%;
  background: linear-gradient(150deg, rgba(255, 236, 244, 0.95), rgba(255, 173, 203, 0.85));
  box-shadow: 0 0 5px rgba(255, 143, 177, 0.25);
  animation: petalSpin var(--rd, 3.4s) linear var(--delay, 0s) infinite;
}
@keyframes petalSpin {
  0%   { transform: rotateX(0deg)   rotateZ(0deg);   opacity: 1; }
  25%  { transform: rotateX(90deg)  rotateZ(34deg);  opacity: 0.4; }
  50%  { transform: rotateX(180deg) rotateZ(68deg);  opacity: 1; }
  75%  { transform: rotateX(270deg) rotateZ(102deg); opacity: 0.4; }
  100% { transform: rotateX(360deg) rotateZ(136deg); opacity: 1; }
}

/* 1枚ずつ、位置・大きさ・速さ・揺れ幅・回転の周期を変える。
   小さいもの(遠く)は遅く・淡く(6)。周期どうしが割り切れないようにして、
   同じ動きが繰り返して見えないようにしている */
.petal:nth-child(1)  { --x: 7%;  --w: 12px; --d: 13s; --delay: 0s;   --amp: 30px; --sd: 2.1s; --rd: 3.7s; --op: .9; }
.petal:nth-child(2)  { --x: 17%; --w: 9px;  --d: 16s; --delay: -4s;  --amp: 22px; --sd: 2.6s; --rd: 4.9s; --op: .7; }
.petal:nth-child(3)  { --x: 26%; --w: 7px;  --d: 19s; --delay: -9s;  --amp: 17px; --sd: 3.1s; --rd: 6.2s; --op: .55; }
.petal:nth-child(4)  { --x: 35%; --w: 13px; --d: 12s; --delay: -2s;  --amp: 34px; --sd: 1.7s; --rd: 3.1s; --op: .95; }
.petal:nth-child(5)  { --x: 45%; --w: 8px;  --d: 17s; --delay: -11s; --amp: 20px; --sd: 2.9s; --rd: 5.3s; --op: .65; }
.petal:nth-child(6)  { --x: 55%; --w: 11px; --d: 14s; --delay: -6s;  --amp: 28px; --sd: 2.3s; --rd: 4.1s; --op: .85; }
.petal:nth-child(7)  { --x: 65%; --w: 7px;  --d: 20s; --delay: -14s; --amp: 16px; --sd: 3.3s; --rd: 6.7s; --op: .5; }
.petal:nth-child(8)  { --x: 74%; --w: 12px; --d: 12.5s; --delay: -3s; --amp: 32px; --sd: 1.9s; --rd: 3.4s; --op: .9; }
.petal:nth-child(9)  { --x: 84%; --w: 9px;  --d: 15.5s; --delay: -8s; --amp: 24px; --sd: 2.7s; --rd: 4.6s; --op: .75; }
.petal:nth-child(10) { --x: 93%; --w: 10px; --d: 14.5s; --delay: -13s; --amp: 26px; --sd: 2.2s; --rd: 5.8s; --op: .8; }

/* Perfect を見た人のタイトルは冬。花びらを雪に替える(2026-09-11)。
   落ち方(3層の動き)はそのまま使い、形と色だけ変える。雪は平板ではないので回さない */
body.is-winter-title .petal { height: var(--w, 11px); }
body.is-winter-title .petal > i > b {
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.95) 35%, rgba(255, 255, 255, 0) 70%);
  box-shadow: none;
  animation: none;
}

/* ---------------- 一枚絵(似顔絵など) ---------------- */

/* 写真を立てかけたように少し傾けて出す。立ち絵より手前、本文より奥 */
.cg {
  position: fixed;
  z-index: 1;
  left: 5%;
  top: 14%;
  width: min(32vw, 360px);
  height: auto;
  /* 縁は紙の余白。純白にすると写真のマットに見えて、紙そのものに見えない */
  border: 6px solid #f2ece0;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 12px 30px rgba(0, 0, 0, 0.5);
  /* 出る時は、少し小さく・少し下から浮かび上がる(2026-09-05 本人指示)。
     0.7秒だと「出たか分からない」速さだったので 1.3秒に伸ばし、
     浮き上がる距離も 10px → 18px にした(2026-09-06 本人指摘) */
  transform: rotate(-4deg) scale(0.9) translateY(18px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 1.3s ease, transform 1.3s cubic-bezier(0.16, 0.8, 0.3, 1);
}
.cg.is-shown { opacity: 1; transform: rotate(-4deg) scale(1) translateY(0); }
/* 広い画面の結末では、既定の位置(上から14%)だと「NEW」の帯と見出しに紙が
   重なる。結末は左に文章・右に立ち絵の組みなので、紙は上へ逃がして小さくする */
@media (min-width: 1000px) {
  body.is-ending .cg { top: 10%; width: min(22vw, 250px); }
}
/* 結末では紙をもう少し下へ(2026-09-07 本人指示)。上に寄りすぎていて、
   立ち絵の顔と同じ高さに並んでいた。本文の枠にぶつからない範囲で下げる
   —— 390x713 で紙の下に 255px 空いているので、10dvh 下げても 184px 残る */
@media (max-width: 999px) and (min-height: 561px) {
  body.is-ending .cg { top: 24dvh; }
}
/* 横向きの端末は縦の余白が少ない(実測 320x553 で紙と枠のすき間 66px)。
   同じだけ下げるとぶつかるので、下げ幅を抑える */
/* 高さ 560px 以下の結末の一枚絵は、下の「立ち絵を縮める」指定の後ろにまとめて書いてある */
.cg:not([src]) { display: none; }

/* 一枚絵を大きく見る(2026-09-12)。紙をタップすると開き、どこをタップしても閉じる。
   ピンチで拡大しなくても絵を見られるようにするためのもの。
   暗幕にぼかし(backdrop-filter)は使わない —— 拡大と同じくメモリを食うので単色で敷く */
.cg-viewer {
  position: fixed;
  inset: 0;
  z-index: 1100;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding-block: calc(16px + env(safe-area-inset-top, 0px)) calc(16px + env(safe-area-inset-bottom, 0px));
  padding-inline: 16px;
  background: rgba(8, 5, 12, 0.9);
  animation: cgViewerIn 0.22s ease-out;
}
.cg-viewer[hidden] { display: none; }
.cg-viewer-img {
  width: auto;
  height: auto;
  max-width: min(100%, 900px);
  max-height: calc(100vh - 110px);
  max-height: calc(100dvh - 110px);
  /* 縁は小さい紙(.cg)と同じ色。大きくしても同じ紙に見えるようにする */
  border: 8px solid #f2ece0;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.6);
}
.cg-viewer-hint {
  margin: 0;
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  color: var(--text-dim);
}
@keyframes cgViewerIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .cg-viewer { animation: none; }
}
/* 狭い画面。ここは横幅が足りないので、絵をそのまま出すと立ち絵に重なって
   ハナエの顔を隠してしまう(390px 幅で実測 165px の重なり)。
   一枚絵が出ている間だけ立ち絵を縮めて右端へ寄せ、絵を左に置いて左右に分ける。
   390px 幅での内訳: 絵 40vw+枠12px=168px(左端 8px) / 立ち絵 44dvh=約184px幅
   (右端 6px)→ 間に 24px 残る */
@media (max-width: 999px) {
  .cg {
    left: 2vw;
    top: 14dvh;
    bottom: auto;
    width: min(40vw, 240px);
  }
  /* 紙が出る時の寄せはゆっくり。ただし悪魔化の拡大はここを使わない ——
     あれは一瞬で大きくなる方が効くので、既定の 0.42 秒に任せる */
  body.has-cg:not(.is-demon) .sprite { --sprite-move: 0.9s; }
  /* 悪魔化の時だけは、寄せ方を以前の書き方(位置指定)に戻す。
     変形で寄せると拡大の土台が変わり、見た目の大きさと位置がずれる
     (実測 390x713 で 337x596 → 429x759 になっていた。2026-09-06 本人指摘)。
     ここは一瞬で切り替わる見せ場なので、滑らかさより元の絵を優先する */
  body.has-cg.is-demon .sprite {
    left: auto;
    right: max(6px, 1.5vw);
    margin: 0;
  }
}
/* left/right を切り替えると、途中の値が作れないので一瞬で飛ぶ。
   位置は中央のままにして、変形で右へ寄せる —— これなら滑らかに動く。
   50% は立ち絵自身の幅なので、縮みながら寄っても右端に合い続ける。
   悪魔化の時は別の変形(拡大)が要るので、そちらには掛けない。

   縦向き(高さ 561px 以上)だけに掛ける。横向きの端末では立ち絵が元から
   右端に立っているので、同じ変形を足すと画面の外へ出る(実測 320x553 で -76px) */
@media (max-width: 999px) and (min-height: 561px) {
  body.has-cg:not(.is-demon) .sprite {
    transform: translateX(calc(50vw - 50% - max(6px, 1.5vw)));
  }
}
/* 縮めるのは、右へ寄せただけでは足りない端末だけ。640px より広ければ
   (portrait のタブレット、横向きの端末)寄せるだけで 150px 以上あくので、
   立ち絵は大きいまま見せる。
   0.786 は元の 44dvh ÷ 56dvh。起点が右上なので、右端に合わせたまま
   左下へ縮む(高さを直接変えていた頃と同じ収まりになる) */
@media (max-width: 640px) and (min-height: 561px) {
  body.has-cg:not(.is-demon) .sprite {
    transform: translateX(calc(50vw - 50% - max(6px, 1.5vw))) scale(0.786);
  }
}
/* 悪魔化の時の高さも以前のまま(縮めた土台の上で 1.9 倍にする) */
@media (max-width: 640px) {
  body.has-cg.is-demon .sprite {
    height: min(44vh, 520px);
    height: min(44dvh, 520px);
  }
}
/* 横向きの端末は立ち絵が元から右端。寄せる必要が無いので、縮めるだけ。
   ただし縮小率が違う —— こちらの基準は 94dvh なので 44 ÷ 94 = 0.468。
   縦向きと同じ 0.786 にすると紙と 58px 重なる(実測 320x553) */
@media (max-width: 640px) and (max-height: 560px) {
  body.has-cg:not(.is-demon) .sprite { transform: scale(0.468); }
}
/* 高さ 560px 以下の結末では、紙を右下(立ち絵の胸から下の前)に置く(2026-09-11)。
   この高さは縦横どちらも「左に文章・右に立ち絵」の組みになる(iPhone SE の Safari を
   縦に持った 375x553 も入る)。以前は紙が左上で文章の列と同じ側にあり、読み終わった後の
   「NEW」の帯と見出し(似顔絵エンドでは本文と「謝る / いじる」まで)に重なっていた
   (844x390 / 375x553 / 320x553 で実測)。
   大きさは幅と高さの上限で決める(width/height を auto のままにすると、置換要素は縦横比を
   保ったまま縮む)。幅の上限は右の列の幅: 100vw - (左余白 16px + 文章の列 62vw) から、
   右余白と -4deg の傾きで広がる分を引いた 38vw - 46px。正方形のペーパーナプキンも縦長の
   似顔絵も同じ枠に収まる。紙が左に無いので、立ち絵を縮める必要もない */
@media (max-width: 999px) and (max-height: 560px) {
  body.is-ending .cg {
    left: auto;
    right: max(6px, 2vw);
    top: auto;
    bottom: calc(10px + env(safe-area-inset-bottom, 0px));
    width: auto;
    height: auto;
    max-width: calc(38vw - 46px);
    max-height: min(46dvh, 190px);
  }
  body.has-cg:not(.is-demon) .sprite { transform: none; }
}

/* 画面が低い端末(iPhone SE の Safari 表示 553px、360x640 級の Chrome 570px など)は、
   ロゴ+名前欄+ボタン2つ+7段の図鑑がどうやっても収まらない。
   そこだけ図鑑を2列に落として高さを半分にする(上から下への順位は左→右で保たれる) */
/* iPhone 13 の Safari は下部バーのぶん表示領域が 713px ほどしかない。
   閾値が 720px だと2列に落ちてしまっていたので、本当に小さい端末だけに絞る
   (iPhone SE の Safari 553px / 360x640 級の Chrome 570px)。2026-09-05 本人指摘 */
@media (max-width: 999px) and (max-height: 620px) {
  .gallery-grid { grid-template-columns: repeat(2, 1fr); }
  /* 2列だと1マスが半分の幅しかなく、名前とハートを横に並べると折り返して
     かえって高くなる。ここだけ名前の下にハートを敷く */
  .gallery-cell {
    flex-direction: column;
    gap: 1px;
    padding: 4px 6px;
  }
  .gallery-name { text-align: center; }
  .gallery-hearts { font-size: 0.56rem; }
  #app { padding-top: calc(8px + env(safe-area-inset-top, 0px)); }
  .game-title { margin: 6px 0 8px; }
  .game-logo { width: min(72%, 360px); }
  /* ここまで低い画面では2文とも出すと図鑑の最下段が画面の外へ出るので、
     以前は「好感度が出ない」ほうだけを残していた。その文は 2026-09-11 に削除したので、
     いまは1文(ジャンルと所要時間)しか無い。隠さずに小さく出す */
  .title-lead { font-size: 0.7rem; line-height: 1.5; margin-bottom: 8px; }
  .main-btn { margin-bottom: 8px; }
}

/* タイトルの上半分。図鑑を1列7段で見せたいので、ロゴから「はじめる」までを詰める。
   1列のまま iPhone 13(表示 713px)に収めるのが目的 */
@media (max-width: 999px) {
  #app { padding-top: calc(10px + env(safe-area-inset-top, 0px)); }
  .game-title { margin: 8px 0 10px; }
  .game-logo { width: min(82%, 420px); }
  .title-lead { font-size: 0.74rem; line-height: 1.6; margin-bottom: 10px; }
  .name-form { gap: 6px; margin: 0 auto 12px; }
  #player-name-input { padding: 10px 14px; }
  .main-btn { padding: 12px 36px; margin-bottom: 10px; }
  .ending-gallery { margin-top: 10px; }
}

/* ---------------- 似顔絵を描く画用紙 ---------------- */

/* #app の外に置いてある。中に入れると本文の早送りタップを拾う(バックログと同じ理由) */
.draw-overlay {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 20px 16px calc(20px + env(safe-area-inset-bottom, 0px));
  background: rgba(10, 6, 16, 0.86);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
.draw-overlay[hidden] { display: none; }
/* 長押しで文字が選択されたり、iOS の「コピー」の吹き出しが出たりすると
   線が引けなくなる(2026-09-05 本人指摘)。
   はじめは画用紙の中だけに掛けていたが、それでは足りなかった(2026-09-06 再発)。
   画用紙は画面全体を覆っているものの、**指が画用紙の外の文字に触れた時**に
   iOS 側が選択を始めてしまう。開いている間は画面のどこでも選択させない */
body.is-drawing,
body.is-drawing * {
  -webkit-user-select: none !important;
  user-select: none !important;
  -webkit-touch-callout: none !important;
  -webkit-tap-highlight-color: transparent;
}
.draw-canvas { -webkit-user-drag: none; }

/* 進行表(A4)の裏。本物の紙の見え方に寄せてある。観察したのは7点:
   1. 純白ではない。パルプ由来のわずかな黄みがあり、明るいところで #f8f4ec 前後。
      #fff に近づけるほど「紙」ではなく「光っている板」に見える
   2. 色が均一ではない。雲状の濃淡ムラ(フロック)の上に、細かい繊維の粒が乗る
   3. マット。ハイライトが立たず、光源側から反対側へ緩やかに暗くなるだけ
   4. 完全な平面ではない(一度でも手に取られた紙は必ず反る)= 影が均一にならない。
      接地している側は細く濃い影、浮いている側は広く薄い影になる。
      均一なドロップシャドウは「浮いているカード」であって紙ではない
   5. 厚みで裁ち口が光を拾い、縁に細い明るい線が出る。接地側の縁は逆に暗い
   6. 裁断と手擦れで、4つの角の丸まり方がそれぞれ違う
   7. A4 は 1:1.414。4:3 は画面の比率であって、紙にはその寸法がない */
.draw-paper {
  width: min(88vw, 430px);
  aspect-ratio: 1.4142 / 1;
  background-color: #f1ece0;
  background-image:
    /* 光(3)。左上から入って右下へ抜ける。山なりの反射は作らない */
    linear-gradient(158deg, rgba(255, 255, 255, 0.6) 0%, rgba(255, 255, 255, 0.2) 44%, rgba(92, 78, 58, 0.042) 100%),
    /* 雲状の濃淡ムラ(2)。位置も大きさも割り切れない値にして、繰り返しに見せない */
    radial-gradient(58% 44% at 23% 27%, rgba(255, 253, 246, 0.5), transparent 70%),
    radial-gradient(46% 38% at 74% 63%, rgba(122, 104, 78, 0.055), transparent 72%),
    radial-gradient(37% 52% at 57% 11%, rgba(122, 104, 78, 0.04), transparent 68%),
    radial-gradient(63% 31% at 13% 87%, rgba(122, 104, 78, 0.045), transparent 74%),
    /* 繊維の粒(2)。コピー用紙なので目は細かい */
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='p' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.05' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23p)' opacity='0.28'/%3E%3C/svg%3E");
  background-size: auto, auto, auto, auto, auto, 140px 140px;
  /* 角の丸まり方を4つとも変える(6) */
  border-radius: 2px 5px 3px 6px / 5px 2px 6px 3px;
  box-shadow:
    /* 裁ち口の明線と、接地側の暗い線(5) */
    inset 0 1px 0 rgba(255, 255, 255, 0.85),
    inset 1px 0 0 rgba(255, 255, 255, 0.45),
    inset 0 -1px 0 rgba(92, 78, 58, 0.16),
    inset -1px 0 0 rgba(92, 78, 58, 0.09),
    /* 影(4)。接地している右下は細く濃く、浮いている左下は広く薄い */
    1px 2px 2px rgba(0, 0, 0, 0.34),
    3px 12px 18px rgba(0, 0, 0, 0.32),
    -5px 22px 34px rgba(0, 0, 0, 0.26);
  transform: rotate(-1.4deg);
  overflow: hidden;
}
.draw-canvas {
  display: block;
  width: 100%;
  height: 100%;
  /* 指でなぞった時にページごとスクロールさせない */
  touch-action: none;
  cursor: crosshair;
}
.draw-hint {
  margin: 0;
  min-height: 1.3em;
  font-size: 0.84rem;
  color: var(--text-dim);
  text-align: center;
}
/* 画用紙のすぐ下に置く道具。押しやすさを優先して 44px 幅を確保する */
.draw-tools {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
.draw-tool {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  height: 40px;
  padding: 0 10px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.07);
  color: var(--text);
  font-size: 0.86rem;
  cursor: pointer;
}
.draw-tool svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.draw-tool:disabled { opacity: 0.28; cursor: default; }
.draw-tool:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

.draw-actions {
  display: flex;
  align-items: center;
  gap: 18px;
}
.draw-done {
  padding: 11px 32px;
  border-radius: 999px;
  border: none;
  background: linear-gradient(135deg, var(--accent-strong), var(--purple));
  color: #fff;
  font-size: 0.98rem;
  font-weight: bold;
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(255, 92, 138, 0.35);
}
/* 何も描いていない間は沈ませる。押すこと自体は許して、押されたら理由を伝える */
.draw-done.is-off { opacity: 0.45; }
/* 傾けたまま揺らす。nameShake は translateX だけなので、そのまま使うと
   揺れている間だけ紙がまっすぐになってしまう */
.draw-paper.shake { animation: paperShake 0.4s ease; }
@keyframes paperShake {
  0%, 100% { transform: rotate(-1.4deg) translateX(0); }
  25% { transform: rotate(-1.4deg) translateX(-7px); }
  75% { transform: rotate(-1.4deg) translateX(7px); }
}
.draw-done:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .draw-paper.shake { animation: none; }
}


/* ---------------- ネタバレ表示(どんな結末でも2周遊んだ人だけ。旧称: 答え合わせ → 好感度表示) ----------------
   札は解放済みなら常に描いておき、出す/出さないはこのクラスで切り替える。
   選択肢を作り直すと出現アニメと誤タップ防止のロックが壊れるため。 */

.choice-score,
.hud-meter { display: none; }

body.reveal-on .choice-score {
  display: inline-flex;
  gap: 8px;
  align-items: baseline;
  float: right;
  margin-left: 12px;
  font-size: 0.82rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  opacity: 0.9;
}

.choice-score-pt { color: rgba(255, 255, 255, 0.62); }
.choice-score-pt.is-plus { color: #8fd6a8; }
.choice-score-pt.is-minus { color: #e2879b; }

.choice-score-rival {
  color: #d7b3ff;
  border: 1px solid rgba(215, 179, 255, 0.4);
  border-radius: 999px;
  padding: 0 8px;
  font-size: 0.76rem;
}

body.reveal-on .hud-meter {
  display: inline;
  margin-left: 12px;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, 0.72);
}

/* 全6結末のおまけ(SUPER HANAE)への入口。タイトルの図鑑の下と、達成した結末の画面に出す */
.gallery-bonus { margin: 8px 0 0; text-align: center; }
.bonus-link {
  display: inline-block;
  padding: 8px 18px;
  border-radius: 999px;
  border: 1px solid rgba(255, 214, 110, 0.6);
  background: rgba(255, 196, 64, 0.14);
  color: var(--gold);
  font-size: 0.85rem;
  font-weight: bold;
  text-decoration: none;
}
.bonus-link:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
/* 結末の画面で、この結末で新しく開いたものを知らせる(ネタバレ表示 / おまけ) */
.unlock-notes { text-align: center; margin: 0 0 12px; }
.unlock-notes[hidden] { display: none; }
.unlock-note {
  margin: 0 0 6px;
  font-size: 0.85rem;
  color: var(--accent);
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.9);
}
/* 解放の見出しに続ける説明の行。主役は上の行なので、小さく暗く落とす(2026-09-12) */
.unlock-note.is-sub {
  margin-top: -3px;
  font-size: 0.76rem;
  color: var(--text-dim);
}

/* 「はじめる」と答え合わせボタンを横に並べる。
   左右を 1fr の空き列にして真ん中に「はじめる」を置くと、
   答え合わせを右に足しても「つづきから」と縦に揃ったままになる。
   .main-btn は margin: 0 auto なので、この行の中だけ auto を解く */
.start-row {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  column-gap: 12px;
}
.start-row .main-btn { grid-column: 2; margin-left: 0; margin-right: 0; }
.reveal-btn { grid-column: 3; justify-self: start; }

/* 340px 以下だと 1fr の空き列を確保できないので、中央維持をあきらめて詰める。
   「右に置く」方を優先する */
@media (max-width: 340px) {
  .start-row { grid-template-columns: auto auto; justify-content: center; column-gap: 8px; }
  .start-row .main-btn { grid-column: 1; padding-left: 26px; padding-right: 26px; }
  .reveal-btn { grid-column: 2; }
}


/* 「答え合わせ」と「オン/オフ」を2行に積んで幅を詰める。
   狭い画面でも「はじめる」の右に収めるため(2026-09-09 本人指示)。
   1行だと 360px 幅で右の列に入らず、下へ回さざるを得なかった */
/* display を持たせているので、hidden 属性だけでは消えない。
   UA の [hidden]{display:none} よりこちらが強いため明示する
   (.skip-btn でも同じ手当てをしている) */
.reveal-btn[hidden] { display: none; }

.reveal-btn {
  margin: 0 0 14px;
  padding: 8px 14px;
  border-radius: 16px;
  border: 1px solid rgba(255, 255, 255, 0.3);
  background: rgba(10, 6, 16, 0.55);
  color: var(--text-dim);
  cursor: pointer;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  line-height: 1.25;
  white-space: nowrap;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.reveal-label { font-size: 0.76rem; }
/* オンの時だけ金色のネオンで縁がほわほわ光る。目立たせるのが目的(2026-09-09 本人指示)。
   色は作品の他の金色(--gold 系)に合わせる。
   オフは [aria-pressed="true"] が外れるだけで光も消えるので、専用の指定は要らない */
.reveal-btn[aria-pressed="true"] {
  border-color: var(--gold-line);
  background: var(--gold-fill);
  color: var(--gold);
  animation: revealGlow 2.4s ease-in-out infinite;
}

@keyframes revealGlow {
  0%, 100% {
    border-color: rgba(255, 209, 102, 0.6);
    box-shadow: 0 0 4px rgba(255, 209, 102, 0.45),
                0 0 11px rgba(255, 209, 102, 0.28),
                inset 0 0 6px rgba(255, 230, 168, 0.14);
  }
  50% {
    border-color: rgba(255, 230, 168, 1);
    box-shadow: 0 0 9px rgba(255, 209, 102, 0.85),
                0 0 24px rgba(255, 209, 102, 0.5),
                inset 0 0 10px rgba(255, 230, 168, 0.28);
  }
}

/* 動きを減らす設定では点滅させない。ただし目立たせるのが目的なので、
   光そのものは中間の強さで点けたままにする */
@media (prefers-reduced-motion: reduce) {
  .reveal-btn[aria-pressed="true"] {
    animation: none;
    border-color: rgba(255, 230, 168, 0.9);
    box-shadow: 0 0 7px rgba(255, 209, 102, 0.7),
                0 0 18px rgba(255, 209, 102, 0.4);
  }
}
.reveal-state {
  font-weight: bold;
  font-size: 0.9rem;
}


/* ボタンを長押しした時に、文字が選択されて iOS の「コピー / ペースト」の
   吹き出しが出るのを止める。「つづける」を押し損ねて長押しになると出てしまい、
   押し直すまで邪魔になる(2026-09-09 本人指摘)。
   ボタンの文字を選択したい場面は無いので、button 全体に掛ける */
button {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}
