:root {
  --bg: #14161c;
  --panel: #1e212b;
  --panel-alt: #262a37;
  --border: #333849;
  --text: #e8e9ee;
  --text-dim: #9aa0b4;
  --accent: #4fc3f7;
  --danger: #ef5350;
  --success: #66bb6a;

  --rarity-n: #8d95a8;
  --rarity-sr: #4fc3f7;
  --rarity-ur: #ffb300;
  --rarity-secret: #e040fb;

  /* 実プレイ改善: 戦闘ログのキーワード強調用。文章全体ではなく「誰が・何を
     使って・結果どうなったか」に関わる決まった単語だけに使う(js/ui/render.js
     のLOG_KEYWORD_RULES/LOG_CARD_CATEGORY_CLASS参照)。 */
  --log-player: #64b5f6;
  --log-cpu: #ff6b6b;
  --log-atk: #ffa726;
  --log-def: #4dd0e1;
  --log-damage: #ff5252;
  --log-heal: #81c784;
  --log-buff: #80cbc4;
  --log-debuff: #ce93d8;
  --log-counter: #ffd54f;
  --log-city-event: #ffb347;

  /* UI/UX強化(このセッションで追加): ログ中のカード名の色をカテゴリ
     (属性)ごとに統一するための配色。既存のPLAYER/CPU色(--log-player/-cpu)・
     結果強調色(--log-damage/-heal等)とは別の視覚チャンネルとして、
     「誰が(PLAYER/CPU、既存)」「何を(カテゴリ、ここ)」「結果どうなったか
     (強調、既存)」の3段階で情報を追えるようにする(js/ui/render.jsの
     LOG_CARD_CATEGORY_CLASS参照)。既にテーマ色が決まっているBUFF/DEBUFF/
     COUNTER(STRESS)はその色をそのまま流用し、SECRETカテゴリはカード自体の
     レアリティ色(--rarity-secret)をそのまま流用する(SECRETカテゴリの
     カードは全てSECRETレアリティのため、意味が一致し違和感がない)。 */
  --log-cat-gang: #ff4d94;
  --log-cat-pd: #5c7cfa;
  --log-cat-ems: #2bbbad;
  --log-cat-food: #c9a186;
  --log-cat-mech: #90a4ae;
  --log-cat-material: #b0a825;
  --log-cat-civ: #9ccc9c;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-width: 960px;
  background: var(--bg);
  color: var(--text);
  font-family: "Hiragino Sans", "Yu Gothic", "Segoe UI", system-ui, sans-serif;
}

.app-header {
  position: relative;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 20px;
  border-bottom: 1px solid var(--border);
  background: var(--panel);
}

.app-header h1 {
  font-size: 18px;
  margin: 0;
  letter-spacing: 0.05em;
}

/* Phase7: 常時ターン数表示 */
.turn-counter {
  font-size: 18px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--accent, #7fd1ff);
}

.phase-tag {
  font-size: 11px;
  color: var(--text-dim);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 2px 6px;
  margin-left: 8px;
}

.header-buttons {
  display: flex;
  gap: 8px;
}

#debug-toggle-btn.btn-danger {
  background: var(--danger);
  color: #24070a;
}

.debug-banner {
  background: repeating-linear-gradient(
    45deg,
    #ff9800,
    #ff9800 12px,
    #212121 12px,
    #212121 24px
  );
  color: #fff3e0;
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.02em;
  text-align: center;
  padding: 6px 12px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}

.debug-banner.hidden {
  display: none;
}

.debug-panel {
  background: #2a1608;
  border: 2px dashed #ff9800;
  border-radius: 10px;
  padding: 10px 14px;
}

.debug-panel.hidden {
  display: none;
}

.debug-panel-title {
  font-size: 12px;
  font-weight: 700;
  color: #ffb300;
  margin-bottom: 8px;
}

.debug-panel-controls {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}

.debug-panel-controls select {
  background: var(--panel-alt);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 8px;
  font-size: 12px;
  max-width: 260px;
}

/* Phase3: フルカスタム(MECH-03)のATK/DEF再配分入力 */
.fullcustom-panel {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  background: var(--panel-alt);
  border: 1px solid var(--accent);
  border-radius: 8px;
  padding: 8px 10px;
  margin: 8px 0;
  font-size: 12px;
}

.fullcustom-panel.hidden {
  display: none;
}

.fullcustom-panel label {
  display: flex;
  align-items: center;
  gap: 4px;
}

.fullcustom-panel input[type="number"] {
  width: 60px;
  background: #0d0f14;
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 3px 6px;
}

/* Phase9/Phase4(演出中の入力ロックと表示同期の最終仕上げ): 既存の.btn:disabled
   (opacity 0.35 / cursor not-allowed)と同じ視覚言語をそのまま流用する。 */
.fullcustom-panel input[type="number"]:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.fullcustom-hint {
  color: var(--text-dim);
}

/* Phase3: 目撃者(CIV-01)で公開された相手の手札の表示 */
.reveal-panel {
  background: var(--panel-alt);
  border: 1px solid var(--rarity-secret);
  border-radius: 8px;
  padding: 8px 10px;
  margin-bottom: 10px;
}

.reveal-panel.hidden {
  display: none;
}

.reveal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 6px;
  font-size: 12px;
  font-weight: 700;
  color: var(--rarity-secret);
}

.reveal-cards {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  color: var(--text);
}

/* 実プレイ改善(ユーザー確定): 1920×1080・ブラウザ100%表示で、CPU/中央戦闘/PLAYER手札の
   主要な戦闘情報を縦スクロールなしで同時に把握できるようにする。情報自体は削除せず、
   各エリアのpadding/margin・カードサイズ・戦闘ログの高さをvh/clamp()で詰めている
   (ブラウザズーム80%を前提にはしていない。100%表示のまま収まることを狙った調整)。 */
#game-root {
  display: flex;
  flex-direction: column;
  gap: clamp(6px, 1vh, 12px);
  padding: 10px 20px 14px;
  max-width: 1600px;
  margin: 0 auto;
}

.player-area {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 8px 16px;
}

.player-area-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 4px;
}

.player-name {
  font-weight: 700;
  letter-spacing: 0.05em;
}

.hand-count {
  font-size: 12px;
  color: var(--text-dim);
}

.hp-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
  /* 第3弾: `-X`ダメージ数値表示(.hp-damage-pop)をこの行の右寄りに
     絶対配置で重ねるための基準にする。 */
  position: relative;
}

.hp-bar-track {
  flex: 1;
  height: 14px;
  background: #0d0f14;
  border-radius: 7px;
  overflow: hidden;
  border: 1px solid var(--border);
}

.hp-bar-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--danger), var(--success));
  width: 40.4%;
  transition: width 0.3s ease;
}

.hp-text {
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  min-width: 70px;
  text-align: right;
}

/* Phase4: 継続中のBUFF/DEBUFF表示 */
.status-effects {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 4px;
  min-height: 0;
}

.status-effects:empty {
  margin-bottom: 0;
}

.status-badge {
  position: relative;
  font-size: 10px;
  font-weight: 700;
  padding: 2px 6px;
  border-radius: 4px;
}

/* BUFF/DEBUFFバッジのホバーツールチップ。バッジ自体の見た目・文言は変更せず、
   ホバー時のみ内容を#tooltip-portalへ複製して表示する(見切れ修正により、
   ここでの:hover表示切り替えはjs/ui/render.jsのJS側へ移動した)。 */
.status-badge-buff {
  background: rgba(102, 187, 106, 0.18);
  color: var(--success);
}

.status-badge-debuff {
  background: rgba(239, 83, 80, 0.18);
  color: var(--danger);
}

/* Part E仕上げ: CITY EVENT継続効果(2ターン継続系)のヘッダー常時バッジ */
.city-event-status {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-left: 12px;
}

.status-badge-city-event {
  background: rgba(255, 179, 71, 0.18);
  color: #ffb347;
  border: 1px solid rgba(255, 179, 71, 0.4);
}

/* Phase4: STRESS-03「エクスカリバー」の無効化対象選択パネル */
.counter-target-panel {
  background: var(--panel-alt);
  border: 1px solid var(--danger);
  border-radius: 8px;
  padding: 8px 10px;
  margin: 8px 0;
  font-size: 12px;
}

.counter-target-panel.hidden {
  display: none;
}

.counter-target-title {
  font-weight: 700;
  color: var(--danger);
  margin-bottom: 6px;
}

.counter-target-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* Part E仕上げ: FOOD/MATERIAL「公開して選ぶ」形式のトグル選択パネル
   (食べ比べ/VIP待遇/裏メニュー/ホームレスクラフト/受注型予約販売) */
.reveal-pick-panel {
  background: var(--panel-alt);
  border: 1px solid var(--rarity-secret);
  border-radius: 8px;
  padding: 8px 10px;
  margin: 8px 0;
  font-size: 12px;
}

.reveal-pick-panel.hidden {
  display: none;
}

.reveal-pick-title {
  font-weight: 700;
  color: var(--rarity-secret);
  margin-bottom: 6px;
}

.reveal-pick-cards {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.reveal-pick-card {
  background: var(--panel);
  border: 2px solid var(--border);
  border-radius: 6px;
  padding: 6px 8px;
  font-size: 11px;
  color: var(--text);
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 6px;
  text-align: left;
}

/* 通常カード画像146枚組み込み(ユーザー確定仕様セクション9): 飲食店/素材屋の
   「公開して選ぶ」候補の小さなサムネイル画像。元画像の縦横比を維持したまま
   全体表示(object-fit:contain、トリミングなし)。 */
.reveal-pick-card-image {
  width: 32px;
  height: 40px;
  object-fit: contain;
  flex: 0 0 auto;
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.2);
}

.reveal-pick-card-label {
  flex: 1 1 auto;
}

.reveal-pick-card:hover {
  border-color: var(--rarity-secret);
}

/* Phase9/Phase4(演出中の入力ロックと表示同期の最終仕上げ): 既存の.btn:disabled
   (opacity 0.35 / cursor not-allowed)と同じ視覚言語をそのまま流用する。 */
.reveal-pick-card:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.reveal-pick-card:disabled:hover {
  border-color: var(--border);
}

.reveal-pick-card-selected {
  border-color: var(--accent, #5b9dff);
  background: rgba(91, 157, 255, 0.18);
}

.hand {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  min-height: 78px;
}

.hand-hidden .card {
  visibility: hidden;
}

/* プレイテスト指摘5番(このセッションで変更): PLAYER手札(#player-hand)は、
   従来のファン型(扇形・重なりあり)配置から「重ならない横並び」配置へ
   変更した。CPU手札(#cpu-hand)は上の.hand(flex-wrap、非公開表示)のまま
   一切変更していない。
   - 各カードは絶対配置をやめ、通常のflexアイテムとして横一列(手札配列の
     並び順どおり)に並ぶ。これによりカード同士が重ならず、クリック領域が
     自然に独立する。回転角・放物線状の縦位置・重なりz-index・露出座標計算・
     ファン型変形(以前の--fan-*カスタムプロパティ)は、このPLAYER手札にはもう
     使わない(js/ui/fan-hand.jsは他所で使っていないため呼び出しをやめた。
     モジュール自体は当面残置し、削除はしていない)。
   - 手札が収まりきらない場合は、このコンテナ単体だけがoverflow-x:autoで
     横スクロールする。ページ全体が横スクロールすることはない(このコンテナ
     自体は親の.player-areaの幅を超えて広がらないブロック要素のまま)。
   - overflow-x:autoを指定すると仕様上overflow-yの計算値も自動的にautoになる
     (bothがvisibleのままにはできない)ため、縦方向は「実際にpadding内に
     収まっていれば見た目上スクロールバーは出ない」という前提で、ホバー/
     選択時の拡大・浮き上がり分を吸収できるよう上下に十分な余白を確保する。 */
#player-hand {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: flex-end;
  gap: 10px;
  min-height: 132px;
  /* 実プレイ回帰: 「配達員」等の長文カード(プレイテスト指摘6番)を折り返し
     表示に変更した結果(.card-stats/.card-abilityにoverflow-wrap:break-word)、
     素材屋系カード等、特にテキストが長いカードは1枚だけ縦に大きく伸びうる。
     #player-handにmin-heightしか無いと、そのカードに合わせてコンテナ自体が
     無制限に縦へ伸び、画面全体の縦スクロールを引き起こしてしまう
     (主要戦闘UIが1画面に収まることを確認する既存回帰テストの対象)。
     そのため上限(max-height)を設け、まれにそれを超える場合だけ
     #player-hand内部だけで縦スクロールする(overflow-x:autoの指定により
     overflow-yも自動的にauto扱いになる、上のコメント参照)ようにし、
     ページ全体の高さには影響させない。通常のカード(ATK/DEF付き等)は
     min-height程度に収まるため、普段はこの上限に達すること自体がない。 */
  max-height: 210px;
  padding: 28px 10px 14px;
  overflow-x: auto;
}

#player-hand.hand-fan-empty {
  min-height: 78px;
  padding: 10px;
}

/* プレイテスト指摘修正(このセッションで追加、指摘1番): PLAYER手札の全カードを
   種類・カテゴリ・レアリティ・文字数に関係なく同じ外寸(幅112px×高さ128px)に
   統一する。従来は.card側のmin-height:76pxにより、効果説明文が長いカードほど
   縦に伸びてカードごとに高さがバラバラだった(実測76px〜164px)。
   128pxという値は、全146枚のカードを実際にこの手札表示で計測した結果
   (scripts/verify_uniform_card_size.mjsで再現可能)、94.5%(138/146枚)は
   そのままの内容量で収まる高さであり、残る長文カードだけ(.card-stats側の
   -webkit-line-clamp:4により)効果説明が4行+省略記号で打ち切られる
   (カード名・カテゴリ・レアリティ・ATK/DEF・主要な効果内容の出だしは
   どのカードでも判別できる状態を維持し、全文は既存のホバー/キーボード
   フォーカス用ツールチップで確認できる)。高さを固定倍率で切り詰めるのでは
   なく、rem/font-sizeはそのまま(文字を極端に小さくして押し込む方法は
   取っていない)。#player-hand専用のスコープなので、CPU手札(非公開表示)や
   将来の別のカード表示UIには影響しない。overflow:hiddenは、上記の
   line-clampで計算しきれない極端なケース(例: カード名が3行以上に達する等)
   に備えた安全策で、通常はline-clamp側の省略表示が先に効くため実際に
   このoverflow:hiddenが可視の「不格好な途中切れ」を起こすことは無い。 */
#player-hand .card {
  flex: 0 0 auto;
  margin: 0;
  width: 112px;
  height: 128px;
  overflow: hidden;
  scroll-margin: 20px;
  transition: transform 0.16s ease, border-color 0.1s ease, box-shadow 0.16s ease, outline-color 0.1s ease;
}

.card {
  position: relative;
  width: 112px;
  min-height: 76px;
  background: var(--panel-alt);
  border: 2px solid var(--border);
  border-radius: 8px;
  padding: 5px 6px;
  font-size: 11px;
  cursor: pointer;
  user-select: none;
  transition: transform 0.1s ease, border-color 0.1s ease;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.card:hover:not(.card-disabled) {
  transform: translateY(-3px);
  border-color: var(--accent);
}

/* プレイテスト指摘5番: 横並びPLAYER手札のホバー/キーボードフォーカス演出。
   カード同士がもう重ならないため、以前のような大掛かりなz-index引き上げは
   不要。少し持ち上げ+わずかに拡大するだけで十分読みやすく、隣のカードや
   ボタンを隠さない(gapで確保した隙間の範囲内に収まる控えめな量)。
   disabledなカードは対象外(既存仕様どおりホバー演出なし)。 */
#player-hand .card:hover:not(.card-disabled),
#player-hand .card:focus:not(.card-disabled),
#player-hand .card:focus-visible:not(.card-disabled) {
  transform: translateY(-10px) scale(1.06);
  border-color: var(--accent);
  z-index: 5;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.55);
}

/* キーボードフォーカス中のカード: 色だけに依存しない識別のため、
   破線アウトライン+上部の小さな▲マーカーを併用する(第6弾セクション4、
   横並びレイアウトへ変更後も表示方法自体は変えていない)。 */
#player-hand .card:focus,
#player-hand .card:focus-visible {
  outline: 3px dashed #ffffff;
  outline-offset: 3px;
}

#player-hand .card:focus::before,
#player-hand .card:focus-visible::before {
  content: "▲ FOCUS";
  position: absolute;
  top: -20px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: #ffffff;
  text-shadow: 0 0 4px rgba(0, 0, 0, 0.85);
  white-space: nowrap;
  pointer-events: none;
}

/* プレイテスト指摘5番: 選択中カードは約1.10〜1.15倍に拡大し、既存の
   青枠(.card-selected、border-width 3px + box-shadow、上のセクション参照)と
   組み合わせて「選択されている」ことが一目でわかるようにする。隣接カードは
   もう重なっていないため、ここでも大きなz-indexは不要(ホバーより少しだけ
   手前で十分)。選択解除時はこのtransformが外れ、通常サイズへ自動的に戻る。 */
#player-hand .card-selected {
  transform: scale(1.12);
  transform-origin: bottom center;
  z-index: 3;
}

/* ホバー/フォーカスと選択状態が重なった場合も破綻しないよう、拡大率を
   1.10〜1.15の範囲内でひとつだけ決めておく(2つのtransformが競合しない)。 */
#player-hand .card-selected:hover:not(.card-disabled),
#player-hand .card-selected:focus:not(.card-disabled),
#player-hand .card-selected:focus-visible:not(.card-disabled) {
  transform: translateY(-10px) scale(1.14);
  z-index: 6;
}

#player-hand .card-removal-target {
  z-index: 8;
}

/* 実プレイ改善: ツールチップの見切れ修正で表示方式を変更したため、カード自身の
   ホバーによるz-index引き上げ・.card-tooltipの表示切り替えは不要になった
   (常にbody直下の#tooltip-portalへ複製して表示するため、以下の
   .card-tooltip自体は各カード内部に残る「内容のテンプレート」であり、
   常に非表示のまま=画面上に直接表示されることはない)。 */
.card-tooltip {
  position: absolute;
  bottom: calc(100% + 10px);
  left: 50%;
  transform: translateX(-50%);
  width: 210px;
  background: #0a0c11;
  border: 1px solid var(--accent);
  border-radius: 8px;
  padding: 9px 11px;
  font-size: 11px;
  line-height: 1.55;
  color: var(--text);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  z-index: 40;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5);
  white-space: normal;
}

/* 実プレイ改善: カード説明ツールチップの見切れ修正。
   カード/バトルログ内カード名/BUFF・DEBUFFバッジ、いずれのホバー時も、
   実際に画面へ表示されるのはこの共有ポータル1つだけ(js/ui/render.jsが
   .card-tooltipの中身(タイトル/各行)をここへ複製し、position:fixedで
   画面内に収まるよう動的にtop/leftを計算する)。body直下の要素なので、
   戦闘ログ(overflow-y:auto)などの祖先要素によるクリッピングを受けず、
   常に他のUI要素より前面に表示される。 */
.card-tooltip-portal {
  position: fixed;
  top: 0;
  left: 0;
  width: 230px;
  max-width: calc(100vw - 16px);
  background: #0a0c11;
  border: 1px solid var(--accent);
  border-radius: 8px;
  padding: 9px 11px;
  font-size: 11px;
  line-height: 1.55;
  color: var(--text);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.12s ease;
  z-index: 1000;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5);
  white-space: normal;
}

.card-tooltip-portal.tooltip-visible {
  opacity: 1;
  visibility: visible;
}

.card-tooltip-title {
  font-weight: 700;
  color: var(--accent);
  margin-bottom: 4px;
}

.card-tooltip-line {
  white-space: normal;
  /* プレイテスト指摘6番: 「配達員」の効果説明中の「(N=1/SR=2/UR=3/SECRET=4)」の
     ような、空白を含まない長い英数字の並びが210px幅のツールチップからはみ出す
     ことがあったため、必要な場合だけ単語の途中でも折り返せるようにする
     (通常の日本語文章の折り返し位置には影響しない、はみ出し防止の保険)。
     カード名・効果説明の文言自体は一切変更していない。 */
  overflow-wrap: break-word;
  word-break: break-word;
}

.card-tooltip-line:last-child:not(:only-child) {
  margin-top: 4px;
  color: var(--rarity-ur);
  font-weight: 600;
}

/* 手札カード拡大プレビュー(このセッションで追加)
   PLAYER手札の固定サイズ(112×128px)はそのまま維持しつつ、カード画像の
   細部を確認しやすくするため、ホバー(タッチ端末は1回目のタップ)時だけ
   別レイヤーとして画面上に大きく表示する。#card-tooltip-portal(名前・数値等の
   テキストツールチップ)とは完全に独立したレイヤーであり、互いの表示・
   位置計算には一切干渉しない。position:fixed + pointer-events:noneのため、
   表示中も手札のクリック・横スクロール・カードサイズには一切影響しない
   (js/ui/render.jsのinitCardImagePreview参照)。 */
.card-image-preview-portal {
  position: fixed;
  top: 0;
  left: 0;
  display: none;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 10px;
  background: rgba(10, 12, 17, 0.92);
  border: 2px solid var(--accent);
  border-radius: 12px;
  box-shadow: 0 10px 36px rgba(0, 0, 0, 0.6);
  pointer-events: none;
  z-index: 1500;
}

.card-image-preview-portal.preview-visible {
  display: flex;
}

.card-image-preview-img {
  display: block;
  /* 推奨表示サイズ(高さ550〜650px程度)を狙いつつ、必ず画面内に収まる
     ようクランプする。元画像の縦横比は維持し、引き伸ばし・切り抜きは行わない
     (width/heightをautoにし、max-width/max-heightだけで制約する)。 */
  width: auto;
  height: auto;
  max-height: min(620px, 75vh);
  max-width: min(420px, 32vw);
  object-fit: contain;
  border-radius: 8px;
  background: var(--panel-alt, #14161c);
}

/* 使用不可(disabled/locked)状態の反映: プレビュー画像自体を減光し、
   バッジで理由を示す(手札カード側の既存の見た目判定をそのまま踏襲するだけで、
   カードの状態判定ロジック自体には一切手を加えない)。 */
.card-image-preview-portal.preview-state-disabled .card-image-preview-img {
  filter: grayscale(0.65) brightness(0.5);
}

/* 選択中(selected/removal-target)状態の反映。 */
.card-image-preview-portal.preview-state-selected {
  border-color: var(--rarity-ur, #ffb300);
  box-shadow: 0 10px 36px rgba(0, 0, 0, 0.6), 0 0 0 2px rgba(255, 179, 0, 0.35);
}

.card-image-preview-badge {
  font-size: 12px;
  font-weight: 700;
  color: var(--text);
  background: rgba(0, 0, 0, 0.55);
  border-radius: 6px;
  padding: 3px 10px;
}

.card-image-preview-badge[hidden] {
  display: none;
}

/*
 * Phase3 UI改善: カテゴリ共通の「1アクション単位の選択制御」の見た目。
 * js/ui/controller.js の classifyCard が返す状態(selected/selectable/
 * disabled/normal/removal-target)にそれぞれ対応させているだけで、GANG/PDなど
 * カテゴリ別のスタイルは持たない(Phase4以降の新カテゴリもこのクラスを
 * そのまま使う)。removal-targetはUI/UX強化(このセッション)で追加。
 */

/* 現在選択中: 青い太枠 */
.card-selected {
  border-width: 3px;
  border-color: var(--accent);
  background: #223142;
  box-shadow: 0 0 0 2px rgba(79, 195, 247, 0.35);
  padding: 5px 6px; /* border-widthが2px→3pxになった分だけ内側を詰めて外形サイズを揃える */
}

/* 追加選択可能: 発光/パルス */
.card-selectable {
  border-color: var(--accent);
  animation: card-selectable-pulse 1.4s ease-in-out infinite;
}

@keyframes card-selectable-pulse {
  0%,
  100% {
    box-shadow: 0 0 0 0 rgba(79, 195, 247, 0.55);
  }
  50% {
    box-shadow: 0 0 12px 3px rgba(79, 195, 247, 0.55);
  }
}

/* 選択不可: 暗く表示 */
.card-disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Part E仕上げ(セクション5): CITY EVENTカテゴリLOCK中のカード。
   既存の.card-disabled(暗さ0.4・クリック不可)に重ねて、さらに強めのグレー
   スケール+LOCKアイコンバッジで「ロック中」だと一目でわかるようにする。 */
.card-locked {
  filter: grayscale(70%);
}

.card-lock-badge {
  position: absolute;
  top: -9px;
  left: -6px;
  background: var(--danger);
  color: #fff;
  font-size: 12px;
  padding: 1px 4px;
  border-radius: 4px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
  z-index: 1;
}

.card-tooltip-lock-reason {
  color: var(--danger);
  font-weight: 700;
}

/* UI/UX強化(このセッションで追加): 「このアクションで手札から手放される
   (捨てる/消費する/渡す)」候補として選ばれているカードの見た目。
   飲食店の捨て札選択・素材屋の消費対象選択・詐欺師の譲渡カード選択という
   3つの既存UIが共有する1つのコンポーネント(js/ui/controller.jsの
   getMarkedForRemovalInstanceIds/classifyCard参照)。通常の"selected"(青枠)
   とは色・浮き上がり・バッジの3点で明確に違いが分かるようにしている。 */
.card-removal-target {
  border-width: 3px;
  border-color: var(--danger);
  background: #3a1f22;
  transform: translateY(-8px);
  box-shadow: 0 0 14px 3px rgba(239, 83, 80, 0.65);
  padding: 5px 6px;
  animation: card-removal-target-pulse 1.1s ease-in-out infinite;
}

.card-removal-target:hover {
  transform: translateY(-8px); /* 通常のhover浮き上がり(-3px)より既に高いため据え置き */
}

@keyframes card-removal-target-pulse {
  0%,
  100% {
    box-shadow: 0 0 10px 2px rgba(239, 83, 80, 0.5);
  }
  50% {
    box-shadow: 0 0 18px 5px rgba(239, 83, 80, 0.85);
  }
}

.card-removal-target-badge {
  position: absolute;
  top: -9px;
  right: -6px;
  background: var(--danger);
  color: #fff;
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.03em;
  padding: 2px 5px;
  border-radius: 4px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
  z-index: 1;
}

.card-name {
  font-weight: 700;
  font-size: 12px;
  line-height: 1.2;
  overflow-wrap: break-word;
  word-break: break-word;
}

/* プレイテスト指摘(このセッションで追加、指摘1番): PLAYER手札の全カード外寸を
   統一するため、カード名が極端に長い場合でも2行までに制限し省略記号を付ける
   (カード名自体は変更しない。表示上の折り返し・省略のみ)。#player-handの
   カードだけに適用し、他の場所(CPU手札は非公開表示のため実質影響なし、
   将来カード名を表示する別のUIができた場合に備えて念のためスコープを絞る)。 */
#player-hand .card-name {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.card-meta {
  color: var(--text-dim);
  font-size: 10px;
  overflow-wrap: break-word;
  word-break: break-word;
}

.card-rarity {
  display: inline-block;
  font-size: 9px;
  font-weight: 700;
  padding: 1px 5px;
  border-radius: 3px;
  width: fit-content;
}
.card-rarity.N { background: rgba(141,149,168,0.2); color: var(--rarity-n); }
.card-rarity.SR { background: rgba(79,195,247,0.2); color: var(--rarity-sr); }
.card-rarity.UR { background: rgba(255,179,0,0.2); color: var(--rarity-ur); }
.card-rarity.SECRET { background: rgba(224,64,251,0.2); color: var(--rarity-secret); }

.card-stats {
  font-size: 10px;
  font-weight: 600;
  line-height: 1.3;
  /* プレイテスト指摘6番(このセッションで追加): 「配達員」等、ATK/DEFを持たず
     効果テキスト(description)をそのまま表示するカードで、「(N=1/SR=2/UR=3/
     SECRET=4)」のような空白のない長い英数字の並びが112px幅のカード枠から
     はみ出していた問題の修正。必要な場合だけ単語の途中でも折り返せるように
     するだけで、カード名・効果説明の文言自体・他カードの見た目には影響しない
     (通常の日本語文章は従来どおりの位置で折り返される)。 */
  overflow-wrap: break-word;
  word-break: break-word;
}

/* プレイテスト指摘(このセッションで追加、指摘1番): 従来ここにあった
   `margin-top: auto;` は、内容が短いカードで.card-statsを常にカード最下部へ
   押し下げる指定だった(=名前とstatsの間に空白が生まれる配置)。カード外寸を
   固定した今回の変更では、statsをmeta直下から自然な順序で流し、余った空白は
   カードの一番下にまとまるようにする方が「短い効果文のカードは余った部分を
   空白として扱う」という要件に沿うため、margin-top:autoは削除した
   (文言・情報の内容自体は変えていない、配置のみの変更)。
   あわせて、ATK/DEFを持たないカードのstatsには効果説明の全文がそのまま
   入るため、固定した外寸(#player-hand .card、下記)からはみ出す長文だけを
   最大4行+省略記号で打ち切る(通常の短い「ATK n / DEF n」表示は1行に
   収まるため、この省略は事実上発動しない)。全文は既存のホバー/
   キーボードフォーカス用ツールチップ(既存のcard-tooltip、变更なし)で
   引き続き確認できる。 */
#player-hand .card-stats {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.card-ability {
  font-size: 9px;
  color: var(--accent);
  overflow-wrap: break-word;
  word-break: break-word;
}

/* ============================================================
   通常カード画像146枚組み込み(ユーザー確定仕様セクション6・7):
   カード画像(assets/cards/*.webp)が用意されているカードは、この
   .card-has-imageクラスにより画像を主体とした見た目に切り替える。
   - .card自体の固定外寸(112×128px、既存)・横並び・選択時1.12倍拡大は
     一切変更しない。ここで変更するのは.card内部の見た目だけ。
   - .card-imageはobject-fit:containで、元画像の縦横比を維持したまま
     カード全体(画像の上下左右いずれも)を必ず表示する。引き伸ばし・
     押し潰し・トリミングは行わない。
   - クリック範囲は従来どおり.card要素全体(=画像の表示範囲と一致)。
   - 名前・カテゴリ・数値・能力名の直接表示は、画像モードでは非表示にし
     (削除ではなく非表示。他コードからの参照・既存のDOM構造は保持)、
     既存のホバー/フォーカス用ツールチップ(has-tooltip)側にすべてまとめる
     (buildFullCardTooltip、js/ui/render.js参照)。長い効果説明文を
     小さな画像の上へ無理に重ねて表示することはしない。
   - 画像の読み込みに失敗した場合は、呼び出し元(buildCardElement)が
     このクラス自体を外し、既存のテキスト表示のみへ自動的に戻る。
   ============================================================ */
.card.card-has-image {
  padding: 0;
  overflow: hidden;
}

.card.card-has-image .card-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: var(--panel-alt);
  border-radius: 6px;
  pointer-events: none;
}

.card.card-has-image .card-name,
.card.card-has-image .card-meta,
.card.card-has-image .card-stats,
.card.card-has-image .card-ability {
  display: none;
}

.card.card-has-image .card-rarity {
  position: absolute;
  top: 3px;
  left: 3px;
  z-index: 2;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}

#battle-area {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 16px;
}

#battle-stats {
  display: flex;
  gap: 12px;
  margin-bottom: 6px;
}

.stat-box {
  flex: 1;
  background: var(--panel-alt);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 5px 10px;
  text-align: center;
}

.stat-label {
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.08em;
}

.stat-value {
  font-size: 20px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

#action-panel {
  margin-bottom: 6px;
}

.turn-indicator {
  font-size: 13px;
  font-weight: 700;
  color: var(--accent);
  margin: 0 0 4px;
}

.selection-error {
  font-size: 12px;
  color: var(--danger);
  min-height: 14px;
  margin: 0 0 4px;
}

.action-buttons {
  display: flex;
  gap: 8px;
}

.btn {
  border: none;
  border-radius: 6px;
  padding: 8px 16px;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  letter-spacing: 0.03em;
}

.btn-primary {
  background: var(--accent);
  color: #0b1620;
}

.btn-secondary {
  background: var(--panel-alt);
  color: var(--text);
  border: 1px solid var(--border);
}

.btn-danger {
  background: var(--danger);
  color: #24070a;
}

.btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.battle-log {
  background: #0d0f14;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  /* 実プレイ改善: 固定150pxだと縦幅の狭い画面で全体が収まらないため、
     ビューポート高さに応じて可変にする(内容を削らず高さだけ調整)。 */
  height: clamp(90px, 15vh, 150px);
  overflow-y: auto;
  font-size: 12px;
  color: var(--text-dim);
  line-height: 1.6;
}

.battle-log div.log-turn-marker {
  color: var(--accent);
  font-weight: 700;
  margin-top: 4px;
}

/* バトルログ内の、実際に使用され公開済みのカード名へのホバーツールチップ。
   見切れ修正により、実際の表示は#tooltip-portalへ複製する形に変わっている
   (js/ui/render.js参照)。 */
.log-card-ref {
  position: relative;
  border-bottom: 1px dotted var(--accent);
  cursor: help;
  font-weight: 600;
}

/* UI/UX強化(このセッションで変更): ログ中のカード名の色は、カード自体の
   レアリティではなく「カードカテゴリ(属性)」で統一する
   (PLAYER/CPU色=誰が、カード名の色=何属性のカードを使ったか、結果強調色=
   結果どうなったか、の3段階で追えるようにするユーザー確定仕様)。
   下線色も文字色と揃え、カテゴリの塊として認識しやすくする。 */
.log-card-cat-gang { color: var(--log-cat-gang); border-bottom-color: var(--log-cat-gang); }
.log-card-cat-pd { color: var(--log-cat-pd); border-bottom-color: var(--log-cat-pd); }
.log-card-cat-ems { color: var(--log-cat-ems); border-bottom-color: var(--log-cat-ems); }
.log-card-cat-food { color: var(--log-cat-food); border-bottom-color: var(--log-cat-food); }
.log-card-cat-mech { color: var(--log-cat-mech); border-bottom-color: var(--log-cat-mech); }
.log-card-cat-material { color: var(--log-cat-material); border-bottom-color: var(--log-cat-material); }
.log-card-cat-civ { color: var(--log-cat-civ); border-bottom-color: var(--log-cat-civ); }
.log-card-cat-buff { color: var(--log-buff); border-bottom-color: var(--log-buff); }
.log-card-cat-debuff { color: var(--log-debuff); border-bottom-color: var(--log-debuff); }
.log-card-cat-counter { color: var(--log-counter); border-bottom-color: var(--log-counter); }
.log-card-cat-secret { color: var(--rarity-secret); border-bottom-color: var(--rarity-secret); }

/* レアリティは色そのものではなく太さ/発光で軽く示す副次的なアクセントに
   とどめる(「カード名の色=カテゴリ」の原則を崩さないため)。 */
.log-card-ref.log-rarity-accent-UR { font-weight: 800; }
.log-card-ref.log-rarity-accent-SECRET {
  font-weight: 800;
  text-shadow: 0 0 6px currentColor;
}

/* 実プレイ改善: ログ本文中のキーワード強調。「誰が(PLAYER/CPU)・何を
   (カード名は上記.log-card-refで別途対応)・結果どうなったか(TOTAL ATK/
   TOTAL DEF/DAMAGE/回復/ダメージ/BUFF/DEBUFF/COUNTER/CITY EVENT)」が
   一目で追えるよう、対象の単語だけに色/太字を付ける(js/ui/render.jsの
   buildKeywordHighlightedNodes参照)。 */
.log-kw-player { color: var(--log-player); font-weight: 700; }
.log-kw-cpu { color: var(--log-cpu); font-weight: 700; }
.log-kw-atk { color: var(--log-atk); font-weight: 700; }
.log-kw-def { color: var(--log-def); font-weight: 700; }
.log-kw-damage { color: var(--log-damage); font-weight: 800; }
.log-kw-damage-word { color: var(--log-damage); }
.log-kw-heal { color: var(--log-heal); font-weight: 700; }
.log-kw-buff { color: var(--log-buff); }
.log-kw-debuff { color: var(--log-debuff); }
.log-kw-counter { color: var(--log-counter); }
.log-kw-city-event { color: var(--log-city-event); font-weight: 700; }

.result-overlay {
  position: fixed;
  inset: 0;
  z-index: 10;
  background: rgba(5, 6, 9, 0.82);
  display: flex;
  align-items: center;
  justify-content: center;
}

.result-overlay.hidden {
  display: none;
}

.result-box {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 32px 40px;
  text-align: center;
}

#result-text {
  font-size: 24px;
  font-weight: 700;
  margin: 0 0 16px;
}

/* Phase7: ターン切替/CITY EVENT発生の一時演出。
   ゲーム本体(HP・手札・ログ)は常にrender()で即座に最新化されており、
   この演出はその上に一時的に重ねて出すだけの表示専用レイヤー。 */
.event-splash-overlay {
  position: fixed;
  inset: 0;
  z-index: 25;
  background: rgba(5, 6, 9, 0.72);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none; /* 下の画面操作をブロックしない(演出はあくまで表示専用) */
  animation: event-splash-fade 0.25s ease-out;
}

.event-splash-overlay.hidden {
  display: none;
}

@keyframes event-splash-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

.event-splash-box {
  background: var(--panel);
  border: 2px solid var(--accent);
  border-radius: 16px;
  padding: 28px 56px;
  text-align: center;
  box-shadow: 0 0 40px rgba(79, 195, 247, 0.35);
  /* 追補修正(CITY EVENT表示品質・1920x1080確認): このオーバーレイはflex
     (align-items:center; justify-content:center)の中央に置かれるだけで幅の
     指定が無く、追補修正で追加した行(label・「対象:両者」・付随ログの転記等)
     によって内容が増えても画面外へ伸びないよう、最大幅と折り返しを明示する。 */
  max-width: min(720px, 90vw);
  max-height: 90vh;
  overflow: hidden;
  box-sizing: border-box;
  position: relative;
}

.event-splash-box.event-splash-city-event {
  border-color: #ffb347;
  box-shadow: 0 0 40px rgba(255, 179, 71, 0.35);
  /* CITY EVENT画像組み込み: この段階(第2段階)のみ画像を表示することがあるため、
     既存のevent-splash-box(通常720px上限)より少し広く取る。新しい別系統の
     UIデザインではなく、既存のevent-splash-box自体の幅上限を調整するだけ。 */
  max-width: min(900px, 92vw);
}

/* CITY EVENT画像組み込み: 対応するCITY EVENT画像の表示(要件どおり縦横比維持・
   切り抜き禁止・引き伸ばし禁止のobject-fit:contain、画面中央、
   max-height:80vh/max-width:70vwを上限としつつ、同じevent-splash-box内で
   アイコン・タイトル・本文行(既存の名称・効果テキスト)が引き続き表示される
   だけの余白を残すため、box自身のmax-height(90vh)からその分を差し引いた
   高さも上限に加える。背景・枠線・発光は既存のevent-splash-box/
   event-splash-city-eventをそのまま流用し、新しい別系統のUIデザインは
   追加しない。1920x1080・1366x768のいずれでも画面内へ収まり、ページ全体の
   スクロールは発生させない(box側のoverflow:hiddenと合わせて担保)。 */
.event-splash-image {
  display: block;
  margin: 0 auto 14px;
  max-width: min(70vw, 820px);
  max-height: min(80vh, calc(90vh - 260px));
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: 10px;
}

.event-splash-image.hidden {
  display: none;
  margin-bottom: 0;
}

/* 実プレイ改善(「1実ターン完全処理」方式・ユーザー事前承認済み):
   EMS・飲食店・素材屋などの単独アクション演出は既存のこのデザインを流用する。
   ターン切替(青系)・CITY EVENT(オレンジ系)と見分けが付くよう、単独アクションは
   緑系、行動不能スキップは無彩色寄りのグレーで区別する。 */
.event-splash-box.event-splash-action-use {
  border-color: var(--success);
  box-shadow: 0 0 40px rgba(102, 187, 106, 0.35);
}

.event-splash-box.event-splash-turn-skip {
  border-color: var(--text-dim);
  box-shadow: 0 0 40px rgba(154, 160, 180, 0.35);
}

.event-splash-title {
  font-size: 34px;
  font-weight: 800;
  letter-spacing: 0.08em;
  margin: 0 0 10px;
  color: var(--accent);
  overflow-wrap: break-word;
  word-break: break-word;
}

.event-splash-box.event-splash-city-event .event-splash-title {
  color: #ffb347;
}

.event-splash-box.event-splash-action-use .event-splash-title {
  color: var(--success);
}

.event-splash-box.event-splash-turn-skip .event-splash-title {
  color: var(--text-dim);
}

.event-splash-lines {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.event-splash-line {
  font-size: 20px;
  font-weight: 600;
  overflow-wrap: break-word;
  word-break: break-word;
}

/* 追補修正: 付随ログの転記行(buildEventSplashContentのextraConfirmedLines)は
   本文より情報量が多くなりがちなため、行数が増えても圧迫感が出ないよう
   本文行よりわずかに小さく・やや控えめな色にする(内容自体は無加工の転記)。 */
.event-splash-line:nth-child(n+4) {
  font-size: 15px;
  font-weight: 500;
  color: var(--text-secondary, #aab2c0);
}

/* Part E仕上げ: CITY EVENT演出強化。
   イベント固有の見た目は「大型アイコン(絵文字)+軽量なCSSアニメーション数種」の
   組み合わせで表現する(34種それぞれに専用の映像素材は用意しない、という
   ユーザー確定の方針どおり)。 */
.event-splash-icon {
  font-size: 48px;
  line-height: 1;
  margin-bottom: 8px;
}

.event-splash-icon:empty {
  display: none;
  margin-bottom: 0;
}

/* CITY EVENTの第1段階(汎用バナー)専用。第2段階(イベント名+固有演出+結果)は
   引き続き event-splash-city-event を使う。 */
.event-splash-box.event-splash-city-event-banner {
  border-color: #ffb347;
  box-shadow: 0 0 40px rgba(255, 179, 71, 0.35);
  animation: city-event-banner-pulse 1s ease-in-out infinite;
}

.event-splash-box.event-splash-city-event-banner .event-splash-title {
  color: #ffb347;
}

@keyframes city-event-banner-pulse {
  0%, 100% { box-shadow: 0 0 40px rgba(255, 179, 71, 0.35); }
  50% { box-shadow: 0 0 64px rgba(255, 179, 71, 0.65); }
}

/* 通常のCITY EVENT(強盗系・単発系の大半)向けの軽量な演出 */
.city-event-fx-glow .event-splash-icon {
  animation: city-event-icon-pop 0.5s ease-out;
}

@keyframes city-event-icon-pop {
  0% { transform: scale(0.4); opacity: 0; }
  70% { transform: scale(1.15); opacity: 1; }
  100% { transform: scale(1); }
}

/* ロック系(武器没収・カテゴリ営業停止8種)専用: LOCKらしい引き締まった見た目 */
.city-event-fx-lock {
  border-color: var(--danger) !important;
  box-shadow: 0 0 40px rgba(239, 83, 80, 0.4) !important;
}

.city-event-fx-lock .event-splash-title {
  color: var(--danger) !important;
}

/* ユーザー確定仕様: 「神のいたずら」「ストレス値MAX」「サーバーラグ」
   「荒らし出現」の4種は通常のCITY EVENTより強い画面演出にする。
   画面全体を短時間シェイクさせ、event-splash-boxにも専用の強めの発光/
   グリッチを加える。 */
.event-splash-box.city-event-fx-strong {
  animation: city-event-strong-shake 0.5s ease-in-out 2;
  box-shadow: 0 0 70px rgba(255, 71, 87, 0.6);
}

@keyframes city-event-strong-shake {
  0%, 100% { transform: translate(0, 0); }
  20% { transform: translate(-6px, 3px); }
  40% { transform: translate(6px, -3px); }
  60% { transform: translate(-4px, -2px); }
  80% { transform: translate(4px, 2px); }
}

/* サーバーラグ専用: テキストがグリッチっぽくずれる */
.city-event-fx-glitch .event-splash-title,
.city-event-fx-glitch .event-splash-line {
  animation: city-event-glitch 0.18s steps(2) infinite;
}

@keyframes city-event-glitch {
  0% { transform: translate(0, 0); }
  50% { transform: translate(2px, -1px); }
  100% { transform: translate(-2px, 1px); }
}

/* ============================================================
   第5弾(SECRET・CITY EVENT個別豪華演出) + 追補修正(CITY EVENT34種の
   本当の個別化): CITY EVENT 34種の個別モーション。
   js/ui/city-event-presentation.jsのmotionKindごとに1つずつ、重複しない
   専用のkeyframeアニメーション・固有の視覚オブジェクトを用意する
   (scripts/phase15_presentation_registry_check.mjsで34/34重複ゼロを機械監査)。
   --city-event-accent(イベントごとの固有色。render.js側で都度セットする)は
   引き続き使うが、色だけの差別化ではなく、動き・オブジェクトの形そのものが
   イベントごとに異なる。
   共通プリミティブとして明示的に再利用してよいのは:
     - この直後の.event-splash-box[class*="city-event-fx-"](枠線・発光色)
     - ロック系9件が共有する「🔒スタンプが打たれる」共通モーション
       (city-event-lock-stamp。ただしロック系9件はそれぞれ別に固有の主要
       オブジェクト―シャッター/心電図/工具/木箱/通信バー/封印マーク/矢印の
       破砕/鎖―を持っており、スタンプだけの差ではない)
   の2つのみで、他はイベントごとに専用のkeyframe名・専用の疑似要素構造を持つ。
   ============================================================ */
.event-splash-box[class*="city-event-fx-"] {
  border-color: var(--city-event-accent, #ffb347) !important;
  box-shadow: 0 0 40px color-mix(in srgb, var(--city-event-accent, #ffb347) 35%, transparent) !important;
}
.event-splash-box[class*="city-event-fx-"] .event-splash-title {
  color: var(--city-event-accent, #ffb347) !important;
}

/* ---- 強盗系17件: 手札のみ増減(3件) ---- */

/* 1) atm-cash-burst(ATM強盗): ATMから紙幣が2枚、左右に飛び出す */
@keyframes city-event-atm-icon-pop { 0% { transform: scale(0.6); opacity: 0; } 55% { transform: scale(1.2); opacity: 1; } 100% { transform: scale(1); } }
@keyframes city-event-atm-bill-pop { 0% { opacity: 0; transform: translateY(0) scale(0.5); } 40% { opacity: 1; } 100% { opacity: 0; transform: translateY(-36px) scale(1.1); } }
.city-event-fx-atm-cash-burst .event-splash-icon { display: inline-block; animation: city-event-atm-icon-pop 0.45s ease-out; }
.city-event-fx-atm-cash-burst::before, .city-event-fx-atm-cash-burst::after {
  content: "💵"; position: absolute; top: 22%; font-size: 18px; opacity: 0;
  animation: city-event-atm-bill-pop 0.55s ease-out 0.1s 1;
}
.city-event-fx-atm-cash-burst::before { left: 14%; }
.city-event-fx-atm-cash-burst::after { right: 14%; animation-delay: 0.22s; }

/* 2) store-shutter-raid(店舗強盗): シャッターが一瞬下りてすぐ開く */
@keyframes city-event-store-shutter-slide {
  0% { transform: translateY(-100%); }
  45% { transform: translateY(0); }
  80% { transform: translateY(0); }
  100% { transform: translateY(-100%); }
}
.city-event-fx-store-shutter-raid { position: relative; overflow: hidden; }
.city-event-fx-store-shutter-raid::before {
  content: ""; position: absolute; inset: 0;
  background: repeating-linear-gradient(0deg, color-mix(in srgb, var(--city-event-accent, #f2a71b) 55%, transparent) 0 6px, rgba(20, 21, 28, 0.55) 6px 12px);
  animation: city-event-store-shutter-slide 0.8s ease-in-out 1;
  /* 不具合修正(Phase 3-4-6-7 追補: 不具合1「CITY EVENTでもオレンジ色化」の
     直接の原因のひとつ): animation-fill-modeが未指定だと、0.8秒のシャッター
     退避演出(city-event-store-shutter-slide、transform: translateY(-100%)で
     画面外へ)終了と同時にtransformがこの::before要素の基本スタイル
     (transform無し=box全体を覆う位置)へ戻ってしまい、本来「シャッターが
     上に開いて消える」はずのオレンジ縞模様のオーバーレイ(inset:0でbox全体=
     画像・イベント名・効果説明を覆う)が、CITY EVENT本編表示中の残り時間
     ずっと不透明なまま残留してしまっていた(city-event-fx-bank-door-breachで
     既に修正済みの不具合と同一の原因)。アニメーション終了時点の状態
     (画面外へ退避済み)を維持するだけの修正であり、色・動き・発動条件は
     一切変更していない。 */
  animation-fill-mode: forwards;
}

/* 3) vault-crack-small(フリーカ強盗): 小さな金庫にヒビが一瞬走る */
@keyframes city-event-vault-crack-flash { 0%, 100% { opacity: 0; } 40%, 60% { opacity: 1; } }
.city-event-fx-vault-crack-small { position: relative; }
.city-event-fx-vault-crack-small .event-splash-icon { display: inline-block; animation: city-event-icon-pop 0.5s ease-out; }
.city-event-fx-vault-crack-small::before {
  content: ""; position: absolute; left: 50%; top: 10%; width: 3px; height: 70%;
  background: linear-gradient(#fff, var(--city-event-accent, #e07b00));
  transform: translateX(-50%) rotate(12deg); opacity: 0;
  animation: city-event-vault-crack-flash 0.5s ease-in-out 1;
}

/* ---- 強盗系17件: HPのみ増減(4件) ---- */

/* 4) bobcat-breach(ボブキャットヘイスト): 重機が現場を揺らし、砂埃が舞う */
@keyframes city-event-bobcat-shake {
  0%, 100% { transform: translate(0, 0); }
  20% { transform: translate(-3px, 1px); }
  40% { transform: translate(3px, -1px); }
  60% { transform: translate(-2px, 1px); }
  80% { transform: translate(2px, 0); }
}
@keyframes city-event-bobcat-dust { 0% { opacity: 0; transform: translate(-50%, 0) scale(0.6); } 30% { opacity: 0.9; } 100% { opacity: 0; transform: translate(-50%, -24px) scale(1.4); } }
.city-event-fx-bobcat-breach { position: relative; }
.city-event-fx-bobcat-breach .event-splash-icon { display: inline-block; animation: city-event-bobcat-shake 0.5s ease-in-out 1; }
.city-event-fx-bobcat-breach::after {
  content: "💨"; position: absolute; bottom: 8%; left: 50%; font-size: 16px; opacity: 0;
  animation: city-event-bobcat-dust 0.7s ease-out 1;
}

/* 5) thermite-cut(テルミットミッション): 溶断の火花が明滅する */
@keyframes city-event-thermite-spark { 0% { opacity: 0; } 30% { opacity: 1; } 60% { opacity: 0; } 80% { opacity: 1; } 100% { opacity: 0; } }
.city-event-fx-thermite-cut { position: relative; }
.city-event-fx-thermite-cut .event-splash-icon { animation: city-event-icon-pop 0.4s ease-out; filter: drop-shadow(0 0 6px color-mix(in srgb, var(--city-event-accent, #ff8a3d) 80%, transparent)); }
.city-event-fx-thermite-cut::before, .city-event-fx-thermite-cut::after {
  content: "✨"; position: absolute; top: 30%; font-size: 14px; opacity: 0;
  animation: city-event-thermite-spark 0.5s steps(3) 1;
}
.city-event-fx-thermite-cut::before { left: 20%; }
.city-event-fx-thermite-cut::after { right: 20%; animation-delay: 0.08s; }

/* 6) train-crossing-heist(列車強盗): 列車が横切るように滑り込む */
@keyframes city-event-train-crossing { 0% { transform: translateX(-60px); opacity: 0; } 40% { opacity: 1; } 70% { transform: translateX(6px); } 100% { transform: translateX(0); } }
@keyframes city-event-train-rail { 0% { background-position-x: 0; } 100% { background-position-x: -40px; } }
.city-event-fx-train-crossing-heist { position: relative; overflow: hidden; }
.city-event-fx-train-crossing-heist .event-splash-icon { display: inline-block; animation: city-event-train-crossing 0.55s ease-in; }
.city-event-fx-train-crossing-heist::before {
  content: ""; position: absolute; left: 0; right: 0; top: 52%; height: 3px;
  background: repeating-linear-gradient(90deg, var(--city-event-accent, #3fa7ff) 0 10px, transparent 10px 20px);
  opacity: 0.5; animation: city-event-train-rail 0.5s linear 1;
}

/* 7) jewelry-glass-shatter(ヴァンジェリコ強盗): ショーケースのガラス片が飛び散る */
@keyframes city-event-glass-shatter-icon { 0% { transform: scale(1); } 30% { transform: scale(1.15) rotate(-4deg); } 100% { transform: scale(1) rotate(0deg); } }
@keyframes city-event-glass-shard { 0% { opacity: 0; transform: translateY(0) scale(0.4); } 30% { opacity: 1; } 100% { opacity: 0; transform: translateY(28px) scale(1.2); } }
.city-event-fx-jewelry-glass-shatter { position: relative; }
.city-event-fx-jewelry-glass-shatter .event-splash-icon { display: inline-block; animation: city-event-glass-shatter-icon 0.4s ease-out; }
.city-event-fx-jewelry-glass-shatter::before, .city-event-fx-jewelry-glass-shatter::after {
  content: ""; position: absolute; top: 38%; width: 0; height: 0;
  border-left: 6px solid transparent; border-right: 6px solid transparent;
  border-bottom: 10px solid color-mix(in srgb, var(--city-event-accent, #63c9ff) 80%, white 20%);
  opacity: 0; animation: city-event-glass-shard 0.55s ease-out 1;
}
.city-event-fx-jewelry-glass-shatter::before { left: 30%; animation-delay: 0.05s; }
.city-event-fx-jewelry-glass-shatter::after { right: 30%; transform: rotate(180deg); }

/* ---- 強盗系17件: 手札とHPが同時にマイナス(4件) ---- */

/* 8) lab-alarm-breach(ヒューメインラボ強盗): 警報が短く明滅する */
@keyframes city-event-lab-alarm { 0%, 100% { box-shadow: 0 0 20px color-mix(in srgb, var(--city-event-accent, #ff4d4d) 30%, transparent); } 50% { box-shadow: 0 0 55px color-mix(in srgb, var(--city-event-accent, #ff4d4d) 90%, transparent); } }
.city-event-fx-lab-alarm-breach { animation: city-event-lab-alarm 0.3s ease-in-out 4; }
.city-event-fx-lab-alarm-breach .event-splash-icon { animation: city-event-icon-pop 0.3s ease-out; }

/* 9) airfield-runway-raid(飛行場襲撃): 滑走路を離陸するように斜めに飛び立つ */
@keyframes city-event-airfield-takeoff { 0% { transform: translate(-50px, 10px) rotate(-6deg); opacity: 0; } 60% { opacity: 1; } 100% { transform: translate(0, 0) rotate(0deg); } }
.city-event-fx-airfield-runway-raid { position: relative; }
.city-event-fx-airfield-runway-raid .event-splash-icon { display: inline-block; animation: city-event-airfield-takeoff 0.5s ease-out; }
.city-event-fx-airfield-runway-raid::before {
  content: ""; position: absolute; left: 10%; right: 10%; bottom: 14%; height: 2px;
  background: repeating-linear-gradient(90deg, color-mix(in srgb, var(--city-event-accent, #ff3939) 70%, white 30%) 0 8px, transparent 8px 16px);
  opacity: 0.6;
}

/* 10) vault-door-heavy(大型金庫強盗): 分厚い金庫の扉が奥へ開く */
@keyframes city-event-vault-door-heavy { 0% { transform: perspective(300px) rotateY(0deg); } 60% { transform: perspective(300px) rotateY(-55deg); } 100% { transform: perspective(300px) rotateY(-25deg); } }
.city-event-fx-vault-door-heavy { box-shadow: 0 0 55px color-mix(in srgb, var(--city-event-accent, #e02323) 55%, transparent) !important; }
.city-event-fx-vault-door-heavy .event-splash-icon { display: inline-block; animation: city-event-vault-door-heavy 0.6s ease-in-out; transform-origin: left center; }

/* 11) artifact-pedestal-lift(アーティファクトヘイスト): 展示物が台座から持ち上がる */
@keyframes city-event-artifact-lift { 0% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(-14px) rotate(8deg); } 100% { transform: translateY(0) rotate(0deg); } }
@keyframes city-event-artifact-glow { 0%, 100% { opacity: 0.3; } 50% { opacity: 0.9; } }
.city-event-fx-artifact-pedestal-lift { position: relative; }
.city-event-fx-artifact-pedestal-lift .event-splash-icon { display: inline-block; animation: city-event-artifact-lift 0.6s ease-in-out; }
.city-event-fx-artifact-pedestal-lift::after {
  content: ""; position: absolute; left: 50%; bottom: 10%; width: 40px; height: 8px; border-radius: 50%;
  background: color-mix(in srgb, var(--city-event-accent, #c81414) 50%, transparent);
  transform: translateX(-50%); animation: city-event-artifact-glow 0.6s ease-in-out 1;
  /* 不具合修正(Phase 3-4-6-7 追補): animation-fill-mode未指定によりアニメ
     終了後に台座の光がキーフレーム上限(opacity:0.3)より明るい初期値
     (opacity:1)へ戻り残留する問題(store-shutter-raidと同一原因)。 */
  animation-fill-mode: forwards;
}

/* ---- 強盗系17件: 手札とHPが同時にプラス(4件) ---- */

/* 12) cruise-wave-heist(客船強盗): 船が波に乗って揺れる */
@keyframes city-event-cruise-bob { 0%, 100% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(-8px) rotate(-3deg); } }
@keyframes city-event-cruise-wave-move { 0%, 100% { transform: translateX(-50%) scaleX(0.7); } 50% { transform: translateX(-50%) scaleX(1); } }
.city-event-fx-cruise-wave-heist { position: relative; }
.city-event-fx-cruise-wave-heist .event-splash-icon { display: inline-block; animation: city-event-cruise-bob 0.5s ease-in-out 2; }
.city-event-fx-cruise-wave-heist::after {
  /* 実プレイ指摘(再修正): 前回は位置調整(padding内へ固定px化)で対応したが、
     調整後も「切り取り線のような横線」が不自然に見えるとのご指摘のため、
     位置を直すのではなく、この装飾線自体を生成しないようにした。
     content: none を指定すると疑似要素そのものが生成されなくなり、
     background/animationの指定があっても一切描画されない。
     画像・イベント名・効果説明・ログ・外周枠・テーマ色・アイコンの
     アニメーション(city-event-cruise-bob)には影響しない。 */
  content: none;
}

/* 13) oil-rig-platform-raid(オイルリグ強盗): 海上施設が波でわずかに傾く */
@keyframes city-event-oil-rig-sway { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }
@keyframes city-event-oil-rig-wave-line { 0%, 100% { transform: translateX(-50%) scaleX(0.7); } 50% { transform: translateX(-50%) scaleX(1); } }
.city-event-fx-oil-rig-platform-raid { position: relative; }
.city-event-fx-oil-rig-platform-raid .event-splash-icon { display: inline-block; animation: city-event-oil-rig-sway 0.5s ease-in-out 2; }
.city-event-fx-oil-rig-platform-raid::before {
  /* 実プレイ指摘(再修正): city-event-fx-cruise-wave-heistと同じ理由により、
     位置調整ではなくこの装飾線自体を生成しないようにした(content: none)。
     画像・イベント名・効果説明・ログ・外周枠・テーマ色・アイコンの
     アニメーション(city-event-oil-rig-sway)には影響しない。 */
  content: none;
}

/* 14) cargo-container-shift(小型貨物船強盗): コンテナが積み替わる */
@keyframes city-event-cargo-shift { 0% { transform: translateX(-20px); opacity: 0.4; } 100% { transform: translateX(0); opacity: 1; } }
@keyframes city-event-cargo-box { 0% { opacity: 0; transform: translateY(10px); } 50% { opacity: 0.8; } 100% { opacity: 0; transform: translateY(-6px); } }
.city-event-fx-cargo-container-shift { position: relative; }
.city-event-fx-cargo-container-shift .event-splash-icon { display: inline-block; animation: city-event-cargo-shift 0.45s ease-out; }
.city-event-fx-cargo-container-shift::before, .city-event-fx-cargo-container-shift::after {
  content: ""; position: absolute; top: 38%; width: 22px; height: 16px; border-radius: 2px;
  background: color-mix(in srgb, var(--city-event-accent, #27ae60) 55%, transparent);
  opacity: 0; animation: city-event-cargo-box 0.5s ease-out 1;
}
.city-event-fx-cargo-container-shift::before { left: 14%; }
.city-event-fx-cargo-container-shift::after { right: 14%; animation-delay: 0.1s; }

/* 15) bank-door-breach(ユニオン銀行強盗): 大型の両開き扉がこじ開けられる */
@keyframes city-event-bank-door-open { 0% { transform: scaleX(1); opacity: 0.9; } 60% { transform: scaleX(0.15); opacity: 0.6; } 100% { transform: scaleX(0); opacity: 0; } }
.city-event-fx-bank-door-breach { position: relative; overflow: hidden; }
.city-event-fx-bank-door-breach::before, .city-event-fx-bank-door-breach::after {
  /* 表示不具合修正(実プレイ指摘): この2枚の疑似要素は、位置指定(top:0;bottom:0;
     width:52%)により本来box全体を覆う不透明な帯で、city-event-bank-door-open
     アニメーション(scaleX(1)→0、opacity 0.9→0)によって「両開きの扉が開いて
     消える」演出を意図したものだった。しかしanimation-fill-modeが指定されて
     いなかったため、0.6秒のアニメーション終了と同時に、要素はアニメーション前の
     基本スタイル(transform無し=scaleX(1)相当、opacity未指定=初期値の1=不透明)
     へ戻ってしまい、CITY EVENT本編表示中(4秒間)の残り約3.4秒間、box全体(=
     画像・イベント名・効果説明)を不透明な緑一色の帯2枚で覆い隠したままに
     なっていた。これがユニオン銀行だけ「画像も名称も効果も表示されず緑一色で
     覆われる」不具合の原因。animation-fill-mode: forwardsを指定し、アニメーション
     終了時点の状態(scaleX(0)・opacity:0=完全に非表示)を維持するよう修正した。
     画像パス・内部ID対応・アニメーション自体の内容(色・動き)は変更していない。 */
  content: ""; position: absolute; top: 0; bottom: 0; width: 52%;
  background: color-mix(in srgb, var(--city-event-accent, #1e8449) 35%, #14151c 65%);
  animation: city-event-bank-door-open 0.6s ease-in-out 1;
  animation-fill-mode: forwards;
}
.city-event-fx-bank-door-breach::before { left: 0; transform-origin: left; }
.city-event-fx-bank-door-breach::after { right: 0; transform-origin: right; }

/* ---- 強盗系17件: 手札とHPが独立に±ランダム(2件) ---- */

/* 16) ranch-dust-stampede(牧場襲撃): 家畜が走り抜け、両側に砂埃が舞う */
@keyframes city-event-ranch-stampede {
  0%, 100% { transform: translateX(0) rotate(0deg); }
  25% { transform: translateX(-4px) rotate(-3deg); }
  50% { transform: translateX(4px) rotate(3deg); }
  75% { transform: translateX(-3px) rotate(-2deg); }
}
@keyframes city-event-ranch-dust-puff { 0% { opacity: 0; transform: scale(0.4); } 40% { opacity: 0.7; } 100% { opacity: 0; transform: scale(1.6) translateY(-10px); } }
.city-event-fx-ranch-dust-stampede { position: relative; }
.city-event-fx-ranch-dust-stampede .event-splash-icon { display: inline-block; animation: city-event-ranch-stampede 0.5s ease-in-out 2; }
.city-event-fx-ranch-dust-stampede::before, .city-event-fx-ranch-dust-stampede::after {
  content: ""; position: absolute; bottom: 8%; width: 14px; height: 14px; border-radius: 50%;
  background: color-mix(in srgb, var(--city-event-accent, #b98b3e) 40%, transparent);
  opacity: 0; animation: city-event-ranch-dust-puff 0.7s ease-out 1;
}
.city-event-fx-ranch-dust-stampede::before { left: 22%; }
.city-event-fx-ranch-dust-stampede::after { right: 22%; animation-delay: 0.15s; }

/* 17) weapon-rack-raid(アミュネーション強盗): 武器棚の警報バーが明滅する */
@keyframes city-event-weapon-rack-flash { 0%, 100% { opacity: 0; } 50% { opacity: 0.8; } }
.city-event-fx-weapon-rack-raid { position: relative; }
.city-event-fx-weapon-rack-raid .event-splash-icon { animation: city-event-icon-pop 0.35s ease-out; }
.city-event-fx-weapon-rack-raid::before {
  /* 実プレイ指摘(再修正): 前回は位置調整(padding内へ固定px化+点滅化)で
     対応したが、点滅中に見える金色・ベージュ色の横線自体が不自然とのご指摘の
     ため、位置やタイミングを直すのではなく、この装飾線自体を生成しないように
     した(content: none)。画像・イベント名・効果説明・ログ・外周枠・テーマ色・
     アイコンのアニメーション(city-event-icon-pop)には影響しない。 */
  content: none;
}

/* terminal-glitch: /help。端末風の走査線が入る */
@keyframes city-event-terminal-scan {
  0% { background-position: 0 -100%; }
  100% { background-position: 0 200%; }
}
.city-event-fx-terminal-glitch {
  background-image: linear-gradient(rgba(84, 224, 199, 0.18) 50%, transparent 50%);
  background-size: 100% 6px;
  animation: city-event-terminal-scan 0.6s linear 2;
}

/* dice-roll: ギャンブラー。サイコロのように回転して静止する */
@keyframes city-event-dice-roll {
  0% { transform: rotate(0deg) scale(0.7); opacity: 0; }
  70% { transform: rotate(420deg) scale(1.1); opacity: 1; }
  100% { transform: rotate(360deg) scale(1); }
}
.city-event-fx-dice-roll .event-splash-icon {
  display: inline-block;
  animation: city-event-dice-roll 0.55s ease-out;
}

/* swap-arrows: 手違い。左右が入れ替わるような往復移動 */
@keyframes city-event-swap {
  0% { transform: translateX(-10px); }
  50% { transform: translateX(10px); }
  100% { transform: translateX(0); }
}
.city-event-fx-swap-arrows .event-splash-icon {
  display: inline-block;
  animation: city-event-swap 0.4s ease-in-out 2;
}

/* coin-rain: 億万長者。小さなコイン粒子が降り注ぐ(疑似要素、DOM追加なし) */
.city-event-fx-coin-rain {
  position: relative;
  overflow: hidden;
}
.city-event-fx-coin-rain::before,
.city-event-fx-coin-rain::after {
  content: "💰";
  position: absolute;
  top: -10%;
  font-size: 20px;
  opacity: 0.9;
  animation: city-event-coin-fall 0.9s ease-in 1;
}
.city-event-fx-coin-rain::before { left: 18%; animation-delay: 0s; }
.city-event-fx-coin-rain::after { left: 74%; animation-delay: 0.12s; }
@keyframes city-event-coin-fall {
  0% { top: -10%; opacity: 0; transform: rotate(0deg); }
  20% { opacity: 1; }
  100% { top: 92%; opacity: 0; transform: rotate(200deg); }
}

/* hp-flow-redistribute: 神のいたずら(強演出)。HPが流れ込むような発光の波 */
@keyframes city-event-hp-flow {
  0% { box-shadow: -30px 0 40px color-mix(in srgb, var(--city-event-accent, #af7ac5) 60%, transparent); }
  50% { box-shadow: 30px 0 40px color-mix(in srgb, var(--city-event-accent, #af7ac5) 60%, transparent); }
  100% { box-shadow: 0 0 40px color-mix(in srgb, var(--city-event-accent, #af7ac5) 40%, transparent); }
}
.city-event-fx-hp-flow-redistribute {
  animation: city-event-hp-flow 0.9s ease-in-out 1;
}

/* network-lag: サーバーラグ(強演出)。コマ落ちのように一瞬静止と復帰を繰り返す */
@keyframes city-event-network-lag {
  0%, 100% { opacity: 1; transform: translate(0, 0); }
  20% { opacity: 0.4; transform: translate(3px, 0); }
  40% { opacity: 1; transform: translate(-3px, 0); }
  60% { opacity: 0.5; transform: translate(2px, -2px); }
  80% { opacity: 1; transform: translate(0, 0); }
}
.city-event-fx-network-lag {
  animation: city-event-network-lag 0.6s steps(2) 2;
}

/* disguise-flicker: ストレス値MAX(強演出)。手札の見た目が別物に見える明滅 */
@keyframes city-event-disguise {
  0%, 100% { filter: hue-rotate(0deg); }
  25% { filter: hue-rotate(90deg); }
  50% { filter: hue-rotate(180deg); }
  75% { filter: hue-rotate(270deg); }
}
.city-event-fx-disguise-flicker .event-splash-icon {
  animation: city-event-disguise 0.8s linear 1;
}

/* danger-siren: 荒らし出現(強演出)。赤/青の点滅で警戒感を出す */
@keyframes city-event-siren {
  0%, 100% { box-shadow: 0 0 50px rgba(255, 46, 46, 0.7); }
  50% { box-shadow: 0 0 50px rgba(61, 132, 255, 0.7); }
}
.city-event-fx-danger-siren {
  animation: city-event-siren 0.3s ease-in-out 3;
}

/* ---- ロック系9件(武器没収+カテゴリ営業停止8種) ----
   共通プリミティブとして「🔒スタンプが右上にポップする」city-event-lock-stamp
   のみ共有する(このスタンプは全9件共通の補助バッジであり、各イベントの
   主要モーション・主要オブジェクトはそれぞれ完全に別物: シャッター/心電図/
   工具/木箱/通信バー/COUNTER封印マーク/矢印の破砕/鎖)。 */
@keyframes city-event-lock-stamp {
  0% { transform: translate(30%, -30%) scale(0) rotate(-20deg); opacity: 0; }
  55% { transform: translate(30%, -30%) scale(1.25) rotate(8deg); opacity: 1; }
  100% { transform: translate(30%, -30%) scale(1) rotate(0deg); opacity: 1; }
}
[class*="-lock"].event-splash-box::before,
.event-splash-box[class*="-lock"]::before {
  content: "🔒";
  position: absolute;
  top: 6%;
  right: 6%;
  font-size: 22px;
  opacity: 0;
  animation: city-event-lock-stamp 0.5s ease-out 0.15s 1 forwards;
  z-index: 2;
}

/* 18) weapon-confiscate-seal(武器没収/PLUS): 武器アイコンが叩き落とされ、
   ✕マークが重なって封印される(スタンプはlock-stampへ委譲、これは主要object) */
@keyframes city-event-weapon-confiscate-drop {
  0% { transform: translateY(-16px) rotate(-15deg); opacity: 0; }
  60% { transform: translateY(4px) rotate(5deg); opacity: 1; }
  100% { transform: translateY(0) rotate(0deg); }
}
@keyframes city-event-weapon-confiscate-cross {
  0%, 40% { opacity: 0; transform: scale(0.4); }
  70% { opacity: 1; transform: scale(1.15); }
  100% { opacity: 1; transform: scale(1); }
}
.city-event-fx-weapon-confiscate-seal { position: relative; }
.city-event-fx-weapon-confiscate-seal .event-splash-icon {
  display: inline-block;
  animation: city-event-weapon-confiscate-drop 0.45s ease-out;
}
.city-event-fx-weapon-confiscate-seal::after {
  content: "✕";
  position: absolute;
  left: 50%;
  top: 34%;
  font-size: 34px;
  font-weight: 900;
  color: color-mix(in srgb, var(--city-event-accent, #95a5a6) 85%, white 15%);
  transform: translateX(-50%) scale(0.4);
  opacity: 0;
  animation: city-event-weapon-confiscate-cross 0.45s ease-out 0.1s 1 forwards;
}

/* 19) food-shutter-lock(飲食店営業停止): 縦縞のシャッターパネルが下から
   せり上がって画面を覆い、閉まったまま停止する(2ターンロックの見た目) */
@keyframes city-event-food-shutter-rise {
  0% { transform: translateY(100%); }
  100% { transform: translateY(0%); }
}
.city-event-fx-food-shutter-lock { position: relative; overflow: hidden; }
.city-event-fx-food-shutter-lock::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0; height: 46%;
  background: repeating-linear-gradient(90deg,
    color-mix(in srgb, var(--city-event-accent, #e67e22) 65%, transparent) 0 8px,
    rgba(20, 21, 28, 0.7) 8px 16px);
  border-top: 2px solid color-mix(in srgb, var(--city-event-accent, #e67e22) 80%, white 20%);
  transform: translateY(100%);
  animation: city-event-food-shutter-rise 0.5s ease-out 1 forwards;
}

/* 20) ems-flatline-lock(医療崩壊): 心電図の波形が乱れてから一直線(フラット
   ライン)になり、そのまま停止する */
@keyframes city-event-ems-flatline-jitter {
  0% { transform: scaleY(1); }
  20% { transform: scaleY(1.8); }
  40% { transform: scaleY(0.4); }
  60% { transform: scaleY(1.4); }
  80% { transform: scaleY(0.7); }
  100% { transform: scaleY(0.08); }
}
.city-event-fx-ems-flatline-lock { position: relative; }
.city-event-fx-ems-flatline-lock::after {
  content: "";
  position: absolute;
  left: 12%; right: 12%; top: 50%; height: 3px;
  background: repeating-linear-gradient(90deg,
    color-mix(in srgb, var(--city-event-accent, #e74c3c) 85%, white 15%) 0 4px,
    transparent 4px 10px, color-mix(in srgb, var(--city-event-accent, #e74c3c) 85%, white 15%) 10px 14px, transparent 14px 30px);
  transform-origin: center;
  animation: city-event-ems-flatline-jitter 0.6s ease-in 1 forwards;
}

/* 21) mechanic-tool-vanish-lock(整備士不在): 工具アイコンが弾かれるように
   飛んで消える(整備工程が止まったことを表す) */
@keyframes city-event-mechanic-tool-fly {
  0% { opacity: 1; transform: translate(0, 0) rotate(0deg); }
  100% { opacity: 0; transform: translate(38px, -30px) rotate(120deg); }
}
.city-event-fx-mechanic-tool-vanish-lock { position: relative; }
.city-event-fx-mechanic-tool-vanish-lock::after {
  content: "🔧";
  position: absolute;
  left: 46%;
  top: 40%;
  font-size: 20px;
  animation: city-event-mechanic-tool-fly 0.5s ease-in 1 forwards;
}

/* 22) material-crate-crumble-lock(素材不足): 資材の木箱が崩れ落ちる */
@keyframes city-event-material-crumble {
  0% { transform: scaleY(1) rotate(0deg); opacity: 1; }
  50% { transform: scaleY(0.7) rotate(-4deg); }
  100% { transform: scaleY(0.15) rotate(-10deg); opacity: 0.7; }
}
.city-event-fx-material-crate-crumble-lock { position: relative; }
.city-event-fx-material-crate-crumble-lock .event-splash-icon {
  display: inline-block;
  transform-origin: bottom center;
  animation: city-event-material-crumble 0.55s ease-in 1 forwards;
}

/* 23) civ-signal-drop-lock(通信障害): 通信バーが右から順に消灯していく */
@keyframes city-event-civ-signal-drop {
  0% { opacity: 1; transform: scaleY(1); }
  100% { opacity: 0.15; transform: scaleY(0.2); }
}
.city-event-fx-civ-signal-drop-lock { position: relative; }
.city-event-fx-civ-signal-drop-lock::after {
  content: "";
  position: absolute;
  left: 50%; bottom: 18%; width: 46px; height: 18px;
  transform: translateX(-50%);
  background:
    linear-gradient(to top, color-mix(in srgb, var(--city-event-accent, #3498db) 85%, white 15%) 0 100%) left/8px 40% no-repeat,
    linear-gradient(to top, color-mix(in srgb, var(--city-event-accent, #3498db) 85%, white 15%) 0 100%) 16px/8px 65% no-repeat,
    linear-gradient(to top, color-mix(in srgb, var(--city-event-accent, #3498db) 85%, white 15%) 0 100%) 32px/8px 100% no-repeat;
  background-position: 0 100%, 16px 100%, 32px 100%;
  transform-origin: bottom;
  animation: city-event-civ-signal-drop 0.55s ease-in 1 forwards;
}

/* 24) counter-ban-seal-lock(禁煙エリア/COUNTER封鎖): 対象アイコンへ
   角度の効いたスタンプが叩きつけられるように打たれる(steps()で硬質な動き) */
@keyframes city-event-counter-ban-slam {
  0% { transform: scale(2.4) rotate(-24deg); opacity: 0; }
  100% { transform: scale(1) rotate(-8deg); opacity: 1; }
}
.city-event-fx-counter-ban-seal-lock .event-splash-icon {
  display: inline-block;
  animation: city-event-counter-ban-slam 0.28s steps(4, end) 1 forwards;
}
.city-event-fx-counter-ban-seal-lock {
  border-style: double !important;
}

/* 25) buff-arrow-shatter-lock(バフ解除期間): 上昇矢印が一度尖って伸びた
   直後に破片となって砕け散る */
@keyframes city-event-buff-arrow-pop {
  0% { transform: translateY(0) scale(1); opacity: 1; }
  40% { transform: translateY(-14px) scale(1.3); opacity: 1; }
  70% { transform: translateY(-14px) scale(1.05); opacity: 1; }
  100% { transform: translateY(-14px) scale(0.4); opacity: 0; }
}
@keyframes city-event-buff-arrow-shard {
  0% { opacity: 0; transform: translate(0, -14px) scale(0.3); }
  60% { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--shard-x, 14px), 10px) scale(0.6); }
}
.city-event-fx-buff-arrow-shatter-lock { position: relative; }
.city-event-fx-buff-arrow-shatter-lock .event-splash-icon {
  display: inline-block;
  animation: city-event-buff-arrow-pop 0.5s ease-in 1 forwards;
}
.city-event-fx-buff-arrow-shatter-lock::before,
.city-event-fx-buff-arrow-shatter-lock::after {
  content: "◆";
  position: absolute;
  left: 50%; top: 34%; font-size: 10px;
  color: color-mix(in srgb, var(--city-event-accent, #f9c74f) 85%, white 15%);
  opacity: 0;
  animation: city-event-buff-arrow-shard 0.45s ease-out 0.3s 1 forwards;
}
.city-event-fx-buff-arrow-shatter-lock::before { --shard-x: -16px; }
.city-event-fx-buff-arrow-shatter-lock::after { --shard-x: 16px; }

/* 26) debuff-chain-seal-lock(デバフ禁止令): 鎖アイコンが左右から巻き付いて
   締め上げる(scaleXで挟み込む動き) */
@keyframes city-event-chain-wrap {
  0% { opacity: 0; transform: translateX(var(--chain-from, -30px)) scaleX(1.4); }
  70% { opacity: 1; }
  100% { opacity: 1; transform: translateX(0) scaleX(1); }
}
.city-event-fx-debuff-chain-seal-lock { position: relative; }
.city-event-fx-debuff-chain-seal-lock::before,
.city-event-fx-debuff-chain-seal-lock::after {
  content: "⛓️";
  position: absolute;
  top: 40%; font-size: 20px; opacity: 0;
  animation: city-event-chain-wrap 0.5s ease-in 1 forwards;
}
.city-event-fx-debuff-chain-seal-lock::before { left: 20%; --chain-from: -30px; }
.city-event-fx-debuff-chain-seal-lock::after { right: 20%; --chain-from: 30px; transform: scaleX(-1); }

/* ============================================================
   Phase8: 戦闘演出UI(js/ui/battle-stage.js参照。将来のカード/背景デザイン
   差し替えを見据えた構造)
   ============================================================
   実際の攻撃/防御/COUNTER/ダメージ確定処理(controller.jsの演出キュー)から
   呼び出される。将来のPhaseで本格的なカード画像・背景・エフェクト・効果音を
   追加する際は、このブロックのCSSカスタムプロパティ(--battle-stage-*)と
   各セレクタの内容だけを差し替えれば、js/ui/battle-stage.js自体は
   変更不要な想定(カード情報の取得・表示ロジックは変えず、見た目だけ差し替える)。
   ============================================================ */
:root {
  --battle-stage-bg: rgba(5, 6, 9, 0.88);
  --battle-stage-card-bg: var(--panel);
  --battle-stage-card-border: var(--border);
  --battle-stage-attacker-accent: var(--danger);
  --battle-stage-defender-accent: #4fc3f7;
  --battle-stage-counter-negate: #b388ff;
  --battle-stage-counter-mitigate: #ffb347;
  --battle-stage-counter-reflect: #ff6e6e;
  /* UI/UX強化(ユーザー確定仕様6番): 攻撃・防御以外の単独アクションカード
     (EMS/飲食店/素材屋/一般市民/BUFF/DEBUFF等)の中央演出用アクセント色。
     攻撃側(赤)・防御側(青)のどちらとも被らない色にしてある。 */
  --battle-stage-ability-accent: #4fd1a0;
}

.battle-stage-overlay {
  position: fixed;
  inset: 0;
  z-index: 24; /* event-splash-overlay(25)より下、通常UIより上 */
  background: var(--battle-stage-bg);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: space-between;
  padding: clamp(12px, 3vh, 28px) 24px;
  gap: 8px;
  pointer-events: none; /* Phase7のターン演出と同じく、表示専用で操作をブロックしない */
}

.battle-stage-overlay.hidden {
  display: none;
}

.battle-stage-hp-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  max-width: 480px;
  margin: 0 auto;
  width: 100%;
  /* 第3弾: `-X`ダメージ数値表示(.hp-damage-pop-stage)をこの行に重ねるための基準。 */
  position: relative;
}

.battle-stage-hp-label {
  font-size: 12px;
  font-weight: 700;
  color: var(--text-secondary, #aab2c0);
  min-width: 44px;
}

.battle-stage-hp-track {
  flex: 1;
  height: 12px;
  background: #0d0f14;
  border-radius: 6px;
  overflow: hidden;
  border: 1px solid var(--border);
}

.battle-stage-hp-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--danger), var(--success));
  width: 0%;
  /* メインのHPバー(.hp-bar-fill、0.3s)より少し長めにして、戦闘演出の
     「見せ場」としてHPが減っていく様子がはっきり分かるようにする。 */
  transition: width 0.6s ease;
}

.battle-stage-hp-text {
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary, #aab2c0);
  min-width: 64px;
  text-align: right;
}

/* 攻撃側/防御側の物理的な配置(画面の上段=CPU側・下段=PLAYER側は常に固定し、
   「どちらが攻撃側/防御側か」はorderの入れ替えだけで表現する。これにより
   「PLAYERが攻撃ならPLAYER側(下)から、CPUが攻撃ならCPU側(上)から中央へ
   出てくる」という向きが、js/ui/battle-stage.jsのDOM操作を変えずCSSだけで成立する。 */
.battle-stage-arena {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(8px, 2vh, 20px);
  min-height: 0;
}

.battle-stage-counter,
.battle-stage-totals-row {
  order: 1;
}

.battle-stage-overlay[data-attacker-side="cpu"] .battle-stage-attacker {
  order: 0;
}
.battle-stage-overlay[data-attacker-side="cpu"] .battle-stage-defender {
  order: 2;
}
.battle-stage-overlay[data-attacker-side="player"] .battle-stage-attacker {
  order: 2;
}
.battle-stage-overlay[data-attacker-side="player"] .battle-stage-defender {
  order: 0;
}

.battle-stage-side {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: stretch;
  justify-content: center;
  gap: 10px;
  min-height: 96px;
  width: 100%;
}

/* 戦闘演出プロトタイプ(今回改修): 以前はDOM挿入と同時にkeyframeアニメーション
   (battle-stage-pop-in/in-top/in-bottom)が即座に再生されていたが、「本体→
   PLUS→メカニック」の段階的な登場(js/ui/battle-stage.jsのcardEnter/
   addSupportCard参照)を実現するため、transition+クラス付与方式に変更した。
   カードの内容(名前・ロール等)自体はDOM挿入と同時に確定するため既存の
   回帰テスト(内容を即座に確認するもの)には影響しない。見た目の登場
   (不透明度・移動・拡大)だけを.battle-stage-card-settledクラスの付与
   タイミングで制御する。 */
/* 中央バトル演出カード拡大(このセッションで追加)
   同じ側(.battle-stage-side、攻撃側/防御側それぞれ)に同時に表示される
   カード枚数(本体のみ/本体+PLUS等2枚/3枚以上)に応じて、カード画像の
   基準高さ(--stage-card-img-h)をCSS変数として切り替える。カード幅は
   ここから「カード画像のおおよその縦横比(3:4程度)」を掛けて逆算し、
   画像がobject-fit:containで表示されたときに枠の横幅いっぱいに近い状態で
   収まるようにする(元の縦横比は維持したまま、引き伸ばし・トリミングはしない)。
   高さはvh、幅は追加でvwも上限に使うことで、1920×1080・1366×768のどちらでも
   画面外へはみ出さないようにしている(実機相当のPlaywright計測で確認済み)。
   既存の演出順序・発光・突撃・衝突・フラッシュ・レアリティ演出・数値公開順・
   入力ロック・BGM/SEの仕組み自体(js/ui/battle-stage.js)は一切変更していない。 */
.battle-stage-side {
  --stage-card-ar: 0.75; /* カード画像のおおよその縦横比(width/height) */
}

/* 1枚だけ(本体のみ、または本体を含まない単独アクションカード)。 */
.battle-stage-side:not(:has(.battle-stage-minicard ~ .battle-stage-minicard)) {
  --stage-card-img-h: clamp(160px, 27vh, 340px);
}
/* ちょうど2枚(本体+PLUS等)。 */
.battle-stage-side:has(.battle-stage-minicard ~ .battle-stage-minicard):not(:has(.battle-stage-minicard ~ .battle-stage-minicard ~ .battle-stage-minicard)) {
  --stage-card-img-h: clamp(130px, 21vh, 260px);
}
/* 3枚以上: 画面幅に収まる範囲で均等に縮小(既存の150px幅ベースを踏襲しつつ、
   画像自体は既存より少し大きく見せる)。 */
.battle-stage-side:has(.battle-stage-minicard ~ .battle-stage-minicard ~ .battle-stage-minicard) {
  --stage-card-img-h: clamp(90px, 15vh, 190px);
}

.battle-stage-minicard {
  background: var(--battle-stage-card-bg);
  border: 2px solid var(--battle-stage-card-border);
  border-radius: 12px;
  /* 枠内の余白を減らし、画像を枠の横幅いっぱいに近い状態で表示する。 */
  padding: 6px 6px 8px;
  width: calc(var(--stage-card-img-h, 170px) * var(--stage-card-ar, 0.75) + 12px);
  max-width: 34vw;
  text-align: center;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 0.4s ease-out, transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.3s ease;
}

.battle-stage-minicard[data-owner-side="cpu"] {
  transform: translateY(-46px) scale(0.7);
}
.battle-stage-minicard[data-owner-side="player"] {
  transform: translateY(46px) scale(0.7);
}
.battle-stage-minicard.battle-stage-card-settled {
  opacity: 1;
  transform: translateY(0) scale(1);
}
/* 本体カードは「最も大きく表示」(ユーザー確定仕様1番)。 */
.battle-stage-minicard.battle-stage-card-settled.battle-stage-minicard-body {
  transform: translateY(0) scale(1.08);
}

/* レアリティ別演出(簡易版・UI/UX強化でN<SR<UR<SECRETの差をより明確に強化)。
   正式カードデザイン確定後はここだけ差し替えれば良いよう、独立したクラスに
   まとめてある(ユーザー確定仕様9番、js/ui/battle-stage.jsのRARITY_ENTER_CLASS/
   RARITY_ENTER_FX参照)。
   N: 演出なし(基本のスライド+軽い拡大のみ、発光ほぼ無し)
   SR: Nより強い発光 + 登場直後に短い光のパルスが1回だけ走る
   UR: SRより強い発光(外側へ広がる) + 登場時に画面フラッシュ+軽いシェイク +
       「UR」の一瞬の強調バッジ
   SECRET: 登場前に暗転+「SECRET」表示(既存) + 最も強い発光 + 強めのフラッシュ+
       シェイク + 「SECRET」の強調バッジ、という他レアリティとは明確に違う特別感 */
.battle-stage-minicard.battle-stage-rarity-sr.battle-stage-card-settled {
  box-shadow: 0 0 14px 3px rgba(100, 181, 246, 0.55);
}
.battle-stage-minicard.battle-stage-rarity-sr-pulse {
  animation: battle-stage-rarity-sr-pulse-anim 0.5s ease-out;
}
@keyframes battle-stage-rarity-sr-pulse-anim {
  0% { box-shadow: 0 0 14px 3px rgba(100, 181, 246, 0.55); }
  50% { box-shadow: 0 0 26px 8px rgba(100, 181, 246, 0.85); }
  100% { box-shadow: 0 0 14px 3px rgba(100, 181, 246, 0.55); }
}
.battle-stage-minicard.battle-stage-rarity-ur.battle-stage-card-settled {
  box-shadow: 0 0 30px 10px rgba(255, 193, 7, 0.75);
}
.battle-stage-minicard.battle-stage-rarity-secret.battle-stage-card-settled {
  box-shadow: 0 0 40px 14px rgba(179, 136, 255, 0.85);
}

/* rarity-callout等の絶対配置子要素をカード自身に対して基準づけるための
   positioning context(第5弾追加: 個別演出のpseudo-element配置にも使う)。
   offsetを与えないためレイアウトへの影響は無い。 */
.battle-stage-minicard {
  position: relative;
}

/* ============================================================
   第5弾: SECRET全22枚 個別演出(js/ui/secret-presentation.jsの
   motionKindとcardEnter()側のapplySecretIndividualFxが対応)。
   ・カード名/固有タグライン/効果結果はテキストとして子要素追加、
   ・固有モーションはbox-shadow/filter/outline/擬似要素のみで表現し、
     既存のtransition対象(opacity/transform/box-shadow)のうち
     transformには触れない(登場アニメーションの最終位置を壊さないため)。
   ・すべて有限時間のanimationで、infiniteループは使わない
     (Section11「無限CSSアニメーションループ禁止」)。
   ============================================================ */
.battle-stage-minicard-secret-fx-line {
  margin-top: 4px;
  font-size: 10px;
  font-weight: 700;
  line-height: 1.3;
  color: color-mix(in srgb, var(--secret-fx-accent, #b388ff) 82%, white 18%);
  max-width: 100%;
  overflow-wrap: break-word;
  word-break: break-word;
}

.battle-stage-minicard-secret-fx-result {
  margin-top: 3px;
  font-size: 10px;
  font-weight: 800;
  line-height: 1.35;
  color: #ffffff;
  background: color-mix(in srgb, var(--secret-fx-accent, #b388ff) 45%, #14151c 55%);
  border-radius: 6px;
  padding: 2px 6px;
  max-width: 100%;
  overflow-wrap: break-word;
  word-break: break-word;
  box-sizing: border-box;
}

/* 追補修正: 歪み対応(EMS-08)も他の21枚と同じcardEnter()経路になったため、
   このクラスは現状.battle-stage-minicard-secret-fx-line/-resultの内側
   (幅150pxのミニカード内)でしか使われないが、念のため単体でも安全に
   折り返せるよう明示しておく。 */
.battle-stage-revive-tagline {
  margin-top: 4px;
  font-size: 12px;
  font-weight: 700;
  color: color-mix(in srgb, var(--secret-fx-accent, #00e5ff) 85%, white 15%);
  max-width: 100%;
  overflow-wrap: break-word;
  word-break: break-word;
}

/* ---- BODY系(4): burnout-slam / berserk-ignite / fortress-guard / rex-devour ---- */
.battle-stage-minicard.secret-fx-burnout-slam {
  animation: secret-fx-burnout-slam-anim 0.9s ease-out;
}
@keyframes secret-fx-burnout-slam-anim {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--secret-fx-accent, #ff5f2e) 85%, transparent), 0 0 40px 14px rgba(179, 136, 255, 0.85); filter: brightness(1); }
  18% { box-shadow: 0 0 0 8px color-mix(in srgb, var(--secret-fx-accent, #ff5f2e) 65%, transparent), 0 0 55px 22px color-mix(in srgb, var(--secret-fx-accent, #ff5f2e) 55%, transparent); filter: brightness(1.6); }
  45% { box-shadow: 0 0 0 24px color-mix(in srgb, var(--secret-fx-accent, #ff5f2e) 0%, transparent), 0 0 40px 14px rgba(179, 136, 255, 0.85); filter: brightness(1.05); }
  100% { box-shadow: 0 0 40px 14px rgba(179, 136, 255, 0.85); filter: brightness(1); }
}

.battle-stage-minicard.secret-fx-berserk-ignite {
  animation: secret-fx-berserk-ignite-anim 1s steps(1, end) 3;
}
@keyframes secret-fx-berserk-ignite-anim {
  0%, 100% { filter: brightness(1) hue-rotate(0deg); box-shadow: 0 0 40px 14px rgba(179, 136, 255, 0.85); }
  50% { filter: brightness(1.5) hue-rotate(-8deg); box-shadow: 0 0 30px 10px color-mix(in srgb, var(--secret-fx-accent, #ff3b3b) 75%, transparent); }
}

.battle-stage-minicard.secret-fx-fortress-guard {
  animation: secret-fx-fortress-guard-anim 0.85s ease-out;
}
@keyframes secret-fx-fortress-guard-anim {
  0% { outline: 0px solid color-mix(in srgb, var(--secret-fx-accent, #3b6bff) 90%, transparent); outline-offset: 14px; box-shadow: 0 0 40px 14px rgba(179, 136, 255, 0.85); }
  55% { outline: 4px solid color-mix(in srgb, var(--secret-fx-accent, #3b6bff) 90%, transparent); outline-offset: 0px; box-shadow: 0 0 26px 6px color-mix(in srgb, var(--secret-fx-accent, #3b6bff) 70%, transparent); }
  100% { outline: 0px solid transparent; outline-offset: 0px; box-shadow: 0 0 40px 14px rgba(179, 136, 255, 0.85); }
}

.battle-stage-minicard.secret-fx-rex-devour {
  animation: secret-fx-rex-devour-anim 0.7s ease-in-out;
}
@keyframes secret-fx-rex-devour-anim {
  0%, 100% { filter: drop-shadow(0 0 0 color-mix(in srgb, var(--secret-fx-accent, #2ecc71) 80%, transparent)); }
  20% { filter: drop-shadow(6px 0 0 color-mix(in srgb, var(--secret-fx-accent, #2ecc71) 80%, transparent)); }
  40% { filter: drop-shadow(-6px 0 0 color-mix(in srgb, var(--secret-fx-accent, #2ecc71) 80%, transparent)); }
  60% { filter: drop-shadow(4px 0 0 color-mix(in srgb, var(--secret-fx-accent, #2ecc71) 80%, transparent)); }
  80% { filter: drop-shadow(-2px 0 0 color-mix(in srgb, var(--secret-fx-accent, #2ecc71) 80%, transparent)); }
}

/* ---- PLUS系(4): c4-detonation / carbine-burst / shield-deflect / heli-scan ---- */
.battle-stage-minicard.secret-fx-c4-detonation {
  animation: secret-fx-c4-detonation-anim 0.6s ease-out;
}
@keyframes secret-fx-c4-detonation-anim {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--secret-fx-accent, #ff7a1a) 90%, transparent); filter: contrast(1) brightness(1); }
  20% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--secret-fx-accent, #ff7a1a) 80%, transparent); filter: contrast(1.4) brightness(1.7); }
  50% { box-shadow: 0 0 0 22px color-mix(in srgb, var(--secret-fx-accent, #ff7a1a) 0%, transparent); filter: contrast(1) brightness(1); }
  100% { box-shadow: 0 0 0 0 transparent; }
}

.battle-stage-minicard.secret-fx-carbine-burst {
  animation: secret-fx-carbine-burst-anim 0.7s steps(1, end) 4;
}
@keyframes secret-fx-carbine-burst-anim {
  0%, 60%, 100% { box-shadow: 0 0 14px 4px color-mix(in srgb, var(--secret-fx-accent, #c0392b) 40%, transparent); }
  30% { box-shadow: 0 0 26px 10px color-mix(in srgb, var(--secret-fx-accent, #c0392b) 90%, transparent); }
}

.battle-stage-minicard.secret-fx-shield-deflect {
  position: relative;
  overflow: hidden;
}
.battle-stage-minicard.secret-fx-shield-deflect::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, transparent 40%, color-mix(in srgb, var(--secret-fx-accent, #2980b9) 75%, white 25%) 50%, transparent 60%);
  transform: translateX(-120%);
  animation: secret-fx-shield-deflect-sweep 0.8s ease-out;
  /* 不具合修正(Phase 3-4-6-7 追補: displayバグ監査): animation-fill-modeが
     未指定だとアニメーション終了(0.8秒後)と同時にtransform/opacityがこの
     ::after要素の基本スタイル(transform無し=中央/opacity未指定=1)へ戻り、
     本来「画面外へ抜けて消える」はずの帯がカード中央に不透明なまま
     再出現し、そのカードの残り表示時間ずっと残留してしまっていた
     (city-event-fx-bank-door-breachで既に修正済みの不具合と同一の原因)。
     アニメーション終了時点の状態(画面外・opacity:0)を維持するだけの
     修正であり、色・動き・発動条件は一切変更していない。 */
  animation-fill-mode: forwards;
  pointer-events: none;
}
@keyframes secret-fx-shield-deflect-sweep {
  0% { transform: translateX(-120%); opacity: 0; }
  15% { opacity: 1; }
  60% { transform: translateX(120%); opacity: 1; }
  100% { transform: translateX(120%); opacity: 0; }
}

.battle-stage-minicard.secret-fx-heli-scan {
  position: relative;
  overflow: hidden;
}
.battle-stage-minicard.secret-fx-heli-scan::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 30%;
  background: linear-gradient(180deg, transparent, color-mix(in srgb, var(--secret-fx-accent, #16a085) 55%, transparent), transparent);
  animation: secret-fx-heli-scan-sweep 1s linear;
  /* 不具合修正(Phase 3-4-6-7 追補): animation-fill-mode未指定によりアニメ
     終了後に帯が再出現し残留する問題(shield-deflectと同一原因)。 */
  animation-fill-mode: forwards;
  pointer-events: none;
}
@keyframes secret-fx-heli-scan-sweep {
  0% { top: -30%; opacity: 0; }
  10% { opacity: 1; }
  90% { opacity: 1; }
  100% { top: 100%; opacity: 0; }
}

/* ---- COUNTER系(2): excalibur-seal / oshaburi-mirror ---- */
.battle-stage-minicard.secret-fx-excalibur-seal {
  position: relative;
  overflow: hidden;
}
.battle-stage-minicard.secret-fx-excalibur-seal::before {
  content: "";
  position: absolute;
  inset: -10% -40%;
  background: linear-gradient(20deg, transparent 46%, color-mix(in srgb, var(--secret-fx-accent, #f1c40f) 85%, white 15%) 50%, transparent 54%);
  transform: translateX(-140%);
  animation: secret-fx-excalibur-seal-slice 0.55s cubic-bezier(0.4, 0, 0.2, 1);
  /* 不具合修正(Phase 3-4-6-7 追補): animation-fill-mode未指定によりアニメ
     終了後に斜線が中央に再出現し残留する問題(shield-deflectと同一原因)。 */
  animation-fill-mode: forwards;
  pointer-events: none;
}
@keyframes secret-fx-excalibur-seal-slice {
  0% { transform: translateX(-140%); }
  100% { transform: translateX(140%); }
}

.battle-stage-minicard.secret-fx-oshaburi-mirror {
  animation: secret-fx-oshaburi-mirror-anim 0.9s ease-in-out;
}
@keyframes secret-fx-oshaburi-mirror-anim {
  0%, 100% { box-shadow: 0 0 40px 14px rgba(179, 136, 255, 0.85); }
  25% { box-shadow: -14px 0 22px 4px color-mix(in srgb, var(--secret-fx-accent, #9b59b6) 70%, transparent), 14px 0 22px 4px color-mix(in srgb, var(--secret-fx-accent, #9b59b6) 70%, transparent); }
  55% { box-shadow: 10px 0 18px 3px color-mix(in srgb, var(--secret-fx-accent, #9b59b6) 55%, transparent), -10px 0 18px 3px color-mix(in srgb, var(--secret-fx-accent, #9b59b6) 55%, transparent); }
}

/* ---- 単独能力系(8) ----
   distortion-revive(EMS-08)は追補修正で他の21種と全く同じcardEnter()経路
   (.battle-stage-minicardへクラス付与)に統一された(js/ui/battle-stage.jsの
   showRevive参照)。セレクタ自体は元々クラス単体(.battle-stage-minicardに
   限定しない)で書いてあり、この統一後も問題なく機能するためそのまま残している。 */
.secret-fx-distortion-revive {
  position: relative;
  animation: secret-fx-distortion-revive-flicker 0.9s steps(1, end) 4;
}
.secret-fx-distortion-revive::after {
  content: "";
  position: absolute;
  inset: -30%;
  border-radius: 50%;
  border: 3px solid color-mix(in srgb, var(--secret-fx-accent, #00e5ff) 70%, transparent);
  animation: secret-fx-distortion-revive-vortex 1.1s ease-in;
  /* 不具合修正(Phase 3-4-6-7 追補): animation-fill-mode未指定によりアニメ
     終了後にリング枠が不透明のまま再出現し残留する問題(shield-deflectと同一原因)。 */
  animation-fill-mode: forwards;
  pointer-events: none;
}
@keyframes secret-fx-distortion-revive-flicker {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(1.6) saturate(1.4); }
}
@keyframes secret-fx-distortion-revive-vortex {
  0% { transform: rotate(0deg) scale(0.3); opacity: 0; }
  30% { opacity: 1; }
  100% { transform: rotate(300deg) scale(1); opacity: 0; }
}

.battle-stage-minicard.secret-fx-hp-swap-vortex {
  position: relative;
  overflow: hidden;
}
.battle-stage-minicard.secret-fx-hp-swap-vortex::after {
  content: "";
  position: absolute;
  inset: 18%;
  border-radius: 50%;
  background: conic-gradient(from 0deg, transparent, color-mix(in srgb, var(--secret-fx-accent, #00c896) 65%, transparent), transparent 70%);
  animation: secret-fx-hp-swap-vortex-spin 1s linear;
  /* 不具合修正(Phase 3-4-6-7 追補): animation-fill-mode未指定によりアニメ
     終了後にリングが不透明のまま再出現し残留する問題(shield-deflectと同一原因)。 */
  animation-fill-mode: forwards;
  pointer-events: none;
}
@keyframes secret-fx-hp-swap-vortex-spin {
  0% { transform: rotate(0deg); opacity: 0; }
  15% { opacity: 1; }
  85% { opacity: 1; }
  100% { transform: rotate(360deg); opacity: 0; }
}

.battle-stage-minicard.secret-fx-menu-reveal-select {
  position: relative;
  overflow: hidden;
}
.battle-stage-minicard.secret-fx-menu-reveal-select::before {
  content: "";
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--secret-fx-accent, #e67e22) 55%, transparent);
  animation: secret-fx-menu-reveal-wipe 0.8s ease-in-out;
  /* 不具合修正(Phase 3-4-6-7 追補: 不具合1「FOOD-08がオレンジ色化する」の
     直接の原因): animation-fill-modeが未指定だと、0.8秒のワイプ演出終了と
     同時にclip-pathがこの::before要素の基本スタイル(クリップ無し=カード
     全体を覆う)へ戻ってしまい、本来「一瞬だけ薄く光って消える」はずの
     オレンジ色のオーバーレイ(--secret-fx-accent、カード画像・本体・
     下部表示すべてを含むカード全体をinset:0で覆う)が、そのカードの
     残り表示時間(最大数秒間)ずっと不透明に近い状態で残留してしまっていた。
     アニメーション終了時点の状態(完全にクリップされ非表示)を維持する
     だけの修正であり、色・動き・発動条件・SECRET演出自体は一切変更していない。 */
  animation-fill-mode: forwards;
  pointer-events: none;
}
@keyframes secret-fx-menu-reveal-wipe {
  0% { clip-path: inset(0 100% 0 0); }
  50% { clip-path: inset(0 0 0 0); }
  100% { clip-path: inset(0 0 0 100%); }
}

.battle-stage-minicard.secret-fx-gear-recustom {
  position: relative;
}
.battle-stage-minicard.secret-fx-gear-recustom::after {
  content: "\2699";
  position: absolute;
  top: -10px;
  right: -10px;
  font-size: 22px;
  color: color-mix(in srgb, var(--secret-fx-accent, #7f8c8d) 85%, white 15%);
  animation: secret-fx-gear-recustom-spin 1s steps(8, end);
  /* 不具合修正(Phase 3-4-6-7 追補): animation-fill-mode未指定によりアニメ
     終了後に⚙アイコンが不透明のまま再出現し残留する問題(shield-deflectと同一原因)。 */
  animation-fill-mode: forwards;
  pointer-events: none;
}
@keyframes secret-fx-gear-recustom-spin {
  0% { transform: rotate(0deg); opacity: 0; }
  10% { opacity: 1; }
  90% { opacity: 1; }
  100% { transform: rotate(180deg); opacity: 0; }
}

.battle-stage-minicard.secret-fx-coin-duplicate {
  animation: secret-fx-coin-duplicate-anim 0.8s ease-out;
}
@keyframes secret-fx-coin-duplicate-anim {
  0% { box-shadow: 0 0 40px 14px rgba(179, 136, 255, 0.85); }
  40% { box-shadow: -10px 0 0 3px color-mix(in srgb, var(--secret-fx-accent, #d4af37) 80%, transparent), 10px 0 0 3px color-mix(in srgb, var(--secret-fx-accent, #d4af37) 80%, transparent), 0 0 30px 10px color-mix(in srgb, var(--secret-fx-accent, #d4af37) 60%, transparent); }
  100% { box-shadow: 0 0 40px 14px rgba(179, 136, 255, 0.85); }
}

.battle-stage-minicard.secret-fx-checkveh-scan {
  position: relative;
  overflow: hidden;
}
.battle-stage-minicard.secret-fx-checkveh-scan::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 22%;
  background: color-mix(in srgb, var(--secret-fx-accent, #1abc9c) 45%, transparent);
  animation: secret-fx-checkveh-scan-move 0.9s steps(10, end);
  /* 不具合修正(Phase 3-4-6-7 追補): animation-fill-mode未指定によりアニメ
     終了後にスキャン帯がカード左端に不透明のまま再出現し残留する問題
     (shield-deflectと同一原因)。 */
  animation-fill-mode: forwards;
  pointer-events: none;
}
@keyframes secret-fx-checkveh-scan-move {
  0% { left: -22%; opacity: 0.9; }
  100% { left: 100%; opacity: 0.9; }
}

.battle-stage-minicard.secret-fx-rollback-record {
  animation: secret-fx-rollback-record-anim 0.9s ease-out;
}
@keyframes secret-fx-rollback-record-anim {
  0% { outline: 0px solid color-mix(in srgb, var(--secret-fx-accent, #f9c74f) 90%, transparent); outline-offset: 0px; }
  60% { outline: 2px solid color-mix(in srgb, var(--secret-fx-accent, #f9c74f) 90%, transparent); outline-offset: 10px; }
  100% { outline: 0px solid transparent; outline-offset: 16px; }
}

.battle-stage-minicard.secret-fx-starvation-drain {
  animation: secret-fx-starvation-drain-anim 1.1s ease-in;
}
@keyframes secret-fx-starvation-drain-anim {
  0% { filter: grayscale(0) brightness(1); box-shadow: 0 0 40px 14px rgba(179, 136, 255, 0.85); }
  70% { filter: grayscale(0.7) brightness(0.75); box-shadow: 0 0 18px 4px color-mix(in srgb, var(--secret-fx-accent, #6c3483) 55%, transparent); }
  100% { filter: grayscale(0) brightness(1); box-shadow: 0 0 40px 14px rgba(179, 136, 255, 0.85); }
}

/* ---- 独立SECRET(4): meditation-freeze / ban-vaporize / server-restart-reboot / compensation-ticket ---- */
.battle-stage-minicard.secret-fx-meditation-freeze {
  animation: secret-fx-meditation-freeze-anim 1s ease-out;
}
@keyframes secret-fx-meditation-freeze-anim {
  0% { filter: brightness(1); outline: 0px solid color-mix(in srgb, var(--secret-fx-accent, #8e44ad) 85%, transparent); outline-offset: 16px; }
  60% { filter: brightness(0.7) saturate(1.3); outline: 5px solid color-mix(in srgb, var(--secret-fx-accent, #8e44ad) 85%, transparent); outline-offset: 0px; }
  100% { filter: brightness(0.85); outline: 3px solid color-mix(in srgb, var(--secret-fx-accent, #8e44ad) 60%, transparent); outline-offset: 0px; }
}

.battle-stage-minicard.secret-fx-ban-vaporize {
  animation: secret-fx-ban-vaporize-anim 1s ease-in forwards;
}
@keyframes secret-fx-ban-vaporize-anim {
  0% { filter: blur(0) brightness(1); box-shadow: 0 0 40px 14px rgba(179, 136, 255, 0.85); }
  60% { filter: blur(2px) brightness(1.5); box-shadow: 0 0 50px 18px color-mix(in srgb, var(--secret-fx-accent, #e74c3c) 75%, transparent); }
  100% { filter: blur(0) brightness(1); box-shadow: 0 0 40px 14px rgba(179, 136, 255, 0.85); }
}

.battle-stage-minicard.secret-fx-server-restart-reboot {
  animation: secret-fx-server-restart-reboot-anim 1s steps(1, end) 4;
}
@keyframes secret-fx-server-restart-reboot-anim {
  0%, 100% { filter: brightness(1); box-shadow: 0 0 40px 14px rgba(179, 136, 255, 0.85); }
  50% { filter: brightness(0.15); box-shadow: 0 0 20px 6px color-mix(in srgb, var(--secret-fx-accent, #00b894) 90%, transparent); }
}

.battle-stage-minicard.secret-fx-compensation-ticket {
  animation: secret-fx-compensation-ticket-anim 0.7s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes secret-fx-compensation-ticket-anim {
  0% { outline: 6px solid color-mix(in srgb, var(--secret-fx-accent, #0984e3) 90%, transparent); outline-offset: -6px; box-shadow: 0 0 40px 14px rgba(179, 136, 255, 0.85); }
  60% { outline: 2px solid color-mix(in srgb, var(--secret-fx-accent, #0984e3) 70%, transparent); outline-offset: 4px; box-shadow: 0 0 26px 8px color-mix(in srgb, var(--secret-fx-accent, #0984e3) 60%, transparent); }
  100% { outline: 0px solid transparent; outline-offset: 0px; box-shadow: 0 0 40px 14px rgba(179, 136, 255, 0.85); }
}

/* UI/UX強化: UR/SECRET登場時に、そのカード自身の上へ短く浮かび上がる
   強調バッジ(「UR」「SECRET」)。中央のATTACK/DEFENSEフラッシュや
   TOTAL ATK/DEF表示と場所が重ならないよう、あえてカード自身に紐付けている
   (どのカードが該当レアリティなのか一目で分かる)。 */
.battle-stage-minicard-rarity-callout {
  position: absolute;
  top: -14px;
  left: 50%;
  transform: translate(-50%, 0) scale(0.4);
  font-size: 15px;
  font-weight: 900;
  letter-spacing: 0.1em;
  padding: 2px 10px;
  border-radius: 999px;
  background: #0a0c11;
  opacity: 0;
  pointer-events: none;
  z-index: 3;
  white-space: nowrap;
}
.battle-stage-minicard-rarity-callout-ur {
  color: var(--rarity-ur);
  box-shadow: 0 0 10px rgba(255, 179, 0, 0.8);
}
.battle-stage-minicard-rarity-callout-secret {
  color: var(--rarity-secret);
  box-shadow: 0 0 14px rgba(224, 64, 251, 0.9);
}
.battle-stage-minicard-rarity-callout-play {
  animation: battle-stage-rarity-callout-anim 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes battle-stage-rarity-callout-anim {
  0% { opacity: 0; transform: translate(-50%, 6px) scale(0.3); }
  25% { opacity: 1; transform: translate(-50%, 0) scale(1.3); }
  55% { opacity: 1; transform: translate(-50%, 0) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -6px) scale(0.9); }
}

.battle-stage-attacker .battle-stage-minicard {
  border-color: var(--battle-stage-attacker-accent);
}

.battle-stage-defender .battle-stage-minicard {
  border-color: var(--battle-stage-defender-accent);
}

.battle-stage-minicard-rarity {
  display: inline-block;
  font-size: 10px;
  font-weight: 800;
  padding: 1px 6px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.12);
}

/* ============================================================
   通常カード画像146枚組み込み(ユーザー確定仕様セクション6・8): 中央演出
   (攻撃/防御/PLUS/COUNTER/EMS/飲食店/素材屋/一般市民/BUFF/DEBUFF/独立SECRET、
   いずれもbuildStageCardElement経由)のミニカード画像。手札(112×128px)より
   大きく見せつつ、1920×1080/1366×768等の狭い画面でも縦スクロールが
   発生しないよう、画面の高さに応じて上限を可変にしている(clamp)。
   object-fit:containで元画像の縦横比を維持したまま必ずカード全体を表示し、
   トリミングはしない。既存の能力名・効果数値・TOTAL ATK/DEF・ダメージ表示
   (この画像の下に続く既存要素)は変更せず、そのまま画像の下に表示され続ける。
   ============================================================ */
.battle-stage-minicard-image {
  display: block;
  width: 100%;
  height: var(--stage-card-img-h, clamp(90px, 15vh, 190px));
  max-height: 46vh;
  max-width: 32vw;
  object-fit: contain;
  border-radius: 8px;
  margin-bottom: 4px;
  background: rgba(0, 0, 0, 0.25);
}

.battle-stage-minicard-role {
  display: inline-block;
  margin-left: 4px;
  font-size: 10px;
  font-weight: 700;
  color: var(--text-secondary, #aab2c0);
}

.battle-stage-minicard-name {
  margin-top: 4px;
  font-size: 14px;
  font-weight: 800;
}

.battle-stage-minicard-meta,
.battle-stage-minicard-stats {
  margin-top: 2px;
  font-size: 11px;
  color: var(--text-secondary, #aab2c0);
}

.battle-stage-minicard-ability {
  margin-top: 4px;
  font-size: 10px;
  line-height: 1.35;
  color: var(--battle-stage-defender-accent);
  max-width: 100%;
  overflow-wrap: break-word;
  word-break: break-word;
}

.battle-stage-side-noguard {
  align-items: center;
  justify-content: center;
}

.battle-stage-noguard-label {
  font-size: 22px;
  font-weight: 800;
  letter-spacing: 0.08em;
  color: var(--text-secondary, #aab2c0);
  border: 2px dashed var(--border);
  border-radius: 12px;
  padding: 12px 28px;
  animation: battle-stage-pop-in 0.25s ease-out;
}

.battle-stage-counter {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  background: var(--battle-stage-card-bg);
  border: 2px solid var(--battle-stage-counter-mitigate);
  border-radius: 12px;
  padding: 8px 18px 12px;
  animation: battle-stage-counter-pop 0.3s ease-out;
}

.battle-stage-counter.hidden {
  display: none;
}

.battle-stage-counter[data-kind="negate"] {
  border-color: var(--battle-stage-counter-negate);
}
.battle-stage-counter[data-kind="reflect_percent"] {
  border-color: var(--battle-stage-counter-reflect);
}

.battle-stage-counter-label {
  font-size: 18px;
  font-weight: 900;
  letter-spacing: 0.12em;
  color: var(--battle-stage-counter-mitigate);
  animation: battle-stage-counter-flash 0.5s ease-out;
}
.battle-stage-counter[data-kind="negate"] .battle-stage-counter-label {
  color: var(--battle-stage-counter-negate);
}
.battle-stage-counter[data-kind="reflect_percent"] .battle-stage-counter-label {
  color: var(--battle-stage-counter-reflect);
}

.battle-stage-counter .battle-stage-minicard {
  /* COUNTERカード自体は通常カードより一回り大きく、少し強めのポップで
     拡大表示する(ユーザー確定仕様4番: 通常カードと明確に違う演出)。
     登場自体は共通の.battle-stage-card-settled(transition方式)に乗せる。 */
  width: 170px;
  transition-duration: 0.34s;
}
.battle-stage-counter .battle-stage-minicard.battle-stage-card-settled {
  transform: scale(1.06);
}

.battle-stage-counter-effect {
  font-size: 11px;
  max-width: 220px;
  text-align: center;
  color: var(--text-secondary, #aab2c0);
}

.battle-stage-totals-row {
  text-align: center;
}

/* 追補修正(EMS-08はみ出しバグの根本原因への対応):
   .battle-stage-arena は display:flex; flex-direction:column; align-items:center
   であり、align-itemsがstretchではないため、この行(.battle-stage-totals-row。
   flexアイテム)の幅はストレッチされず「中身に合わせたfit-content」になる。
   その結果、幅の制約が無いまま長いテキストを入れると、折り返しの基準となる
   利用可能幅が実質無限大になり、1行のまま画面外へ伸びてしまう
   (元々[data-mode="ability"]用に.battle-stage-totalsだけmax-width:640pxを
   与えていたが、.battle-stage-damageには同様の制約が無く、EMS-08のタグライン
   でこの問題が顕在化した)。ここで両方に共通のmax-width・折り返し設定を
   与えることで、mode(battle/ability)によらず常に画面内に収まるようにする。
   （数値・レイアウトの見た目だけの変更で、DOM構造・ゲームロジックは無変更）。 */
.battle-stage-totals,
.battle-stage-damage {
  text-align: center;
  font-weight: 800;
  max-width: min(720px, 92vw);
  margin-left: auto;
  margin-right: auto;
  overflow-wrap: break-word;
  word-break: break-word;
}

.battle-stage-totals {
  font-size: 18px;
}

/* 第7弾追補(既知問題の解消): battle-stage-totals-popはjs/ui/battle-stage.js
   (popTotals)が5箇所(攻撃/防御/ノーガード/能力/歪み対応の各TOTAL ATK・
   TOTAL DEF・DAMAGE確定タイミング)で付け外ししているが、対応する
   @keyframesが未実装だった(実装漏れ)。表示するテキスト自体(TOTAL ATK/
   TOTAL DEF/DAMAGEの数値)やダメージ計算タイミング・hit-stop/ノックバックの
   タイミングには一切触れず、既存の.battle-stage-summary-pop/
   .battle-stage-action-flash-anim(いずれもscale+opacityの短い「ポップ」)と
   同系統の、必要最小限の強調演出だけを追加する。
   第7弾最終追補(項目4): このアニメーション自体の長さ(--totals-pop-duration)も
   演出速度設定(1.0/1.25/1.5x)に連動してスケールする(js/ui/battle-stage.js
   popTotals()がtotalsPopDurationMs()=scaleMs(450)をインラインスタイルとして
   都度設定する)。カスタムプロパティが未設定の場合(万一の初回描画タイミング等)
   に備え、0.45s(1.0倍速相当)をフォールバックとして残す。 */
.battle-stage-totals.battle-stage-totals-pop {
  animation: battle-stage-totals-pop-anim var(--totals-pop-duration, 0.45s) cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes battle-stage-totals-pop-anim {
  0% {
    opacity: 0.55;
    transform: scale(0.82);
    text-shadow: 0 0 0 rgba(255, 196, 64, 0);
  }
  40% {
    opacity: 1;
    transform: scale(1.14);
    text-shadow: 0 0 16px rgba(255, 196, 64, 0.85);
  }
  100% {
    opacity: 1;
    transform: scale(1);
    text-shadow: 0 0 0 rgba(255, 196, 64, 0);
  }
}
@media (prefers-reduced-motion: reduce) {
  .battle-stage-totals.battle-stage-totals-pop {
    animation: none;
  }
}

.battle-stage-damage {
  margin-top: 4px;
  font-size: 26px;
  color: var(--danger);
}

/* 実プレイ改善: 攻撃・防御・COUNTER処理がすべて終わった後にだけ表示する、
   TOTAL ATK / VS / TOTAL DEF / DAMAGEのまとめ表示。カード説明(10px前後)は
   もちろん、通常の.battle-stage-totals/.battle-stage-damageよりも大きく、
   一目で戦闘結果が分かるサイズにしている。カード同士が重ならない既存の
   レイアウト(.battle-stage-arena/.battle-stage-side)には影響しない
   独立したブロックとして、.battle-stage-totals-row内(カード行の間の
   既存スペース)に収まるようにしてある。 */
.battle-stage-summary {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: clamp(10px, 2vw, 24px);
  animation: battle-stage-summary-pop 0.3s ease-out;
}

.battle-stage-summary.hidden {
  display: none;
}

.battle-stage-summary-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

.battle-stage-summary-label {
  font-size: clamp(12px, 1.6vw, 16px);
  font-weight: 800;
  letter-spacing: 0.06em;
  color: var(--text-secondary, #aab2c0);
}

.battle-stage-summary-value {
  font-size: clamp(28px, 5vw, 48px);
  font-weight: 900;
  line-height: 1;
}

.battle-stage-summary-atk .battle-stage-summary-value {
  color: var(--battle-stage-attacker-accent);
}

.battle-stage-summary-def .battle-stage-summary-value {
  color: var(--battle-stage-defender-accent);
}

.battle-stage-summary-vs {
  font-size: clamp(16px, 2vw, 22px);
  font-weight: 800;
  color: var(--text-secondary, #aab2c0);
  letter-spacing: 0.08em;
}

.battle-stage-summary-damage-block .battle-stage-summary-label {
  color: var(--danger);
}

.battle-stage-summary-damage-value {
  font-size: clamp(32px, 6vw, 56px);
  color: var(--danger);
}

@keyframes battle-stage-summary-pop {
  from {
    opacity: 0;
    transform: scale(0.85);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes battle-stage-pop-in {
  from {
    opacity: 0;
    transform: scale(0.8);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes battle-stage-in-top {
  from {
    opacity: 0;
    transform: translateY(-36px) scale(0.85);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@keyframes battle-stage-in-bottom {
  from {
    opacity: 0;
    transform: translateY(36px) scale(0.85);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@keyframes battle-stage-counter-pop {
  0% {
    opacity: 0;
    transform: scale(0.4);
  }
  60% {
    opacity: 1;
    transform: scale(1.12);
  }
  100% {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes battle-stage-counter-flash {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.35;
  }
}

/* ============================================================
   戦闘演出プロトタイプ(今回追加分): カード演出のみを対象とした簡易版。
   正式なカード画像・背景・効果音が確定するまでのプレースホルダーであり、
   js/ui/battle-stage.js内のcardEnter/addSupportCard/impact/applyHpVisual
   という演出単位の関数と1対1に対応する形でCSSも独立させてある(見た目だけを
   差し替える際にここだけ変更すれば済むようにするため)。
   ============================================================ */
:root {
  --battle-stage-flash-color: rgba(255, 255, 255, 0.85);
  --battle-stage-block-color: #8fb8ff;
}

/* SECRETカード登場前の暗転→SECRET表示(ユーザー確定仕様9番)。 */
.battle-stage-secret-veil {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0);
  pointer-events: none;
  z-index: 1;
  border-radius: 12px;
  transition: background 0.25s ease;
}
.battle-stage-secret-veil.battle-stage-secret-veil-active {
  background: rgba(0, 0, 0, 0.55);
}
.battle-stage-secret-label {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.6);
  font-size: clamp(22px, 4vw, 40px);
  font-weight: 900;
  letter-spacing: 0.18em;
  color: #b388ff;
  opacity: 0;
  text-shadow: 0 0 18px rgba(179, 136, 255, 0.8);
  transition: opacity 0.2s ease, transform 0.2s ease;
  z-index: 2;
  pointer-events: none;
}
.battle-stage-secret-label.battle-stage-secret-label-active {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}

/* ATTACK/DEFENSEフラッシュラベル(ユーザー確定仕様1・3番: カード登場と
   同時に一瞬大きく表示)。 */
.battle-stage-action-flash {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.6);
  font-size: clamp(28px, 5vw, 52px);
  font-weight: 900;
  letter-spacing: 0.14em;
  color: var(--text-primary, #f2f4f8);
  opacity: 0;
  pointer-events: none;
  z-index: 3;
  text-shadow: 0 0 20px rgba(0, 0, 0, 0.6);
}
.battle-stage-action-flash.battle-stage-action-flash-play {
  animation: battle-stage-action-flash-anim 0.7s ease-out;
}
@keyframes battle-stage-action-flash-anim {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.5);
  }
  25% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.15);
  }
  70% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1);
  }
}
.battle-stage-action-flash[data-role="attack"] {
  color: var(--battle-stage-attacker-accent);
}
.battle-stage-action-flash[data-role="defense"] {
  color: var(--battle-stage-defender-accent);
}
/* UI/UX強化(ユーザー確定仕様6番「攻撃・防御以外のカードにも中央演出」)。 */
.battle-stage-action-flash[data-role="ability"] {
  color: var(--battle-stage-ability-accent);
}

/* UI/UX強化(ユーザー確定仕様6番): 単独アクションカード使用時は、対面側
   (defender)スロット・COUNTER枠を表示しない「1枚だけを中央に見せる」
   レイアウトにする(js/ui/battle-stage.jsのshowAbility参照)。攻撃/防御時の
   レイアウト(.battle-stage-arena/[data-attacker-side])自体は変更せず、
   defender側だけを一時的に隠す。 */
.battle-stage-overlay[data-mode="ability"] .battle-stage-defender {
  display: none;
}
.battle-stage-overlay[data-mode="ability"] .battle-stage-totals-row {
  order: 1;
}
.battle-stage-overlay[data-mode="ability"] .battle-stage-attacker {
  order: 0;
}
.battle-stage-overlay[data-mode="ability"] .battle-stage-totals {
  /* 能力名が無いカードはdescription(効果説明、長文になりがち)を代わりに
     表示するため(js/ui/battle-stage.jsのshowAbility参照)、通常のTOTAL ATK
     数値表示より控えめなサイズにし、折り返し・最大幅を設定してオーバーレイ
     からはみ出さないようにする。 */
  font-size: clamp(15px, 2.2vw, 21px);
  max-width: 640px;
  margin: 0 auto;
  white-space: normal;
  line-height: 1.4;
}

/* 単独使用カード(EMS/FOOD/MECH/MATERIAL/CIV/BUFF/DEBUFF/独立SECRET)の
   中央表示を大型化する(実プレイ改善要望)。攻撃・防御(data-mode="battle")の
   左右VS演出、PLUS/COUNTERの表示は一切変更せず、単独使用カード
   (data-mode="ability")のときだけ、既存の「1枚だけ」用サイズ基準
   (--stage-card-img-h)と.battle-stage-minicard-imageの上限を、
   PLAYER/CPUどちらの使用時にも共通で引き上げる(showAbility/showRevive
   いずれも同じdata-mode="ability"を経由するため、この1箇所の追加だけで
   両方に適用される)。目安は画像高さ500〜650px程度・max-height:75vh・
   max-width:40vwで、元画像の縦横比はobject-fit:containで維持したまま
   (既存どおり切り抜き・引き伸ばしはしない)。1920×1080ではカード内の文章が
   読める大きさを優先し、1366×768ではclamp/vh/vw上限により自動的に縮小され、
   ページ全体のスクロールは発生しない(battle-stage-overlayは
   position:fixed;inset:0で常に画面サイズに固定されており、既存の
   攻撃/防御(data-mode="battle")の大型化実装と同じ仕組みを踏襲)。
   カード名・カテゴリ・レアリティ・効果説明は既存どおり画像の下にそのまま
   表示され、削除していない。 */
.battle-stage-overlay[data-mode="ability"] .battle-stage-side:not(:has(.battle-stage-minicard ~ .battle-stage-minicard)) {
  --stage-card-img-h: clamp(300px, 62vh, 650px);
}
.battle-stage-overlay[data-mode="ability"] .battle-stage-minicard-image {
  max-height: 75vh;
  max-width: 40vw;
}
.battle-stage-overlay[data-mode="ability"] .battle-stage-minicard {
  max-width: 42vw;
}

/* COUNTER割り込み演出(ユーザー確定仕様4番: 通常カードと明確に違う演出)。
   一瞬「COUNTER!」を大きく表示してから、カード自体は既存の
   .battle-stage-card-settled(共通transition)で拡大表示される。 */
.battle-stage-counter-burst {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.4);
  font-size: clamp(30px, 6vw, 58px);
  font-weight: 900;
  letter-spacing: 0.12em;
  color: var(--battle-stage-counter-mitigate);
  opacity: 0;
  pointer-events: none;
  z-index: 4;
  text-shadow: 0 0 24px rgba(0, 0, 0, 0.7);
}
.battle-stage-counter-burst.battle-stage-counter-burst-negate {
  color: var(--battle-stage-counter-negate);
}
.battle-stage-counter-burst.battle-stage-counter-burst-reflect_percent {
  color: var(--battle-stage-counter-reflect);
}
.battle-stage-counter-burst.battle-stage-counter-burst-play {
  animation: battle-stage-counter-burst-anim 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes battle-stage-counter-burst-anim {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.3);
  }
  30% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.2);
  }
  75% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1);
  }
}

/* COUNTER無効化演出(ユーザー確定仕様4番): ねこポリス(能力のみ無効化)と
   エクスカリバー(カードごと無効化)を明確に区別するバッジ。 */
.battle-stage-nullify-badge {
  display: block;
  min-height: 14px;
  font-size: 11px;
  font-weight: 900;
  letter-spacing: 0.05em;
  color: var(--battle-stage-counter-negate);
}

/* 衝突演出(ユーザー確定仕様6番): 画面シェイク・フラッシュ・DAMAGE数値の
   一瞬拡大。派手すぎないよう振れ幅は小さめにしてある。 */
@keyframes battle-stage-shake {
  0%,
  100% {
    transform: translate3d(0, 0, 0);
  }
  20% {
    transform: translate3d(-6px, 2px, 0);
  }
  40% {
    transform: translate3d(5px, -2px, 0);
  }
  60% {
    transform: translate3d(-4px, 1px, 0);
  }
  80% {
    transform: translate3d(3px, -1px, 0);
  }
}
.battle-stage-overlay.battle-stage-shake {
  animation: battle-stage-shake 0.32s ease-in-out;
}

/* UI/UX強化(衝突演出大幅強化・ユーザー確定仕様3番): DAMAGEありの衝突は
   従来のshakeより明確に強い振れ幅・時間にする。既存のbattle-stage-shakeは
   UR/SECRET登場演出などで今まで通り使うため、別クラス+別keyframeとして
   追加する(既存演出の強さには影響しない)。 */
@keyframes battle-stage-shake-strong {
  0%,
  100% {
    transform: translate3d(0, 0, 0);
  }
  15% {
    transform: translate3d(-12px, 5px, 0);
  }
  30% {
    transform: translate3d(10px, -6px, 0);
  }
  45% {
    transform: translate3d(-9px, 4px, 0);
  }
  60% {
    transform: translate3d(7px, -3px, 0);
  }
  75% {
    transform: translate3d(-4px, 2px, 0);
  }
  90% {
    transform: translate3d(2px, -1px, 0);
  }
}
.battle-stage-overlay.battle-stage-shake-strong {
  animation: battle-stage-shake-strong 0.42s ease-in-out;
}

.battle-stage-flash-layer {
  position: absolute;
  inset: 0;
  background: var(--battle-stage-flash-color);
  opacity: 0;
  pointer-events: none;
  z-index: 30;
}
.battle-stage-flash-layer.battle-stage-flash-active {
  animation: battle-stage-flash-anim 0.22s ease-out;
}
/* UI/UX強化(衝突演出大幅強化): DAMAGEありの衝突用の、より強い/長いフラッシュ。 */
.battle-stage-flash-layer.battle-stage-flash-active-strong {
  animation: battle-stage-flash-anim-strong 0.3s ease-out;
}
@keyframes battle-stage-flash-anim {
  0% {
    opacity: 0.55;
  }
  100% {
    opacity: 0;
  }
}
@keyframes battle-stage-flash-anim-strong {
  0% {
    opacity: 0.85;
  }
  100% {
    opacity: 0;
  }
}

/* UI/UX強化(衝突演出大幅強化・ユーザー確定仕様3番「衝撃波のようなCSSエフェクト」):
   攻撃側カードが防御側カードへ到達した瞬間、防御側カードの位置を中心に一瞬だけ
   広がるリング。座標(left/top)はjs/ui/battle-stage.jsのplayShockwave()が
   getBoundingClientRectから都度計算して設定する(カード枚数・画面サイズが
   変わっても常に防御側カードの実位置に合わせるため)。 */
.battle-stage-shockwave {
  position: absolute;
  width: 10px;
  height: 10px;
  margin-left: -5px;
  margin-top: -5px;
  border-radius: 50%;
  border: 3px solid rgba(255, 255, 255, 0.9);
  box-shadow: 0 0 18px 4px rgba(255, 200, 80, 0.6);
  opacity: 0;
  pointer-events: none;
  z-index: 25;
}
.battle-stage-shockwave.battle-stage-shockwave-play {
  animation: battle-stage-shockwave-anim 0.45s ease-out;
}
@keyframes battle-stage-shockwave-anim {
  0% {
    opacity: 0.9;
    transform: scale(0.2);
  }
  100% {
    opacity: 0;
    transform: scale(9);
  }
}

/* UI/UX強化(衝突演出大幅強化・ユーザー確定仕様3番「攻撃側カードそのものが
   防御側カードへ高速で突っ込む」): 移動距離(--charge-dx/--charge-dy)は
   js/ui/battle-stage.jsのplayChargeCollision()が攻撃側/防御側カードの実際の
   画面上の位置から都度計算する(レイアウト・画面サイズに関わらず必ず相手
   カードの方向・距離だけ動く)。開始・終了の見た目(scale)は、このカードが
   既に持っている「登場済み」の見た目(通常カードscale(1)/本体カード
   scale(1.08))と一致させ、アニメーション終了時に不自然なジャンプが出ない
   ようにしてある(--charge-scaleはJS側で本体カードかどうかに応じて設定)。 */
@keyframes battle-stage-charge-anim {
  0% {
    transform: translate3d(0, 0, 0) scale(var(--charge-scale, 1));
  }
  40% {
    transform: translate3d(var(--charge-dx, 0px), var(--charge-dy, 0px), 0) scale(var(--charge-scale, 1));
  }
  100% {
    transform: translate3d(0, 0, 0) scale(var(--charge-scale, 1));
  }
}
.battle-stage-minicard.battle-stage-minicard-charge-play {
  animation: battle-stage-charge-anim 0.38s cubic-bezier(0.3, 0, 0.2, 1);
  z-index: 20;
}
/* DAMAGE 0(BLOCK/NO DAMAGE)の場合は、同じ突進だが距離を短くし(JS側で
   --charge-dx/dyの値自体を小さく計算する)、「防御側の手前で止められた」
   ことが分かるようにする(ユーザー確定仕様3番)。 */
.battle-stage-minicard.battle-stage-minicard-charge-blocked-play {
  animation: battle-stage-charge-anim 0.26s cubic-bezier(0.3, 0, 0.2, 1);
  z-index: 20;
}

/* UI/UX強化(衝突演出大幅強化・ユーザー確定仕様3番「防御側カードを少し
   後方へ弾く」): 攻撃側カードが到達した瞬間、防御側カードだけ攻撃の向きへ
   さらに一瞬だけ押し出されてから戻る。距離(--knockback-dx/dy)もカードの
   実位置から計算する。 */
@keyframes battle-stage-knockback-anim {
  0% {
    transform: translate3d(0, 0, 0) scale(var(--knockback-scale, 1));
  }
  25% {
    transform: translate3d(var(--knockback-dx, 0px), var(--knockback-dy, 0px), 0) scale(var(--knockback-scale, 1));
  }
  100% {
    transform: translate3d(0, 0, 0) scale(var(--knockback-scale, 1));
  }
}
.battle-stage-minicard.battle-stage-minicard-knockback-play {
  animation: battle-stage-knockback-anim 0.32s ease-out;
}

.battle-stage-summary-damage-value.battle-stage-damage-pop {
  animation: battle-stage-damage-pop-anim 0.35s ease-out;
}
@keyframes battle-stage-damage-pop-anim {
  0% {
    transform: scale(1);
  }
  35% {
    transform: scale(1.35);
  }
  100% {
    transform: scale(1);
  }
}
/* UI/UX強化(衝突演出大幅強化・ユーザー確定仕様3番「DAMAGE数値を大きく拡大」):
   従来のdamage-popより明確に大きく拡大する専用バリエーション。DAMAGE>0の
   衝突が実際に起きた瞬間にだけ使う(js/ui/battle-stage.js参照)。 */
.battle-stage-summary-damage-value.battle-stage-damage-pop-big {
  animation: battle-stage-damage-pop-big-anim 0.5s cubic-bezier(0.3, 0, 0.3, 1.4);
}
@keyframes battle-stage-damage-pop-big-anim {
  0% {
    transform: scale(1);
  }
  45% {
    transform: scale(2.1);
    color: var(--log-damage, #ff6b6b);
  }
  100% {
    transform: scale(1);
  }
}

/* DAMAGE 0の場合は強い衝突演出の代わりに「防がれた」ことが分かる表示にする
   (ユーザー確定仕様6番)。 */
.battle-stage-block-label {
  margin-top: 6px;
  text-align: center;
  font-size: clamp(16px, 2.4vw, 24px);
  font-weight: 900;
  letter-spacing: 0.08em;
  color: var(--battle-stage-block-color);
  opacity: 0;
  transition: opacity 0.25s ease;
}
.battle-stage-block-label.battle-stage-block-label-show {
  opacity: 1;
}

/* COUNTERによってダメージが変化した場合の「変化前→変化後」の視覚化
   (ユーザー確定仕様4番)。通常(COUNTERで変化しない/COUNTERを使っていない)
   場合は常に空・非表示のまま。 */
.battle-stage-damage-before {
  font-size: clamp(14px, 2vw, 18px);
  font-weight: 700;
  color: var(--text-secondary, #aab2c0);
  text-decoration: line-through;
  min-height: 1.2em;
  opacity: 0;
  transition: opacity 0.25s ease;
}
.battle-stage-damage-before.battle-stage-damage-before-show {
  opacity: 1;
}

/* ============================================================
   実プレイ改善: 縦幅の狭いデスクトップ環境(ノートPC等)向けの追加調整。
   文字やカード内容が読めなくなるレベルまでは縮小しない範囲で、
   1920×1080・ブラウザ100%より縦が狭い場合にさらに詰める。
   ============================================================ */
@media (max-height: 820px) {
  .app-header {
    padding: 4px 20px;
  }
  #game-root {
    gap: 4px;
    padding: 4px 20px 8px;
  }
  .player-area {
    padding: 4px 14px;
  }
  .hand {
    min-height: 70px;
  }
  .card {
    min-height: 68px;
  }
  .battle-log {
    height: clamp(42px, 6vh, 75px);
  }
  /* プレイテスト追加指摘1番(2026-09-03): PLAYER手札カードの外寸統一(#player-hand .card
     を種類問わず固定112×128pxへ)に伴い、このカードmin-height:68px(旧・可変高さ時代の
     下限値)は#player-hand .cardの固定height指定(詳細度が高いため既に無効)には効かないが、
     読みやすさのため数値自体は据え置く。かわりに.battle-logの高さ(上のclamp)を
     70/120→56/100pxへ縮め、確保した約20〜36px分の余白を、カード統一で増えた
     手札の実高さ(旧の可変高さ時代の平均よりも大きくなるケースがある)の吸収に充てる
     (詳細は次のコメント参照)。 */
  /* プレイテスト指摘5番: 追加解像度確認(1366×768等、縦幅の狭い環境)向けに、
     横並びPLAYER手札の縦方向の余白も詰める。縦スクロールが発生しないことを
     最優先し、既存の.hand(min-height:78px)に対する増分を最小限に抑える。
     (実プレイ回帰: デバッグモード表示中(#debug-banner+#debug-panel、
     合計約114px)と10枚手札+BUFF/DEBUFF表示、かつ素材屋系などテキストが
     長いカードが混在する負荷の高い条件で、1366×768のdocHeightが768pxを
     わずかに超えていたため、この余白を切り詰めたうえで、上の#player-hand
     本体側に追加したmax-height(はみ出す場合は#player-hand内部だけで
     縦スクロールし、ページ全体の縦スクロールにはならない)と合わせて
     対応した。) */
  #player-hand {
    min-height: 78px;
    max-height: 138px;
    padding: 2px 8px 1px;
  }
  #player-hand.hand-fan-empty {
    min-height: 64px;
    padding: 8px;
  }
}

/* ============================================================
   第3弾: 基本バトル演出完成
   (2)`-X`ダメージ数値表示 / (3)UR専用の電気・パーティクル演出 /
   (1)ドロー演出 / (5)(6)巨大K.O./VICTORY・DEFEAT・DRAW演出 /
   (7)正式なリザルト表示
   ============================================================ */

/* --- (2) `-X`ダメージ数値表示 ---
   .hp-row/.battle-stage-hp-row(いずれも本ファイル内でposition:relative済み)を
   基準に、HP表示の少し上へ絶対配置で重ねる。ダメージSEのlight/medium/heavy
   判定(js/ui/battle-stage.jsのclassifyDamageTier、soundManager.pickDamageTierEvent
   を流用)と同じ区分をそのまま大きさ・揺れ幅の段階分けに使っている。 */
.hp-damage-pop {
  position: absolute;
  left: 50%;
  top: -4px;
  transform: translate(-50%, 0) scale(0.6);
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  color: var(--log-damage, #ff6b6b);
  text-shadow: 0 0 10px rgba(255, 60, 60, 0.7), 0 0 2px rgba(0, 0, 0, 0.8);
  opacity: 0;
  pointer-events: none;
  white-space: nowrap;
  z-index: 5;
}
.hp-damage-pop-stage {
  top: -20px;
  z-index: 6;
}
.hp-damage-pop-light {
  font-size: clamp(15px, 1.8vw, 19px);
}
.hp-damage-pop-medium {
  font-size: clamp(19px, 2.4vw, 25px);
}
.hp-damage-pop-heavy {
  font-size: clamp(25px, 3.2vw, 33px);
}
.hp-damage-pop-play.hp-damage-pop-light {
  animation: hp-damage-pop-anim 0.9s ease-out;
}
.hp-damage-pop-play.hp-damage-pop-medium {
  animation: hp-damage-pop-anim-strong 0.9s ease-out;
}
.hp-damage-pop-play.hp-damage-pop-heavy {
  animation: hp-damage-pop-anim-heavy 0.9s ease-out;
}
@keyframes hp-damage-pop-anim {
  0% {
    opacity: 0;
    transform: translate(-50%, 6px) scale(0.7);
  }
  15% {
    opacity: 1;
    transform: translate(-50%, -4px) scale(1);
  }
  70% {
    opacity: 1;
    transform: translate(-50%, -12px) scale(1);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -24px) scale(0.95);
  }
}
@keyframes hp-damage-pop-anim-strong {
  0% {
    opacity: 0;
    transform: translate(-50%, 8px) scale(0.6);
  }
  15% {
    opacity: 1;
    transform: translate(-50%, -6px) scale(1.15);
  }
  70% {
    opacity: 1;
    transform: translate(-50%, -16px) scale(1.05);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -30px) scale(1);
  }
}
@keyframes hp-damage-pop-anim-heavy {
  0%,
  8% {
    opacity: 0;
    transform: translate(-50%, 10px) scale(0.5);
  }
  20% {
    opacity: 1;
    transform: translate(-53%, -8px) scale(1.35);
  }
  30% {
    transform: translate(-47%, -10px) scale(1.3);
  }
  40% {
    transform: translate(-51%, -12px) scale(1.25);
  }
  75% {
    opacity: 1;
    transform: translate(-50%, -20px) scale(1.15);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -36px) scale(1.05);
  }
}

/* --- (3) UR専用の電気・パーティクル演出 ---
   js/ui/battle-stage.jsのspawnUrParticles()が.battle-stage-arenaの子要素として
   都度座標計算のうえ追加する(既存のplayShockwave()と同じ考え方)。カード自身の
   DOM・レイアウトには一切触れない、独立したオーバーレイ層。 */
.battle-stage-minicard-ur-fx {
  position: absolute;
  pointer-events: none;
  z-index: 5;
  overflow: visible;
}
.battle-stage-minicard-ur-arc {
  position: absolute;
  inset: 0;
  border-radius: 14px;
  padding: 3px;
  background: conic-gradient(
    from 0deg,
    rgba(255, 215, 90, 0.95),
    rgba(255, 215, 90, 0) 25%,
    rgba(255, 215, 90, 0.95) 55%,
    rgba(255, 215, 90, 0) 85%
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  opacity: 0;
  animation: battle-stage-ur-arc-spin 0.62s linear;
}
@keyframes battle-stage-ur-arc-spin {
  0% {
    opacity: 0;
    transform: rotate(0deg);
  }
  15% {
    opacity: 1;
  }
  85% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: rotate(280deg);
  }
}
.battle-stage-minicard-ur-spark {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 5px;
  height: 5px;
  margin: -2.5px 0 0 -2.5px;
  border-radius: 50%;
  background: #ffe27a;
  box-shadow: 0 0 6px 2px rgba(255, 226, 122, 0.9);
  opacity: 0;
  transform: rotate(var(--ur-spark-angle, 0deg)) translateX(0);
  animation: battle-stage-ur-spark-fly 0.5s ease-out;
  animation-delay: var(--ur-spark-delay, 0ms);
  animation-fill-mode: both;
}
@keyframes battle-stage-ur-spark-fly {
  0% {
    opacity: 0;
    transform: rotate(var(--ur-spark-angle, 0deg)) translateX(6px) scale(0.6);
  }
  25% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: rotate(var(--ur-spark-angle, 0deg)) translateX(48px) scale(1);
  }
}

/* --- (1) ドロー演出 ---
   js/ui/draw-presentation.js参照。battle-stage-overlay/event-splash-overlayとは
   独立した専用レイヤー(常に両者より前面)。表示専用・pointer-events:noneで
   既存の手札操作を妨害しない。 */
.draw-presentation-layer {
  position: fixed;
  inset: 0;
  z-index: 26; /* event-splash-overlay(25)より上、常に見える */
  pointer-events: none;
  overflow: hidden;
}
/* 中央バトル演出カード拡大(このセッションで追加): ドロー演出の飛翔カードも
   従来(56×78px)より大きく見せる。既存の移動アニメーション・タイミング・
   段階的表示(1枚ずつ)・CPU非公開の仕組み自体は一切変更せず、見た目の
   サイズだけを引き上げる。position:absolute + margin-left/top(-半分)で
   中心基準に配置する既存方式を踏襲するため、幅・高さの半分の値を
   そのままマイナスmarginに指定している。 */
.draw-fly-card {
  position: absolute;
  width: 108px;
  height: 144px;
  margin-left: -54px;
  margin-top: -72px;
  transform: translate3d(0, 0, 0) scale(0.7);
  opacity: 0;
  transition: transform 0.38s cubic-bezier(0.2, 0.6, 0.3, 1), opacity 0.38s ease-out;
  perspective: 400px;
}
.draw-fly-card-play {
  opacity: 1;
  transform: translate3d(var(--draw-dx, 0px), var(--draw-dy, 0px), 0) scale(1);
}
.draw-fly-card-face {
  position: absolute;
  inset: 0;
  border-radius: 8px;
  border: 2px solid var(--border);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  backface-visibility: hidden;
  transition: transform 0.2s ease, opacity 0.2s ease;
  font-size: 10px;
  text-align: center;
  padding: 4px;
  overflow: hidden;
  box-sizing: border-box;
}
.draw-fly-card-back {
  background: linear-gradient(135deg, #2a2f3d, #1a1d26);
  color: var(--text-dim);
}
.draw-fly-card-back::after {
  content: "?";
  font-size: 20px;
  font-weight: 900;
  opacity: 0.5;
}
.draw-fly-card-front {
  background: var(--panel);
  color: var(--text-primary, #f2f4f8);
  transform: rotateY(180deg);
  opacity: 0;
}
/* PLAYER側のみ付与されるクラス(CPU側は最後まで裏面のまま、既存の
   「CPU手札の内容を公開しない」方針を守るため付与されない)。 */
.draw-fly-card-revealed .draw-fly-card-back {
  transform: rotateY(180deg);
  opacity: 0;
}
.draw-fly-card-revealed .draw-fly-card-front {
  transform: rotateY(0deg);
  opacity: 1;
}
.draw-fly-card-rarity {
  font-size: 9px;
  font-weight: 900;
  color: var(--battle-stage-attacker-accent, #ff6b6b);
}
.draw-fly-card-name {
  font-size: 9px;
  line-height: 1.15;
  word-break: break-all;
}

/* 通常カード画像146枚組み込み(ユーザー確定仕様セクション9): カードドロー演出の
   小さな飛翔カード(56×78px)にも画像を表示する。既存のPLAYER手札
   (.card-has-image)と同じ考え方で、画像が用意されている場合は画像を面全体に
   contain表示し、テキスト(レアリティ・名前)は隠す代わりに小さな角バッジとして
   画像の上に残す(情報自体は失わない)。画像読み込み失敗時はimg要素ごと
   JS側で削除され、このクラスは付いたままでも中身が無いだけなので、下の
   .draw-fly-card-rarity/nameは通常どおり表示され続ける。 */
.draw-fly-card-front:has(.draw-fly-card-image) {
  padding: 2px;
}
.draw-fly-card-image {
  position: absolute;
  inset: 2px;
  width: calc(100% - 4px);
  height: calc(100% - 4px);
  object-fit: contain;
  border-radius: 5px;
}
.draw-fly-card-front:has(.draw-fly-card-image) .draw-fly-card-rarity {
  position: absolute;
  top: 1px;
  left: 2px;
  z-index: 1;
  text-shadow: 0 0 3px #000, 0 0 3px #000;
}
.draw-fly-card-front:has(.draw-fly-card-image) .draw-fly-card-name {
  display: none;
}

/* --- (5)(6) 巨大K.O.演出 / VICTORY・DEFEAT・DRAW演出 ---
   js/ui/match-outcome.js参照。battle-stage-overlay(24)/event-splash-overlay(25)/
   draw-presentation-layer(26)よりさらに前面(常に一番上)に表示する、独立した
   オーバーレイ。CITY EVENT/DEBUFF等battle-stageを経由しない経路でのKOでも
   正しく表示できる。 */
.match-outcome-overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.match-outcome-overlay.hidden {
  display: none;
}
.match-outcome-text {
  font-size: clamp(48px, 10vw, 120px);
  font-weight: 900;
  letter-spacing: 0.08em;
  opacity: 0;
  transform: scale(0.4);
  text-shadow: 0 0 10px rgba(0, 0, 0, 0.9);
}
.match-outcome-text.match-outcome-play {
  animation: match-outcome-pop 0.9s cubic-bezier(0.3, 0, 0.2, 1.3);
}
@keyframes match-outcome-pop {
  0% {
    opacity: 0;
    transform: scale(0.3);
  }
  20% {
    opacity: 1;
    transform: scale(1.2);
  }
  35% {
    transform: scale(1);
  }
  80% {
    opacity: 1;
    transform: scale(1);
  }
  100% {
    opacity: 0;
    transform: scale(1.05);
  }
}
.match-outcome-ko {
  color: #ffdd55;
  text-shadow: 0 0 60px rgba(255, 221, 85, 0.85), 0 0 12px rgba(0, 0, 0, 0.9);
}
.match-outcome-victory {
  color: #4fd18a;
  text-shadow: 0 0 60px rgba(79, 209, 138, 0.85), 0 0 12px rgba(0, 0, 0, 0.9);
}
.match-outcome-defeat {
  color: #ff5a5a;
  text-shadow: 0 0 60px rgba(255, 90, 90, 0.85), 0 0 12px rgba(0, 0, 0, 0.9);
}
.match-outcome-draw {
  color: #aab2c0;
  text-shadow: 0 0 50px rgba(170, 178, 192, 0.75), 0 0 12px rgba(0, 0, 0, 0.9);
}

/* --- (7) 正式なリザルト表示 --- */
.result-details {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 8px 0 16px;
  font-size: 14px;
  color: var(--text-secondary, #aab2c0);
}
.result-detail-line {
  margin: 0;
}

/* ============================================================
   第6弾: 最終設定画面(MASTER/BGM/SE音量・3種MUTE・演出速度)。
   既存の黒×ゴールド×ネオン基調(--rarity-ur=ゴールド系/--rarity-secret=
   ネオン紫/--accentのネオン水色)に合わせ、汎用の白背景フォームUIには
   しない。開閉はjs/ui/settings-panel.jsが担当。
   ============================================================ */

.settings-open-btn {
  border: 1px solid rgba(255, 179, 0, 0.55);
  color: #ffd479;
}
.settings-open-btn:hover {
  border-color: var(--rarity-ur);
  box-shadow: 0 0 10px rgba(255, 179, 0, 0.35);
}

.settings-overlay {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(5, 4, 2, 0);
  backdrop-filter: blur(0px);
  transition: background 0.2s ease, backdrop-filter 0.2s ease;
  padding: 24px;
}

.settings-overlay.hidden {
  display: none;
}

.settings-overlay.settings-overlay-visible {
  background: rgba(5, 4, 2, 0.72);
  backdrop-filter: blur(3px);
}

.settings-dialog {
  width: min(560px, 100%);
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  background: linear-gradient(165deg, #17130c 0%, #1b1610 40%, #120f0a 100%);
  border: 1px solid rgba(255, 179, 0, 0.45);
  border-radius: 14px;
  box-shadow: 0 0 0 1px rgba(224, 64, 251, 0.12), 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 40px rgba(255, 179, 0, 0.12);
  padding: 20px 24px 18px;
  color: var(--text);
  transform: translateY(14px) scale(0.97);
  opacity: 0;
  transition: transform 0.2s ease, opacity 0.2s ease;
}

.settings-overlay-visible .settings-dialog {
  transform: translateY(0) scale(1);
  opacity: 1;
}

.settings-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid rgba(255, 179, 0, 0.25);
  padding-bottom: 10px;
  margin-bottom: 14px;
}

.settings-title {
  margin: 0;
  font-size: 18px;
  letter-spacing: 0.08em;
  background: linear-gradient(90deg, #ffd479, #ffb300 55%, #e040fb);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.settings-close-btn {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-dim);
  width: 30px;
  height: 30px;
  border-radius: 8px;
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
}
.settings-close-btn:hover,
.settings-close-btn:focus-visible {
  border-color: var(--rarity-ur);
  color: #ffd479;
  outline: none;
}

.settings-section {
  margin-bottom: 18px;
}

.settings-section-title {
  margin: 0 0 10px;
  font-size: 12px;
  letter-spacing: 0.08em;
  color: var(--rarity-ur);
  text-transform: uppercase;
}

.settings-row {
  display: grid;
  grid-template-columns: 68px 1fr 34px auto;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
}

.settings-label {
  font-size: 12px;
  font-weight: 700;
  color: var(--text-dim);
  letter-spacing: 0.04em;
}

.settings-value {
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--text);
  text-align: right;
}

.settings-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 4px;
  border-radius: 2px;
  background: linear-gradient(90deg, rgba(255, 179, 0, 0.55), rgba(224, 64, 251, 0.4));
  outline: none;
  cursor: pointer;
}
.settings-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #ffd479;
  border: 2px solid #120f0a;
  box-shadow: 0 0 6px rgba(255, 179, 0, 0.7);
  cursor: pointer;
}
.settings-slider::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #ffd479;
  border: 2px solid #120f0a;
  box-shadow: 0 0 6px rgba(255, 179, 0, 0.7);
  cursor: pointer;
}
.settings-slider:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 3px rgba(79, 195, 247, 0.55);
}

.settings-mute-btn {
  background: var(--panel-alt);
  color: var(--text-dim);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.03em;
  padding: 5px 6px;
  cursor: pointer;
}
.settings-mute-btn-active {
  background: rgba(239, 83, 80, 0.18);
  border-color: var(--danger);
  color: #ff8a80;
}
.settings-mute-btn:focus-visible {
  outline: 2px dashed #ffffff;
  outline-offset: 2px;
}

.settings-speed-group {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.settings-speed-btn {
  flex: 1 1 auto;
  min-width: 110px;
  background: var(--panel-alt);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}
/* 状態を色だけに依存させない: 選択中は枠線・背景に加えて先頭に✓を付与する。 */
.settings-speed-btn-active {
  border-color: var(--rarity-ur);
  background: rgba(255, 179, 0, 0.14);
  color: #ffd479;
}
.settings-speed-btn-active::before {
  content: "✓ ";
}
.settings-speed-btn:focus-visible {
  outline: 2px dashed #ffffff;
  outline-offset: 2px;
}

.settings-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  border-top: 1px solid rgba(255, 179, 0, 0.2);
  padding-top: 14px;
  margin-top: 4px;
  flex-wrap: wrap;
}

.settings-reset-confirm {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1 100%;
  background: rgba(239, 83, 80, 0.1);
  border: 1px solid rgba(239, 83, 80, 0.4);
  border-radius: 8px;
  padding: 8px 10px;
  font-size: 12px;
}
.settings-reset-confirm.hidden {
  display: none;
}
#settings-reset-btn.hidden {
  display: none;
}
.settings-reset-confirm-text {
  flex: 1;
  color: var(--text);
}

@media (max-height: 820px) {
  .settings-dialog {
    padding: 14px 18px 14px;
    max-height: calc(100vh - 24px);
  }
  .settings-overlay {
    padding: 12px;
  }
}

/* プレイテスト追加指摘2番(2026-09-03): CITY EVENT手動発動(テストモード専用)。
   既存の.settings-row(音量スライダー専用の4列グリッド)は流用せず、
   セレクト+ボタンに適した専用レイアウトを新設する(既存行への影響なし)。 */
#settings-city-event-section.hidden {
  display: none;
}

.settings-city-event-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.settings-city-event-select {
  flex: 1 1 240px;
  min-width: 180px;
  background: var(--panel-alt);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 8px;
  font-size: 12px;
}

.settings-city-event-select:disabled,
#settings-city-event-trigger-btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.settings-city-event-pending {
  margin-top: 8px;
  font-size: 12px;
  color: #ffd479;
  background: rgba(255, 179, 0, 0.12);
  border: 1px solid rgba(255, 179, 0, 0.4);
  border-radius: 6px;
  padding: 6px 8px;
}

.settings-city-event-pending.hidden {
  display: none;
}

/* メイン画面側の予約中インジケーター(設定画面を閉じても見える常設バナー)。
   デザインは既存.debug-bannerと対になる配色(こちらは予約中=進行中を示す
   琥珀色の単色背景。斜め縞の.debug-bannerとは意図的に見分けが付くようにする)。 */
.city-event-manual-pending-banner {
  background: #3a2a08;
  border-top: 1px solid rgba(255, 179, 0, 0.5);
  border-bottom: 1px solid rgba(255, 179, 0, 0.5);
  color: #ffd479;
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.02em;
  text-align: center;
  padding: 6px 12px;
}

.city-event-manual-pending-banner.hidden {
  display: none;
}

/* ============================================================
   左右VSバトル配置(このセッションで追加): 通常時の「CPU上・PLAYER下」の
   画面構成(.battle-stage-arena/[data-attacker-side]による既存のorder制御)は
   一切変更せず、js/ui/battle-stage.jsが既存のタイミングで設定している
   `#battle-stage-overlay`の`data-mode="battle"`(攻撃・防御演出)属性だけを
   目印にCSSだけで左右展開レイアウトへ切り替える(単独アクションカード演出
   `data-mode="ability"`・歪み対応`data-mode="ability"`は対象外のため、
   EMS/FOOD/MECH/MATERIAL/CIV/BUFF/DEBUFF/独立SECRETは従来どおり中央1枚表示の
   ままになる)。JS側の変更は、`#battle-stage-totals`をATK/DEF/ability用の
   3つのspanへ分割し、対応する6箇所の書き込み先を変更した点のみ(数値・
   更新順序・タイミング自体は不変)。攻撃側=常に左・防御側=常に右という基準は、
   既存の`#battle-stage-attacker`/`#battle-stage-defender`という要素自体が
   元々「攻撃側/防御側」で名付けられている(CPU/PLAYERどちらが攻撃側かは
   `renderCardGroup`呼び出し側が既に解決済み)ため、この2つのIDへ直接
   position:absoluteで左右を割り当てるだけで、ターン入れ替わり時も自動的に
   正しい側へ表示される。
   ============================================================ */

/* 攻撃側(常に左)・防御側(常に右)を、既存のflex column配置から独立させて
   絶対配置にする。カードの登場アニメーション自体
   (.battle-stage-minicard[data-owner-side]のtranslateY/scale)は変更しない
   ため、「PLAYERは下から・CPUは上からカードが登場する」動きは維持したまま、
   その着地先(コンテナの左右位置)だけが変わる形になる。 */
.battle-stage-overlay[data-mode="battle"] .battle-stage-attacker,
.battle-stage-overlay[data-mode="battle"] .battle-stage-defender {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: auto;
  max-width: 44vw;
  margin: 0;
}
.battle-stage-overlay[data-mode="battle"] .battle-stage-attacker {
  left: 2vw;
  justify-content: flex-start;
}
.battle-stage-overlay[data-mode="battle"] .battle-stage-defender {
  right: 2vw;
  justify-content: flex-end;
}

/* COUNTERパネルは防御側(右)の陣地内、カード群と重ならないよう下寄せで配置する
   (COUNTERカード自体のサイズは既存どおりwidth:170px・小さめ固定のまま変更しない)。 */
.battle-stage-overlay[data-mode="battle"] .battle-stage-counter {
  position: absolute;
  right: 2vw;
  bottom: 3%;
  top: auto;
  transform: none;
  max-width: 44vw;
  z-index: 4;
}

/* 左右各1枚/2枚/3枚以上で、通常時(中央1枚演出)より大きくカード画像を
   表示する。既存のカード枚数カウント手法(:has()による兄弟要素カウント)を
   踏襲しつつ、`[data-mode="battle"]`配下限定・より高い詳細度で上書きする
   ことでRound2で追加した中央演出用の基準値より優先される。 */
.battle-stage-overlay[data-mode="battle"] .battle-stage-side:not(:has(.battle-stage-minicard ~ .battle-stage-minicard)) {
  --stage-card-img-h: clamp(220px, 52vh, 520px);
}
.battle-stage-overlay[data-mode="battle"] .battle-stage-side:has(.battle-stage-minicard ~ .battle-stage-minicard):not(:has(.battle-stage-minicard ~ .battle-stage-minicard ~ .battle-stage-minicard)) {
  --stage-card-img-h: clamp(180px, 42vh, 400px);
}
.battle-stage-overlay[data-mode="battle"] .battle-stage-side:has(.battle-stage-minicard ~ .battle-stage-minicard ~ .battle-stage-minicard) {
  --stage-card-img-h: clamp(130px, 30vh, 300px);
}

/* Round2で設定した.battle-stage-minicard-imageのmax-height(46vh)は、
   上下に攻撃側/防御側を積んでいた旧レイアウト向けの上限だった。左右配置では
   縦方向によりゆとりがあるため、左右配置時だけ上限を引き上げる
   (--stage-card-img-h自体は上記の52vh/42vh/30vhで既に画面に収まる範囲に
   制限されているため、ここでの引き上げが新たなはみ出しを生むことはない)。 */
.battle-stage-overlay[data-mode="battle"] .battle-stage-minicard-image {
  max-height: 64vh;
  max-width: 38vw;
}

/* 2枚(本体+PLUS等)は少し重ねて扇状に、3枚以上はしっかり重ねて表示面積を
   確保する(単純な均等縮小にしない)。重ねても名前・レアリティ・数値は
   画像の下にそのまま表示され続けるため確認できる。後から登場したカードほど
   DOM順で後ろにあるため、既存のz-index追加無しで自然に上に重なる。 */
.battle-stage-overlay[data-mode="battle"] .battle-stage-side:has(.battle-stage-minicard ~ .battle-stage-minicard):not(:has(.battle-stage-minicard ~ .battle-stage-minicard ~ .battle-stage-minicard)) .battle-stage-minicard + .battle-stage-minicard {
  margin-left: -60px;
}
.battle-stage-overlay[data-mode="battle"] .battle-stage-side:has(.battle-stage-minicard ~ .battle-stage-minicard ~ .battle-stage-minicard) .battle-stage-minicard + .battle-stage-minicard {
  margin-left: -50px;
}

/* 常設の「ATTACK」「DEFENSE」ラベル。攻撃側/防御側コンテナ自体は
   renderCardGroup()がinnerHTMLで再構築するため、ラベルはそれとは独立した
   兄弟要素にして常設し、data-mode="battle"のときだけCSSで表示する。 */
.battle-stage-side-label {
  display: none;
  position: absolute;
  top: 4%;
  font-size: clamp(13px, 1.8vw, 20px);
  font-weight: 900;
  letter-spacing: 0.14em;
  text-shadow: 0 0 10px rgba(0, 0, 0, 0.6);
  pointer-events: none;
  z-index: 3;
}
.battle-stage-side-label-attacker {
  left: 3%;
  color: var(--battle-stage-attacker-accent);
}
.battle-stage-side-label-defender {
  right: 3%;
  color: var(--battle-stage-defender-accent);
}
.battle-stage-overlay[data-mode="battle"] .battle-stage-side-label {
  display: block;
}

/* 中央の常設「VS」表示。TOTAL ATK/TOTAL DEFが確定していく様子と同時に
   中央へ見えるようにするため、battle-stage-totals内のATK/DEFが左右の
   端(space-between)へ移動している間、VSは常にアリーナ中央に固定表示する。
   最終結果パネル(#battle-stage-summary、自身にも"VS"を含む)や反射ダメージ
   表示(#battle-stage-damage)が中央に出ているときだけ、重複を避けるため
   一時的に透明にする(いずれも既存の表示ロジック自体は変更しない)。 */
.battle-stage-vs-label {
  display: none;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: clamp(20px, 3.2vw, 38px);
  font-weight: 900;
  letter-spacing: 0.12em;
  color: var(--text-secondary, #aab2c0);
  text-shadow: 0 0 14px rgba(0, 0, 0, 0.55);
  opacity: 1;
  transition: opacity 0.2s ease;
  pointer-events: none;
  z-index: 2;
}
.battle-stage-overlay[data-mode="battle"] .battle-stage-vs-label {
  display: block;
}
.battle-stage-overlay[data-mode="battle"] .battle-stage-arena:has(#battle-stage-summary:not(.hidden)) .battle-stage-vs-label,
.battle-stage-overlay[data-mode="battle"] .battle-stage-arena:has(#battle-stage-damage:not(:empty)) .battle-stage-vs-label {
  opacity: 0;
}

/* TOTAL ATK/TOTAL DEFを、それぞれ左(攻撃側)・右(防御側)のカード群近くへ
   個別配置する。js/ui/battle-stage.js側は#battle-stage-totals-atk/-defという
   2つのspanへ個別に書き込むだけで、更新タイミング・更新順序(PLUS・COUNTERの
   公開に合わせて更新される既存順序)自体は一切変更していない。
   追補修正: 当初はflexのspace-betweenで並べていたが、その位置が画面中央
   (=左右に拡大されたカード群の縦方向の中心)と重なり、カードが手前に
   描画されてTOTAL ATK/DEFの文字が隠れてしまっていた。ATTACK/DEFENSEラベルと
   同じ「カード群の上」の帯へ個別にposition:absoluteで配置することで、
   カードと重ならない位置へ確実に表示する。 */
.battle-stage-overlay[data-mode="battle"] .battle-stage-totals-atk,
.battle-stage-overlay[data-mode="battle"] .battle-stage-totals-def {
  position: absolute;
  top: 9%;
  display: inline-block;
  font-size: clamp(15px, 2.3vw, 25px);
  font-weight: 900;
  white-space: nowrap;
  text-shadow: 0 0 10px rgba(0, 0, 0, 0.6);
  z-index: 3;
}
.battle-stage-totals-atk,
.battle-stage-totals-def {
  display: none;
}
.battle-stage-overlay[data-mode="battle"] .battle-stage-totals-atk {
  left: 3%;
  color: var(--battle-stage-attacker-accent);
  text-align: left;
}
.battle-stage-overlay[data-mode="battle"] .battle-stage-totals-def {
  right: 3%;
  color: var(--battle-stage-defender-accent);
  text-align: right;
}
/* mode="ability"用(単独アクションカードの能力名/効果説明)テキストは
   専用spanのまま、battle時だけ非表示にする(ability時の見た目は既存のまま
   変更しない)。 */
.battle-stage-overlay[data-mode="battle"] .battle-stage-totals-ability {
  display: none;
}
