/* ========================================================
   文豪パーティRPG（プロトタイプ）- スタイル一本化ファイル
   CSSはこのファイルにのみ書く。JS側での document.createElement('style')
   によるCSS注入は行わない（book-battleで実際に事故が起きたパターンのため）。
======================================================== */

/* ============================================================
   デザイントークン：「文界」＝物語の記憶が積もる異世界。
   book-battle（墨×朱×金の五行絵巻）とは配色を混在させず、
   深い藍×金×紫を基調にした「夜の書庫」トーンにしている。
============================================================ */
:root {
  --bg-void: #0c0b14;
  --bg-deep: #14121f;
  --bg-panel: #1d1a2c;
  --bg-panel-light: #262238;
  --border-soft: #3a3552;

  --text-main: #f2ecdc;
  --text-sub: #a79cc4;
  --text-faint: #6f6790;

  /* ドット絵の立ち絵に共通で乗せる白フチ。暗い背景に沈まないようにするための
     ピクセルアウトラインで、drop-shadowをblur無しで4方向に重ねている。
     以前は同じ4行を各所（タイトル・会話・戦闘・カットイン…）に書き写して
     いたため、新しく立ち絵を出す画面を足すたびに付け忘れが起きていた
     （相関図・編成画面・エンディング・登場人物一覧で実際に抜けていた）。
     立ち絵を表示する箇所では必ず filter に var(--sprite-outline) を含めること。
     ※描き込まれたイラストのボス立ち絵（.enemy-panel-portrait）は対象外。 */
  --sprite-outline:
    drop-shadow(1px 0 0 #fff)
    drop-shadow(-1px 0 0 #fff)
    drop-shadow(0 1px 0 #fff)
    drop-shadow(0 -1px 0 #fff);

  --gold: #caa456;
  /* ボス登場カットイン専用の朱。奥義カットインの金と対比させ、
     「技」と「敵の出現」を色でも区別する。 */
  --vermillion: #b3271f;
  --gold-light: #e8cf8e;
  --violet: #8a5cc9;
  /* 紫文字（状態異常バッジ、絆コンボのカナ表記等）が視認性が悪いとの指摘を
     受け、明度を上げて濃い紫系の背景パネルの上でも読みやすくした
     （旧: #b79bea）。 */
  --violet-light: #d3c0f7;

  --hp-color: #d94f4f;
  --hp-color-low: #ff2f2f;
  --bunryoku-color: #3f9c93;
  --kihaku-color: #caa456;


  --font-display: 'Shippori Mincho B1', 'Noto Serif JP', serif;
  --font-body: 'Noto Sans JP', sans-serif;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-family: var(--font-body);
}

html, body {
  height: 100%;
}

body {
  background: radial-gradient(ellipse at 50% -10%, #241f3a 0%, var(--bg-void) 60%);
  color: var(--text-main);
  min-height: 100vh;
  overflow-x: hidden;
  /* スマホでコマンドボタン等を連打すると、ブラウザ標準の「ダブルタップで
     拡大」ジェスチャーとして誤認識され、意図せずページが拡大表示に
     なることがあった。ゲーム内にダブルタップ拡大を必要とする操作は
     ないため、manipulationでその判定自体を無効化する（pinchによる
     拡大操作自体は残るため、アクセシビリティ上の拡大手段は失われない）。 */
  touch-action: manipulation;
}

button {
  cursor: pointer;
  border: none;
  font-family: var(--font-body);
}

#app-root {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* ============================================================
   タイトル画面
============================================================ */
/* 「はじめる」を押してからの暗転。読む姿勢に切り替わる間を作るためのもので、
   長さは app.js の TITLE_EXIT_MS と揃えること。 */
.title-screen-out {
  pointer-events: none;
  animation: titleScreenOut 0.9s ease-in forwards;
}
@keyframes titleScreenOut {
  0% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.04); }
}

.title-screen {
  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 28px;
  text-align: center;
  padding: 24px;
  overflow: hidden;
  background:
    radial-gradient(ellipse at 50% 30%, rgba(138, 92, 201, 0.18) 0%, transparent 60%),
    var(--bg-void);
}

/* タイトル画面＝「入口のページ」に漂わせるキラキラ演出。絆コンボバッジの
   .combo-spark と同じ「文字をdrop-shadowで光らせて明滅させる」技法を
   画面全体にゆるく散らして使い回している。 */
.title-sparkle {
  position: absolute;
  color: var(--gold-light);
  text-shadow: 0 0 10px rgba(232, 207, 142, 0.9), 0 0 18px rgba(138, 92, 201, 0.6);
  pointer-events: none;
  animation: titleSparkleTwinkle 3.2s ease-in-out infinite;
}
.title-sparkle-1 { top: 12%; left: 10%; font-size: 16px; animation-delay: 0s; }
.title-sparkle-2 { top: 22%; right: 14%; font-size: 12px; animation-delay: 0.6s; }
.title-sparkle-3 { top: 68%; left: 8%; font-size: 14px; animation-delay: 1.2s; }
.title-sparkle-4 { top: 78%; right: 10%; font-size: 18px; animation-delay: 1.8s; }
.title-sparkle-5 { top: 40%; left: 4%; font-size: 10px; animation-delay: 2.2s; }
.title-sparkle-6 { top: 8%; right: 30%; font-size: 11px; animation-delay: 2.8s; }
@keyframes titleSparkleTwinkle {
  0%, 100% { opacity: 0; transform: scale(0.5) translateY(6px); }
  50% { opacity: 1; transform: scale(1.1) translateY(-4px); }
}

.title-version-badge {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 13px;
  letter-spacing: 2px;
  color: var(--bg-void);
  background: linear-gradient(180deg, var(--gold-light), var(--gold));
  padding: 5px 18px;
  border-radius: 20px;
  box-shadow: 0 0 16px rgba(202, 164, 86, 0.5);
}

.title-cast-row {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 18px;
}
.title-cast-sprite {
  height: 96px;
  width: auto;
  image-rendering: pixelated;
  filter:
    var(--sprite-outline)
    drop-shadow(0 10px 14px rgba(0,0,0,0.5));
  animation: portraitIn 0.4s ease-out both;
}
.title-cast-sprite:nth-child(2) { animation-delay: 0.08s; }
.title-cast-sprite:nth-child(3) { animation-delay: 0.16s; }

.title-version-desc {
  max-width: 480px;
  font-size: 15px;
  line-height: 1.8;
  color: var(--text-sub);
}

.title-version-tags {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
}
.title-version-tag {
  font-size: 12px;
  padding: 4px 14px;
  border-radius: 14px;
  background: rgba(138, 92, 201, 0.16);
  border: 1px solid rgba(138, 92, 201, 0.4);
  color: var(--violet-light);
}

.title-sfx-credit {
  font-size: 12px;
  color: var(--text-faint);
}

/* 「ご利用にあたって」への入口。遊ぶ前に必ず読ませたいものではないので、
   クレジットと同じ控えめな見え方にして、はじめるボタンの邪魔をしないようにする。
   ただし押せることは分かるように、下線と当たり判定（44px）は確保する。 */
.btn-title-terms {
  appearance: none;
  background: none;
  border: none;
  padding: 8px 12px;
  min-height: 44px;
  font-family: inherit;
  font-size: 12px;
  color: var(--text-faint);
  text-decoration: underline;
  text-decoration-color: rgba(202, 164, 86, 0.4);
  text-underline-offset: 3px;
  cursor: pointer;
}
.btn-title-terms:hover,
.btn-title-terms:focus-visible {
  color: var(--gold-light);
  text-decoration-color: var(--gold);
}

/* 開始前に音を確認・ミュートできるトグル。効果音がいきなり鳴ることへの
   配慮として、はじめるボタンの直前に配置している。細かい音量調整は
   バトル画面の⚙設定（.settings-overlay）で行うため、ここでは
   ON/OFFの簡易切り替えのみにとどめ、その旨を注意書きで案内する。
   .title-screenはflex+gap:28pxで子要素を並べているため、チェックボックスと
   注意書きをこの.title-mute-block1個にまとめて、2行が離れて見えないようにする。 */
.title-mute-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
.title-mute-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--text-sub);
  cursor: pointer;
}
.title-mute-toggle input {
  width: 16px;
  height: 16px;
  accent-color: var(--violet-light);
  cursor: pointer;
}
.title-mute-note {
  font-size: 12px;
  color: var(--text-faint);
}

.title-logo-main {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(32px, 7vw, 56px);
  color: var(--gold-light);
  text-shadow: 0 0 24px rgba(202, 164, 86, 0.45), 0 2px 0 rgba(0,0,0,0.4);
  letter-spacing: 2px;
}

.title-logo-sub {
  margin-top: 10px;
  font-family: var(--font-display);
  font-size: clamp(14px, 2.6vw, 18px);
  color: var(--text-sub);
  letter-spacing: 4px;
  /* 書き換え演出で一文字ずつ動かすので、行の高さが文字の動きで詰まらないよう
     あらかじめ確保しておく（確保しないと、消えていく途中で下の要素が動く）。 */
  min-height: 1.6em;
}

/* 二周目のサブタイトル書き換え（app.js の playSubtitleRewrite）。
   一周目の一文が光になって消え、新しい一文が書き加えられる。
   --d は文字ごとの遅れで、app.js が一文字ずつ与える。 */
.sub-char {
  display: inline-block;
  white-space: pre; /* 空白文字も一マスとして残す */
}
.sub-char-out { animation: subCharOut 0.55s ease-in var(--d, 0s) both; }
@keyframes subCharOut {
  0% { opacity: 1; transform: translateY(0) scale(1); }
  45% {
    opacity: 1;
    color: #fffdf4;
    text-shadow: 0 0 14px rgba(255, 248, 216, 0.95), 0 0 26px rgba(255, 233, 168, 0.7);
  }
  100% {
    opacity: 0;
    transform: translateY(-14px) scale(1.3);
    color: #fffdf4;
    text-shadow: 0 0 22px rgba(255, 248, 216, 0.9);
  }
}
/* 書き加えられる側。光の点から文字が結ばれるように、明るく大きい状態から
   本来の色・大きさへ落ち着かせる。100%で color/text-shadow を書かないのは、
   指定しなければ元の（.title-logo-sub の）値へ戻ってくれるため。 */
.sub-char-in { animation: subCharIn 0.6s cubic-bezier(0.16, 1, 0.3, 1) var(--d, 0s) both; }
@keyframes subCharIn {
  0% {
    opacity: 0;
    transform: translateY(9px) scale(1.35);
    color: #fffdf4;
    text-shadow: 0 0 24px rgba(255, 248, 216, 0.95);
  }
  55% { opacity: 1; }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}

.title-credit {
  font-size: 12px;
  color: var(--text-faint);
  letter-spacing: 1px;
}

.btn-title-start {
  margin-top: 12px;
  padding: 14px 48px;
  font-size: 17px;
  font-weight: 900;
  color: var(--bg-void);
  background: linear-gradient(180deg, var(--gold-light), var(--gold));
  border-radius: 30px;
  box-shadow: 0 0 24px rgba(202, 164, 86, 0.5), inset 0 1px 0 rgba(255,255,255,0.5);
  transition: transform 0.15s ease;
}
.btn-title-start:hover { transform: translateY(-2px) scale(1.03); }
.btn-title-start:active { transform: translateY(0) scale(0.98); }

/* 中断した続きがある時だけ「はじめから」を添える。主役は「続きから」なので、
   こちらは箔を張らず、控えめな枠だけにして取り違えを防ぐ。 */
.btn-title-restart {
  margin-top: 10px;
  padding: 9px 26px;
  font-size: 14px;
  font-weight: 700;
  color: var(--text-sub);
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid var(--border-soft);
  border-radius: 24px;
  cursor: pointer;
  transition: transform 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.btn-title-restart:hover {
  transform: translateY(-2px);
  border-color: var(--gold-dim, #6b5320);
  color: var(--text-main);
}
.btn-title-restart:active { transform: translateY(0) scale(0.98); }

/* ============================================================
   二周目に現れる「奥義の栞」ボタン（app.js の revealBookmarkButton）
   ============================================================
   置いてあるのではなく「現れた」ように見せたいので、光の粒を散らしながら
   浮かび上がらせ、しばらく縁が息をする。ただし本編の「はじめる」より
   目立たせない（タイトルの主役は依然として本編なので、色は控えめな箔に留め、
   大きさも一回り小さくしてある）。 */
.bookmark-reveal {
  position: relative;
  margin-top: 12px;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
}
.btn-bookmark-shelf {
  padding: 10px 30px;
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-indent: 0.12em;
  color: var(--gold-light);
  background: linear-gradient(180deg, rgba(38, 31, 20, 0.95), rgba(24, 20, 14, 0.95));
  border: 1px solid var(--gold);
  border-radius: 26px;
  cursor: pointer;
  animation:
    bookmarkBtnIn 0.75s cubic-bezier(0.16, 1, 0.3, 1) both,
    bookmarkBtnGlow 2.6s ease-in-out 0.75s infinite;
  transition: transform 0.15s ease;
}
.btn-bookmark-shelf:hover { transform: translateY(-2px) scale(1.03); }
.btn-bookmark-shelf:active { transform: translateY(0) scale(0.98); }

/* 栞の隣に並べる「文豪一覧」。栞と同じ大きさ・形だが、こちらは読み物への
   入口なので光らせない（強調したいのは新しく増えた栞の方）。 */
.btn-title-cast {
  padding: 10px 30px;
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-indent: 0.12em;
  color: var(--text-sub);
  background: linear-gradient(180deg, rgba(30, 27, 44, 0.95), rgba(20, 18, 30, 0.95));
  border: 1px solid var(--border-soft);
  border-radius: 26px;
  cursor: pointer;
  animation: bookmarkBtnIn 0.75s cubic-bezier(0.16, 1, 0.3, 1) 0.12s both;
  transition: transform 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.btn-title-cast:hover {
  transform: translateY(-2px) scale(1.03);
  border-color: var(--gold-dim, #6b5320);
  color: var(--text-main);
}
.btn-title-cast:active { transform: translateY(0) scale(0.98); }

/* 同じ列に並ぶ「終章に挑む」。栞・文豪一覧が読み物への入口なのに対し、
   これだけは戦いに入るボタンなので、朱（--vermillion）で色を分ける。
   演出を色・形・動きで区別するのと同じ理由で、押し間違えると通しで
   遊び直すことになるため、見た目で取り違えないようにしておく。 */
.btn-title-finale {
  padding: 10px 30px;
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-indent: 0.12em;
  color: #f0b8b2;
  background: linear-gradient(180deg, rgba(44, 20, 18, 0.95), rgba(28, 14, 13, 0.95));
  border: 1px solid var(--vermillion);
  border-radius: 26px;
  cursor: pointer;
  animation: bookmarkBtnIn 0.75s cubic-bezier(0.16, 1, 0.3, 1) 0.24s both;
  transition: transform 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.btn-title-finale:hover {
  transform: translateY(-2px) scale(1.03);
  border-color: #e0554b;
  color: #ffe2de;
}
.btn-title-finale:active { transform: translateY(0) scale(0.98); }
@keyframes bookmarkBtnIn {
  0% { opacity: 0; transform: scale(0.6); filter: brightness(2.4); }
  60% { opacity: 1; }
  100% { opacity: 1; transform: scale(1); filter: brightness(1); }
}
@keyframes bookmarkBtnGlow {
  0%, 100% { box-shadow: 0 0 12px rgba(202, 164, 86, 0.35); }
  50% { box-shadow: 0 0 26px rgba(202, 164, 86, 0.75); }
}
/* ボタンが結ばれる瞬間に外へ散る光の粒。一度きりで、そのあとは消える。 */
.bookmark-mote {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 4px;
  height: 4px;
  margin: -2px;
  border-radius: 50%;
  background: #fffdf4;
  box-shadow: 0 0 8px 2px rgba(255, 244, 205, 0.9);
  pointer-events: none;
  animation: bookmarkMote 0.9s ease-out var(--delay, 0s) both;
}
@keyframes bookmarkMote {
  0% { opacity: 0; transform: translate(0, 0) scale(0.4); }
  25% { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--dx, 0), var(--dy, 0)) scale(1.1); }
}

/* ============================================================
   ストーリー画面（簡易ビジュアルノベル風）
============================================================ */
.story-screen {
  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  /* 以前は画面下部に寄せていたが、キャラトーク表示は画面中央に置きたい
     という要望を受けて中央寄せに変更。「次へ」ヒントだけは.story-next-hint
     側で絶対配置にして画面下部に固定している。 */
  justify-content: center;
  padding: 24px;
  gap: 12px;
  cursor: pointer;
  background:
    radial-gradient(ellipse at 50% 20%, rgba(79, 167, 156, 0.10) 0%, transparent 55%),
    var(--bg-void);
  /* 場面転換（タイトル/バトルからの切り替わり）に一呼吸を持たせる、
     画面全体の控えめなフェードイン。 */
  animation: storyScreenIn 0.5s ease-out both;
}
@keyframes storyScreenIn {
  0% { opacity: 0; }
  100% { opacity: 1; }
}

/* 硯友社パート（序章後編）専用の背景演出。バトル画面と同じ図書室イラスト
   （battle-library.jpg）を使い、「本棚の色がどこか褪せて見えた」という
   ナレーションに合わせてモノクロ寄りに沈めておき、ボスを倒すと徐々に
   色が戻る演出にする（.story-screen-restoredはCHAPTER1_VICTORYの1行目
   ＝「色彩が戻っていく」の表示と同時にJS側で付与される）。
   本文・立ち絵まで色褪せると読みにくくなるため、背景専用の::beforeレイヤー
   （z-index:0）に画像とfilterをまとめ、実際のコンテンツ側（z-index:1）は
   影響を受けないようにしている。 */
/* 章ごとの背景は「--scene-bg にその章の画像を入れたシーンクラス」を
   ストーリー画面とバトル画面の両方に付けることで切り替える（app.js が
   episode.scene から `scene-{id}` というクラス名を組み立てて付与する）。
   --scene-bg が未設定の場面（序章前編の図書館など）では none になり、
   下地の --bg-void だけが見える。 */
.scene-kenyusha { --scene-bg: url('../assets/backgrounds/battle-library.jpg'); }
.scene-myojo { --scene-bg: url('../assets/backgrounds/myoujou_ongakudou.jpg'); }
.scene-shizenshugi { --scene-bg: url('../assets/backgrounds/shizenshugi_no_kouya.jpg'); }
.scene-buraiha { --scene-bg: url('../assets/backgrounds/burai_ha_no_urashouji.jpg'); }
.scene-tantei { --scene-bg: url('../assets/backgrounds/tantei_no_kirimachi.jpg'); }
.scene-sanninjogo { --scene-bg: url('../assets/backgrounds/sanninjogo_zashiki.jpg'); }
.scene-kokou { --scene-bg: url('../assets/backgrounds/kokou_no_itadaki.jpg'); }
.scene-kuhaku { --scene-bg: url('../assets/backgrounds/kuhaku_no_chushin.jpg'); }

/* 終章「空白の中心」の背景は、素材そのものがほぼ白黒（あらゆる物語が色を
   失った場所という設定に沿った絵）。他の章と同じ grayscale(1) を掛けても
   見た目が変わらず、撃破後に色が戻る演出が成立しない。
   そこで終章だけは「色を抜く」のではなく「白く沈ませる」方向で暗くしておき、
   撃破時に明るさとわずかな色味が戻ることで変化を感じられるようにする。 */
.story-screen.scene-kuhaku::before {
  filter: grayscale(1) brightness(0.42) contrast(0.9);
}
.story-screen.scene-kuhaku.story-screen-restored::before {
  filter: grayscale(0) brightness(1.2) contrast(1.05) sepia(0.15);
}

.story-screen {
  background: var(--bg-void);
}
.story-screen::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  /* 画像（battle-library.jpg）は1920×1080の横長素材のため、縦長のスマホ画面
     いっぱい（inset:0）をcoverで覆おうとすると、縦横比の差が大きすぎて
     画像の中央付近をごく一部（横幅の3割以下）に切り出す形になり、
     「背景が拡大しすぎ」て見える不具合があった。高さを画面上部の一定範囲
     （最大500px）に制限し、その下端をvar(--bg-void)へグラデーションで
     溶け込ませることで、切り出し倍率を抑えつつ自然にトークボックス側へ
     つながるようにした。 */
  height: min(60vh, 500px);
  /* 負のz-indexにすることで、position指定のない子要素（.story-narration等の
     absolute配置ではない要素も含む）より確実に背面へ回す。0だと逆に
     テキストより手前に描画されてしまう（CSS2.1の重なり順：static配置の
     子要素は「stack level 0の positioned要素」より先に描画されるため）。 */
  z-index: -1;
  /* var(--scene-bg, none) にしておくと、シーンクラスが付いていない画面でも
     background-image の指定として妥当（none）になり、宣言全体が無効化されない。 */
  background-image:
    linear-gradient(180deg, rgba(12, 11, 20, 0.35) 0%, rgba(12, 11, 20, 0.75) 70%, var(--bg-void) 100%),
    var(--scene-bg, none);
  background-size: cover, cover;
  background-position: center 30%, center 30%;
  background-repeat: no-repeat, no-repeat;
  /* grayscale(0.9)だと元の色調（茶系の書斎）とあまり変わらず見え、ボス撃破後に
     色が戻る演出に気づきにくいとの指摘があった。完全な白黒(grayscale(1))に
     してから戻す方が、before/afterの差がはっきりわかる。 */
  filter: grayscale(1) brightness(0.5);
  transition: filter 1.8s ease;
}
.story-screen.story-screen-restored::before {
  /* 単にgrayscale(0)に戻すだけだと、素材自体が元々渋い茶系の色味のため
     「色が戻った」感が乏しく気づきにくいとの指摘があった。彩度・明るさを
     素の状態より少し持ち上げて、変化をはっきり感じられるようにする。 */
  filter: grayscale(0) brightness(1.15) saturate(1.3);
}

/* 行送り時、表示中の要素（ナレーション or トークボックス）を一度フェード
   アウトさせてから中身を差し替える「間」の演出。中身の差し替え自体は
   renderLine()側で行い、このクラスは差し替え直前の一瞬だけ付与される。 */
.story-fade-out { transition: opacity 0.15s ease; opacity: 0; }

/* 地の文（ナレーション、speaker:null）専用の表示。誰かのセリフの時の
   枠つきトークボックス（.story-layout）とは切り替え式で、こちらは枠も
   背景パネルも持たず、画面中央にテキストだけがふわっと浮かび上がる
   演出にする（クリックでの進行はstory-screen全体で拾うため
   pointer-events:noneにして裏の.story-screenのクリックを妨げない）。 */
.story-narration {
  position: absolute;
  z-index: 1; /* 立ち絵の層より手前 */
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: calc(100% - 64px);
  max-width: 640px;
  text-align: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 21px;
  line-height: 2;
  color: var(--text-main);
  text-shadow: 0 2px 16px rgba(0,0,0,0.7);
  pointer-events: none;
  /* .story-textと同じ理由（孤立行対策）。中央揃えテキストは特に
     行の長さがバラつくと目立つため恩恵が大きい。 */
  text-wrap: balance;
}
.story-narration[hidden] { display: none; }
.story-narration-in { animation: storyNarrationRise 0.7s ease-out both; }
@keyframes storyNarrationRise {
  0% { opacity: 0; transform: translate(-50%, -50%) translateY(16px); }
  100% { opacity: 1; transform: translate(-50%, -50%) translateY(0); }
}

/* 立ち絵＋テキストボックスの横並びレイアウト。話者が判定できない
   地の文（ナレーション）の場合は .story-portrait が hidden のまま。 */
.story-layout {
  /* 立ち絵の層（.story-actor-layer）より手前に置く */
  position: relative;
  z-index: 1;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 20px;
  width: 100%;
  max-width: 900px;
  margin: 0 auto;
}
/* .story-layoutはクラスでdisplay:flexを指定しているため、[hidden]のUA既定
   スタイル(display:none)より詳細度で並び、そのままだと打ち消されてしまう。
   ナレーション表示時に確実に隠すため明示する。 */
.story-layout[hidden] { display: none; }
.story-layout-in { animation: storyLayoutRise 0.4s ease-out both; }
@keyframes storyLayoutRise {
  0% { opacity: 0; transform: translateY(10px); }
  100% { opacity: 1; transform: translateY(0); }
}

.story-portrait-slot {
  width: 170px;
  height: 240px;
  flex-shrink: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

/* 高さを基準に揃える（幅ではなく）。立ち絵素材は横幅の差が大きい
   （例：泉鏡花は着物の裾が広く他キャラより幅広）が、縦方向の高さは
   素材間でほぼ揃っているため、height基準でスケールすることで
   元画像を修正しなくても見かけ上のキャラサイズを他キャラと揃えられる。
   幅がスロットより広い場合は .story-portrait-slot 側がoverflow既定値(visible)
   のためはみ出しても切れない。 */
.story-portrait {
  height: 100%;
  width: auto;
  /* 生成画像の縁にうっすら白い滲み（ハロー）が乗っていることがあるため、
     隠すのではなく輪郭に意図的な白フチを重ねて「狙った縁取り」に見せる
     （元画像は直さない）。drop-shadowを4方向に0blurで重ねるピクセルアウトライン
     の定番手法。 */
  filter:
    var(--sprite-outline)
    drop-shadow(0 12px 18px rgba(0,0,0,0.5));
  animation: portraitIn 0.25s ease-out;
}
.story-portrait[hidden] { display: none; }

@keyframes portraitIn {
  0% { opacity: 0; transform: translateY(10px); }
  100% { opacity: 1; transform: translateY(0); }
}

.story-box {
  max-width: 620px;
  width: 100%;
  flex: 1 1 auto;
  min-width: 0;
  background: linear-gradient(180deg, var(--bg-panel-light), var(--bg-panel));
  border: 1px solid var(--border-soft);
  border-radius: 14px;
  padding: 26px 30px;
  /* 以前は高さを固定し、はみ出す分を内部スクロールにしていたが、セリフが
     長いキャラでスクロールが発生し窮屈だった。テキスト量に応じて自然に
     伸びるようにし、min-heightだけ設けて短い行でも極端に小さくならない
     ようにする。行送り時は一度フェードアウトしてから中身を差し替える
     （.story-fade-out）ため、高さが変わる瞬間は見えずガタつかない。
     max-heightは異常に長い文章が来た場合の保険で、通常は到達しない。 */
  min-height: 160px;
  max-height: 62vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 12px 32px rgba(0,0,0,0.4);
  position: relative;
}

.story-speaker {
  display: inline-block;
  /* .story-box が flex-direction:column のため、align-items:stretch の
     デフォルトでこのバッジが横幅いっぱいに引き伸ばされてしまう。
     inline-blockだけでは防げないため明示的に指定する。 */
  align-self: flex-start;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 15px;
  color: var(--gold-light);
  background: rgba(202, 164, 86, 0.12);
  border: 1px solid rgba(202, 164, 86, 0.4);
  padding: 4px 16px;
  border-radius: 20px;
  margin-bottom: 14px;
}

/* ルビ（<ruby><rt>）。本文の明朝と馴染むよう色をやや落とし、主張させない。
   ルビを振った語だけ書体や太さを変えないこと（本文から浮いて、そこだけ
   強調しているように見えてしまう）。
   【行間について】ルビは行の高さを押し広げるので、何もしないとルビのある行
   だけ間延びして見える。rt 自身の行の高さを 1 に詰めることで、親文字のすぐ上に
   寄せつつ増分を抑えている。**この line-height: 1 を外さないこと。**
   実測（本文21px・3行での1行あたりの増分）:
     rtのem   .story-text(行間1.8)   .story-narration(行間2.0)
     0.42     +0.7px                 +0.0px
     0.50     +1.3px                 +0.3px   ← 採用
     0.60     +2.0px                 +1.0px
     0.70     +2.3px                 +1.3px
   本文側の行間にもともと余裕があるため、0.5em でも1px程度しか増えない。
   これ以上小さくしてもスマホで読めなくなるだけで、行間の得は1px未満しかない。
   逆に大きくしたい場合も、増分が2pxを超えると行のガタつきが見え始める。 */
/* 【書体について】ファイル冒頭の `* { font-family: var(--font-body) }` は
   <ruby> や <rt> にも直接当たるため、何もしないと明朝の本文の中で
   ルビを振った語だけがゴシックで描かれる（親からの継承より、要素に直接
   当たっている宣言の方が強いため）。結果、ルビを振った箇所だけが太く
   浮いて見え、そこを強調しているように読めてしまう。
   inherit に戻すことで、本文がどの書体でも親文字とルビが追従する。 */
/* 【ルビに <ruby>/<rt> を使わないこと】
   <ruby> は環境ごとにレイアウトの解釈が大きく割れる。実際に
   ①素の <ruby>：スマホで行間が広がる
   ②font-size を詰める：変わらず
   ③ruby/rt に line-height:1：Chromeでは収まるがスマホでは収まらない
   ④rt を position:absolute：行間は直ったが、スマホでルビが親文字に重なった
   と、四度作り直している。原因は display:ruby / ruby-text という特殊な
   表示種別で、position や display の指定がどこまで効くかが環境依存なため。
   普通の <span> なら特殊な規則が一切効かないので、どの環境でも同じに組める。
   データ側（data/*.js）は
     <span class="ruby-word">親文字<span class="ruby-read">よみ</span></span>
   の形で書く。**<ruby>タグに戻さないこと。** */

/* 【ルビは行の流れから外して絶対配置する】
   ルビを普通に組むと、ブラウザは「親文字の行の高さ」の上にルビの高さを
   積み増すため、ルビのある行だけ間隔が広がってガタつく。
   font-size を詰める・ruby と rt に line-height:1 を指定する、という順で
   試したが、Chromeでは収まってもスマホの実機では収まらなかった
   （実測で ルビのある行53pt / ない行35pt）。行の高さの計算は環境ごとに
   解釈が割れるため、値の調整では潰しきれない。
   rt を position:absolute にすると行のボックスの計算から完全に外れるので、
   どの環境でも行の高さは親文字だけで決まり、ルビの有無で変わらなくなる。
   **この position:absolute を外さないこと。** 外すと行間のガタつきが再発する。

   副次的な効果と注意:
   - 親文字がルビの幅に合わせて広がらなくなる（「牛 肉」のような字間の
     間延びが起きない）
   - 逆に、読みが親文字より長いと左右にはみ出す。1文字に4拍以上のルビ
     （頂→いただき等）は隣の文字の上に少しかかるが、その位置は元々余白なので
     実害はない。ルビの振り分け自体を増やす時だけ、極端に長い読みに注意する */
.ruby-word {
  font-family: inherit;
  position: relative;
  display: inline-block;
  line-height: 1;
  /* 親文字の下端が周囲の文字とずれないよう、ベースラインで揃える。 */
  vertical-align: baseline;
}
.ruby-read {
  font-family: inherit;
  position: absolute;
  left: 50%;
  /* 親文字の箱の上端からさらに少し持ち上げる。100%だけだと、字の輪郭が
     箱の上端ぎりぎりまで来ている書体でルビが接して見えるため。 */
  bottom: calc(100% + 0.04em);
  transform: translateX(-50%);
  font-size: 0.5em;
  line-height: 1;
  white-space: nowrap;
  color: var(--text-sub);
  user-select: none;
  pointer-events: none;
}
/* ルビが入っても行の高さが変わらないよう、本文側にあらかじめ1行分の余裕を
   持たせてある（.story-text / .story-narration の line-height）。
   ルビの有無で行がガタつく場合は、rt の font-size ではなく本文側の
   line-height を調整すること。 */

.story-text {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 21px;
  line-height: 1.8;
  color: var(--text-main);
  white-space: pre-wrap;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  /* ルビは行の外へはみ出して描かれる（.ruby-read は position:absolute）ため、
     1行目のルビだけが overflow-y:auto の切り取りに引っかかって上が欠ける。
     ルビ1つぶんの高さ（0.5em）＋わずかな余裕を内側の余白として確保する。
     この余白を消すなら、同時に overflow の指定も見直すこと。 */
  padding-top: 0.62em;
  /* 素朴な折り返しだと「る。」のような1〜2文字だけの孤立行ができやすいため、
     当初text-wrap:balance、次いでprettyで対策を試みた。だが.story-textは
     左揃えのため、どちらも行の長さを揃える／整えるために実際の幅より
     狭い「使用幅」を選ぶことがあり、実機（スマホ）で右側にだけ不自然な
     余白ができる不具合が繰り返し報告された（中央揃えの.story-narration等
     では左右対称になるため問題にならない）。左揃えテキストでは常に
     全幅を使う保証がないと再発するため、text-wrapの指定自体をやめ、
     孤立行のリスクより「右側が不自然に空く」方を確実に回避することを
     優先した。 */
}

/* .story-screenをcenter寄せに変えた際、このヒントも一緒に中央へ
   引っ張られてしまわないよう、絶対配置で画面下部に固定する。 */
/* ============================================================
   会話パートに立たせておく相手の立ち絵（終章の終焉の編者）
   ============================================================
   戦闘が終わっても姿が消えない相手を、勝利会話の間ずっと画面に残すための層。
   台詞と重ならないよう、テキストより後ろ・画面の上寄りに置き、
   薄く表示して背景側の存在として扱う。 */
.story-actor-layer {
  position: absolute;
  left: 0;
  right: 0;
  /* 画面の上端に接しないよう少し下げる（立ち絵の頭が切れて見えるため） */
  top: 3%;
  height: 55%;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 0;
}
.story-actor {
  max-height: 100%;
  max-width: 62%;
  width: auto;
  object-fit: contain;
  opacity: 0.55;
  filter: drop-shadow(0 12px 28px rgba(0,0,0,0.6));
  animation: storyActorIn 1.2s ease-out both;
}
@keyframes storyActorIn {
  from { opacity: 0; }
  to { opacity: 0.55; }
}
/* 「輪郭がほどけ、白紙のローブが無数の頁に還って舞い上がった」。
   倒されて崩れるのではなく空へ還る場面なので、下へ落とさず、
   輪郭を滲ませながら上へ薄れていく動きにしている。 */
.story-actor-dissolving .story-actor {
  animation: storyActorDissolve 2.6s ease-in forwards;
}
@keyframes storyActorDissolve {
  0% { opacity: 0.55; filter: blur(0); transform: translateY(0) scale(1); }
  55% { opacity: 0.3; filter: blur(3px); transform: translateY(-14px) scale(1.02); }
  100% { opacity: 0; filter: blur(10px); transform: translateY(-46px) scale(1.05); }
}
/* 還っていく頁。--x（横へのばらつき）・--delay・--spin は app.js が
   一枚ずつ違う値を入れるので、同じ動きの繰り返しには見えない。 */
.story-page-burst {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.story-page {
  position: absolute;
  bottom: 22%;
  width: 12px;
  height: 16px;
  background: linear-gradient(160deg, #f6f1e2, #cfc7b2);
  box-shadow: 0 1px 4px rgba(0,0,0,0.45);
  opacity: 0;
  animation: storyPageRise 2.8s ease-out var(--delay, 0s) forwards;
}
@keyframes storyPageRise {
  0% { opacity: 0; transform: translate(0, 0) rotate(0deg); }
  15% { opacity: 0.9; }
  100% {
    opacity: 0;
    transform: translate(calc(var(--x, 0) * 120px), -58vh) rotate(var(--spin, 180deg));
  }
}

/* 「無題の書」を開く演出。14コマのスプライトシートを background-position で
   1コマずつ送る（送りは app.js の playBookEffect）。地の文は画面中央に出るので、
   本はその上に置き、文章と重ならない高さで止める。 */
.story-book {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 200px;
  height: 176px;
  transform: translate(-50%, -250px);
  background-image: url('../assets/effects/book-flip.png');
  background-repeat: no-repeat;
  background-position: 0 0;
  image-rendering: pixelated;
  filter: drop-shadow(0 10px 22px rgba(0,0,0,0.6));
  pointer-events: none;
  z-index: 2;
  animation: storyBookIn 0.3s ease-out both;
}
@keyframes storyBookIn {
  from { opacity: 0; transform: translate(-50%, -238px); }
  to { opacity: 1; transform: translate(-50%, -250px); }
}
.story-book-out { animation: storyBookOut 0.65s ease-in forwards; }
@keyframes storyBookOut {
  from { opacity: 1; transform: translate(-50%, -250px); }
  to { opacity: 0; transform: translate(-50%, -272px); }
}

/* エンドロールのあと、本が閉じるところだけを見せる画面。
   .story-book を流用するので、同じ位置関係（中央やや上）になるよう
   ストーリー画面と同じ土台にしてある。 */
.return-screen {
  position: relative;
  min-height: 100vh;
  background: var(--bg-void);
  overflow: hidden;
}
/* こちらは画面に本しか出ないので、文章を避ける必要がなく中央に置ける。
   入りのアニメーションも専用に持たせること。storyBookIn を流用すると、
   その終了状態（translate(-50%, -238px)）がアニメーション優先で
   transform の指定を上書きし、本が画面上部に寄ったままになる。 */
.return-screen .story-book {
  animation: returnBookIn 0.5s ease-out both;
}
@keyframes returnBookIn {
  from { opacity: 0; transform: translate(-50%, calc(-50% + 14px)); }
  to { opacity: 1; transform: translate(-50%, -50%); }
}

/* ============================================================
   世界を移る光（app.js の playLightBurst）
   ============================================================
   序章で文界へ落ちる場面と、終章で現実へ帰る場面に同じものを当てている。
   「同じ光で行き、同じ光で帰る」ことが対になっているので、片方だけ色や
   速さを変えないこと。
   body直下にfixedで置くのは、画面の差し替え（clearRoot）を跨いで白を
   保ったまま裏の画面を入れ替えるため。 */
.world-shift {
  position: fixed;
  inset: 0;
  z-index: 200;
  pointer-events: none;
  overflow: hidden;
}
.world-shift-out { animation: worldShiftOut 0.85s ease-out forwards; }
@keyframes worldShiftOut { from { opacity: 1; } to { opacity: 0; } }

/* 本から湧いて放射状に散る光の粒。--dx/--dy/--delay/--size は
   app.js が一粒ずつ違う値で与える。 */
.world-shift-mote {
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--size, 3px);
  height: var(--size, 3px);
  margin: calc(var(--size, 3px) / -2);
  border-radius: 50%;
  background: #fffdf4;
  box-shadow: 0 0 8px 2px rgba(255, 249, 224, 0.9);
  opacity: 0;
  animation: worldShiftMote 1.4s ease-out var(--delay, 0s) both;
}
@keyframes worldShiftMote {
  0% { opacity: 0; transform: translate(0, 0) scale(0.4); }
  22% { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--dx, 0), var(--dy, 0)) scale(1.5); }
}

/* 白い膜。中心から広がって画面を覆う。覆いきる時間は app.js の
   LIGHT_COVER_MS と揃えること（ずれると、まだ白くないうちに裏の画面が
   差し替わって切り替わりが見えてしまう）。 */
.world-shift-veil {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 50%, #fffdf6 0%, #fff9e8 55%, #fff7e0 100%);
  opacity: 0;
  transform: scale(0.2);
  animation: worldShiftVeil 1.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes worldShiftVeil {
  0% { opacity: 0; transform: scale(0.2); }
  38% { opacity: 0.55; }
  100% { opacity: 1; transform: scale(2.4); }
}

/* 「輪郭の薄れかけた娘が座っていた」（番外編・樋口一葉）。
   読まれなくなって存在が消えかけている状態を、立ち絵の薄さと
   ゆっくりした明滅で表す。倒れて消えるのではなく「消えかけたまま在る」
   ので、動きは大きくせず、ただ頼りなく揺らぐ程度にとどめる。
   立ち直る行（effect: 'characterRestored'）でクラスが外れ、
   transition で自然に濃さが戻る。 */
.story-portrait-fading {
  opacity: 0.42;
  filter: grayscale(0.45) blur(0.6px);
  animation: portraitFlicker 4.2s ease-in-out infinite;
}
@keyframes portraitFlicker {
  0%, 100% { opacity: 0.42; }
  38% { opacity: 0.26; }
  62% { opacity: 0.5; }
}
.story-portrait, .char-intro-portrait {
  transition: opacity 0.8s ease-out, filter 0.8s ease-out;
}

.story-next-hint {
  position: absolute;
  left: 24px;
  right: 24px;
  bottom: 20px;
  max-width: 760px;
  width: calc(100% - 48px);
  margin: 0 auto;
  text-align: right;
  font-size: 12px;
  color: var(--text-faint);
  animation: hintBlink 1.6s ease-in-out infinite;
}

/* pause 付きの行を自動送りしている間は、押しても進まないので案内を消す。
   出したままだと「押しているのに反応しない」と受け取られてしまう。 */
.story-screen-auto .story-next-hint {
  animation: none;
  opacity: 0;
  transition: opacity 0.3s ease;
}

@keyframes hintBlink {
  0%, 100% { opacity: 0.4; }
  50% { opacity: 1; }
}

/* ============================================================
   キャラクター初登場カットイン
   必殺技カットイン（.chuuni-cutin、斜めの帯が高速でスライドインする
   中二病演出）とは意図的に違うトーンにする：金枠のプロフィールカードが
   暗転の上にふわっと現れる、落ち着いた「図鑑ページを開く」ような見せ方。
   閉じるボタン、またはカード外（オーバーレイ自体）のクリックでも閉じる。
============================================================ */
.char-intro-overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(6, 5, 10, 0.82);
  cursor: pointer;
  animation: charIntroFadeIn 0.25s ease-out both;
}
.char-intro-overlay-out { animation: charIntroFadeOut 0.2s ease-in forwards; }
@keyframes charIntroFadeIn { 0% { opacity: 0; } 100% { opacity: 1; } }
@keyframes charIntroFadeOut { 0% { opacity: 1; } 100% { opacity: 0; } }

.char-intro-card {
  position: relative;
  width: min(360px, 100%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 32px 28px 24px;
  background: linear-gradient(180deg, var(--bg-panel-light), var(--bg-panel));
  /* 枠が細く頼りないとの指摘を受けて4pxに。厚みのある額縁のように見せる。 */
  border: 4px solid var(--gold);
  border-radius: 18px;
  box-shadow: 0 0 0 1px rgba(202, 164, 86, 0.25), 0 24px 60px rgba(0,0,0,0.6), 0 0 40px rgba(202, 164, 86, 0.15);
  cursor: default;
  animation: charIntroCardIn 0.35s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes charIntroCardIn {
  0% { opacity: 0; transform: translateY(18px) scale(0.94); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}

.char-intro-close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 30px;
  height: 30px;
  min-width: 30px;
  min-height: 30px;
  border-radius: 50%;
  background: var(--bg-panel);
  border: 1px solid var(--border-soft);
  color: var(--text-sub);
  font-size: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.char-intro-close:hover { border-color: var(--gold); color: var(--gold-light); }

.char-intro-portrait-slot {
  width: 100%;
  height: 200px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.char-intro-portrait {
  height: 100%;
  width: auto;
  image-rendering: pixelated;
  filter:
    var(--sprite-outline)
    drop-shadow(0 14px 20px rgba(0,0,0,0.5));
}

.char-intro-name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 26px;
  color: var(--gold-light);
  text-shadow: 0 0 16px rgba(202, 164, 86, 0.4);
}
.char-intro-kana {
  font-size: 13px;
  letter-spacing: 2px;
  color: var(--text-sub);
}

.char-intro-tags {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
  margin-top: 6px;
}
.char-intro-tag {
  /* ルビ付きのピルは背が高くなり、横のピルも stretch で同じ高さに揃う。
     中身を中央に置いておかないと、ルビの無い方だけ文字が上に寄って見える。 */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  padding: 3px 12px;
  border-radius: 12px;
  background: rgba(202, 164, 86, 0.12);
  border: 1px solid rgba(202, 164, 86, 0.4);
  color: var(--gold-light);
}
/* ルビは行の外へはみ出して描かれるので、余白の狭いピルの中で使うと枠の上に
   乗ってしまう（流派名の「明星派」等）。ルビを含む時だけ上を広げる。
   広げた分はルビが埋めるので、見た目の重心は上下に偏らない。 */
.char-intro-tag:has(.ruby-word) {
  padding-top: 11px;
  padding-bottom: 4px;
}
/* 相関図の流派名はピルではないが、すぐ上に名前があるため同様に間を空ける。 */
.relationship-role:has(.ruby-word) { margin-top: 8px; }

/* キャラ紹介カードの「代表作」欄。role/schoolタグ（金枠のピル）とは
   見た目のトーンを変え、書物の目次のような静かな並びにする。 */
.char-intro-works {
  margin-top: 8px;
  padding-top: 10px;
  border-top: 1px solid rgba(255,255,255,0.08);
  width: 100%;
  text-align: center;
}
.char-intro-works-label {
  display: block;
  font-size: 11px;
  letter-spacing: 2px;
  color: var(--text-faint);
  margin-bottom: 4px;
}
.char-intro-works-list {
  font-family: var(--font-display);
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-main);
}


/* 必殺技解説カード（showTechniqueExplainCard）のフレーバーテキスト用。
   キャラ紹介カードには無い要素だが、同じ.char-introクラス群の一部として
   自然になじむよう本文サイズ・行間を合わせている。 */
.char-intro-desc {
  margin-top: 4px;
  font-size: 14px;
  line-height: 1.7;
  color: var(--text-sub);
  text-align: center;
}

/* ============================================================
   バトル画面：全体レイアウト
============================================================ */
.battle-screen {
  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  /* 背景イラストは章ごとに切り替える（--scene-bg。上部のシーンクラス定義を参照）。
     app.js がバトル画面のラッパーにもシーンクラスを付けるため、カスタム
     プロパティの継承でここに届く。シーン未指定の場面では図書館の画像に
     フォールバックする。上に暗転グラデーションを重ねてUIの可読性を確保する。 */
  background-image:
    linear-gradient(180deg, rgba(12, 11, 20, 0.55) 0%, rgba(12, 11, 20, 0.75) 55%, rgba(12, 11, 20, 0.92) 100%),
    radial-gradient(ellipse at 50% 0%, rgba(138, 92, 201, 0.14) 0%, transparent 55%),
    var(--scene-bg, url('../assets/backgrounds/battle-library.jpg'));
  background-size: cover, cover, cover;
  background-position: center, center, center 30%;
  background-repeat: no-repeat, no-repeat, no-repeat;
  background-color: var(--bg-deep);
  overflow: hidden;
}

.battle-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 20px;
  background: rgba(12, 11, 20, 0.6);
  border-bottom: 1px solid var(--border-soft);
  font-size: 13px;
  color: var(--text-sub);
  flex-shrink: 0;
}

.battle-scene-label { font-weight: 700; }
.battle-topbar-right { display: flex; align-items: center; gap: 10px; }
.battle-round-label {
  font-family: var(--font-display);
  color: var(--gold-light);
  font-weight: 800;
  letter-spacing: 1px;
}

.settings-btn {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: rgba(255,255,255,0.06);
  border: 1px solid var(--border-soft);
  color: var(--text-sub);
  font-size: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.settings-btn:hover { border-color: var(--gold); color: var(--gold-light); }

/* ============================================================
   設定パネル（歯車ボタン）。今はSE音量のみだが、.settings-rowを
   増やすだけでBGM設定等を追加できる構造にしてある。
============================================================ */
.settings-overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(6, 5, 10, 0.72);
  padding: 20px;
}
.settings-overlay[hidden] { display: none; }

.settings-card {
  position: relative;
  width: min(360px, 100%);
  background: linear-gradient(180deg, var(--bg-panel-light), var(--bg-panel));
  border: 1px solid var(--border-soft);
  border-radius: 14px;
  padding: 24px 22px;
  box-shadow: 0 20px 48px rgba(0,0,0,0.5);
}
.settings-title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 18px;
  color: var(--gold-light);
  margin-bottom: 16px;
}
.settings-close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--bg-panel);
  border: 1px solid var(--border-soft);
  color: var(--text-sub);
  font-size: 14px;
}
.settings-close:hover { border-color: var(--gold); color: var(--gold-light); }
.settings-row {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 0;
}
.settings-row + .settings-row { border-top: 1px solid var(--border-soft); }
.settings-row-label { font-size: 14px; color: var(--text-main); }
.settings-range { width: 100%; accent-color: var(--violet-light); }
.settings-note {
  margin-top: 12px;
  font-size: 12px;
  color: var(--text-faint);
}

.battle-stage {
  /* かつては flex:1 で天井まで引き伸ばしていたため、敵/味方エリアと
     ログ/コマンドバーの間に大きな空白ができていた（DESIGN.md 5章）。
     コンテンツ量に応じた自然な高さに収め、余った縦スペースは
     battle-log-panel（flex:1）側に持たせて意味のあるコンテンツで埋める。 */
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* 縦の狭い画面ではここも詰める。敵エリアと味方エリアの間の余白は、
     画面が低いほど「間延び」として効いてくる。 */
  gap: clamp(12px, 2.4vh, 22px);
  padding: clamp(10px, 2.2vh, 20px) 16px 8px;
  position: relative;
}

/* --- 敵エリア --- */
.enemy-area {
  display: flex;
  justify-content: center;
  width: 100%;
}

.enemy-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 14px 26px;
  transition: opacity 0.4s ease;
}
.enemy-panel.is-fainted { opacity: 0.25; filter: grayscale(1); }

.enemy-sprite-slot {
  width: 160px;
  height: 160px;
  display: flex;
  align-items: center;
  justify-content: center;
  filter: drop-shadow(0 10px 18px rgba(0,0,0,0.5));
  position: relative;
}

.enemy-sprite {
  height: 100%;
  width: auto;
  image-rendering: pixelated;
  filter:
    var(--sprite-outline);
}

/* 立ち絵がある敵（陳腐の亡霊など）専用のレイアウト。
   ・キャラクター絵は味方チビキャラと違い描き込まれたイラストのため、
     ドット絵向けのpixelated/白フチ縁取りは外し、代わりに不穏な発光を添える。
   ・「画面奥」の存在感を出すため大きく表示し、名前・HPは従来の下配置
     ではなくキャラに重ねるオーバーレイに変更。
   ・.enemy-areaに負のmargin-bottomを与え、手前の.party-areaが立ち絵の
     下半身にかぶるようにする（下半身が隠れてもよいという要望に対応）。
     ハードな重なりの継ぎ目が目立たないよう、立ち絵の下端はmask-imageで
     ふわっと透過フェードさせている。 */
/* 「絵をもっと大きく・下に移動、頭は隠れないように、パーティカードの
   後ろに半分程度なら隠れてもよい」という要望を受け、スロットをキャラの
   表示サイズより一回り大きくしてalign-items:flex-endでキャラを枠の
   下側にアンカーする（=枠上部に頭上の余白＝headroomができる）方式に
   統一した（従来はモバイル幅だけこの方式で、PC幅は等倍で頭上の余白が
   なかった）。 */
.enemy-area-portrait { margin-bottom: -167px; }
/* 敵エリアの大きさは画面の高さにも連動させる。固定pxのままだと、縦の狭い
   ノートPCで戦闘画面全体がファーストビューに収まらず、コマンドバーや履歴が
   画面外へ押し出されていた。縦に余裕のある画面では従来どおりの大きさを保ち、
   狭い画面でだけ縮むように min() で頭打ちにしてある。 */
.enemy-panel-portrait { position: relative; padding-bottom: min(130px, 10vh); }
.enemy-panel-portrait .enemy-sprite-slot {
  width: min(320px, 36vh);
  height: min(320px, 36vh);
  align-items: flex-end;
}
.enemy-panel-portrait .enemy-sprite {
  height: min(250px, 29vh);
  /* 敵の絵は縦長（終焉の編者）と横長（無理解の巨人・孤独の影）が混在する。
     高さだけを指定すると横長の絵が画面幅を超えてしまうため、横幅にも上限を
     置き、収まらない場合は object-fit で縮める。object-position: bottom で
     接地は保つ（下端が浮くと重なりの演出が崩れるため）。 */
  width: auto;
  max-width: min(86vw, 560px);
  object-fit: contain;
  object-position: bottom;
  image-rendering: auto;
  filter: drop-shadow(0 0 22px rgba(120, 140, 220, 0.35)) drop-shadow(0 14px 18px rgba(0,0,0,0.5));
  -webkit-mask-image: linear-gradient(to bottom, black 72%, transparent 96%);
  mask-image: linear-gradient(to bottom, black 72%, transparent 96%);
}
.enemy-panel-portrait .enemy-info {
  position: absolute;
  top: -14px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
}

/* ラスボス（終焉の編者）だけは、最終決戦の格として一回り大きく見せる。
   「戦闘画面だと遠くにいるように見える」という指摘への対応で、他のボスは
   従来の大きさのままにしてある。敵パネルには data-id が振られているので、
   立ち絵の大きさを敵ごとに変えたい場合はこの形で足していく。

   仕組みは3つの値の連動：
     ①.enemy-sprite の height … 絵そのものの大きさ
     ②.enemy-sprite-slot の height … 枠。絵は flex-end で下端に接地しているので、
       枠を高くするぶんだけ絵の「下端」が下がる（上端＝名前側は動かない）
     ③.enemy-area-portrait の margin-bottom … 枠を高くした分をそのまま
       負のマージンで引き戻す。これでパーティ欄の位置は変わらないまま、
       絵の下半身だけがパーティカードに重なる
   ②と③を同じ量だけ動かすのが要点で、片方だけ変えると画面全体が下にずれる。 */
.enemy-area-portrait:has(> .enemy-panel-portrait[data-id='shuen_no_hensha']) {
  margin-bottom: -267px;
}
/* 他のボスと同じく、縦の狭い画面では縮める。
   ここで縮めるのは②の枠だけで、③の margin-bottom は動かさないこと。
   枠が縮むと絵の下端も同じだけ上がるため、引き戻す量が同じなら重なり具合は
   変わらないまま、画面全体の高さだけが縮む（②と③を同量で動かすのは、
   party欄の位置を保ったまま絵を大きくしたい時の話）。 */
.enemy-panel-portrait[data-id='shuen_no_hensha'] .enemy-sprite-slot { height: min(420px, 47vh); }
.enemy-panel-portrait[data-id='shuen_no_hensha'] .enemy-sprite { height: min(350px, 39vh); }

/* デバフ（状態異常）一覧は敵絵の右下に重ねると顔やHPバーと干渉しやすいため、
   敵絵の左側に縦一列で並べる専用レイアウトにした（.enemy-status-rail）。
   .enemy-sprite-slotに対して絶対配置するので、パネルの縦幅計算には影響しない。 */
.enemy-status-rail {
  position: absolute;
  right: 100%;
  top: 50%;
  transform: translateY(-50%);
  margin-right: 10px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  max-width: 130px;
}

/* バグ修正（DESIGN.md 4.1/7章）：align-items:center のままだと子要素の
   .bar-row（flex内で.bar{flex:1}を含む）が最小幅に潰れ、HPバーがほぼ0幅で
   見えなくなっていた。align-items を stretch にして .bar-row の幅を確保し、
   併せて .ally-panel と同格の「カード」として視覚的存在感を揃える。 */
.enemy-info {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
  width: 240px;
  padding: 4px 16px;
}
.enemy-name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 18px;
  color: var(--text-main);
  text-align: center;
  text-shadow: 0 0 10px rgba(0,0,0,0.7), 0 1px 2px rgba(0,0,0,0.8);
}

/* --- 味方エリア --- */
.party-area {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px;
  width: 100%;
  max-width: 960px;
  /* ボスの立ち絵は負のマージンでパーティ欄の上に食い込ませているが、
     そのままだと絵が手前に来て味方の名前やHPを隠してしまう。
     カード側を前面に出し、ボスの下半身がカードの「後ろ」に回り込む
     ようにすることで、迫力を出しつつ情報は読める状態を保つ。 */
  position: relative;
  z-index: 3;
}

/* 幅は固定せず、収まる範囲で縮んで一列に並ぶようにする。
   280px固定だと4人編成のとき .party-area（max-width:960px）に収まらず
   3枚＋1枚の二段になり、それだけで150px近く縦に伸びていた。
   flex-basis を小さめに取って伸縮を許し、上限（max-width）で3人以下の時に
   間延びしないよう押さえる、という組み合わせにしてある。 */
.ally-panel {
  display: flex;
  align-items: center;
  gap: 12px;
  background: linear-gradient(135deg, var(--bg-panel-light), var(--bg-panel));
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  padding: 12px 16px;
  flex: 1 1 200px;
  max-width: 280px;
  min-width: 0;
  box-shadow: 0 4px 8px rgba(0,0,0,0.3);
  transition: border-color 0.3s ease, box-shadow 0.3s ease, opacity 0.4s ease;
}

.ally-panel.is-active {
  border-color: var(--gold);
  box-shadow: 0 0 0 1px var(--gold), 0 0 18px rgba(202, 164, 86, 0.35);
}

.ally-panel.is-fainted { opacity: 0.35; filter: grayscale(1); }

.ally-sprite-slot {
  width: 58px;
  height: 78px;
  flex-shrink: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  overflow: visible;
}

/* story-portrait と同じ理由でheight基準に統一し、輪郭に白フチを重ねる
   （4.1/白フチ対応）。.ally-sprite-slot は overflow:visible 済みなので
   幅がスロットよりわずかに広いキャラがいても切れない。 */
.ally-sprite {
  height: 100%;
  width: auto;
  image-rendering: pixelated;
  filter:
    var(--sprite-outline)
    drop-shadow(0 6px 8px rgba(0,0,0,0.45));
}

.ally-info { flex: 1; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.ally-name { font-family: var(--font-display); font-weight: 700; font-size: 18px; color: var(--text-main); }

/* width:100% を明示し、align-items:center を持つ親（.enemy-info 等）配下でも
   .bar{flex:1} が潰れないようにする（DESIGN.md Don't 7章）。 */
.bar-row { display: flex; align-items: center; gap: 6px; width: 100%; }
.bar-tag { font-size: 12px; color: var(--text-faint); width: 16px; flex-shrink: 0; }

.bar {
  position: relative;
  flex: 1;
  height: 10px;
  background: rgba(0,0,0,0.4);
  border-radius: 5px;
  overflow: hidden;
  border: 1px solid rgba(255,255,255,0.06);
}
.bar-fill {
  height: 100%;
  width: 0%;
  border-radius: 5px;
  transition: width 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}
.hp-bar .bar-fill { background: linear-gradient(90deg, #7fce6b, var(--hp-color)); }
.hp-bar .bar-fill.bar-fill-low { background: var(--hp-color-low); animation: hpLowPulse 0.9s ease-in-out infinite; }
@keyframes hpLowPulse { 0%,100% { filter: brightness(1); } 50% { filter: brightness(1.4); } }
.bunryoku-bar .bar-fill { background: linear-gradient(90deg, #2a6f68, var(--bunryoku-color)); }
.kihaku-bar .bar-fill { background: linear-gradient(90deg, #8a6a2c, var(--kihaku-color)); }

.bar-label {
  font-size: 12px;
  color: var(--text-sub);
  min-width: 28px;
  text-align: right;
  flex-shrink: 0;
}
.hp-row .bar-label { width: 64px; }
/* 筆(bunryoku)は素の数値（例:"0"〜"100"）、気(kihaku)はパーセント表記
   （例:"0%"〜"100%"）で文字数が異なり、min-widthだけだとラベル幅が値ごとに
   変わってバーの長さがガタつく。両者を同じ固定幅にして、バー本体(flex:1)の
   長さを常に揃える。HPだけタグが無い分バーが長くなるのは意図通りでよい。 */
.bunryoku-row .bar-label,
.kihaku-row .bar-label {
  width: 34px;
}

.status-badges { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 2px; min-height: 18px; }
.status-badge {
  font-size: 12px;
  padding: 2px 8px;
  border-radius: 10px;
  background: rgba(138, 92, 201, 0.18);
  border: 1px solid rgba(138, 92, 201, 0.4);
  color: var(--violet-light);
  white-space: nowrap;
}
.status-badge-ailment { background: rgba(217, 79, 79, 0.18); border-color: rgba(217, 79, 79, 0.45); color: #f0a3a3; }
.status-badge-flag { background: rgba(202, 164, 86, 0.16); border-color: rgba(202, 164, 86, 0.4); color: var(--gold-light); }

/* 状態異常の件数バッジ。デスクトップでは使わず、狭い画面（下のメディア
   クエリ参照）でだけ立ち絵右上に表示し、バフ/デバフが増えてもカードの
   縦方向を圧迫しないようにする。 */
.status-corner-badge {
  display: none;
}

/* ============================================================
   ログ・コマンドバー
============================================================ */
/* 直近5〜8行を保持する簡易履歴ログ（10章）。画面最下部に固定配置する。
   <details>/<summary> を使い、モバイルではタップで折りたたみ／展開できる
   ようにする（JS不要でネイティブにトグルできる。デフォルトのopen/close状態
   だけ battle-ui.js 側で画面幅に応じて切り替える）。
   閉じている間はsummaryの高さだけに自然に収まる（`height`ではなく`[open]`側にのみ
   固定高さを付けているため）。
   開いている間は `height`（`max-height`ではなく）を明示することで、flex列内の
   子（.battle-log-list, flex:1）に対して確定した主軸サイズを与えている。
   `max-height`だけだと.battle-log-panel自身の高さはコンテンツに応じて可変のまま
   （＝flexの主軸サイズが不定）になり、中の.battle-log-list（flex:1）は
   「残り空間を埋める」計算ができず自分のコンテンツ量まで伸びてしまう。結果として
   親のmax-height+overflow:hiddenで無言のまま切り詰められるだけでスクロールバーが
   出ず、古いログが読めなくなる不具合が実際に発生した（min-height:0だけでは
   直らなかった）。 */
.battle-log-panel {
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  background: rgba(10, 9, 16, 0.55);
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* 縦の狭い画面では履歴も詰める。開いた高さを変えたら、下の
   .battle-log-list の max-height も同じ式に揃えること（下のコメント参照）。 */
.battle-log-panel[open] {
  height: min(220px, 24vh);
}

.battle-log-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 14px;
  cursor: pointer;
  list-style: none;
  flex-shrink: 0;
  font-size: 13px;
}
.battle-log-summary::-webkit-details-marker { display: none; }
.battle-log-summary::marker { content: ''; }

.battle-log-summary-label {
  font-weight: 700;
  color: var(--gold-light);
  flex-shrink: 0;
  letter-spacing: 1px;
}

.battle-log-latest {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: right;
  color: var(--text-sub);
}

.battle-log-icon {
  flex-shrink: 0;
  color: var(--text-faint);
  transition: transform 0.2s ease;
}
.battle-log-panel[open] .battle-log-icon { transform: rotate(180deg); }

.battle-log-list {
  /* 検証の結果、<details>要素は display:flex を指定しても、非summary子要素に
     flex:1（＋min-height:0）を与えるだけでは実際の利用可能スペースまで
     縮んでくれない（Chromiumで実測済みの挙動）。そのため flex-grow に頼らず、
     .battle-log-panel[open] の height（220px/モバイル170px）から
     .battle-log-summary の実測高さ(37px)＋ボーダー(2px)を差し引いた値を
     max-height に直接指定している。これが無いと、ログが増え続けても
     .battle-log-list自身は中身ぶん際限なく伸び、親のoverflow:hiddenで
     無言のまま切り詰められるだけでスクロールバーが出ず、古いログが
     読めなくなる不具合が実際に発生した。 */
  max-height: calc(min(220px, 24vh) - 39px);
  overflow-y: auto;
  padding: 0 14px 10px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  border-top: 1px solid rgba(255,255,255,0.06);
  padding-top: 8px;
}

.battle-log-line {
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-faint);
}
.battle-log-line.is-latest { color: var(--text-main); font-weight: 700; }
/* 敵の攻撃由来の行だけ見分けやすいよう色を変える（味方の行と混ざって読みにくいとの指摘）。 */
.battle-log-line-enemy { color: var(--hp-color); }
.battle-log-line-enemy.is-latest { color: var(--hp-color-low); }

.battle-command-bar {
  position: relative;
  min-height: 110px;
  background: rgba(10, 9, 16, 0.75);
  border-top: 1px solid var(--border-soft);
  padding: 8px 16px 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.cmd-actor-label {
  font-size: 12px;
  color: var(--gold-light);
  font-weight: 700;
  letter-spacing: 1px;
}

.cmd-buttons {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  max-width: 900px;
}

/* コマンドボタン本体＋詳細（?）ボタン＋ツールチップをまとめるラッパー。
   ?ボタンをbutton要素の外に出すことで、button内にbuttonを入れ子にする
   （無効なHTML）を避けつつ、コマンド選択のクリックと詳細表示のタップを分離する。
   .cmd-item を display:flex にして中の .cmd-btn を高さいっぱいに伸ばすことで、
   同じ行内の他のボタン（効果テキストが2行になり自然と背が高くなるものがある）と
   高さが揃うようにする（flexの align-items:stretch は .cmd-buttons 側で
   デフォルト適用されるが、.cmd-item の子である .cmd-btn 自身が伸びないと
   見た目上は揃わないため、ここでも明示的に伸ばす）。 */
.cmd-item { position: relative; display: flex; }

.cmd-btn {
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  padding: 10px 18px;
  min-width: 128px;
  min-height: 48px;
  background: var(--bg-panel-light);
  border: 1px solid var(--border-soft);
  border-radius: 8px;
  color: var(--text-main);
  transition: transform 0.12s ease, border-color 0.2s ease;
}
.cmd-btn:hover:not(:disabled) { transform: translateY(-2px); border-color: var(--gold); }
.cmd-btn:disabled { opacity: 0.35; cursor: not-allowed; }

.cmd-title { font-family: var(--font-display); font-weight: 800; font-size: 16px; }
.cmd-sub { font-size: 13px; color: var(--text-sub); }
.cmd-effect { font-size: 13px; font-weight: 700; color: var(--text-sub); }

/* 戦闘開始直後「どこを押せばいいかわかりにくい」への対応。通常攻撃は
   毎ターン必ず選べる安全なコマンドなので、自分の手番の間はゆっくり
   光って視線を誘導する。ロック中（:disabled）は光らせない。 */
.cmd-normal { animation: cmdNormalAttract 1.7s ease-in-out infinite; }
.cmd-normal:disabled { animation: none; }
@keyframes cmdNormalAttract {
  0%, 100% { box-shadow: 0 0 0 rgba(232, 207, 142, 0); border-color: var(--border-soft); }
  50% { box-shadow: 0 0 16px rgba(232, 207, 142, 0.55); border-color: var(--gold-light); }
}
.cmd-normal .cmd-title { color: #e2ddf0; }
.cmd-skill .cmd-title { color: #6fd0c4; }
.cmd-skill .cmd-effect { color: #6fd0c4; }
.cmd-ultimate .cmd-title { color: var(--gold-light); }
.cmd-ultimate .cmd-effect { color: var(--gold-light); }
.cmd-ultimate:not(:disabled) { border-color: rgba(202, 164, 86, 0.5); box-shadow: 0 0 10px rgba(202, 164, 86, 0.25); }

/* 絆コンボ用バッジ。.battle-screen(position:relative)の右上に固定配置する
   専用スロット(.combo-badge-slot)にposition:absoluteで重ねるだけなので、
   出現/消滅しても通常攻撃・スキル・必殺技の3ボタン行(.cmd-buttons)の位置・
   折り返しには一切影響しない（誤クリック対策）。以前はコマンドバー直上に
   置いていたが、キャラの技名ポップアップ（パーティエリア直上に出る）と
   縦位置が近く見た目上ぶつかっていたため、パーティエリアから離れた右上の
   独立エリアに変更した。「特別な技」感を出すため横長ボタンではなく円形の
   宝珠風ボタン＋キラキラ明滅する光の粒（.combo-spark）で演出し、使うか・
   いつ使うかはプレイヤーの任意（タップした瞬間にそのまま発動する）。 */
.combo-badge-slot {
  position: absolute;
  top: 52px;
  right: 14px;
  z-index: 25;
}

.combo-badge-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  animation: comboBadgeIn 0.3s ease-out both;
}

/* .combo-badge と詳細(?)ボタン・ツールチップを重ねて配置するための、
   バッジと同寸法のポジショニングコンテナ。この箱の大きさ＝circleの見た目の
   大きさなので、丸のサイズを変えるときは.combo-badgeと両方直す。 */
.combo-badge-inner {
  position: relative;
  width: 92px;
  height: 92px;
}

.combo-badge {
  position: relative;
  width: 92px;
  height: 92px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at 34% 28%, #dcc2f9, #8a5cc9 55%, #4b3178 100%);
  border: 2px solid var(--violet-light);
  box-shadow: 0 0 16px rgba(138, 92, 201, 0.65), 0 6px 14px rgba(0,0,0,0.4), inset 0 2px 5px rgba(255,255,255,0.4);
  animation: comboOrbPulse 1.8s ease-in-out 0.3s infinite;
}
.combo-badge:hover:not(:disabled) { transform: scale(1.06); }
/* 発動条件は満たしているが今は実行者の手番ではない「待機中」の見た目。
   完全に消すのではなく淡く控えめにして「使えるがまだ押せない」ことを示す
   （手番ごとにバッジ自体が消えたり出たりする不具合の修正）。 */
.combo-badge:disabled { opacity: 0.55; cursor: not-allowed; animation: none; box-shadow: 0 0 8px rgba(138, 92, 201, 0.35); }
.combo-badge:disabled .combo-spark { animation-play-state: paused; opacity: 0; }

.combo-badge-icon {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 32px;
  color: #fff;
  text-shadow: 0 0 8px rgba(255,255,255,0.85), 0 1px 3px rgba(0,0,0,0.5);
}

/* 絆コンボの詳細(?)ボタン。.cmd-info-btn の見た目をそのまま流用し、
   PCはホバー／スマホはタップで.cmd-tooltipを開く（コマンドボタンと同じ仕組み）。
   バッジが画面右上に固定配置のため、通常の.cmd-tooltip（上方向・中央基準で開く）
   のままだと画面外にはみ出す。右上を基点に「下・右揃え」で開くよう上書きする。 */
.combo-info-btn { z-index: 2; }
.combo-badge-inner .cmd-tooltip {
  bottom: auto;
  top: calc(100% + 8px);
  left: auto;
  right: 0;
  transform: translateY(4px);
}
/* ホバー可能なデバイス（PC）だけ:hoverで開く。タッチデバイスは:hoverが
   タップ後も擬似的に残ることがあり、❓を押していないのに説明が出たり
   消えなかったりする不具合の原因になっていたため、hover:hoverでガードする。
   タップでの開閉は下の.is-open（メディアクエリなし＝常時有効）が担う。 */
@media (hover: hover) {
  .combo-badge-inner:hover .cmd-tooltip {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
  }
}
.combo-badge-inner .cmd-tooltip.is-open {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

/* キラキラ明滅する光の粒。円の周りに4つ、位置と明滅タイミングをずらして
   配置することで単調に見えない瞬きを作る。 */
.combo-spark {
  position: absolute;
  color: #fff;
  text-shadow: 0 0 6px rgba(255,255,255,0.95), 0 0 12px rgba(202,164,86,0.8);
  pointer-events: none;
  animation: comboSparkTwinkle 1.6s ease-in-out infinite;
}
.combo-spark-1 { top: -6px; right: 4px; font-size: 13px; animation-delay: 0s; }
.combo-spark-2 { bottom: 2px; left: -9px; font-size: 10px; animation-delay: 0.45s; }
.combo-spark-3 { top: 14px; right: -11px; font-size: 9px; animation-delay: 0.9s; }
.combo-spark-4 { bottom: -6px; left: 18px; font-size: 8px; animation-delay: 1.2s; }

.combo-badge-caption {
  font-size: 12px;
  font-weight: 700;
  line-height: 1.4;
  color: var(--violet-light);
  text-align: center;
  max-width: 130px;
  text-shadow: 0 0 6px rgba(0,0,0,0.85);
}

@keyframes comboBadgeIn {
  0% { opacity: 0; transform: translateY(-8px) scale(0.85); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes comboOrbPulse {
  0%, 100% { box-shadow: 0 0 16px rgba(138, 92, 201, 0.65), 0 6px 14px rgba(0,0,0,0.4), inset 0 2px 5px rgba(255,255,255,0.4); }
  50% { box-shadow: 0 0 28px rgba(138, 92, 201, 1), 0 6px 14px rgba(0,0,0,0.4), inset 0 2px 5px rgba(255,255,255,0.4); }
}
@keyframes comboSparkTwinkle {
  0%, 100% { opacity: 0; transform: scale(0.3) rotate(0deg); }
  50% { opacity: 1; transform: scale(1.2) rotate(25deg); }
}

/* 詳細（?）ボタン：ホバー（PC）／タップ（スマホ）どちらでも
   .cmd-tooltip に効果・フレーバーテキストを表示するトリガー（6章）。 */
.cmd-info-btn {
  position: absolute;
  top: -7px;
  right: -7px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--bg-panel);
  border: 1px solid var(--border-soft);
  color: var(--text-sub);
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
  min-width: 24px;
  min-height: 24px;
}
.cmd-info-btn:hover,
.cmd-info-btn.is-open { border-color: var(--gold); color: var(--gold-light); }

.cmd-tooltip {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  width: max-content;
  max-width: min(260px, 78vw);
  background: rgba(10, 9, 16, 0.97);
  border: 1px solid var(--border-soft);
  border-radius: 8px;
  padding: 10px 12px;
  box-shadow: 0 12px 32px rgba(0,0,0,0.4);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease, transform 0.15s ease;
  z-index: 30;
  text-align: left;
}
/* ホバー可能なデバイス（PC）だけ:hoverで開く。スマホ等タッチデバイスでは
   タップ時に:hoverが擬似的に効いてしまい、❓を押さなくても説明が出たり
   消えなかったりする不具合があったため、hover:hoverでガードする
   （タップでの開閉は.is-openが常時担当）。 */
@media (hover: hover) {
  .cmd-item:hover .cmd-tooltip {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
    pointer-events: auto;
  }
}
.cmd-tooltip.is-open {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
  pointer-events: auto;
}

.cmd-tooltip-concept {
  margin-bottom: 6px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border-soft);
  font-size: 11px;
  line-height: 1.5;
  color: var(--gold-light);
}
.cmd-tooltip-desc {
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-main);
}
.cmd-tooltip-origin {
  margin-top: 6px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-sub);
}

/* ============================================================
   キャラクターアニメーション（攻撃／被弾／戦闘不能／勝利）
   立ち絵は1枚絵のみのため、CSSアニメーションだけで動きを表現する。
============================================================ */
.ally-sprite, .enemy-sprite, .enemy-silhouette {
  transition: filter 0.15s ease;
}

@keyframes animAttackLunge {
  0% { transform: translateX(0) scale(1); }
  35% { transform: translateX(14px) scale(1.04) rotate(-2deg); }
  55% { transform: translateX(10px) scale(1.03); }
  100% { transform: translateX(0) scale(1); }
}
.anim-attack { animation: animAttackLunge 0.42s ease-out; }

@keyframes animHurtShake {
  0% { transform: translateX(0); filter: brightness(1) saturate(1); }
  15% { transform: translateX(-8px); filter: brightness(1.5) saturate(3) sepia(0.5) hue-rotate(-30deg); }
  30% { transform: translateX(7px); }
  45% { transform: translateX(-5px); }
  60% { transform: translateX(4px); filter: brightness(1.3) saturate(2.4) sepia(0.4) hue-rotate(-30deg); }
  100% { transform: translateX(0); filter: brightness(1) saturate(1); }
}
.anim-hurt { animation: animHurtShake 0.42s ease-out; }

@keyframes animFaintDrop {
  0% { transform: translateY(0) rotate(0deg); opacity: 1; }
  100% { transform: translateY(28px) rotate(8deg); opacity: 0.15; }
}
.anim-faint { animation: animFaintDrop 0.55s ease-in forwards; }

/* 「倒したのに姿は消えない」相手（enemies.js の defeatStaging: 'unbroken'）用。
   崩れ落ちるのではなく、力が抜けて色を失い、その場に立ち尽くしたまま残る。 */
@keyframes animUnbroken {
  0% { filter: none; transform: translateY(0); }
  100% { filter: grayscale(0.55) brightness(0.72); transform: translateY(4px); }
}
.anim-unbroken { animation: animUnbroken 1.1s ease-out forwards; }

@keyframes animVictoryBounce {
  0%, 100% { transform: translateY(0); }
  30% { transform: translateY(-14px); }
  55% { transform: translateY(0); }
  75% { transform: translateY(-6px); }
}
.anim-victory { animation: animVictoryBounce 0.7s ease-in-out 2; }

/* ============================================================
   ダメージ／状態異常ポップアップ（演出専用レイヤー .popup-layer）
============================================================ */
.popup-layer {
  /* ダメージ数字はshowPopup()内でthis.root（.battle-screen）基準のオフセットを
     計算して配置しているため、.battle-screenにabsolute配置する必要がある。 */
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 40;
}
.cutin-layer {
  /* .battle-screenはmin-height:100vhのため、実際の高さはコマンドバー・ログ等を
     含めた合計でビューポートより大きくなる（特にスマホ）。cutin-layerが
     absolute（.battle-screen基準）のままだと、フルスクリーン演出のはずの
     カットインがその合計高さ全体に引き伸ばされ、中央寄せの立ち絵・テキストが
     広大な空白の中にぽつんと浮いて見える不具合になっていた。position:fixedに
     変更してもなお実機（スマホ）で改善しなかったため、加えてbattle-ui.jsの
     テンプレート側で.cutin-layer自体を.battle-screenの外（兄弟要素）へ
     移動した。.battle-screenのoverflow:hiddenの子孫のままだと、環境によっては
     fixed要素がその祖先基準でクリップ・計算される場合があるため（overflowを
     持つ祖先内のposition:fixedの扱いはブラウザ間で差が出ることがある）、
     祖先自体を無関係にする方が確実。カットインはキャラクターの実位置と
     無関係な純粋な画面演出なので、常にビューポートちょうどを覆うようにする。 */
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 40;
}

.damage-popup {
  position: absolute;
  transform: translate(-50%, 0);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 22px;
  color: var(--hp-color-low);
  text-shadow: 0 0 10px rgba(0,0,0,0.7), 0 2px 0 rgba(0,0,0,0.5);
  animation: popupFloat 0.9s ease-out forwards;
  white-space: nowrap;
}
.popup-miss { color: var(--text-sub); font-size: 16px; }
.popup-status { color: var(--violet-light); font-size: 14px; }

@keyframes popupFloat {
  0% { opacity: 0; transform: translate(-50%, 6px) scale(0.8); }
  20% { opacity: 1; transform: translate(-50%, -10px) scale(1.08); }
  100% { opacity: 0; transform: translate(-50%, -46px) scale(1); }
}

.small-technique-text {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  font-size: 12px;
  color: var(--text-sub);
  background: rgba(10, 9, 16, 0.75);
  border: 1px solid var(--border-soft);
  padding: 2px 9px;
  border-radius: 10px;
  white-space: nowrap;
  animation: smallTextPop 0.7s ease-out forwards;
}
@keyframes smallTextPop {
  0% { opacity: 0; transform: translateX(-50%) translateY(4px); }
  20% { opacity: 1; transform: translateX(-50%) translateY(-2px); }
  100% { opacity: 0; transform: translateX(-50%) translateY(-10px); }
}

/* ============================================================
   スキル演出：中程度のバナー
============================================================ */
/* バナー背後の一瞬の暗転オーバーレイ。バナー単体（左右端が透過する
   グラデーション）だけだと戦闘フィールドと重なって文字が読みにくかったため、
   画面幅いっぱいの半透明の暗転を一瞬挟んでコントラストを確保する（8章）。
   フルカットイン（.chuuni-cutin）ほど大仰にはしない。 */
.tech-banner-dim {
  position: absolute;
  inset: 0;
  background: rgba(6, 5, 10, 0.55);
  animation: techBannerDimIn 0.15s ease-out both;
}
.tech-banner-dim-out { animation: techBannerDimOut 0.25s ease-in forwards; }
@keyframes techBannerDimIn { 0% { opacity: 0; } 100% { opacity: 1; } }
@keyframes techBannerDimOut { 0% { opacity: 1; } 100% { opacity: 0; } }

/* 端まで届く横長バー形式（以前は中央寄せの小さな箱で、画面幅を持て余していた）。
   left/right:0で画面幅いっぱいに広げ、中央からscaleXで開く演出はそのまま活かす。 */
.tech-banner {
  position: absolute;
  top: 42%;
  left: 0;
  right: 0;
  transform: translateY(-50%) scaleX(0);
  transform-origin: center;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 16px 20px;
  background: linear-gradient(
    90deg,
    rgba(15, 13, 24, 0),
    rgba(14, 12, 22, 0.97) 10%,
    rgba(14, 12, 22, 0.97) 90%,
    rgba(15, 13, 24, 0)
  );
  border-top: 2px solid var(--bunryoku-color);
  border-bottom: 2px solid var(--bunryoku-color);
  animation: bannerIn 0.28s ease-out forwards;
  overflow: visible;
}
.tech-banner-enemy-special { border-color: var(--hp-color); }
.tech-banner-out { animation: bannerOut 0.25s ease-in forwards; }

@keyframes bannerIn {
  0% { transform: translateY(-50%) scaleX(0); opacity: 0; }
  100% { transform: translateY(-50%) scaleX(1); opacity: 1; }
}
@keyframes bannerOut {
  0% { transform: translateY(-50%) scaleX(1); opacity: 1; }
  100% { transform: translateY(-50%) scaleX(1); opacity: 0; }
}

/* 以前は立ち絵をバー全体に対する絶対配置（left:6%）にしていたため、
   PCの広い画面だと画面左端寄りに置かれ、中央のテキストから離れて
   浮いて見える不具合があった。.chuuni-portrait-slotと同じ考え方で、
   テキストと同じflexの並びに乗せてグループとして中央寄せする。
   サイズも「小カットイン」の割に大きすぎたため140px→96pxに縮小。 */
.tech-banner-portrait-slot {
  flex: 0 0 auto;
  height: 96px;
  display: flex;
  align-items: flex-end;
}
.tech-banner-portrait {
  height: 100%;
  width: auto;
  pointer-events: none;
  image-rendering: pixelated;
  filter:
    var(--sprite-outline)
    drop-shadow(0 10px 16px rgba(0,0,0,0.55));
}

.tech-banner-text {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}
.tech-banner-name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 24px;
  color: var(--text-main);
}
.tech-banner-kana {
  font-family: var(--font-display);
  font-size: 13px;
  color: var(--gold-light);
  letter-spacing: 3px;
}

/* ============================================================
   必殺技・絆コンボ演出：フルカットイン
   book-battle同様、clip-pathのパーセンテージ指定で帯の形そのものを
   描画する方式（回転+width拡大で画面外へ逃がす方式は環境依存の不具合が
   あったため不採用。CLAUDE.md 6.3章 / book-battle実装を踏襲）。
============================================================ */
.cutin-flash {
  animation: cutinFlash 0.2s ease-out;
}
@keyframes cutinFlash {
  0% { background: rgba(255,255,255,0.55); }
  100% { background: rgba(255,255,255,0); }
}

/* ============================================================
   奥義・絆コンボの解説カード（battle-ui.js の showTechniqueCard）
   ============================================================
   キャラ初登場カード（.char-intro-card、金枠のプロフィールカード）とは
   別designにして、「その人の紹介」と「技の紹介」を取り違えないようにしている。
   トレーディングカードに寄せ、技名を主役に据えた作り。
   色は --card-accent 系の変数だけを差し替え、構造は奥義・絆コンボで共通。 */
.tech-card-overlay {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(4, 3, 8, 0.82);
  cursor: pointer;
  animation: techCardOverlayIn 0.2s ease-out both;
}
.tech-card-overlay-out { animation: techCardOverlayOut 0.5s ease-in forwards; }
@keyframes techCardOverlayIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes techCardOverlayOut { from { opacity: 1; } to { opacity: 0; } }

/* 閉じた札が、いったん手前に迫ってから縮んで手元（画面の下）へ吸い込まれる。
   「集まっている」ことの含みを持たせるための伏線で、のちの「奥義の栞」に
   つながる。長さは battle-ui.js の CARD_ABSORB_MS と揃えること。
   3Dのめくりを持つ .tech-card-inner ではなく、その外側の .tech-card-flip に
   掛ける。内側に掛けると rotateY(180deg) を打ち消してしまい、吸い込まれる
   途中で札が裏返る。 */
.tech-card-absorb { animation: techCardAbsorb 0.52s cubic-bezier(0.45, 0, 0.75, 0) forwards; }
@keyframes techCardAbsorb {
  0% { transform: scale(1) translateY(0); opacity: 1; }
  26% { transform: scale(1.06) translateY(-18px); opacity: 1; }
  100% { transform: scale(0.08) translateY(60vh); opacity: 0; }
}

/* めくりの土台。3Dの回転はこの2枚重ね（裏面／表面）で作る。
   .tech-card-inner を rotateY(0→180deg) で回し、表面側にあらかじめ
   rotateY(180deg) を掛けておくことで、回し終わりに表面が正位置で現れる。
   高さは表面が通常フローで決め、裏面は inset:0 で追従させる（説明文の長さで
   札の高さが変わるため、どちらかを固定値にすると必ずどちらかが余る）。 */
.tech-card-flip {
  width: min(340px, 100%);
  perspective: 1200px;
}
.tech-card-inner {
  position: relative;
  transform-style: preserve-3d;
  animation: techCardFlip var(--card-flip-dur) cubic-bezier(0.6, 0.02, 0.2, 1) both;
  /* 札の両面は描くのが重い（箔の縁の conic-gradient、裏の地紋、60pxぼかしの
     影、表側は絵とグラデーション2枚＋5重の drop-shadow を持つ立ち絵）。
     合成のヒントが無いと、回り始めの数フレームで層の用意と描き起こしが
     まとめて走り、出だしが引っかかる。回し終わったら battle-ui.js 側で
     will-change を外す（付けたままにすると層を抱えたままになる）。 */
  will-change: transform;
}
/* 前半は裏面を見せたまま置き、後半で一気に返す。返る山でわずかに迫り出させて
   「めくった」手応えを出す。等速で回すと機械的に見える。 */
@keyframes techCardFlip {
  0%   { transform: perspective(1200px) rotateY(0deg) scale(0.92); }
  26%  { transform: perspective(1200px) rotateY(0deg) scale(1); }
  62%  { transform: perspective(1200px) rotateY(96deg) scale(1.06); }
  100% { transform: perspective(1200px) rotateY(180deg) scale(1); }
}

.tech-card-back {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  border-radius: 16px;
  border: 3px solid transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--card-accent);
  background:
    /* 地紋。斜めの格子を薄く重ねてカードの裏らしい手触りにする。 */
    repeating-linear-gradient(45deg, var(--card-pattern) 0 2px, transparent 2px 13px) padding-box,
    repeating-linear-gradient(-45deg, var(--card-pattern) 0 2px, transparent 2px 13px) padding-box,
    linear-gradient(160deg, #221c33, #120f1d) padding-box,
    conic-gradient(
      from 210deg,
      var(--card-accent-dim), var(--card-accent-light),
      var(--card-accent), var(--card-accent-light), var(--card-accent-dim)
    ) border-box;
  box-shadow:
    0 0 0 1px rgba(0,0,0,0.6),
    0 0 34px var(--card-glow),
    0 26px 60px rgba(0,0,0,0.7);
}
/* 縁の内側にもう一本の細い罫を入れる。トレーディングカードの裏面らしさは
   ほとんどこの「二重の縁」から来ている。 */
.tech-card-back-frame {
  position: absolute;
  inset: 10px;
  border: 1px solid var(--card-accent-dim);
  border-radius: 9px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.tech-card-brush {
  width: 54%;
  max-width: 170px;
  height: auto;
  filter: drop-shadow(0 0 12px var(--card-glow));
}

.tech-card {
  position: relative;
  /* 説明文が長い技だと、画面の低い端末で札が画面より高くなる。中身に手が
     届かなくなるのを防ぐ最後の保険としてスクロールを許す（通常は発生しない）。
     ただし、めくっている間は hidden にしておく。3D回転（preserve-3d）の中に
     スクロールできる面があると、札を一枚の絵として合成できず毎フレーム
     描き直しになるため。許可はめくり終わり（.tech-card-flipped）と、
     そもそもめくらない栞の札（.tech-card-inner-static）に限る。 */
  max-height: 92vh;
  overflow-y: hidden;
  padding: 14px 16px 16px;
  border-radius: 16px;
  text-align: center;
  cursor: default;
  backface-visibility: hidden;
  transform: rotateY(180deg);
  /* 縁は金属質の箔に見せたいので、単色ではなく角度で色が変わるグラデーション。 */
  border: 3px solid transparent;
  background:
    linear-gradient(180deg, #1b1729, #14111f) padding-box,
    conic-gradient(
      from 210deg,
      var(--card-accent-dim), var(--card-accent-light),
      var(--card-accent), var(--card-accent-light), var(--card-accent-dim)
    ) border-box;
  box-shadow:
    0 0 0 1px rgba(0,0,0,0.6),
    0 0 34px var(--card-glow),
    0 26px 60px rgba(0,0,0,0.7);
}

.tech-card-flipped .tech-card,
.tech-card-inner-static .tech-card {
  overflow-y: auto;
}

.tech-card-ultimate {
  --card-accent: #caa456;
  --card-accent-light: #f0dca6;
  --card-accent-dim: #6b5320;
  --card-glow: rgba(202, 164, 86, 0.35);
  --card-pattern: rgba(202, 164, 86, 0.09);
  --card-flip-dur: 1.15s;
}
.tech-card-combo {
  --card-accent: #8a5cc9;
  --card-accent-light: #d7bdf5;
  --card-accent-dim: #402a63;
  --card-glow: rgba(138, 92, 201, 0.38);
  --card-pattern: rgba(160, 120, 220, 0.10);
  --card-flip-dur: 1.15s;
}
/* 三十三枚目の栞（読み手自身の一手）。参考作品の絵を持たない唯一の札なので、
   金でも紫でもなく白紙の頁の色で組む。 */
.tech-card-own {
  --card-accent: #b9ae95;
  --card-accent-light: #f2ecdd;
  --card-accent-dim: #6a6252;
  --card-glow: rgba(242, 236, 221, 0.30);
  --card-pattern: rgba(242, 236, 221, 0.08);
  --card-flip-dur: 1.15s;
}

/* 箔の光沢。一度だけ斜めに走らせる。ずっと動かすとうるさいので繰り返さない。 */
.tech-card-sheen {
  position: absolute;
  inset: 0;
  border-radius: 13px;
  overflow: hidden;
  pointer-events: none;
}
.tech-card-sheen::before {
  content: '';
  position: absolute;
  top: -60%;
  left: -70%;
  width: 60%;
  height: 220%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,0.16), transparent);
  /* 走らせるのは transform。以前は left を -70%→130% と動かしていたが、
     left はレイアウトを起こすプロパティなので毎フレーム再レイアウト＋再描画に
     なり、めくりの終わり際と重なって画面がカクついていた。translateX なら
     合成だけで済む。移動量は「元の left の移動幅（親の200%）」を自身の幅
     （親の60%）に換算した333%。回転より前に置いて、傾いた軸ではなく
     真横に走らせる（rotate を先に書くと斜めに流れてしまう）。 */
  transform: translateX(0) rotate(12deg);
  /* めくり終わってから走らせる。回転中に走らせても裏面に隠れて見えない。 */
  animation: techCardSheen 1.4s ease-out 1.2s both;
}
@keyframes techCardSheen {
  from { transform: translateX(0) rotate(12deg); }
  to { transform: translateX(333%) rotate(12deg); }
}

.tech-card-close {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: rgba(0,0,0,0.4);
  border: 1px solid var(--card-accent-dim);
  color: var(--card-accent-light);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  z-index: 2;
}
.tech-card-close:hover { border-color: var(--card-accent); }

/* 種別（奥義／絆コンボ）と文豪名を1行にまとめたヘッダー。
   以前は文豪名が札の最下段にあったが、行を1つ減らして絵に高さを回すため、
   「誰の」「何の札か」を示す情報として同じ行に統合した。 */
.tech-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding-right: 34px; /* 右上の閉じるボタンに文豪名がぶつからないように */
}
/* 札の種別。レアリティ表記の位置づけ。 */
.tech-card-rarity {
  flex: 0 0 auto;
  padding: 3px 14px;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--card-accent-dim), var(--card-accent));
  color: #14111f;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 12px;
  letter-spacing: 0.24em;
  text-indent: 0.24em;
}
.tech-card-owner {
  min-width: 0;
  font-family: var(--font-display);
  font-size: 14px;
  line-height: 1.3;
  letter-spacing: 0.04em;
  color: var(--card-accent-light);
  text-align: right;
}
/* 絆コンボは「A ＆ B」で長くなるので一段小さく。 */
.tech-card-combo .tech-card-owner { font-size: 12px; }

/* 絵柄の枠。必殺技のカット絵に見せたい部分なので、札の中でいちばん大きく取る。
   奥義はここに参考作品の背景（--tech-card-bg）が敷かれ、絆コンボは敷かない。 */
.tech-card-art {
  position: relative;
  margin: 10px 0 0;
  aspect-ratio: 16 / 10;
  border-radius: 10px;
  border: 1px solid var(--card-accent-dim);
  overflow: hidden;
  background-color: #0b0912;
  /* 背景が無い場合に宣言ごと無効にならないよう、フォールバックの none を必ず書く。 */
  background-image: var(--tech-card-bg, none);
  background-size: cover;
  background-position: center;
  /* 背景は232x147pxしかなく2倍前後に拡大されるので、補間せずドットを保つ。 */
  image-rendering: pixelated;
}
/* 下端の暗幕。技名を絵に重ねるため、絵の明るさに関係なく文字が読めるようにする。 */
.tech-card-art::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(8,7,13,0.12) 0%,
    rgba(8,7,13,0.04) 38%,
    rgba(8,7,13,0.62) 74%,
    rgba(8,7,13,0.92) 100%
  );
}
/* 斜めの箔の一閃。カットインの帯と同じ角度にして、演出の言葉づかいを揃える。 */
.tech-card-art::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 42%, var(--card-glow) 50%, transparent 58%);
  opacity: 0.5;
}

/* 絆コンボの地。特定の1作品に紐づかないので絵は敷かず、きらめきの中に
   二人を並べる。星は動かさず、光の膜だけをゆっくり呼吸させる（点滅させると
   目が疲れるうえ、主役であるフレーバーテキストから注意を奪う）。 */
.tech-card-combo .tech-card-art {
  background-image:
    radial-gradient(1.6px 1.6px at 18% 24%, rgba(255,255,255,0.95), transparent 100%),
    radial-gradient(1.2px 1.2px at 74% 18%, rgba(255,255,255,0.8), transparent 100%),
    radial-gradient(1.8px 1.8px at 86% 62%, rgba(232,214,255,0.9), transparent 100%),
    radial-gradient(1.1px 1.1px at 34% 68%, rgba(255,255,255,0.7), transparent 100%),
    radial-gradient(1.4px 1.4px at 58% 34%, rgba(255,255,255,0.85), transparent 100%),
    radial-gradient(1px 1px at 8% 58%, rgba(255,255,255,0.6), transparent 100%),
    radial-gradient(ellipse at 50% 64%, rgba(138,92,201,0.55) 0%, transparent 68%),
    linear-gradient(180deg, #2a2043, #140f22);
}
.tech-card-sparkles {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 30% 40%, rgba(215,189,245,0.28), transparent 46%),
    radial-gradient(circle at 70% 60%, rgba(255,255,255,0.16), transparent 42%);
  animation: techCardSparkle 3.6s ease-in-out infinite;
}
@keyframes techCardSparkle {
  0%, 100% { opacity: 0.45; }
  50% { opacity: 0.9; }
}

/* 立ち絵。背景の絵を見せるため、以前の118pxから少し小さくして手前に浮かせる。 */
.tech-card-figures {
  position: absolute;
  inset: 0;
  display: flex;
  /* 下端に揃えて「その場に立っている」ように見せる。中央に揃えると背景から
     浮いて、絵の中の人物ではなく貼り付けたシールに見えてしまう。 */
  align-items: flex-end;
  justify-content: center;
  gap: 8px;
  /* 下端の技名ブロック（カタカナ技名＋技名の2行分）を丸ごと避ける高さ。
     足りないと、立ち絵の暗い着物の上にカタカナ技名が乗って読めなくなる。 */
  padding-bottom: 58px;
  z-index: 2;
}
.tech-card-portrait {
  height: 104px;
  width: auto;
  image-rendering: pixelated;
  filter: var(--sprite-outline) drop-shadow(0 10px 16px rgba(0,0,0,0.75));
}
/* 二人並ぶ絆コンボは、収まるように少しだけ小さくする。 */
.tech-card-combo .tech-card-portrait { height: 96px; }

/* 技名。このカードの主役なので、他のどの文字よりも大きく。絵の下端に重ねて
   「必殺技のカット絵」らしい見せ方にする。カタカナ技名はルビとして
   一体で組む（別の行に離すと、技名の一部ではなく注釈のように見えてしまう）。 */
.tech-card-name {
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: 6px;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  font-family: var(--font-display);
  color: var(--card-accent-light);
  text-shadow:
    0 0 20px var(--card-glow),
    0 2px 4px rgba(0,0,0,0.9),
    0 0 3px rgba(0,0,0,0.9);
}
.tech-card-name-main {
  font-size: clamp(24px, 6.6vw, 32px);
  font-weight: 800;
  line-height: 1.15;
  text-wrap: balance;
}
/* カタカナ技名。行間をほとんど空けずに真上へ置くことでルビに見せる。 */
.tech-card-name-kana {
  font-size: 12px;
  font-weight: 400;
  line-height: 1.3;
  letter-spacing: 0.14em;
  text-indent: 0.14em;
  color: var(--card-accent);
  text-shadow: 0 1px 3px rgba(0,0,0,0.95);
  text-wrap: balance;
}
/* 白紙の頁。絵の代わりに紙そのものを敷き、そこに名だけが墨で書かれている、
   という見せ方にする。暗幕（::before）も箔の一閃（::after）も外すのは、
   どちらも「絵の上に文字を乗せる」ための仕掛けで、紙の上では邪魔になるため。 */
.tech-card-art-blank {
  background-image: none;
  background-color: #f3ecdc;
  border-color: #9a917d;
  image-rendering: auto;
}
.tech-card-art-blank::before,
.tech-card-art-blank::after { display: none; }
.tech-card-art-blank .tech-card-name {
  inset: 0;
  justify-content: center;
}
.tech-card-art-blank .tech-card-name-main {
  color: #241d14;
  text-shadow: none;
}

.tech-card-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  justify-content: center;
  margin: 10px 0 0;
}
.tech-card-chip {
  font-size: 11px;
  font-weight: 700;
  padding: 3px 10px;
  border-radius: 999px;
  border: 1px solid var(--card-accent-dim);
  background: rgba(0,0,0,0.35);
  color: var(--card-accent-light);
}
/* 由来の一文。カードの「フレーバーテキスト」にあたる部分。
   罫線1本で仕切るだけでは本文が札の地に沈んで読みにくかったので、
   一段暗い面に載せて独立した読みものとして扱う。左揃えなのは、中央揃えだと
   行頭が揃わず数行の文章が追いにくいため（孤立行対策は text-wrap: pretty）。 */
.tech-card-flavor {
  margin: 10px 0 0;
  padding: 11px 13px;
  border-radius: 8px;
  border: 1px solid var(--card-accent-dim);
  background: rgba(0,0,0,0.34);
  font-size: 15px;
  line-height: 1.95;
  color: #e9e3d7;
  text-align: left;
  text-wrap: pretty;
}
/* 絆コンボの札は「二人の関係」を伝えることが主題なので、本文を一段大きく扱う。 */
.tech-card-combo .tech-card-flavor {
  font-size: 16px;
  line-height: 2;
}

/* ============================================================
   ボス登場カットイン（battle-ui.js の showBossIntro）
   ============================================================
   奥義カットイン（.chuuni-cutin、斜めの金の帯）とは別designにして、
   「技を撃つ演出」と「敵が現れる演出」を取り違えないようにしている。
   金ではなく朱、斜めの帯ではなく上下から閉じる黒幕、という対比。 */
.boss-intro {
  position: absolute;
  inset: 0;
  z-index: 60;
  overflow: hidden;
  animation: bossIntroIn 0.25s ease-out both;
}
.boss-intro-out { animation: bossIntroOut 0.38s ease-in forwards; }
@keyframes bossIntroIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes bossIntroOut { from { opacity: 1; } to { opacity: 0; } }

/* 上下から閉じる黒幕。映画の上下黒帯のように視界を絞り、相手だけを見せる。 */
.boss-intro-curtain {
  position: absolute;
  left: 0;
  right: 0;
  height: 22%;
  background: linear-gradient(to bottom, #05040a 62%, rgba(5, 4, 10, 0));
  box-shadow: 0 0 40px 20px rgba(5, 4, 10, 0.9);
}
.boss-intro-curtain-top { top: 0; animation: bossCurtainTop 0.45s cubic-bezier(0.16, 1, 0.3, 1) both; }
.boss-intro-curtain-bottom {
  bottom: 0;
  transform: scaleY(-1);
  animation: bossCurtainBottom 0.45s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes bossCurtainTop { from { transform: translateY(-100%); } to { transform: translateY(0); } }
@keyframes bossCurtainBottom {
  from { transform: scaleY(-1) translateY(-100%); }
  to { transform: scaleY(-1) translateY(0); }
}

.boss-intro-stage {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4%;
  padding: 0 5%;
  /* 半透明だと後ろの戦闘UIが透けて「カットイン」に見えないので、不透明にして
     画面を占有させる。相手だけを見せるのがこの演出の目的。 */
  background:
    radial-gradient(ellipse at 50% 48%, rgba(140, 24, 28, 0.42) 0%, rgba(8, 5, 12, 0.96) 58%),
    #05040a;
}
/* 奥から迫り出してくる動き。最後にわずかに行き過ぎてから戻ることで、
   「どん」と踏み込んだ重さを出している。 */
.boss-intro-sprite {
  max-height: 66%;
  max-width: 46%;
  width: auto;
  object-fit: contain;
  filter: drop-shadow(0 0 26px rgba(200, 40, 40, 0.55)) drop-shadow(0 18px 30px rgba(0,0,0,0.7));
  animation: bossSpriteIn 0.75s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes bossSpriteIn {
  0% { opacity: 0; transform: scale(1.45); filter: blur(8px); }
  70% { opacity: 1; transform: scale(0.97); filter: blur(0); }
  100% { opacity: 1; transform: scale(1); filter: blur(0); }
}

/* 朱色の名札。横から差し込む。 */
.boss-intro-plate {
  position: relative;
  padding: 14px 26px 14px 22px;
  border-left: 5px solid var(--vermillion);
  background: linear-gradient(90deg, rgba(60, 12, 14, 0.92), rgba(20, 8, 12, 0.55));
  text-align: left;
  animation: bossPlateIn 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.35s both;
}
@keyframes bossPlateIn {
  from { opacity: 0; transform: translateX(60px); }
  to { opacity: 1; transform: translateX(0); }
}
.boss-intro-label {
  font-size: 12px;
  letter-spacing: 0.4em;
  color: #e2857f;
  margin-bottom: 4px;
}
.boss-intro-name {
  font-family: var(--font-display);
  font-size: clamp(28px, 5vw, 46px);
  font-weight: 800;
  color: #f6ece2;
  text-shadow: 0 0 18px rgba(200, 40, 40, 0.6), 0 3px 0 rgba(0,0,0,0.5);
  line-height: 1.2;
}
.boss-intro-kana {
  font-family: var(--font-display);
  font-size: 13px;
  letter-spacing: 0.28em;
  color: #cf9d97;
  margin-top: 4px;
}

.chuuni-cutin {
  position: absolute;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4%;
  padding: 0 4%;
  overflow: hidden;
}

.chuuni-band {
  position: absolute;
  inset: 0;
}
.chuuni-band::before,
.chuuni-band::after {
  content: '';
  position: absolute;
  left: 0;
  width: 100%;
}
.chuuni-band::before {
  top: 0;
  height: 100%;
  background: linear-gradient(100deg, var(--stripe-a), var(--stripe-b));
  /* 帯の傾きが急すぎて中央のテキストが帯からはみ出す（＝文字が欠けて
     見える）指摘を受け、左右の段差を26pt→12ptに緩めて水平に近づけた。 */
  clip-path: polygon(0% 34%, 100% 22%, 100% 68%, 0% 80%);
  animation: bandSlideIn 0.45s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.chuuni-band::after {
  top: 0;
  height: 100%;
  background: rgba(8, 7, 13, 0.85);
  clip-path: polygon(0% 39%, 100% 27%, 100% 63%, 0% 75%);
  animation: bandSlideIn 0.45s cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: 0.03s;
}

/* 奥義の背景絵（assets/ultimate-bg/{文豪ID}.png）。battle-ui.js の showCutin が
   .has-art と --cutin-bg を付けた時だけ効く。背景が無い時は上の
   rgba(8,7,13,0.85) のままなので、この指定を足しても従来の見た目は変わらない。

   ・background-color を不透明にしているのは、画像の読み込みに失敗しても
     帯の中が透けず、単に暗い帯として成立させるため。
   ・暗幕（linear-gradient）は技名・説明文の可読性のために必ず重ねる。
     絵によって明度差が大きく、外すと白い絵の上で文字が読めなくなる。
   ・元絵は232x147pxしかなく、PC全幅では7〜8倍に拡大される。pixelated に
     しないと拡大時に滲んでドット絵に見えないので、あえて補間を切っている。 */
.chuuni-band.has-art::after {
  background-color: #08070d;
  background-image:
    linear-gradient(rgba(8, 7, 13, 0.62), rgba(8, 7, 13, 0.62)),
    var(--cutin-bg);
  background-size: cover;
  background-position: center;
  image-rendering: pixelated;
}

.cutin-ultimate .chuuni-band::before { --stripe-a: #caa456; --stripe-b: #7a5a1e; }
.cutin-combo .chuuni-band::before { --stripe-a: #8a5cc9; --stripe-b: #4b3178; }

/* 発動者の立ち絵。以前は position:absolute でテキストと独立に左側へ
   重ねていたため、テキストが長い技だと立ち絵と文字が重なる不具合があった。
   .chuuni-cutin 側を横並びflexにし、立ち絵とテキストを普通のflex子要素
   として並べることで、重ならずに済むようにした（帯 .chuuni-band だけは
   従来通り絶対配置の背景として残す）。 */
.chuuni-portrait-slot {
  flex: 0 0 auto;
  /* キャラが帯に対して大きすぎてバランスが悪いとの指摘を受け、88%→48%に縮小。 */
  height: 48%;
  display: flex;
  align-items: flex-end;
  animation: chuuniPortraitIn 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.08s both;
}
.chuuni-portrait {
  height: 100%;
  width: auto;
  pointer-events: none;
  image-rendering: pixelated;
  filter:
    var(--sprite-outline)
    drop-shadow(0 18px 22px rgba(0,0,0,0.6));
}
@keyframes chuuniPortraitIn {
  0% { opacity: 0; transform: translateX(-36px); }
  100% { opacity: 1; transform: translateX(0); }
}

@keyframes bandSlideIn {
  0% { transform: translateX(-100%); opacity: 0; }
  100% { transform: translateX(0); opacity: 1; }
}

.chuuni-stripe {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 20px 24px;
  animation: stripeContentIn 0.4s ease-out 0.1s both;
  text-align: center;
  min-width: 0;
  max-width: min(480px, 56vw);
}

@keyframes stripeContentIn {
  0% { opacity: 0; transform: translateY(10px); }
  100% { opacity: 1; transform: translateY(0); }
}

.chuuni-name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(26px, 5.5vw, 42px);
  color: var(--text-main);
  text-shadow: 0 0 18px rgba(0,0,0,0.7);
  letter-spacing: 2px;
}
/* 技名と同じ明朝で組む。技名（.chuuni-name）が明朝なのにカタカナ読みだけが
   ゴシックだと、ひと組の技名として読めず注釈のように見えてしまう
   （解説カードの .tech-card-name-kana も同じ理由で明朝に揃えてある）。 */
.chuuni-kana {
  font-family: var(--font-display);
  font-size: clamp(12px, 2.4vw, 15px);
  letter-spacing: 3px;
  color: var(--gold-light);
}
.cutin-combo .chuuni-kana { color: var(--violet-light); }

/* 絆コンボカットインの発動者セリフ（主役の一言）。技名より上に表示する。 */
.chuuni-speaker {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 2px;
  color: var(--violet-light);
  opacity: 0.9;
}
.chuuni-quote {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(18px, 3.2vw, 24px);
  line-height: 1.5;
  color: var(--text-main);
  text-shadow: 0 0 14px rgba(0,0,0,0.6);
  max-width: 520px;
  margin-bottom: 4px;
  /* .story-textと同じ理由（孤立行対策）。 */
  text-wrap: balance;
}

.chuuni-desc {
  margin-top: 4px;
  font-size: 15px;
  line-height: 1.8;
  color: var(--text-sub);
  max-width: 480px;
  text-wrap: balance;
}

.chuuni-cutin-out {
  animation: cutinOut 0.3s ease-in forwards;
}
@keyframes cutinOut {
  0% { opacity: 1; }
  100% { opacity: 0; }
}

/* ============================================================
   絆コンボ「発動可能になった」リアクション
   以前は画面中央に説明トーストを表示していたが、テキストが煩わしいとの
   指摘を受けて廃止。代わりにバッジ自体を一瞬大きくポップさせるだけの
   控えめなリアクションにする（理由・効果は❓の詳細で確認できる）。
============================================================ */
@keyframes comboBadgePop {
  0% { transform: scale(1); }
  40% { transform: scale(1.35); }
  100% { transform: scale(1); }
}
.combo-badge-pop { animation: comboOrbPulse 1.8s ease-in-out 0.3s infinite, comboBadgePop 0.5s ease-out; }

/* ============================================================
   結果画面（勝利／敗北）
============================================================ */
.result-screen {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 20px;
  padding: 32px 20px;
  text-align: center;
  background: radial-gradient(ellipse at 50% 20%, rgba(202, 164, 86, 0.12) 0%, transparent 55%), var(--bg-void);
}

.result-title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(26px, 6vw, 40px);
  color: var(--gold-light);
  text-shadow: 0 0 20px rgba(202, 164, 86, 0.4);
}
.defeat-screen .result-title { color: #d98080; text-shadow: 0 0 20px rgba(217, 79, 79, 0.35); }

.result-body {
  font-size: 18px;
  color: var(--text-sub);
  max-width: 480px;
  line-height: 1.7;
}

/* ボス撃破後、その章に登場したキャラクターの関係性を立ち絵付きで振り返る一覧。
   絆コンボの元になった史実の関係（combo.originStory）をそのまま説明文に使う。 */
.relationship-board {
  width: 100%;
  max-width: 640px;
  margin-top: 4px;
}
.relationship-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 18px;
  color: var(--gold-light);
  margin-bottom: 14px;
}
/* 相関図（縦一列のフローチャート）。カード＋つなぎ線を横一列に並べる旧実装
   （.relationship-row/.relationship-card/.relationship-link）は、画面が
   狭いとカードとつなぎ線がgrid/flexの折り返しで段落ちして崩れていたため、
   縦積みに変更した。縦一列は画面幅に関係なく折り返しが起きないため、
   スマホでも崩れない。 */
.relationship-diagram {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.relationship-node {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: 160px;
  padding: 10px 12px;
  background: linear-gradient(135deg, var(--bg-panel-light), var(--bg-panel));
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  box-shadow: 0 4px 8px rgba(0,0,0,0.25);
}
.relationship-portrait {
  height: 68px;
  width: auto;
  image-rendering: pixelated;
  filter: var(--sprite-outline) drop-shadow(0 6px 10px rgba(0,0,0,0.4));
}
.relationship-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 15px;
  color: var(--text-main);
}
.relationship-role {
  font-size: 13px;
  color: var(--text-faint);
  text-align: center;
}
.relationship-edge {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 6px 0;
}
.relationship-edge-line {
  width: 1px;
  height: 16px;
  background: var(--violet-light);
  opacity: 0.5;
}
.relationship-edge-label {
  font-size: 12px;
  color: var(--violet-light);
  background: var(--bg-void);
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  padding: 2px 12px;
  white-space: nowrap;
}
.relationship-note {
  margin-top: 14px;
  font-size: 15px;
  line-height: 1.7;
  color: var(--text-sub);
}

/* 章末の関係性ボード：2人組ごとの逸話を見出し＋箇条書きで表示する。
   テキストが小さすぎるとの指摘を受け、他の本文サイズ（.result-body等）に
   揃えて読みやすくしている。 */
.relationship-detail-list {
  margin-top: 18px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  text-align: left;
}
.relationship-detail {
  background: linear-gradient(135deg, var(--bg-panel-light), var(--bg-panel));
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  padding: 14px 18px;
}
.relationship-detail-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 16px;
  color: var(--gold-light);
  margin-bottom: 8px;
}
.relationship-detail-bullets {
  list-style: disc;
  padding-left: 1.2em;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.relationship-detail-bullets li {
  font-size: 15px;
  line-height: 1.7;
  color: var(--text-sub);
}

/* 章末の「代表作」一覧。以前はカードを横に並べて折り返す形式で、
   関係性エリア（.relationship-board、縦一列・幅640px）とサイズ感が
   バラバラだった。同じ幅・縦一列構成に揃えてある。
   かつては左端にキャラカラーの帯を付けていたが、色を定義していたのが
   序章の3人（漱石・紅葉・鏡花）だけだったため、序章のクリア画面だけ
   色付き・他の章は灰色という不揃いな見た目になっていた。24人ぶんの色を
   増やすより揃った方が落ち着くので、帯は全員なしにしている。 */
.trivia-board {
  width: 100%;
  max-width: 640px;
  margin: 24px auto 0;
}
.trivia-heading {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 18px;
  color: var(--gold-light);
  margin-bottom: 14px;
}
.trivia-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.trivia-card {
  text-align: left;
  background: linear-gradient(135deg, var(--bg-panel-light), var(--bg-panel));
  border: 1px solid var(--border-soft);
  border-left: 3px solid var(--border-soft);
  border-radius: 10px;
  padding: 14px 18px;
  box-shadow: 0 4px 8px rgba(0,0,0,0.3);
}
.trivia-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 16px;
  color: var(--gold-light);
  margin-bottom: 6px;
}
.trivia-body {
  font-size: 15px;
  line-height: 1.6;
  color: var(--text-sub);
}

.result-proto-note {
  margin-top: 12px;
  font-size: 14px;
  color: var(--text-faint);
}

.btn-result-title, .btn-result-retry, .btn-result-next, .btn-party-decide {
  margin-top: 6px;
  padding: 12px 40px;
  font-size: 15px;
  font-weight: 800;
  color: var(--bg-void);
  background: linear-gradient(180deg, var(--gold-light), var(--gold));
  border-radius: 24px;
  box-shadow: 0 0 18px rgba(202, 164, 86, 0.4);
}
.btn-party-decide:disabled {
  opacity: 0.45;
  box-shadow: none;
  cursor: default;
}

/* 敗北画面の選択肢。顔ぶれを選んで挑む話では2つ並ぶため、
   狭い画面でも押しやすいよう縦積みで中央に揃える。 */
.defeat-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin-top: 6px;
}
/* 「選び直す」は主action（もう一度挑む）に対する副次的な選択肢なので、
   金の塗りではなく縁取りだけの控えめな見た目にして主従を分ける。 */
.btn-result-reselect {
  padding: 11px 32px;
  font-size: 14px;
  font-weight: 700;
  color: var(--gold-light);
  background: transparent;
  border: 1px solid var(--gold);
  border-radius: 24px;
}
.btn-result-reselect:hover {
  background: rgba(202, 164, 86, 0.12);
}

/* ============================================================
   編成画面（終章など party:'select' のエピソードで使う）
   仲間が増えた終盤に、手持ちから戦うメンバーを選ぶための画面。
============================================================ */
.party-select-screen {
  min-height: 100vh;
  padding: 32px 16px 40px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  background: var(--bg-void);
}
.party-select-title {
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 800;
  color: var(--gold-light);
}
.party-select-desc {
  font-size: 14px;
  color: var(--text-sub);
  text-align: center;
  text-wrap: balance;
}
.party-select-grid {
  width: 100%;
  max-width: 720px;
  display: grid;
  /* 画面幅に応じて列数が変わるオートフィル。人数が増えても崩れない。 */
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: 10px;
  margin-top: 8px;
}
.party-select-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 10px 6px;
  background: linear-gradient(180deg, var(--bg-panel-light), var(--bg-panel));
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
.party-select-card.is-selected {
  border-color: var(--gold);
  box-shadow: 0 0 14px rgba(202, 164, 86, 0.45);
  transform: translateY(-2px);
}
.party-select-portrait {
  width: 56px;
  height: 56px;
  object-fit: contain;
  image-rendering: pixelated;
  filter: var(--sprite-outline);
}
.party-select-name {
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 700;
  color: var(--text-main);
}
.party-select-school {
  font-size: 11px;
  color: var(--text-sub);
}
.party-select-footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
}
.party-select-count {
  font-size: 13px;
  color: var(--text-sub);
}

/* ============================================================
   レスポンシブ（DESIGN.md 8章: Mobile ≤480px / Tablet ≤768px）
============================================================ */
@media (max-width: 768px) {
  /* 以前は .ally-panel を width:100% にしていたため、折り返し前提の
     .party-area では1行に1枚しか並ばず、4人編成だとカードだけで4行を
     消費してコマンドバーが画面外へ押し出されていた。人数が増えても
     必ず1行に収まるよう nowrap ＋ 等分（flex:1 1 0）にし、
     立ち絵の上にステータスを積む縦型のコンパクトカードに切り替える。
     min-width:0 が無いとflexアイテムは内容幅より縮まないため必須。 */
  .party-area { flex-wrap: nowrap; gap: 8px; }
  .ally-panel {
    width: auto;
    max-width: 220px;
    flex: 1 1 0;
    min-width: 0;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 4px;
    padding: 8px 6px;
  }
  .ally-sprite-slot { width: 44px; height: 58px; }
  .ally-info { width: 100%; align-items: center; }
  .bar-label { min-width: 0; }
  .hp-row .bar-label { width: auto; }
  .enemy-sprite-slot { width: 130px; height: 130px; }
  /* 立ち絵を大きくしつつ、align-items:flex-endでキャラを枠の下側に
     アンカーし、枠上部に余白（headroom）を作る。名前・HPのオーバーレイは
     この余白に収まるようになり、顔にかからなくなる。 */
  .enemy-panel-portrait .enemy-sprite-slot {
    width: 260px;
    height: 300px;
    align-items: flex-end;
  }
  .enemy-panel-portrait .enemy-sprite { height: 230px; }
  .enemy-area-portrait { margin-bottom: -142px; }
  .enemy-panel-portrait { padding-bottom: 105px; }
  .cmd-item { min-width: 44%; flex: 1 1 44%; }
  .cmd-btn { width: 100%; min-width: 0; }
  .story-layout { flex-direction: column-reverse; align-items: center; gap: 10px; }
  .story-portrait-slot { width: 130px; height: 160px; }
  .story-box { max-width: 520px; }

  /* コマンド詳細ツールチップ・絆コンボの詳細は各ボタン直上に中央寄せで
     出るため、画面端に近いボタンだと吹き出しが画面外へはみ出し、先頭や
     末尾の文字が切れて読めなくなっていた。スマホ〜タブレット幅では
     ボタン追従をやめ、画面中央に固定表示することで必ず収まるようにする。 */
  .cmd-tooltip,
  .cmd-tooltip.is-open,
  .cmd-item:hover .cmd-tooltip {
    position: fixed;
    left: 16px;
    right: 16px;
    top: 50%;
    bottom: auto;
    transform: translateY(-50%);
    width: auto;
    max-width: none;
  }
  .combo-badge-inner .cmd-tooltip,
  .combo-badge-inner .cmd-tooltip.is-open,
  .combo-badge-inner:hover .cmd-tooltip {
    left: 16px;
    right: 16px;
    top: 50%;
    bottom: auto;
    transform: translateY(-50%);
    width: auto;
    max-width: none;
  }

  /* バフ/デバフが増えるとカードの縦幅を圧迫していたインライン一覧を隠し、
     立ち絵右上の件数バッジ（JS側でhidden切り替え）をタップした時だけ
     画面中央にポップアップ表示する。 */
  .ally-sprite-slot, .enemy-sprite-slot { position: relative; }
  /* .status-corner-badgeにdisplay:flexを付けるだけだと、同じ詳細度の
     UA既定スタイル[hidden]{display:none}より後着のauthorスタイルが常に
     勝ってしまい、件数0でhidden属性が付いていてもバッジが表示され続ける
     不具合があった。[hidden]を明示的に上書きして隠す。 */
  .status-corner-badge[hidden] { display: none; }
  .status-corner-badge {
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    top: -6px;
    right: -6px;
    min-width: 22px;
    height: 22px;
    padding: 0 5px;
    border-radius: 11px;
    background: rgba(138, 92, 201, 0.92);
    border: 1px solid var(--violet-light);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    z-index: 3;
    box-shadow: 0 2px 6px rgba(0,0,0,0.45);
  }
  .status-corner-badge.is-open { background: rgba(202, 164, 86, 0.92); border-color: var(--gold-light); }
  /* 件数バッジ+ポップアップ方式は縦幅の限られる味方カード用。敵の状態異常は
     .enemy-status-rail（敵絵の左に常時縦並び）で表示するため対象外にする。 */
  .ally-info .status-badges { display: none; }
  .ally-info .status-badges.is-open {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    position: fixed;
    left: 16px;
    right: 16px;
    top: 50%;
    transform: translateY(-50%);
    justify-content: center;
    background: rgba(10, 9, 16, 0.97);
    border: 1px solid var(--border-soft);
    border-radius: 10px;
    padding: 14px 16px;
    box-shadow: 0 12px 32px rgba(0,0,0,0.45);
    z-index: 40;
  }
  .ally-info .status-badges.is-open .status-badge { font-size: 13px; }
  .enemy-status-rail { max-width: 100px; }
  .enemy-status-rail .status-badge { font-size: 11px; padding: 2px 6px; }

  .enemy-area-portrait:has(> .enemy-panel-portrait[data-id='shuen_no_hensha']) { margin-bottom: -242px; }
  .enemy-panel-portrait[data-id='shuen_no_hensha'] .enemy-sprite-slot { height: 400px; }
  .enemy-panel-portrait[data-id='shuen_no_hensha'] .enemy-sprite { height: 320px; }
}

@media (max-width: 480px) {
  .title-screen { gap: 18px; padding: 20px; }
  .title-cast-sprite { height: 72px; }
  .title-version-desc { font-size: 14px; }

  /* スマホで攻撃ボタンを押すのにスクロールが必要になっていた問題への対応。
     ①敵エリア・パーティエリアを詰める、②パーティカードを3列の横並び
     コンパクト表示にする、③コマンドボタンを縦1列(全幅)ではなく2列に
     並べる、の3点で合計の縦方向消費を大きく減らしている。 */
  .battle-topbar { padding: 6px 12px; }
  .battle-stage { padding: 6px 8px 2px; gap: 6px; }
  .enemy-panel { padding: 2px 12px; gap: 4px; }
  .enemy-sprite-slot { width: 84px; height: 84px; }
  .enemy-info { width: 100%; max-width: 220px; }
  /* 敵の立ち絵は迫力のため大きめに取っているが、4人編成時は縦の余裕が
     無くなるため一段縮める（225→180）。下の負のマージンと padding-bottom で
     パーティカードを敵の足元に潜り込ませる関係は維持する。 */
  .enemy-panel-portrait .enemy-sprite-slot {
    width: 150px;
    height: 180px;
    align-items: flex-end;
  }
  .enemy-panel-portrait .enemy-sprite { height: 132px; }
  .enemy-area-portrait { margin-bottom: -80px; }
  .enemy-panel-portrait { padding-bottom: 62px; }
  .enemy-status-rail { max-width: 76px; margin-right: 6px; }
  .enemy-status-rail .status-badge { font-size: 10px; padding: 1px 5px; }

  /* 4人編成だと3枚＋1枚で折り返して2行になり、パーティエリアだけで
     360px前後を消費していた（その結果コマンドバーが画面外へ押し出され、
     必殺技ボタンを押すのにスクロールが必要になっていた）。
     人数が増えても必ず1行に収まるよう nowrap にし、カードは横幅を
     分け合って縮む（min-width:0 が無いとflexアイテムは内容幅より
     縮まないため、4分割時に溢れる）。 */
  .party-area { gap: 4px; flex-wrap: nowrap; }
  .ally-panel {
    width: auto;
    /* 1〜2人編成（序章前編の夏目漱石単独など）で1枚が横に間延びしないよう
       上限は残しつつ、4人時は等分で縮むようにする。 */
    max-width: 160px;
    flex: 1 1 0;
    min-width: 0;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 2px;
    padding: 6px 4px;
  }
  .ally-sprite-slot { width: 34px; height: 44px; }
  /* 立ち絵が小さいため、件数バッジも一回り小さくして重なりすぎないようにする。 */
  .ally-sprite-slot .status-corner-badge { min-width: 16px; height: 16px; font-size: 9px; top: -4px; right: -6px; }
  .ally-info { width: 100%; align-items: center; gap: 2px; }
  .ally-name { font-size: 11px; }
  .bar-row { gap: 3px; }
  .bar { height: 6px; }
  .bar-tag { width: 10px; font-size: 9px; }
  /* 4分割された狭いカードでは「58/110」のような数値が入りきらず、
     バーそのものが潰れてしまう。HPだけ数値を残し（生死に直結するため）、
     筆力・気迫はバーの長さだけで判断してもらう。 */
  .bar-label { min-width: 0; font-size: 9px; }
  .hp-row .bar-label { width: auto; }
  .bunryoku-row .bar-label, .kihaku-row .bar-label { display: none; }

  .battle-command-bar { padding: 6px 8px 10px; }
  /* 縦1列(100%幅)だと4ボタン分の高さがそのまま画面を圧迫するため、
     2列並びにして高さをおよそ半分にする。 */
  .cmd-item { min-width: 47%; flex: 1 1 47%; }
  /* 技名が長いキャラ（例：与謝野晶子のスキル「君死にたまふことなかれ（筆35）」）
     だと sub が2行に段落ちし、そのボタンだけ高さが変わって不揃いに見えていた
     （実測：90px と 109px が混在）。2行ぶんを見込んだ min-height を与えて
     常に同じ高さにし、内容は縦中央寄せにする。あわせて余白と文字サイズを
     詰めることで、高さを揃えても全体の縦消費が増えないようにしている。 */
  .cmd-btn {
    min-width: 0;
    min-height: 92px;
    padding: 8px 12px;
    gap: 2px;
    justify-content: center;
  }
  .cmd-title { font-size: 14px; }
  .cmd-sub { font-size: 11px; }
  .cmd-effect { font-size: 11px; }

  .combo-badge-slot { top: 44px; right: 8px; }
  .combo-badge-inner { width: 54px; height: 54px; }
  .combo-badge { width: 54px; height: 54px; }
  .combo-badge-icon { font-size: 19px; }
  .combo-badge-caption { max-width: 108px; }

  .story-screen { padding: 12px; }
  .story-next-hint { left: 12px; right: 12px; bottom: 10px; width: calc(100% - 24px); }
  .story-portrait-slot { width: 92px; height: 120px; }
  .story-box { padding: 18px 20px; min-height: 140px; max-height: 55vh; }
  /* ナレーション（地の文＝「ストーリーテキスト」）とキャラトーク（.story-text）
     が逆転して見える、地の文の方を大きくしてほしいとの指摘を受けて修正。
     .story-textはスマホ向けの縮小指定がなく21pxのままのため、ナレーション
     側もそれ以上のサイズにする。 */
  .story-narration { font-size: 22px; line-height: 1.9; width: calc(100% - 24px); }

  /* 立ち絵付きカットインは狭い画面だと横並びの余裕がないため縮小する。 */
  .tech-banner { gap: 8px; }
  .tech-banner-portrait-slot { height: 72px; }
  /* フルカットインは横並びをやめ、立ち絵を小さくして上・テキストを下に
     縦積みにする（横幅が足りずテキストと重なるのを防ぐ）。そのうえで、
     カットインの枠自体を「中身の高さ」に合わせて上下中央に置く。
     枠がビューポート全体（inset:0）のままだと、帯もその高さいっぱいに
     広がり、縦長のスマホでは立ち絵とテキストが広大な暗闇の中央にぽつんと
     浮く「カットインが縦に伸びる」状態になっていた（PC・横向きでは画面が
     縦に短いため同じ指定でも帯らしく見えており、気づきにくかった）。
     枠を中身の高さにすれば、帯は中身に密着した一枚の帯に戻る。 */
  /* 縦積みで台詞が画面中央を占めるため、立ち絵の領域は上寄りに小さく取る */
  /* ボス登場カットインは横幅が足りないので縦積みにする */
  .boss-intro-stage { flex-direction: column; gap: 14px; padding: 0; }
  .boss-intro-sprite { max-width: 78%; max-height: 44%; }
  /* 名札は画面の両端まで伸ばす。中途半端な幅で切れていると、
     宙に浮いた札のように見えて収まりが悪い。 */
  .boss-intro-plate {
    align-self: stretch;
    text-align: center;
    border-left: 0;
    border-top: 4px solid var(--vermillion);
    padding: 14px 18px;
    /* 横並びの時は左から右への濃淡だが、全幅・中央揃えでは左右非対称に
       見えるので、上辺の線に合わせて上から下への濃淡にする。 */
    background: linear-gradient(180deg, rgba(60, 12, 14, 0.92), rgba(20, 8, 12, 0.35));
  }

  /* 地の文の文字が大きく行数も増えるため、本は小さくして上へ逃がす */
  .story-book { transform: translate(-50%, -230px) scale(0.72); }
  .story-book-out { animation: storyBookOutNarrow 0.65s ease-in forwards; }
  /* 帰還画面の本は文章を避ける必要がないので、狭い画面でも中央のまま縮めるだけ。 */
  .return-screen .story-book { animation: returnBookInNarrow 0.5s ease-out both; }
  @keyframes returnBookInNarrow {
    from { opacity: 0; transform: translate(-50%, calc(-50% + 14px)) scale(0.72); }
    to { opacity: 1; transform: translate(-50%, -50%) scale(0.72); }
  }
  @keyframes storyBookOutNarrow {
    from { opacity: 1; transform: translate(-50%, -230px) scale(0.72); }
    to { opacity: 0; transform: translate(-50%, -250px) scale(0.72); }
  }

  .story-actor-layer { top: 2%; height: 40%; }
  .story-actor { max-width: 88%; }

  .chuuni-cutin {
    flex-direction: column;
    gap: 8px;
    /* 上下の余白は、下の帯の斜めの縁に文字が掛からないだけの幅を取る */
    padding: 40px 16px;
    top: 50%;
    bottom: auto;
    height: auto;
    max-height: 100%;
    transform: translateY(-50%);
  }
  /* PC版と同様「キャラが大きすぎる」との指摘を受けて縮小した枠。
     親（.chuuni-cutin）の高さが中身依存になったため、%指定では解決できず
     画像の実寸まで膨らんでしまう。画面の高さを基準にした値に置き換える。 */
  .chuuni-portrait-slot { height: clamp(88px, 16vh, 150px); }
  /* .chuuni-nameはclamp(26px, 5.5vw, 42px)で下限26pxのため、狭い画面では
     「猫ノ眼（気迫MAX）」のような技名でも括弧の途中で折り返され見た目が
     崩れていた。狭画面向けに固定サイズへ下げて1行に収まりやすくする。 */
  .chuuni-name { font-size: 22px; }
  .chuuni-stripe {
    max-width: 92vw;
    padding: 12px 16px;
    /* 縦積みレイアウトだと立ち絵+テキストの合計がカットイン全体
       （.chuuni-cutinはoverflow:hidden）の高さを超え、説明文の下端が
       欠けたり、帯（.chuuni-band）の塗り範囲からはみ出して背後の
       戦闘画面が透けて見える不具合があった。テキスト側に上限を設けて
       収まらない場合だけ内部スクロールさせる安全策にする。 */
    max-height: 60vh;
    overflow-y: auto;
  }
  /* 帯の形。PC版と同じく「同じ傾きの平行四辺形を二重にして、その差分を金縁に
     見せる」構成にする。以前は外側だけを矩形にして内側だけ斜めにしていたため、
     金縁が上下で逆向きの楔形（上は左が太く、下は右が太い）になり「帯がおかしい」
     という指摘になっていた。外側・内側の傾きを揃えれば金縁の太さが一定になる。
     単位を%ではなくpxにしているのは、帯の高さが説明文の長さで変わるため。
     %だと長い説明文のときに内側の帯が相対的に内へ寄り、文字の端が欠ける。
     pxなら高さに関わらず「上下40pxの余白の内側に文字」という関係が保たれる。
     なお帯の外側（左上・右下の三角形）は透過してよい。PC版も同じで、帯は
     戦闘画面の上に浮いている見せ方だからである。かつて背景漏れが問題になったのは
     帯がビューポート全体で、その隙間に中身がはみ出していたときの話であり、
     帯が中身に密着した今は隙間に中身が入り込むことはない。 */
  .chuuni-band::before { clip-path: polygon(0 14px, 100% 0, 100% calc(100% - 14px), 0 100%); }
  .chuuni-band::after { clip-path: polygon(0 22px, 100% 8px, 100% calc(100% - 22px), 0 calc(100% - 8px)); }
  .battle-log-panel[open] { height: 170px; }
  .battle-log-panel[open] .battle-log-list { max-height: 130px; }

  .char-intro-card { padding: 26px 20px 20px; gap: 6px; }
  .char-intro-portrait-slot { height: 150px; }
  .char-intro-name { font-size: 22px; }

  .enemy-area-portrait:has(> .enemy-panel-portrait[data-id='shuen_no_hensha']) { margin-bottom: -150px; }
  .enemy-panel-portrait[data-id='shuen_no_hensha'] .enemy-sprite-slot { height: 250px; }
  .enemy-panel-portrait[data-id='shuen_no_hensha'] .enemy-sprite { height: 190px; }
}

/* ============================================================
   デバッグモード（タイトルの夏目漱石の立ち絵を5回クリックで有効化）
============================================================ */
/* 立ち絵は本来ただの飾りなので、押せることが分かるようにはしない
   （隠しコマンドのため）。ただしクリック自体は拾えるようにしておく。 */
.title-cast-natsume { cursor: default; }

.debug-badge {
  position: fixed;
  left: 8px;
  bottom: 8px;
  z-index: 100;
  padding: 5px 10px;
  font-size: 11px;
  font-weight: 700;
  color: var(--bg-void);
  background: var(--gold-light);
  border-radius: 6px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
  pointer-events: none;
}

/* ============================================================
   エンディング（スタッフロール）
============================================================ */
.credits-screen {
  /* 「次へ進む」の帯の高さ。ロールを流す領域（.credits-viewport）の下端を
     この値だけ持ち上げることで、文字がボタンと重ならないようにしている。 */
  --credits-footer-h: 64px;
  position: relative;
  min-height: 100vh;
  background: var(--bg-void);
  overflow: hidden;
  cursor: pointer;
}
/* 中身を下から上へ流す。移動距離と再生時間は中身の量に応じて
   app.js が --credits-distance と animation で与える。
   下端は「次へ進む」の帯のぶんだけ空けてあり、流れる文字がボタンの下に
   潜り込んで重なって見えないようにしている。 */
.credits-viewport {
  position: absolute;
  inset: 0 0 var(--credits-footer-h) 0;
  overflow: hidden;
}
.credits-roll {
  position: absolute;
  left: 0;
  right: 0;
  /* 画面の一番下（top:100%）から始めると最初の一行が出てくるまで待たされるが、
     逆に上げすぎると始まった時点で文字が見えていて唐突になる。下から入ってくる
     感じを残したいので、画面の8割ほどの高さから流し始める。app.js はこの値を
     実測して流す距離を決めるので、ここを変えるだけで開始位置を調整できる。 */
  top: 80%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 48px;
  padding: 40px 20px 80px;
  text-align: center;
}
@keyframes creditsRoll {
  from { transform: translateY(0); }
  to { transform: translateY(calc(var(--credits-distance) * -1)); }
}
.credits-title {
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 800;
  color: var(--gold-light);
  letter-spacing: 0.12em;
  margin-bottom: 16px;
}
/* ロールの背に、いま流れている章で立ちはだかった相手を薄く浮かべる。
   読ませたいのは文字の方なので、透かし程度に留めること（濃くすると
   キャストの名前が読みにくくなる）。切り替えはゆっくり重ねて、
   前の相手が消えきる前に次が現れるようにする。 */
.credits-boss-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 0;
}
.credits-boss {
  position: absolute;
  left: 50%;
  top: 50%;
  height: min(62vh, 520px);
  width: auto;
  transform: translate(-50%, -50%) scale(1.04);
  opacity: 0;
  /* 立ち絵は輪郭がはっきりしているので、少しぼかして背景に沈める。 */
  filter: blur(1.5px) saturate(0.75);
  transition: opacity 1.1s ease, transform 4s ease-out;
}
.credits-boss.is-visible {
  opacity: 0.16;
  transform: translate(-50%, -50%) scale(1);
}

.credits-block {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  max-width: 620px;
}
.credits-chapter {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 700;
  color: var(--gold);
  letter-spacing: 0.08em;
}
.credits-line {
  font-size: 15px;
  line-height: 1.9;
  color: var(--text-main);
  text-wrap: balance;
}
.credits-cast-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 18px;
}
.credits-cast {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}
.credits-portrait {
  width: 52px;
  height: 52px;
  object-fit: contain;
  image-rendering: pixelated;
  filter: var(--sprite-outline);
}
.credits-cast-name {
  font-family: var(--font-display);
  font-size: 13px;
  color: var(--text-main);
}
.credits-enemy {
  font-size: 12px;
  color: var(--text-sub);
  letter-spacing: 0.05em;
}
/* 案内役の漱石だけは最後に単独で流すので、一回り大きく見せる */
.credits-block-guide { gap: 16px; }
.credits-cast-guide .credits-portrait { width: 76px; height: 76px; }
.credits-cast-guide .credits-cast-name { font-size: 15px; color: var(--gold-light); }

.credits-closing {
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 620px;
}
.credits-closing p {
  font-family: var(--font-display);
  font-size: 17px;
  line-height: 1.9;
  color: var(--text-main);
  text-wrap: balance;
}
.credits-end {
  font-family: var(--font-display);
  font-size: 20px;
  color: var(--gold-light);
  letter-spacing: 0.3em;
  margin-top: 24px;
}
/* 「次へ進む」の帯。.credits-viewport の下端をこの高さぶん空けてあるので
   （--credits-footer-h）、流れてくる文字とは決して重ならない。 */
.credits-footer {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--credits-footer-h);
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(to top, var(--bg-void) 70%, transparent);
}
.btn-credits-next {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  color: var(--gold-light);
  background: rgba(255,255,255,0.06);
  border: 1px solid var(--gold);
  border-radius: 999px;
  padding: 8px 22px;
  cursor: pointer;
}
.btn-credits-next:hover { background: rgba(255,255,255,0.12); }
.btn-credits-next:active { transform: scale(0.97); }

/* スキップの確認。ロールを止めたうえで手前に重ねる。 */
/* display:flex はブラウザ標準の [hidden]{display:none} より強いため、
   hidden を付けただけでは隠れない（実際にエンドロールの開始直後から
   確認ダイアログが出てしまう不具合になった）。明示的に打ち消す。 */
.credits-skip-confirm[hidden] { display: none; }
.credits-skip-confirm {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0,0,0,0.72);
  z-index: 5;
}
.credits-skip-box {
  background: var(--bg-panel);
  border: 1px solid var(--gold);
  border-radius: 10px;
  padding: 22px 26px;
  text-align: center;
  max-width: 320px;
}
.credits-skip-text {
  font-size: 15px;
  color: var(--text-main);
  margin: 0 0 18px;
}
.credits-skip-actions {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
}
.credits-skip-actions button {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  border-radius: 6px;
  padding: 9px 18px;
  cursor: pointer;
}
.btn-credits-stay {
  color: var(--text-sub);
  background: transparent;
  border: 1px solid var(--text-faint);
}
.btn-credits-skip {
  color: var(--bg-void);
  background: var(--gold-light);
  border: 1px solid var(--gold-light);
}

/* ============================================================
   あとがき・登場人物一覧（終章のクリア画面）
============================================================ */
.afterword {
  width: 100%;
  max-width: 640px;
  margin: 20px auto 0;
  padding: 20px 22px;
  text-align: left;
  background: linear-gradient(180deg, var(--bg-panel-light), var(--bg-panel));
  border: 1px solid var(--border-soft);
  border-radius: 12px;
}
.afterword-heading {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 18px;
  color: var(--gold-light);
  margin-bottom: 14px;
}
.afterword-text {
  font-size: 14px;
  line-height: 2;
  color: var(--text-main);
  margin-bottom: 14px;
}
.afterword-text:last-child { margin-bottom: 0; }

.cast-board {
  width: 100%;
  max-width: 720px;
  margin: 24px auto 0;
}
.cast-heading {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 18px;
  color: var(--gold-light);
  margin-bottom: 6px;
}
.cast-note {
  font-size: 12px;
  color: var(--text-sub);
  margin-bottom: 14px;
}
.cast-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 8px;
}
.cast-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 10px 4px;
  background: linear-gradient(180deg, var(--bg-panel-light), var(--bg-panel));
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.cast-card:hover { border-color: var(--gold); transform: translateY(-2px); }
.cast-portrait {
  width: 46px;
  height: 46px;
  object-fit: contain;
  image-rendering: pixelated;
  filter: var(--sprite-outline);
}
.cast-name {
  font-family: var(--font-display);
  font-size: 12px;
  color: var(--text-main);
}

/* 登場人物一覧から開く詳細カード。載せるのは生没年・代表作・来歴の要約だけで、
   技の解説は置かない（戦闘中のコマンド欄で見られるため重複する）。
   立ち絵を名前の左に置くことで縦の場所を節約し、そのぶん立ち絵を大きく、
   本文の文字も読みやすい大きさにしている。 */
.cast-detail-card {
  width: min(460px, 100%);
  max-height: 92vh;
  /* 通常の画面ではスクロールバーが出ないことを実測で確認済み。これは
     文字サイズを大きくしている環境など、想定外に縦が伸びた時に中身が
     カードの外へこぼれるのを防ぐための保険。 */
  overflow-y: auto;
  cursor: default;
}
/* 立ち絵＋名前まわりの横並び。カード自体は中央揃えの縦積みなので、
   この行だけ幅いっぱいに広げて左揃えにする。 */
.cast-detail-head {
  display: flex;
  align-items: center;
  gap: 16px;
  width: 100%;
  margin-bottom: 4px;
}
.cast-detail-card .char-intro-portrait-slot {
  width: auto;
  flex: 0 0 auto;
  /* 横に置いたぶん縦を稼げるので、一覧用としては大きめに取れる。
     画面の高さに追従させ、低い画面では自動的に縮める。 */
  height: clamp(96px, 19vh, 150px);
}
.cast-detail-identity {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  min-width: 0;
  text-align: left;
}
.cast-detail-identity .char-intro-name { text-align: left; }
.cast-detail-identity .char-intro-tags { justify-content: flex-start; }
/* 縦に余裕のない画面では余白だけ詰める（文字の大きさは保つ） */
@media (max-height: 720px) {
  .cast-detail-card { padding: 20px 20px 14px; gap: 5px; }
  .cast-detail-card .cast-detail-head { gap: 12px; }
}
/* 端末を横向きにした時など、縦が極端に短く横に余裕がある場合。
   「立ち絵＋名前」の塊を左に、「代表作＋来歴」を右に置く二段組にする。
   ここだけは本文をわずかに詰めるが、そうしないとどの人物も画面に収まらず、
   結局スクロールが必要になってしまうため。 */
@media (max-height: 560px) and (min-width: 620px) {
  .cast-detail-card {
    width: min(780px, 100%);
    display: grid;
    grid-template-columns: minmax(220px, 280px) 1fr;
    grid-template-areas:
      "head works"
      "head summary"
      ". joined";
    align-content: center;
    column-gap: 18px;
    row-gap: 4px;
    padding: 14px 18px 12px;
    /* 横向きは縦の余白そのものが少ないので、上下の余白の取り分を通常より緩める */
    max-height: 96vh;
  }
  .cast-detail-card .cast-detail-head { grid-area: head; margin-bottom: 0; }
  .cast-detail-card .char-intro-portrait-slot { height: clamp(70px, 30vh, 132px); }
  .cast-detail-card .char-intro-works { grid-area: works; }
  .cast-detail-card .detail-summary { grid-area: summary; font-size: 13px; line-height: 1.7; padding: 9px 11px; }
  .cast-detail-card .detail-joined { grid-area: joined; }
}
.detail-life {
  font-family: var(--font-display);
  font-size: 13px;
  letter-spacing: 0.04em;
  color: var(--gold-light);
}
/* 来歴の本文。ここが一番読ませたい部分なので、カード内の他の要素より
   文字を大きく・行間を広く取っている（立ち絵を横に置いて縦を空けたぶん）。 */
.detail-summary {
  width: 100%;
  margin: 4px 0 0;
  padding: 12px 14px;
  background: rgba(10, 9, 16, 0.5);
  border: 1px solid var(--border-soft);
  border-radius: 8px;
  font-size: 14px;
  line-height: 1.95;
  color: var(--text-main);
  text-align: left;
  text-wrap: pretty;
}
.detail-joined {
  font-size: 12px;
  color: var(--gold-light);
}

@media (max-width: 480px) {
  .credits-title { font-size: 24px; }
  .credits-chapter { font-size: 15px; }
  .credits-line { font-size: 14px; }
  .credits-portrait { width: 44px; height: 44px; }
  .credits-cast-row { gap: 12px; }
  .credits-closing p { font-size: 15px; }
  .afterword { padding: 16px; }
  .afterword-text { font-size: 13px; }
  .cast-grid { grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); }
}

/* ============================================================
   奥義の栞（app.js の showBookmarkShelf）
   ============================================================
   取得済みは奥義カットインの背景を敷いた札、未取得は解説カードの裏面と同じ
   筆の図柄。「まだ返していない札」に見えるので、埋まっていないこと自体が
   説明なしで伝わる。札の比率（3:4）はトレーディングカードに合わせてある。 */
.shelf-screen {
  min-height: 100vh;
  /* width:100% を省かないこと。#app-root は縦フレックスで、そこに
     margin:0 auto を持つ子を置くと、左右のauto余白が align-items:stretch を
     打ち消して「中身なりの幅」に縮む。実際、札が4列しか並ばず、文豪一覧に
     至っては2列になっていた。 */
  width: 100%;
  max-width: 900px;
  margin: 0 auto;
  padding: 28px 20px 48px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.shelf-header {
  display: flex;
  align-items: baseline;
  gap: 14px;
}
.shelf-title {
  font-family: var(--font-display);
  font-size: clamp(24px, 5vw, 32px);
  font-weight: 800;
  color: var(--gold-light);
  text-shadow: 0 0 20px rgba(202, 164, 86, 0.4);
}
.shelf-count {
  font-family: var(--font-display);
  font-size: 16px;
  color: var(--gold);
  letter-spacing: 0.08em;
}
.shelf-lead {
  font-size: 13px;
  color: var(--text-sub);
  margin-bottom: 10px;
}
.shelf-section {
  align-self: flex-start;
  margin-top: 16px;
  font-family: var(--font-display);
  font-size: 15px;
  letter-spacing: 0.18em;
  color: var(--text-sub);
  border-left: 3px solid var(--gold-dim, #6b5320);
  padding-left: 10px;
}
.shelf-grid {
  width: 100%;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: 10px;
}
.shelf-slot {
  position: relative;
  aspect-ratio: 3 / 4;
  border-radius: 8px;
  border: 2px solid #3a3350;
  background: linear-gradient(180deg, #1b1729, #14111f);
  cursor: pointer;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 5px;
  transition: transform 0.15s ease, border-color 0.15s ease;
}
.shelf-slot:hover { transform: translateY(-3px); }

/* 絵の窓。札いっぱいに敷かず横長に収めるのは、元絵が16:10で、縦長の札に
   cover で敷くと中央の狭い縦帯しか見えず何の絵か分からなくなるため
   （解説カードの .tech-card-art と同じ理由・同じ比率）。 */
.shelf-slot-art {
  width: 100%;
  aspect-ratio: 16 / 10;
  flex: 0 0 auto;
  border-radius: 5px;
  background-color: #0b0912;
  background-image: var(--shelf-bg, none);
  background-size: cover;
  background-position: center;
  image-rendering: pixelated;
}
.shelf-slot-ultimate { border-color: #6b5320; }
.shelf-slot-combo { border-color: #402a63; }
.shelf-slot-combo .shelf-slot-art {
  background-image:
    radial-gradient(1.4px 1.4px at 24% 28%, rgba(255,255,255,0.9), transparent 100%),
    radial-gradient(1.6px 1.6px at 74% 62%, rgba(232,214,255,0.9), transparent 100%),
    radial-gradient(1.1px 1.1px at 52% 44%, rgba(255,255,255,0.8), transparent 100%),
    radial-gradient(1.2px 1.2px at 38% 70%, rgba(255,255,255,0.7), transparent 100%),
    radial-gradient(ellipse at 50% 60%, rgba(138,92,201,0.55) 0%, transparent 70%),
    linear-gradient(180deg, #2a2043, #140f22);
}
.shelf-slot-ultimate:hover { border-color: var(--gold); }
.shelf-slot-combo:hover { border-color: #8a5cc9; }

.shelf-slot-name {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 800;
  line-height: 1.3;
  color: #f4ecd8;
  text-wrap: balance;
}
.shelf-slot-combo .shelf-slot-name { color: #e2d3f7; }

/* 未取得。裏面のまま伏せられている札。 */
.shelf-slot-empty {
  border-style: dashed;
  border-color: #332c47;
}
.shelf-slot-empty .shelf-slot-back {
  display: block;
  width: 52%;
  color: #4a4166;
}
.shelf-slot-empty svg { width: 100%; height: auto; display: block; }
.shelf-slot-empty:hover { border-color: #5b5080; }
.shelf-slot-empty:hover .shelf-slot-back { color: #6d5f99; }

/* ---- 三十三枚目の栞 ---- */
/* 三十二枚の先にある一枚。「集める対象」ではないので、金（奥義）とも
   紫（絆コンボ）とも違う白紙の頁の色で見せ、取り違えられないようにする。 */
.shelf-section-final { border-left-color: #8b8397; }
.shelf-slot-blank {
  width: 100%;
  aspect-ratio: 16 / 10;
  flex: 0 0 auto;
  border-radius: 5px;
  background: linear-gradient(160deg, #f6f1e4, #ded5c2);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,0.28);
}
/* まだ三十二枚揃っていない状態。裏面の札よりさらに沈ませ、押しても
   まだ何も無いことが見た目で伝わるようにする。 */
.shelf-slot-sealed { border-color: #2c2740; opacity: 0.7; }
.shelf-slot-sealed .shelf-slot-back { color: #3b3556; }
/* 揃ったがまだ読んでいない状態。ここだけ呼吸するように光らせる
   （タイトルの栞ボタンと同じ「現れた」の見せ方）。 */
.shelf-slot-ready {
  border-color: #cfc6ae;
  animation: shelfFinalGlow 2.6s ease-in-out infinite;
}
.shelf-slot-ready .shelf-slot-name { color: #f6f1e4; letter-spacing: 0.14em; }
@keyframes shelfFinalGlow {
  0%, 100% { box-shadow: 0 0 0 rgba(246,241,228,0); }
  50% { box-shadow: 0 0 18px rgba(246,241,228,0.5); }
}
/* 名前をつけ終えた状態。 */
.shelf-slot-own { border-color: #7d7466; }
.shelf-slot-own:hover { border-color: #cfc6ae; }
.shelf-slot-own .shelf-slot-name { color: #f6f1e4; }

.shelf-note {
  margin-top: 22px;
  max-width: 520px;
  font-size: 12px;
  line-height: 1.8;
  color: var(--text-faint);
  text-align: center;
  text-wrap: pretty;
}
.btn-shelf-back {
  margin-top: 14px;
  padding: 11px 34px;
  font-size: 15px;
  font-weight: 700;
  color: var(--gold-light);
  background: rgba(0,0,0,0.4);
  border: 1px solid #6b5320;
  border-radius: 26px;
  cursor: pointer;
}
.btn-shelf-back:hover { border-color: var(--gold); }

/* ============================================================
   三十三枚目の栞：名づけの画面
   ============================================================
   ゲーム中で唯一のテキスト入力。原稿の一行に書き入れる所作にしたいので、
   四角い入力欄ではなく下線一本にしてある。入力した名はこのブラウザの
   記録にしか残らない（送信先はない）。 */
.naming-screen {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 18px;
}
.naming-card {
  width: 100%;
  max-width: 460px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 30px 24px 26px;
  border: 1px solid #6a6252;
  border-radius: 14px;
  background: linear-gradient(180deg, rgba(24,20,34,0.96), rgba(14,11,22,0.96));
  box-shadow: 0 0 40px rgba(242,236,221,0.12);
}
.naming-label {
  font-family: var(--font-display);
  font-size: 12px;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
  color: #b9ae95;
}
.naming-title {
  margin: 10px 0 0;
  font-family: var(--font-display);
  font-size: clamp(19px, 5.2vw, 24px);
  font-weight: 800;
  color: #f2ecdd;
}
.naming-note {
  margin: 12px 0 0;
  font-size: 12px;
  line-height: 2;
  color: var(--text-faint);
  text-wrap: pretty;
}
.naming-input {
  width: 100%;
  margin: 22px 0 0;
  padding: 8px 4px;
  font-family: var(--font-display);
  font-size: clamp(20px, 5.6vw, 26px);
  font-weight: 800;
  text-align: center;
  color: #f6f1e4;
  background: transparent;
  border: none;
  border-bottom: 1px solid #6a6252;
  border-radius: 0;
  outline: none;
}
.naming-input:focus { border-bottom-color: #cfc6ae; }
.naming-input::placeholder { color: #4f4860; font-weight: 400; font-size: 16px; }
.naming-count {
  margin: 8px 0 0;
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--text-faint);
}
.btn-naming-decide {
  margin-top: 22px;
  padding: 12px 34px;
  font-family: inherit;
  font-size: 15px;
  font-weight: 700;
  color: #241d14;
  background: linear-gradient(90deg, #b9ae95, #f2ecdd);
  border: none;
  border-radius: 26px;
  cursor: pointer;
}
.btn-naming-decide:disabled {
  color: #6a6252;
  background: rgba(0,0,0,0.4);
  border: 1px solid #3a3350;
  cursor: default;
}

/* 栞の詳細は戦闘中の解説カードのスタイルを使い回す。ただしめくりは付けない
   （栞は既に返した札で、何が出るか分かっているため）。3Dの回転を持たない分、
   表面に掛かっている rotateY(180deg) を親側で打ち消しておく。 */
.tech-card-inner-static {
  animation: none !important;
  transform: rotateY(180deg);
  /* 栞の画面の札はめくらないので、合成用の層を確保しておく理由がない。 */
  will-change: auto;
}

/* ============================================================
   注意文・ヒントの小窓（app.js の showStorageNotice / showBookmarkHint）
   ============================================================ */
.notice-overlay {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(4, 3, 8, 0.82);
  cursor: pointer;
  animation: techCardOverlayIn 0.2s ease-out both;
}
.notice-card {
  width: min(400px, 100%);
  padding: 22px 22px 18px;
  border-radius: 14px;
  border: 2px solid #6b5320;
  background: linear-gradient(180deg, #1b1729, #14111f);
  box-shadow: 0 0 30px rgba(202, 164, 86, 0.25), 0 22px 50px rgba(0,0,0,0.7);
  cursor: default;
  text-align: center;
  animation: noticeIn 0.35s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes noticeIn {
  from { opacity: 0; transform: translateY(16px) scale(0.96); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
.notice-card-hint { border-color: #402a63; }
.notice-title {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 800;
  color: var(--gold-light);
  margin-bottom: 12px;
}
.notice-card-hint .notice-title { color: #d7bdf5; }
.notice-body {
  font-size: 14px;
  line-height: 1.9;
  color: #e9e3d7;
  text-align: left;
  text-wrap: pretty;
}
.notice-warn {
  margin-top: 10px;
  padding: 9px 11px;
  border-radius: 7px;
  background: rgba(179, 39, 31, 0.16);
  border: 1px solid rgba(179, 39, 31, 0.5);
  font-size: 13px;
  line-height: 1.8;
  color: #f0cfcb;
  text-align: left;
}
/* 「ご利用にあたって」（app.js の showTerms）。栞の注意書きより項目が多いので、
   幅を少し広げ、画面の低い端末では枠の中だけをスクロールさせる。 */
.notice-card-terms {
  width: min(460px, 100%);
  max-height: 86vh;
  overflow-y: auto;
}
.terms-list {
  margin: 12px 0 0;
  padding: 0;
  text-align: left;
}
.terms-list li {
  list-style: none;
  position: relative;
  padding-left: 15px;
  margin-bottom: 9px;
  font-size: 13.5px;
  line-height: 1.85;
  color: #e9e3d7;
  text-wrap: pretty;
}
.terms-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.85em;
  width: 7px;
  height: 1px;
  background: var(--gold);
}
.terms-credit {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid rgba(202, 164, 86, 0.25);
  font-size: 12px;
  color: var(--text-faint);
}

.btn-notice-ok {
  margin-top: 16px;
  padding: 10px 30px;
  font-size: 15px;
  font-weight: 700;
  color: var(--bg-void);
  background: linear-gradient(180deg, var(--gold-light), var(--gold));
  border-radius: 24px;
  cursor: pointer;
}

@media (max-width: 640px) {
  .shelf-screen { padding: 20px 14px 40px; }
  .shelf-grid { grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); gap: 8px; }
  .shelf-slot-name { font-size: 11px; }
}

/* タイトルから開く文豪一覧（app.js の showCastScreen）。
   一覧そのもの（.cast-board）はクリア画面と共用だが、あちらは本文の幅に
   合わせた読み物の一部なのに対し、こちらは一覧が主役なので横いっぱいに広げる。
   .shelf-screen は align-items:center の縦フレックスで、そのままだと
   .cast-board の幅が中身なりに縮んで2列しか並ばない（実際にそうなっていた）。 */
.cast-screen .cast-board {
  align-self: stretch;
  max-width: 100%;
  margin-top: 8px;
}
.cast-screen .cast-grid {
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
}
/* 画面の見出し（文豪一覧）と重複するので、中の見出しは出さない。 */
.cast-screen .cast-heading { display: none; }

/* 来歴の中の手がかり（app.js の highlightLoreHint）。
   同じ語が光る者どうしを終章の編成で並べると隠しの絆コンボが成立する。
   ネタバレにならないよう、何のヒントかは書かない。「ここだけ色が違う」
   ことにいつか気づいてもらう、という置き方にしてある。
   金の下線＋わずかな発光だけに留め、読みの邪魔をしない。 */
.lore-hint {
  font-style: normal;
  color: var(--gold-light);
  text-shadow: 0 0 10px rgba(202, 164, 86, 0.45);
  border-bottom: 1px solid rgba(202, 164, 86, 0.55);
  padding-bottom: 1px;
}

/* ============================================================
   章の扉ページ（開幕・読了のスチル）
   ============================================================

   章の始まりと終わりに一枚だけ挟む静止画（app.js の showChapterOpening /
   showChapterClear）。既存のカットイン4種（.chuuni-cutin の金×斜め帯、
   .boss-intro の朱×上下幕、.char-intro-card の金枠プロフィール、
   .tech-card の箔縁トレカ）とは見せ方を分けてある：帯も幕もカード枠も使わず、
   本の扉ページの見立てで、罫線と文字だけを静かに置く。派手さで押す画面が
   すでに4種あるので、ここは「静けさ」が他と取り違えられないための特徴になる。
   装飾を足したくなった時も、斜めの帯・上下から閉じる幕・カード枠は
   この画面には持ち込まないこと。 */

.chapter-still {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  cursor: pointer;
  /* 背景はその章の絵。ただし扉は文字を読ませる画面なので、
     ストーリー画面（.story-screen::before）より強い暗幕を重ねて沈めている。
     var(--scene-bg, none) のフォールバックは必須（未定義のまま
     background-image に入れると宣言全体が無効になり、暗幕まで消える）。 */
  background-image:
    linear-gradient(180deg, rgba(10, 9, 16, 0.86) 0%, rgba(10, 9, 16, 0.92) 100%),
    var(--scene-bg, none);
  background-size: cover, cover;
  background-position: center 30%, center 30%;
  background-repeat: no-repeat, no-repeat;
  background-color: var(--bg-void);
  animation: chapterStillIn 0.75s ease both;
}
.chapter-still.chapter-still-out {
  animation: chapterStillOut 0.42s ease both;
}

/* 中身が出そろう前にタップされたときの「いますぐ全部見せる」状態
   （app.js の showChapterStill）。途中のアニメーションを打ち切り、
   最終状態で置く。罫線は scaleX(1)、文字と顔ぶれは translateY(0)、
   「読了」は scale(1) が最終状態なので、transform を消せば揃う。 */
.chapter-still-revealed .chapter-still-rule,
.chapter-still-revealed .chapter-still-label,
.chapter-still-revealed .chapter-still-stage,
.chapter-still-revealed .chapter-still-seal,
.chapter-still-revealed .chapter-still-body,
.chapter-still-revealed .chapter-still-cast-sprite {
  animation: none !important;
  opacity: 1;
  transform: none;
}

@keyframes chapterStillIn {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes chapterStillOut {
  from { opacity: 1; }
  to { opacity: 0; }
}

.chapter-still-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  width: 100%;
  max-width: 640px;
  text-align: center;
}

/* 上下の二重罫。左右に開くように現れるので、幅そのものをアニメーションさせる。 */
.chapter-still-rule {
  width: 100%;
  height: 3px;
  background:
    linear-gradient(180deg,
      rgba(202, 164, 86, 0.75) 0 1px,
      transparent 1px 2px,
      rgba(202, 164, 86, 0.35) 2px 3px);
  transform: scaleX(0);
  animation: chapterRuleOpen 0.9s cubic-bezier(0.22, 0.9, 0.28, 1) 0.1s both;
}

@keyframes chapterRuleOpen {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

.chapter-still-label {
  font-family: var(--font-display);
  font-size: 40px;
  letter-spacing: 0.22em;
  /* letter-spacing は最後の字の後ろにも入るため、中央寄せだと文字列が左に
     寄って見える。同じ値の text-indent で光学的な中央に戻す
     （.tech-card-* など、この style.css の他の字送り指定と同じ書き方）。 */
  text-indent: 0.22em;
  color: var(--gold-light);
  text-shadow: 0 0 18px rgba(202, 164, 86, 0.35);
  animation: chapterTextRise 1s ease 0.35s both;
}

.chapter-still-stage {
  font-family: var(--font-display);
  font-size: 19px;
  letter-spacing: 0.14em;
  text-indent: 0.14em;
  color: var(--text-sub);
  animation: chapterTextRise 1s ease 0.6s both;
}

.chapter-still-body {
  font-size: 15px;
  line-height: 1.9;
  color: var(--text-sub);
  /* 中央寄せの一文なので balance でよい（左揃えの本文に balance を使うと
     右側にだけ余白ができる。CLAUDE.md 6.6 の孤立行対策を参照）。 */
  text-wrap: balance;
  animation: chapterTextRise 1s ease 0.95s both;
}

@keyframes chapterTextRise {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

/* 「読了」「完結」。章名が出てから一拍おいて、この画面でいちばん大きな金の字で
   現れる（章を読み終えた実感がここに集まるので、章名より大きく取っている）。
   はじめは朱印を斜めに押す見せ方にしていたが、傾きも朱の囲みも
   「読み終えた」静けさに合わず、金の字だけで大きく置く形に改めた。 */
.chapter-still-seal {
  font-family: var(--font-display);
  font-size: 56px;
  line-height: 1.15;
  letter-spacing: 0.18em;
  text-indent: 0.18em;
  color: var(--gold-light);
  text-shadow:
    0 0 24px rgba(202, 164, 86, 0.5),
    0 0 60px rgba(202, 164, 86, 0.22);
  animation: chapterSealRise 0.9s cubic-bezier(0.2, 0.9, 0.3, 1) 1.5s both;
}

@keyframes chapterSealRise {
  from { opacity: 0; transform: scale(1.14); }
  to { opacity: 1; transform: scale(1); }
}

/* その章で共に戦った顔ぶれ。 */
.chapter-still-cast {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-end;
  gap: 10px;
  margin-top: 2px;
}
.chapter-still-cast-sprite {
  height: 76px;
  width: auto;
  image-rendering: pixelated;
  /* 立ち絵を出す画面では必ず var(--sprite-outline) を含める（style.css 冒頭の注記）。 */
  filter:
    var(--sprite-outline)
    drop-shadow(0 8px 12px rgba(0, 0, 0, 0.5));
  animation: chapterCastIn 0.5s ease both;
}
/* 顔ぶれは「読了」の字が出きってから並び始める。同時に動くと、
   どちらの変化も互いに気を取られて印象に残らない。 */
.chapter-still-cast-sprite:nth-child(1) { animation-delay: 2.5s; }
.chapter-still-cast-sprite:nth-child(2) { animation-delay: 2.65s; }
.chapter-still-cast-sprite:nth-child(3) { animation-delay: 2.8s; }
.chapter-still-cast-sprite:nth-child(4) { animation-delay: 2.95s; }
.chapter-still-cast-sprite:nth-child(n + 5) { animation-delay: 3.1s; }

@keyframes chapterCastIn {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: translateY(0); }
}

@media (max-width: 600px) {
  .chapter-still { padding: 20px 16px; }
  .chapter-still-inner { gap: 14px; }
  .chapter-still-label { font-size: 29px; }
  .chapter-still-stage { font-size: 16px; }
  .chapter-still-body { font-size: 14px; }
  .chapter-still-seal { font-size: 40px; }
  .chapter-still-cast-sprite { height: 58px; }
}
