/* ========== Webフォント（ローカル同梱） ==========
   Google Fonts の CDN から取得していたものを assets/fonts/ に同梱。
   Electron のオフライン起動でもフォントが落ちないようにするための措置。
   Fira Code は可変フォントのため、ウェイトごとのファイルは不要
   （1サブセット1ファイルで全ウェイトを賄う）。
   ライセンス: 同梱3書体とも SIL Open Font License 1.1（再配布可）。

   ⚠️ IBM Plex と Anton は無改変の TTF、Fira Code は Google Fonts が
      サブセット化した woff2（OFL 上は改変版）。Fira Code は Reserved Font Name を
      宣言していないので名前をそのまま使えるが、**IBM Plex は "Plex" を予約している**。
      サイズを削るために Plex をサブセット化すると、その名前を使えなくなる
      （docs/font-credits.md 参照）。

   ⚠️ Inter は撤去した。IBM Plex Sans JP がラテン文字も持つため、
      'IBM Plex Sans JP', 'Inter' の並びでは Inter の出番が無く、
      ゲーム内で使う文字のうち Plex が持たないのは絵文字1字だけ
      （それは OS 標準の絵文字フォントが受ける）。
      同梱物とプリロードだけが増えている状態だった。 */

/* IBM Plex Sans JP — 日本語本文・UI。
   同梱前は日本語フォントが無く、OS 標準（Windows なら Yu Gothic UI 等）へ
   フォールバックしていたため、プレイする環境で見た目が変わっていた。
   ラテン文字も持つ書体なので、和欧が同じ設計思想で揃う。
   ⚠️ 800/900 は存在しない（100〜700 のみ）。font-black 指定の箇所は
      日本語では 700 で描画される。 */
@font-face {
    font-family: 'IBM Plex Sans JP';
    font-style: normal;
    font-weight: 400;
    /* ローカル読み込みで待ち時間が無いため block。swap だと一瞬フォールバックが見える */
    font-display: block;
    src: url('../assets/fonts/IBMPlexSansJP-Regular.ttf') format('truetype');
}

@font-face {
    font-family: 'IBM Plex Sans JP';
    font-style: normal;
    font-weight: 700;
    font-display: block;
    src: url('../assets/fonts/IBMPlexSansJP-Bold.ttf') format('truetype');
}

/* Anton — ダメージ数値・スキル演出などの極太表示。
   以前は Arial Black / Impact を指定していたが、これらは Windows 標準フォントで
   同梱されておらず、配布先の環境によっては別書体に化けていた。 */
@font-face {
    font-family: 'Anton';
    font-style: normal;
    font-weight: 400;
    font-display: block;
    src: url('../assets/fonts/Anton-Regular.ttf') format('truetype');
}

/* Fira Code — latin */
@font-face {
    font-family: 'Fira Code';
    font-style: normal;
    font-weight: 300 700;
    font-display: swap;
    src: url('../assets/fonts/firacode-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Fira Code — latin-ext */
@font-face {
    font-family: 'Fira Code';
    font-style: normal;
    font-weight: 300 700;
    font-display: swap;
    src: url('../assets/fonts/firacode-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ==========================================================================
   配色トークン（唯一の色定義）
   ==========================================================================
   本作の色は必ずここを参照する。値を書いてよいのはこのブロックだけ。
     - Tailwind : tailwind.config.js が rgb(var(--c-*-rgb) / <alpha-value>) で参照
                  （index.html のクラス名は書き換えない）
     - CSS      : var(--c-*) で参照
     - Phaser   : js/utils/colors.js が getComputedStyle で読み、0x 数値へ変換

   ⚠️ 透明度つきの指定が index.html に126箇所あるため、値は必ず
      「R G B」のチャンネル形式で持つこと（#rrggbb では alpha 修飾子が壊れる）。

   --- なぜ配色を変えたか ---
   マップ背景は H44°（オリーブ〜カーキ）で暖色。対して従来の UI は
   H198°（シアン）／H222°（紺）で、色相が約155°離れたほぼ補色だった。
   UI がゲーム世界と色を1つも共有しておらず、「後から貼り付けた」印象の
   直接の原因になっていた。加えて #00ffff や #ff0000 のような純度100%の色、
   彩度80%以上が35色・延べ154回という一律の高彩度、
   同じ役割の色の重複（emerald と green など）が重なっていた。

   --- 案の切り替え方 ---
   現在は【案C: 現状の整理】。色相は従来のまま彩度を落とし、
   中立色をわずかに暖色側へ寄せてマップとの橋渡しを作っている。
   【案A: 軍用装備・琥珀】へ移行する場合は、**このブロックの値だけ**を
   差し替えれば済む。呼び出し側（HTML/CSS/JS）は一切変更不要。
   ========================================================================== */
:root {
    /* --- 中立色ランプ（下地・面・罫線・文字）---
       従来の slate は H215〜222°・S15〜47% で青みが強かった。
       H210° 前後を保ちつつ彩度を 8〜12% まで落とし、
       「青いパネル」ではなく「ガンメタルの筐体」として読ませる。 */
    --c-n-950-rgb: 20 23 26;
    --c-n-900-rgb: 27 31 35;
    --c-n-800-rgb: 38 43 48;
    --c-n-700-rgb: 52 58 64;
    --c-n-600-rgb: 74 81 87;
    --c-n-500-rgb: 107 115 122;
    --c-n-400-rgb: 143 151 158;
    --c-n-300-rgb: 180 187 193;
    --c-n-200-rgb: 210 215 219;
    --c-n-100-rgb: 230 234 237;

    /* --- アクセント（主役。従来の sky）---
       S93% の #38bdf8 から彩度を落としたスチールブルー。
       純度を下げることで、下地から浮かずに「発光」ではなく「塗装」に見える。 */
    --c-accent-950-rgb: 13 28 38;
    --c-accent-900-rgb: 20 48 63;
    --c-accent-500-rgb: 74 137 173;
    --c-accent-400-rgb: 91 157 196;
    --c-accent-300-rgb: 130 184 214;
    --c-accent-100-rgb: 207 227 238;

    /* --- サポート（味方・成功・TP。従来の emerald / green を統合）--- */
    --c-support-950-rgb: 13 31 20;
    --c-support-900-rgb: 19 48 33;
    --c-support-700-rgb: 47 107 73;
    --c-support-500-rgb: 79 146 104;
    --c-support-400-rgb: 111 170 126;
    --c-support-300-rgb: 150 195 161;

    /* --- 警告（従来の amber / yellow / orange を統合）---
       マップと同じ暖色帯（H43°）なので、彩度を落とすとよく馴染む。 */
    --c-warn-950-rgb: 33 26 12;
    --c-warn-900-rgb: 61 48 20;
    --c-warn-400-rgb: 201 169 97;

    /* --- 隊員の識別色 ---
       これは装飾ではなく機能色。マップ上の小さなラベルとHPバーで
       4人を瞬時に見分けられることが要件なので、他のトークンより彩度を残す。
       ただし従来（#ef4444 / #3b82f6 / #10b981 / #f97316 = 全て S84〜95%）は
       周囲から浮きすぎていたため、彩度を落として色相差で分けている。

       ⚠️ ストライク（赤）とトリガー（橙）は色相が近く、彩度を落とすと
          小さいラベルで見分けがつかなくなる。トリガーは橙ではなく黄寄りに
          振って距離を稼いでいる。値を変えるときは test/colors.test.mjs の
          「隊員の識別色は互いに区別できる」が通ることを確認すること。 */
    --c-unit-strike-rgb: 194 85 80;    /* アタッカー・赤 */
    --c-unit-iron-rgb: 96 138 184;     /* タンク・青 */
    --c-unit-ghost-rgb: 106 174 132;   /* ヒーラー・緑 */
    --c-unit-trigger-rgb: 207 160 73;  /* ハッキング支援・黄土 */

    /* --- 被弾フラッシュ ---
       少尉が被弾したときに画面全体を一瞬赤くする色。
       **意図的に他の赤（danger500 #b4534b）より鮮やかにしている。**

       配色トークン化のとき、この色だけは JS に数値3つで直書きされていたため
       （camera.flash がチャンネルを個別の引数で取る）検出から漏れていた。
       彩度を落として他に揃えることもできるが、被弾は最も伝わるべき情報なので、
       UI のパレットより目立つのが正しいと判断し、現状の色を仕様として登録する。 */
    --c-hit-flash-rgb: 255 50 50;

    /* --- 危険（従来の red / rose を統合）--- */
    --c-danger-300-rgb: 214 145 139;
    --c-danger-950-rgb: 36 16 16;
    --c-danger-600-rgb: 168 74 67;
    --c-danger-500-rgb: 180 83 75;
    --c-danger-400-rgb: 192 90 82;

    /* CSS から直接使う用（Tailwind 以外の記述向け） */
    --c-bg: rgb(var(--c-n-950-rgb));
    --c-surface: rgb(var(--c-n-900-rgb));
    --c-border: rgb(var(--c-n-700-rgb));
    --c-text: rgb(var(--c-n-200-rgb));
    --c-text-dim: rgb(var(--c-n-400-rgb));
    --c-accent: rgb(var(--c-accent-400-rgb));
    --c-support: rgb(var(--c-support-400-rgb));
    --c-warn: rgb(var(--c-warn-400-rgb));
    --c-danger: rgb(var(--c-danger-400-rgb));

    /* 左右のクラスタを画面の端からどれだけ離すか。
       以前ここには --hud-bar-h（上部バーの高さ）があり、
       左パネル・右パネル・ヴィネットがその値を基準に並んでいた。
       全幅のバーを撤去したので、基準は「端からの余白」になる。

       4箇所に同じ数値を直書きしていたのが --hud-bar-h 破綻の原因だったので、
       ここに集約する（js/utils/colors.js:9 のコメント参照）。 */
    --hud-edge-gap: 12px;

    /* ===== 戦闘HUDのレイアウト予算 =====
       オーバーレイHUDは上限を決めておかないと実装中に少しずつ育ち、
       結局いまと同じ「画面が埋まった状態」に戻る。
       「中央はマップ」という約束を、後から測れる形でここに置く。

       単位なしの数値で持つのは、テスト（node:test。CSSを正規表現で読む）から
       比較できるようにするため。使う側で calc(var(--x) * 1px) や
       calc(var(--x) * 1%) に変換する。 */

    /* 下限解像度。以前は 1120x630 だった。1120 は「上部バーに設置ボタン5個を
       並べると1098px要る」という当時のレイアウトからの逆算で、
       オーバーレイ化するとその根拠は消えるため決め直した。

       ⚠️ 重要: electron-main.js の minWidth / minHeight と対で管理する必要がある。
       Electron のウインドウ下限値 = --hud-min-w + 16px（枠幅）
                      = --hud-min-h + 39px（枠高さ）
       枠のオフセット（16, 39）は実測値で、OS やウインドウマネージャに依存する。
       コメントだけでは同期失敗が起きるため、test/hudLayout.test.mjs でテストする。
       値を変更する際は両ファイルと test を同時に更新すること。 */
    --hud-min-w: 1280;
    --hud-min-h: 720;

    /* 下辺にHUDが載ってよい割合（%）。会話ウィンドウが全幅の帯として実在する。
       上辺の予算（--hud-top-budget）は撤去した。全幅の上部バーを廃止して
       左右のクラスタへ分けたため、「上辺の高さの割合」は守る対象を失った。
       中央がマップとして空いていることは、左右の幅の合計で担保する
       （--hud-side-budget + --hud-right-budget、test/hudLayout.test.mjs）。 */
    --hud-bottom-budget: 15;

    /* 左辺の隊員パネルに許す幅（px）。上下の予算とは別枠。
       隊員カードは「読むもの」ではなく「押すもの」（クリックでNPCを選択する）
       なので、端にあるほうが当てやすい。 */
    --hud-side-budget: 240;

    /* 右上のコマンドクラスタに許す幅（px）。左辺の予算とは別枠。
       タイル5枚 = 80px × 5 + gap 5px × 4 = 420px。
       幅はラベルの文字幅から逆算している（.hud-tile のコメント参照）。

       ⚠️ これまで右上のクラスタには枠が無く、当時の --hud-top-budget（上辺10%。
          撤去済み。上の :root コメント参照）を実際には超えていた
          （上部バー75px + スキル行57px = 約140px）。
          test/hudLayout.test.mjs がトークンの足し算しか見ていなかったため
          気づけなかった。 */
    --hud-right-budget: 420;

    /* スキル発動カットイン（斜めの帯）が出てから消えるまでの長さ。単位なしのミリ秒。
       CSS のアニメーション時間と、JS 側でオーバーレイを隠すタイマーの両方がこれを見る。
       2箇所に別々の数値を書くと、片方だけ直したときに
       「帯は消えたのにオーバーレイが残る」「アニメの途中で消える」という壊れ方をする。

       帯は 230px の高さで画面中央を占めるため、長いほど戦況が見えなくなる。
       以前は 2000 だったが、戦闘状況の把握を優先して短くした。 */
    --eye-cutin-ms: 1200;

}

/* ===== 面の質感（共通部品） =====
 *
 * ブリーフィングで作ったが、**ブリーフィング専用にしない**。戦闘HUD・リザルトへ
 * そのまま広げられるよう、画面に依存しない名前と実装にしてある。
 *
 * ⚠️ **ノイズ（.surface-panel）は撤去した。** パネルは半透明で下にぼかした写真が
 *    あるため、粒を乗せる平らな面がそもそも存在しない。実際の背景を敷いて
 *    0〜0.50 まで並べたところ、濃くしても「粒」にはならず**写真が白く霞むだけ**
 *    だった（実機でも「ノイズがあることに気づかない」と指摘を受けた）。
 *    平らな暗い面の上で判定して 0.08 を選んだのが誤りで、判定する下地が
 *    実機と違っていた。効かないものを残すと、次に触る人が「効いているはず」と
 *    誤解するので消す。
 *
 * 質感が要るのは**カード**の方。ここは実際に平らな暗い矩形なので、
 * 浅いグラデーションと上端の明線が効く。
 */

/* ⚠️ 上端の内側に細い明線を入れると、同じ暗さのままでも面が起きて見える。
   ⚠️ この規則を効かせるには、host 側が background の**ショートハンドを使わない**こと。
      ショートハンドは background-image を none にリセットするため、
      順序に関わらずグラデーションが消える（.supply-card で実際に踏んだ）。 */
.surface-card {
    background-image: linear-gradient(
        to bottom,
        rgb(var(--c-n-800-rgb) / 0.35),
        rgb(var(--c-n-950-rgb) / 0.15)
    );
    box-shadow: inset 0 1px 0 rgb(var(--c-n-500-rgb) / 0.18);
}

body {
    margin: 0;
    background-color: rgb(var(--c-n-950-rgb));
    overflow: hidden;
    font-family: 'IBM Plex Sans JP', sans-serif;

    /* 文字をドラッグで選択できないようにする。
       ゲームはマウスのドラッグを操作に使うため、そのたびに周囲の文字が
       青く反転していた（ブリーフィングの本文、隊員カード、タイルのラベルなど）。
       選択できて得をする場面が無いので、画面全体で止める。 */
    -webkit-user-select: none;
    user-select: none;
}

/* ⚠️ 入力欄だけは例外。選択を止めると編集そのものが壊れる
   （カーソル移動・範囲選択・IMEの変換候補の置き換えは、すべて選択に依存する）。
   名前入力 #auth-player-name は日本語も受け付けるので、ここを外すと
   「変換はできるが確定できない」という気づきにくい壊れ方をする。
   test/uiConsistency.test.mjs がこの例外の存在を検査している。 */
input,
textarea,
[contenteditable] {
    -webkit-user-select: text;
    user-select: text;
}

canvas {
    display: block;
}

/* 16:9 ゲームラッパー */
#game-wrapper {
    position: relative;
    width: 100vw;
    height: 56.25vw;
    max-height: 100vh;
    max-width: 177.78vh;
    min-width: calc(var(--hud-min-w) * 1px);
    min-height: calc(var(--hud-min-h) * 1px);
    margin: auto;
    background-color: rgb(var(--c-n-950-rgb));
    overflow: hidden;
    box-shadow: 0 0 50px rgba(0, 0, 0, 0.8);
}

/* ========== HUDパネル共通 ========== */
.hud-panel {
    background: rgb(var(--c-n-950-rgb) / 0.85);
    backdrop-filter: blur(8px);
    border: 1px solid rgb(var(--c-accent-400-rgb) / 0.2);
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
}

/* 上部バー */
/* ========== 左サイドパネル ========== */
.squad-card {
    position: relative;
    background: transparent;
    backdrop-filter: blur(6px);
    padding: 12px 16px;
    min-width: 180px;
    transition: all 0.3s;
    z-index: 1;
}

.squad-card::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image: url('../assets/images/ui/slot_frame.png');
    background-size: 100% 100%;
    background-color: rgb(var(--c-n-900-rgb) / 0.7);
    opacity: 0.7;
    pointer-events: none;
    transition: all 0.3s;
    z-index: -1;
}

.squad-card.selected {
    text-shadow: 0 0 5px rgb(var(--c-warn-400-rgb) / 0.5);
}

.squad-card.selected::before {
    opacity: 1;
    filter: drop-shadow(0 0 8px rgb(var(--c-warn-400-rgb) / 0.6)) brightness(1.2);
}

.squad-card .hp-bar-bg {
    background: rgb(var(--c-n-800-rgb) / 0.8);
    border-radius: 3px;
    height: 6px;
    width: 100%;
}

.squad-card .hp-bar-fill {
    height: 6px;
    border-radius: 3px;
    transition: width 0.3s ease;
}

/* ========== 会話テキストボックス ========== */
.dialogue-container {
    animation: dialogue-slide-up 0.3s ease-out;
}

.dialogue-text-area {}

.dialogue-show {
    display: flex !important;
}

@keyframes dialogue-slide-up {
    from {
        transform: translateY(20px);
        opacity: 0;
    }

    to {
        transform: translateY(0);
        opacity: 1;
    }
}

/* ========== カットイン演出 ========== */
.cutin-content {
    position: relative;
}

#mission-cutin::before {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, transparent 30%, rgba(0, 0, 0, 0.8) 50%, transparent 70%);
    pointer-events: none;
}

/* ========== スタイルボタン ========== */
.cyber-button {
    background: linear-gradient(135deg, rgb(var(--c-accent-900-rgb)) 0%, rgb(var(--c-accent-900-rgb)) 100%);
    transition: all 0.2s ease;
    text-shadow: 0 0 8px rgba(255, 255, 255, 0.5);
}

.cyber-button:hover {
    filter: brightness(1.2);
    box-shadow: 0 0 15px rgb(var(--c-accent-400-rgb) / 0.5);
}

/* ========== ターミナル風フォームの補正 ========== */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
    -webkit-box-shadow: 0 0 0 30px rgb(var(--c-n-950-rgb)) inset !important;
    -webkit-text-fill-color: white !important;
    transition: background-color 5000s ease-in-out 0s;
}

/* ========== ハッキング選択肢ボタンの画像化 ========== */
#hack-options button {
    position: relative;
    background: transparent;
    padding: 16px 20px;
    border: none;
    color: rgb(var(--c-support-400-rgb));
    /* Tailwindの green-400 相当 */
    font-weight: bold;
    text-align: left;
    transition: all 0.3s;
    z-index: 1;
    cursor: pointer;
}

#hack-options button::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image: url('../assets/images/ui/slot_frame.png');
    background-size: 100% 100%;
    opacity: 0.6;
    filter: hue-rotate(-90deg);
    /* 青色の画像をターミナル風の緑色系に変換 */
    pointer-events: none;
    transition: all 0.3s;
    z-index: -1;
}

#hack-options button:hover {
    color: #fff;
    text-shadow: 0 0 8px rgb(var(--c-support-400-rgb) / 0.8);
}

#hack-options button:hover::before {
    opacity: 1;
    filter: hue-rotate(-90deg) brightness(1.5);
}

/* ========== カスタムスクロールバー ========== */
.custom-scrollbar::-webkit-scrollbar {
    width: 6px;
}

.custom-scrollbar::-webkit-scrollbar-track {
    background: rgb(var(--c-n-900-rgb) / 0.3);
    border-radius: 3px;
}

.custom-scrollbar::-webkit-scrollbar-thumb {
    background: rgb(var(--c-accent-400-rgb) / 0.3);
    border-radius: 3px;
}

.custom-scrollbar::-webkit-scrollbar-thumb:hover {
    background: rgb(var(--c-accent-400-rgb) / 0.6);
}

/* ========== 戦闘レポートのパネル ==========
   ⚠️ #game-wrapper は overflow:hidden で、DOM は canvas と一緒には縮まない
      （transform を持たないため）。パネルは min-h-[620px] の**最小**指定で上限が
      無いので、解説が伸びればいくらでも伸び、ウインドウごと切り落とされる
      （実機で2回指摘を受けた）。

   ⚠️ 最初は「上下で確定して要る高さ = 540px」と見積もり、
      max-height: calc(ウインドウ高 - 540px) を解説枠に置いた。これは外した。
      選択肢が3行になる問題では選択肢グリッドだけで 216px を使い、見積りの
      140px を大きく超えるため。**中身の高さは問題ごとに変わるので、固定値で
      予算を切ってはいけない。**

   そこで、計算をやめて実際の余りを取らせる:
     1. パネルはウインドウを超えない（max-height: 100%）
     2. 途中の縦積みが縮めるよう min-height: 0 を通す
        （flex の子は既定で min-height:auto。これが縮小を止める）
     3. あふれるのはクイズエリア。ヘッダとフッタ（「次の問題へ」）は
        常に見える位置に残る
   これで解説がどれだけ長くても、パネルが切れることはない。 */
#report-panel {
    max-height: 100%;
}

#report-body {
    min-height: 0;
}

#report-quiz-area {
    min-height: 0;
    overflow-y: auto;
    /* ⚠️ overflow-y だけ指定すると、overflow-x の `visible` は `auto` に計算される
       （CSS の仕様。片方が visible 以外なら visible は auto になる）。
       その結果、縦に溢れていなくてもマウスを乗せると横スクロールバーが出ていた。
       横は溢れない作りなので明示的に切る。 */
    overflow-x: hidden;
    /* ⚠️ justify-content: center のまま溢れると、はみ出した**上側**が
       スクロールで辿り着けなくなる（スクロール原点より上に出るため）。
       safe を付けると、収まるときだけ中央寄せ、溢れたら先頭寄せに切り替わる。 */
    justify-content: safe center;
    /* 読み終わって勢い余ったスクロールが背後の画面へ伝播しないようにする */
    overscroll-behavior: contain;
}

/* ⚠️ 設問枠と選択肢まで縮むと、選択肢の文字が潰れて読めなくなる。
      縮んでよいのは解説だけ……ではなく、ここでは誰も縮ませず
      クイズエリア自体をスクロールさせる。flex-shrink を殺しておかないと、
      高さが足りないときに全部が均等に潰れる。 */
#report-quiz-area > div {
    flex-shrink: 0;
}

/* ========== 戦闘レポート選択肢ボタン ========== */
.report-option-btn {
    position: relative;
    background: transparent;
    padding: 16px 20px;
    border: none;
    color: rgb(var(--c-accent-400-rgb));
    /* 青系（未回答時） */
    font-weight: bold;
    text-align: left;
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    transition: all 0.2s ease-in-out;
    z-index: 1;
    cursor: pointer;
}

.report-option-btn::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image: url('../assets/images/ui/slot_frame.png');
    background-size: 100% 100%;
    opacity: 0.6;
    pointer-events: none;
    transition: all 0.2s ease-in-out;
    z-index: -1;
}

.report-option-btn:hover {
    color: #fff;
    text-shadow: 0 0 8px rgb(var(--c-accent-400-rgb) / 0.8);
    transform: scale(1.02);
}

.report-option-btn:hover::before {
    opacity: 1;
    filter: brightness(1.3) drop-shadow(0 0 5px rgb(var(--c-accent-400-rgb) / 0.4));
}

/* クイズ回答後のカラー状態 */
.report-option-btn.correct {
    color: rgb(var(--c-support-400-rgb)) !important;
    text-shadow: 0 0 8px rgb(var(--c-support-400-rgb) / 0.8);
}

.report-option-btn.correct::before {
    opacity: 1;
    filter: hue-rotate(-90deg) brightness(1.3) drop-shadow(0 0 5px rgb(var(--c-support-400-rgb) / 0.5)) !important;
}

.report-option-btn.wrong {
    color: rgb(var(--c-danger-400-rgb)) !important;
    text-shadow: 0 0 8px rgb(var(--c-danger-400-rgb) / 0.8);
}

.report-option-btn.wrong::before {
    opacity: 1;
    filter: hue-rotate(140deg) brightness(1.3) drop-shadow(0 0 5px rgb(var(--c-danger-400-rgb) / 0.5)) !important;
}

.report-option-btn.disabled {
    pointer-events: none;
    opacity: 0.4;
}

.report-option-btn.disabled.correct,
.report-option-btn.disabled.wrong {
    opacity: 1;
    /* 正解・不正解の選択肢は非活性化されても目立たせる */
}

/* ========== 勲章（画像） ========== */

/* 勲章は画像アセット。以前は放射グラデーションと擬似要素で描いていたが、
   勲章を1つ足すたびに16進の色を4つ書く必要があり、17箇所たまっていた。
   ⚠️ assets/ は Git 管理外なので、画像が無い環境でも崩れないこと。 */
.medal-image {
    width: 100%;
    height: 100%;
    object-fit: contain;
    filter: drop-shadow(0 0 12px rgb(var(--c-warn-400-rgb) / 0.45));
    animation: medal-float 3s infinite ease-in-out;
}

/* 授与の瞬間に勲章のまわりへ散る粒。
 *
 * ⚠️ リザルト画面の授与演出だけで使う。コレクション画面では出さない。
 *    あちらは6枚が同時に並ぶ「記録を読む」場所で、6箇所が別々に光り続けると
 *    落ち着かない（同期させても不自然になる）。
 *
 * 表面を光の帯が走る演出（仕様7.4）は見送った。勲章は背景透過の PNG で
 * 輪郭が複雑なため、帯をそのまま重ねると透過部分の上も四角い光が横切る。
 * 形に沿わせるには同じ画像を mask に使う必要があり、割に合わない。
 * 粒なら勲章の形に依存しないので、この問題が起きない。
 */
.medal-award-particle {
    position: absolute;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: rgb(var(--c-warn-400-rgb));
    box-shadow: 0 0 6px rgb(var(--c-warn-400-rgb) / 0.9);
    pointer-events: none;
    /* 飛ぶ向きと距離は JS が --dx / --dy に入れる */
    animation: medal-award-particle-ani 900ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes medal-award-particle-ani {
    0%   { transform: translate(-50%, -50%) scale(0.3); opacity: 0; }
    20%  { opacity: 1; }
    100% {
        transform: translate(-50%, -50%) translate(var(--dx), var(--dy)) scale(1);
        opacity: 0;
    }
}

/* コレクション画面で、取得済みの勲章のまわりに常設で瞬く粒。
 *
 * ⚠️ 授与時の .medal-award-particle とは別物。あちらは一度きりに外へ弾けるが、
 *    こちらは6枚が同時に並ぶ画面で出続けるので、同じ動きをループさせると騒がしい。
 *    その場で「点いて消える」だけにし、1周期の大半は消えている状態にしてある
 *    （下の keyframes で、光っているのは全体の約3割）。
 *
 * ⚠️ CSS の infinite アニメーションだけで完結させること。JS のタイマーで
 *    繰り返すと、コレクション画面を閉じたときに止め忘れて残る。
 *    要素はカードごと innerHTML = '' で消えるので、後始末が要らない。
 */
.medal-sparkle {
    position: absolute;
    /* 大きさは JS が --size に入れる。粒が全部同じ大きさだと機械的に見える */
    width: var(--size, 4px);
    height: var(--size, 4px);
    border-radius: 50%;
    background: rgb(var(--c-warn-400-rgb));
    box-shadow: 0 0 8px rgb(var(--c-warn-400-rgb) / 0.95);
    pointer-events: none;
    opacity: 0;
    animation: medal-sparkle-ani 2600ms ease-in-out infinite;
}

/* 点いている時間は1周期の約4割。全部消えている瞬間を作らないと点滅に見えるので、
   常時点灯にはしない。粒ごとの遅延（JS 側）と合わせて、まばらに光り続ける。 */
@keyframes medal-sparkle-ani {
    0%, 58%, 100% { transform: translate(-50%, -50%) scale(0.3); opacity: 0; }
    72%           { transform: translate(-50%, -50%) scale(1);   opacity: 1; }
    86%           { transform: translate(-50%, -50%) scale(0.9); opacity: 0.85; }
}

/* 未取得。色を直書きせず、彩度と明度を落とすだけで済ませる */
.medal-image.locked {
    filter: grayscale(1) brightness(0.35);
    opacity: 0.5;
}

/* 画像が読めなかったときの退避。枠だけ出して名前は隣の要素が持つ */
.medal-image-fallback {
    width: 100%;
    height: 100%;
    border: 2px dashed rgb(var(--c-n-600-rgb));
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 28px;
    color: rgb(var(--c-n-500-rgb));
}

/* 未取得（退避表示側）。画像版の .medal-image.locked と同じく、色を直書きせず
   不透明度だけを落として区別する。画像アセットが未整備の間はここが唯一の見た目差になる */
.medal-image-fallback.locked {
    opacity: 0.35;
}

/* 3D回転（medal-rotate）は廃止した。平らな画像をY軸で回すと裏面が見え、
   コインが回っているように見えてしまう。浮遊だけを残す。 */
@keyframes medal-float {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-6px); }
}

/* 装置（従軍星・樫葉章・等級章）。勲章の下辺に横並びで重ねる */
/* 装置（従軍星・樫葉章・等級章）は勲章の**下**に横並びで置く。
   ⚠️ white-space: nowrap は必須。これが無いと、幅の狭い枠に入ったときに
      「二等」が1文字ずつ縦に折り返される（実際にそうなっていた）。 */
.medal-device {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    white-space: nowrap;
    font-family: 'Fira Code', monospace;
    font-size: 13px;
    font-weight: 700;
    color: rgb(var(--c-warn-400-rgb));
}

.medal-device img {
    width: 20px;
    height: 20px;
    object-fit: contain;
}

/* コレクション内の勲章グリッドアイテムホバー効果 */
.medal-grid-item {
    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.medal-grid-item:hover:not(.locked-item) {
    transform: translateY(-4px) scale(1.02);
    box-shadow: 0 10px 25px -5px rgb(var(--c-support-500-rgb) / 0.2), 0 8px 10px -6px rgb(var(--c-support-500-rgb) / 0.2);
}

.medal-grid-item.locked-item:hover {
    border-color: rgb(var(--c-n-600-rgb) / 0.4) !important;
}

/* コレクション詳細テキストの自動改行・はみ出し防止 */
#col-term-def,
#col-term-note {
    word-break: break-word !important;
    overflow-wrap: break-word !important;
    white-space: pre-wrap !important;
}

/* ========== エンディングロール（スタッフロール） ========== */
#ending-roll-ui {
    background-color: #000;
    transition: opacity 1s ease-in-out;
}

#ending-roll-bg {
    background-image: url('../assets/images/story/bg_ending_roll.png');
    background-color: rgb(var(--c-n-950-rgb));
}

#ending-roll-scroll-container {
    bottom: -150%;
    animation: scrollCredits 80s linear forwards;
}

@keyframes scrollCredits {
    0% {
        transform: translateY(0);
    }

    100% {
        transform: translateY(-260%);
    }
}

.credits-title {
    font-size: 3.5rem;
    font-weight: 900;
    letter-spacing: 0.3em;
    color: rgb(var(--c-accent-400-rgb));
    text-shadow: 0 0 15px rgb(var(--c-accent-400-rgb) / 0.6), 0 0 30px rgb(var(--c-accent-400-rgb) / 0.3);
    margin-bottom: 5rem;
    text-align: center;
    font-family: 'IBM Plex Sans JP', sans-serif;
}

.credits-subtitle {
    font-size: 1.8rem;
    font-weight: 700;
    letter-spacing: 0.15em;
    color: rgb(var(--c-support-400-rgb));
    text-shadow: 0 0 10px rgb(var(--c-support-400-rgb) / 0.5);
    margin-top: 3rem;
    margin-bottom: 2rem;
    text-align: center;
    border-bottom: 1px solid rgb(var(--c-support-400-rgb) / 0.3);
    padding-bottom: 0.5rem;
    width: 400px;
}

.credits-role {
    font-size: 1rem;
    letter-spacing: 0.2em;
    color: rgb(var(--c-n-400-rgb));
    text-transform: uppercase;
    margin-top: 2rem;
    margin-bottom: 0.5rem;
    text-align: center;
}

.credits-name {
    font-size: 1.4rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    color: rgb(var(--c-n-100-rgb));
    text-shadow: 0 0 5px rgb(var(--c-n-100-rgb) / 0.3);
    margin-bottom: 1.5rem;
    text-align: center;
}

.credits-gold {
    color: rgb(var(--c-warn-400-rgb));
    text-shadow: 0 0 8px rgb(var(--c-warn-400-rgb) / 0.6);
}

.credits-thanks {
    font-size: 1.6rem;
    font-weight: 900;
    letter-spacing: 0.2em;
    color: rgb(var(--c-accent-400-rgb));
    text-shadow: 0 0 10px rgb(var(--c-accent-400-rgb) / 0.5);
    margin-top: 6rem;
    margin-bottom: 1rem;
    text-align: center;
}

.credits-thanks-sub {
    font-size: 1.1rem;
    letter-spacing: 0.1em;
    color: rgb(var(--c-n-300-rgb));
    text-align: center;
    margin-bottom: 5rem;
}

.credits-footer {
    font-size: 1.1rem;
    color: rgb(var(--c-n-400-rgb));
    letter-spacing: 0.15em;
    text-align: center;
    margin-top: 8rem;
    margin-bottom: 15rem;
    text-shadow: 0 0 8px rgb(var(--c-n-400-rgb) / 0.3);
}

/* ========== 端末接続カットインの解析ログ ========== */

/* 画面を埋めて流れる緑のログ。「これから侵入する」ことを絵で伝える。
   ⚠️ 行の中身は js/utils/hackStream.js が作る。ここは流し方だけを持つ。
   ⚠️ カットインは戦闘凍結の**前**に走る（まだ敵が動いている）。長く覆うほど
      その間の戦況が見えないので、TERMINAL_CUTIN_MS を伸ばすときは注意。 */
.terminal-stream {
    position: absolute;
    inset: 0;
    display: flex;
    justify-content: space-around;
    /* ⚠️ stretch（既定）だと列が親の高さに引き伸ばされ、
       translateY(-50% → 0) が「中身の半分」でなくなって継ぎ目が飛ぶ。
       列は中身なりの高さを保つこと */
    align-items: flex-start;
    overflow: hidden;
    /* 上下を溶かして、切れ端が唐突に消えないようにする */
    -webkit-mask-image: linear-gradient(to bottom, transparent, #000 18%, #000 82%, transparent);
    mask-image: linear-gradient(to bottom, transparent, #000 18%, #000 82%, transparent);
}

.terminal-stream-col {
    font-family: 'Fira Code', monospace;
    font-size: 0.72rem;
    line-height: 1.5;
    letter-spacing: 0.04em;
    color: rgb(var(--c-support-400-rgb) / 0.55);
    white-space: nowrap;
    /* 列ごとに JS が --stream-ms と --stream-delay を入れる */
    animation: terminal-stream-fall var(--stream-ms, 2400ms) linear var(--stream-delay, 0ms) infinite;
}

/* 先頭の数行だけ明るくして、流れの向きが分かるようにする */
.terminal-stream-col > div:nth-child(-n+3) {
    color: rgb(var(--c-support-400-rgb));
}

@keyframes terminal-stream-fall {
    from { transform: translateY(-50%); }
    to   { transform: translateY(0); }
}

/* 奔流の上に重ねる見出し。⚠️ 背景を敷いて必ず読めるようにする。
   実機で「奔流に埋もれて読みにくい」と指摘を受け、字も箱も一回り大きくした。 */
.terminal-cutin-label {
    font-size: 0.85rem;
    letter-spacing: 0.4em;
    /* 奔流と同じ 400 だと沈む。見出し側は一段明るい 300 を使う */
    color: rgb(var(--c-support-300-rgb));
    text-shadow: 0 0 10px rgb(var(--c-support-400-rgb) / 0.6);
    margin-bottom: 10px;
}

/* ⚠️ 端末が出す文字なので**英数字だけ**を入れること。日本語をここへ入れると、
   等幅かつ字間を空けた組みに載って間延びし、途端に安っぽくなる（実機で指摘）。
   日本語は下の .terminal-cutin-sub に、字間を詰めて小さく添える。 */
.terminal-cutin-headline {
    display: inline-block;
    min-height: 2.4rem;
    padding: 12px 32px;
    /* 端末の出力なので等幅。Fira Code は日本語を持たないが、ここは英数字のみ */
    font-family: 'Fira Code', monospace;
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: 0.16em;
    /* 背後を流れるログ（400）より明るくして、必ず手前に出す */
    color: rgb(var(--c-support-300-rgb));
    text-shadow: 0 0 14px rgb(var(--c-support-400-rgb) / 0.7);
    background: rgb(var(--c-n-950-rgb) / 0.92);
    border: 2px solid rgb(var(--c-support-400-rgb) / 0.7);
    box-shadow: 0 0 28px rgb(var(--c-support-400-rgb) / 0.25);
}

/* 日本語の添え書き。⚠️ 字間を空けない。日本語は詰めたほうが締まって見える */
.terminal-cutin-sub {
    margin-top: 10px;
    font-family: 'IBM Plex Sans JP', sans-serif;
    font-size: 0.9rem;
    letter-spacing: 0.04em;
    color: rgb(var(--c-n-300-rgb));
    text-shadow: 0 0 8px #000000;
}

/* 戦場へ戻るボタン。押すまで時間は止まったまま */
/* ⚠️ 端末の出力ではなく UI のボタンなので、日本語の入る書体で組む。
   等幅＋広い字間だと「準備完了」が間延びする。
   角を斜めに落として、素の矩形ボタンに見えないようにする（戦術UIの意匠）。 */
.terminal-launch {
    position: relative;
    padding: 13px 44px;
    font-family: 'IBM Plex Sans JP', sans-serif;
    font-size: 1rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    color: rgb(var(--c-n-950-rgb));
    background: linear-gradient(
        160deg,
        rgb(var(--c-support-300-rgb)),
        rgb(var(--c-support-500-rgb))
    );
    border: 0;
    /* ⚠️ clip-path は border も影も切る。枠線は下の inset shadow で描く */
    clip-path: polygon(
        12px 0, 100% 0,
        100% calc(100% - 12px), calc(100% - 12px) 100%,
        0 100%, 0 12px
    );
    box-shadow:
        inset 0 0 0 1px rgb(var(--c-n-950-rgb) / 0.35),
        0 0 26px rgb(var(--c-support-400-rgb) / 0.45);
    cursor: pointer;
    transition: filter 120ms ease-out, transform 120ms ease-out;
}

/* 端末らしさの符牒。押す場所であることを記号でも示す */
.terminal-launch::before {
    content: '> ';
    opacity: 0.65;
}

/* 点滅するカーソル。入力待ちであることを伝える */
.terminal-launch::after {
    content: ' _';
    animation: terminal-caret 1s steps(1) infinite;
}

@keyframes terminal-caret {
    0%, 49%   { opacity: 1; }
    50%, 100% { opacity: 0; }
}

.terminal-launch:hover {
    filter: brightness(1.12);
    transform: translateY(-1px);
}

.terminal-launch:active {
    transform: scale(0.97);
}

/* ========== 端末モーダルのスクロールバー ==========
   既定のスクロールバーは灰色で角丸のため、緑一色の端末の中で浮く。
   ⚠️ Electron は Chromium なので ::-webkit-scrollbar が効く。
      標準の scrollbar-color と併用すると、対応版では標準側が優先されて
      ここの指定が無視されるので、片方（webkit 側）だけで組む。 */
#terminal-modal ::-webkit-scrollbar,
#terminal-modal-panel::-webkit-scrollbar {
    width: 8px;
}

#terminal-modal ::-webkit-scrollbar-track,
#terminal-modal-panel::-webkit-scrollbar-track {
    background: rgb(var(--c-support-950-rgb) / 0.6);
    /* 溝の左に1本線を入れて、端末の余白ではなく「軌道」に見せる */
    border-left: 1px solid rgb(var(--c-support-700-rgb) / 0.5);
}

#terminal-modal ::-webkit-scrollbar-thumb,
#terminal-modal-panel::-webkit-scrollbar-thumb {
    /* 角丸にしない。端末の意匠は直線で通す */
    background: rgb(var(--c-support-500-rgb) / 0.75);
    border: 1px solid rgb(var(--c-support-400-rgb) / 0.5);
}

#terminal-modal ::-webkit-scrollbar-thumb:hover,
#terminal-modal-panel::-webkit-scrollbar-thumb:hover {
    background: rgb(var(--c-support-400-rgb));
}

/* 端と端の三角ボタンは出さない（既定のままだと灰色の矢印が残る） */
#terminal-modal ::-webkit-scrollbar-button,
#terminal-modal-panel::-webkit-scrollbar-button {
    display: none;
}

#terminal-modal ::-webkit-scrollbar-corner,
#terminal-modal-panel::-webkit-scrollbar-corner {
    background: transparent;
}

/* ========== ハッキングの侵入シーケンス ========== */

/* ⚠️ #game-wrapper に transform は無く、DOM は canvas と一緒に縮まない。
   最小ウィンドウの描画領域は 1280x720 で、モーダルは max-h-[80%]（=576px）。
   ステージ3は6段＋3択が入るので、収まりは tool/hacking_preview.html で実測する。 */

/* 工程バー。済んだ段・現在地・これからが一目で分かる */
.terminal-steps {
    display: flex;
    gap: 4px;
    margin-bottom: 12px;
}

.terminal-step {
    flex: 1 1 0;
    min-width: 0;
    font-size: 0.6rem;
    letter-spacing: 0.06em;
    text-align: center;
    padding: 3px 2px;
    border-top: 2px solid rgb(var(--c-n-700-rgb));
    color: rgb(var(--c-n-500-rgb));
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.terminal-step.is-done {
    border-top-color: rgb(var(--c-support-400-rgb));
    color: rgb(var(--c-support-400-rgb));
}

/* 現在地。減光ではなく発光で示す（減光は「押せない」しか伝えない） */
.terminal-step.is-current {
    border-top-color: rgb(var(--c-accent-400-rgb));
    color: rgb(var(--c-accent-400-rgb));
    font-weight: 700;
}

.terminal-detection {
    margin-bottom: 12px;
}

.terminal-detection-head {
    display: flex;
    justify-content: space-between;
    font-size: 0.62rem;
    letter-spacing: 0.18em;
    color: rgb(var(--c-n-400-rgb));
    margin-bottom: 3px;
}

.terminal-detection-value {
    font-weight: 700;
    color: rgb(var(--c-n-200-rgb));
}

.terminal-detection-track {
    height: 6px;
    background: rgb(var(--c-n-900-rgb));
    border: 1px solid rgb(var(--c-n-700-rgb));
}

.terminal-detection-fill {
    height: 100%;
    width: 0;
    background: rgb(var(--c-warn-400-rgb));
    transition: width 320ms ease-out;
}

/* 誤答で跳ねたときは赤。増援が積まれたことを色でも伝える */
.terminal-detection-fill.is-alert {
    background: rgb(var(--c-danger-400-rgb));
}

.terminal-detection-note {
    margin-top: 4px;
    font-size: 0.66rem;
    color: rgb(var(--c-danger-400-rgb));
}

.terminal-log {
    min-height: 3.2em;
    margin-bottom: 12px;
    font-size: 0.78rem;
    line-height: 1.6;
    color: rgb(var(--c-support-400-rgb));
    /* ログが伸びてもモーダル全体を押し広げない */
    max-height: 7em;
    overflow-y: auto;
    overflow-x: hidden;
    transition: max-height 240ms ease-out;
}

/* 判断が出ている間はログを畳む。
   ⚠️ ログの役目はそこで終わっており、選択肢を画面外へ押し出してはいけない。
   実測: これが無いと S3 の英語で 107px ぶんスクロールが必要になり、
   3つ目の選択肢が最初は見えない（レポート画面で二度指摘を受けたのと同じ形）。 */
.terminal-log.is-compact {
    max-height: 3.2em;
}

/* 図解があるときは、図解がログの場所を引き継ぐ。
   ⚠️ 両方を出すと 1280x720 で確実に溢れる（実測で 68〜180px）。
      ログは判断を終えれば戻るので、ここでは絵に譲る。 */
.terminal-log.is-swapped {
    display: none;
}

.terminal-log-line::before {
    content: '> ';
    color: rgb(var(--c-n-500-rgb));
}

/* ⚠️ 高さは画面に追従させる。1280x720 では 160px を置く余地が無く、
   固定にすると最小ウィンドウで選択肢が画面外へ出る（実測で確認）。
   13vh → 720px で 94px、1080px で 140px、1440px で 170px（上限）。 */
.terminal-figure {
    display: block;
    width: 100%;
    max-height: clamp(72px, 13vh, 170px);
    object-fit: contain;
    margin-bottom: 8px;
    border: 1px solid rgb(var(--c-n-700-rgb));
}

.terminal-skip {
    margin-top: 10px;
    text-align: right;
    font-size: 0.66rem;
    letter-spacing: 0.16em;
    color: rgb(var(--c-n-500-rgb));
    cursor: pointer;
}

.terminal-skip:hover {
    color: rgb(var(--c-accent-400-rgb));
}

/* ========== クレジット画面 ========== */

.credits-panel {
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: 760px;
    max-height: 100%;
    /* ⚠️ flex の子は既定で min-height: auto。祖先から本体まで全部に要る */
    min-height: 0;
    padding: 20px 28px;
    box-sizing: border-box;
    border: 1px solid rgb(var(--c-border));
    background: rgb(var(--c-n-950-rgb) / 0.95);
}

.credits-panel-title {
    flex-shrink: 0;
    font-size: 1.5rem;
    font-weight: 900;
    letter-spacing: 0.3em;
    text-align: center;
    color: rgb(var(--c-accent-400-rgb));
}

.credits-panel-subtitle {
    flex-shrink: 0;
    margin-top: 4px;
    margin-bottom: 12px;
    font-size: 0.8rem;
    text-align: center;
    color: rgb(var(--c-n-300-rgb));
}

/* 未記入の警告。記入が済めば描画されない（出荷時に空欄が混入するのを防ぐ） */
.credits-warning {
    flex-shrink: 0;
    margin-bottom: 10px;
    padding: 8px 12px;
    font-size: 0.72rem;
    line-height: 1.6;
    color: rgb(var(--c-warn-400-rgb));
    border: 1px solid rgb(var(--c-warn-400-rgb) / 0.5);
    background: rgb(var(--c-warn-400-rgb) / 0.08);
}

.credits-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    /* ⚠️ overflow-y だけ指定すると overflow-x の visible は auto に計算される */
    overflow-x: hidden;
}

.credits-category {
    margin-bottom: 14px;
}

.credits-category-name {
    font-size: 0.78rem;
    letter-spacing: 0.2em;
    color: rgb(var(--c-accent-400-rgb));
    border-bottom: 1px solid rgb(var(--c-n-700-rgb));
    padding-bottom: 4px;
    margin-bottom: 8px;
}

.credits-item {
    margin-bottom: 8px;
}

.credits-item-name {
    font-size: 0.9rem;
    font-weight: 700;
    color: rgb(var(--c-n-50-rgb));
}

/* 権利者・ライセンス・URL。⚠️ ここは訳さない（訳すと帰属表示として成立しない）。
   読ませるための行なので、n-400 では暗すぎた（実機で指摘あり） */
.credits-item-detail {
    font-size: 0.78rem;
    line-height: 1.6;
    color: rgb(var(--c-n-200-rgb));
    word-break: break-all;
}

/* ⚠️ オプション画面は追加前から高さの限界に近く、最小ウィンドウ（実ピクセル 720px）では
   上下が切れうる。#game-wrapper に transform は無いので、DOM は canvas と一緒に縮まない。
   justify-center のまま溢れると上側にスクロールで到達できないため safe center にする */
#options-ui {
    overflow-y: auto;
    overflow-x: hidden;
    justify-content: safe center;
    padding: 16px 0;
}

/* ========== 次章予告（NEXT CHAPTERS） ========== */

/* ⚠️ #game-wrapper に transform は無く、DOM は canvas と一緒に縮まない。
   1920x1080 は canvas の論理解像度であって、ここの制約は「ウィンドウの実ピクセル」で効く。
   最小ウィンドウの描画領域は 1280x720（--hud-min-w / --hud-min-h）なので、
   予算は 見出し88 + 行92x5 + 締め88 + 余白40 = 676px（720px に対し44pxの余裕）。
   実測の行高は指定値と一致しない（ストーリー本文で28px指定が実測46px前後だった）ため、
   予算は使い切らずに余らせてある。 */

#next-chapters-ui {
    background-color: #000000;
}

.dossier-frame {
    display: flex;
    flex-direction: column;
    /* 見出し・一覧・締めを、上下に散らさず画面の中央へまとめて置く。
       header を上端・footer を下端に張り付けると、1920x1080 では上下に
       大きな空きができて構図が崩れる（実機で指摘あり） */
    justify-content: center;
    width: 100%;
    max-width: 1100px;
    height: 100%;
    min-height: 0; /* ⚠️ ここが抜けると、下の overflow-y が効かずに一覧がはみ出す */
    padding: 20px 32px;
    box-sizing: border-box;
    position: relative;
    z-index: 1; /* 背景画像より前 */
}

.dossier-header {
    flex-shrink: 0;
    text-align: center;
    margin-bottom: 14px;
}

.dossier-title {
    font-size: 1.9rem;
    font-weight: 900;
    letter-spacing: 0.32em;
    color: rgb(var(--c-accent-400-rgb));
    text-shadow: 0 0 12px rgb(var(--c-accent-400-rgb) / 0.5);
}

.dossier-subtitle {
    margin-top: 6px;
    font-size: 0.78rem;
    letter-spacing: 0.12em;
    color: rgb(var(--c-n-400-rgb));
}

.dossier-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    /* ⚠️ 1 1 auto にすると、余った高さを全部この一覧が吸って上下が離れる。
       0 1 auto なら中身ぶんの高さに留まり、溢れたときだけ縮んでスクロールする */
    flex: 0 1 auto;
    width: 100%;
    /* ⚠️ flex の子は既定で min-height: auto。1つ抜けるだけで縮小が止まり、はみ出す */
    min-height: 0;
    overflow-y: auto;
    /* ⚠️ overflow-y だけ指定すると、CSS 仕様により overflow-x の visible は auto に
       計算され、横スクロールバーが出る。必ず両方明示する */
    overflow-x: hidden;
    /* ⚠️ center のまま溢れると上側にスクロールで到達できない */
    justify-content: safe center;
}

.dossier-row {
    flex-shrink: 0;
    position: relative;
    padding: 10px 14px;
    border: 1px solid rgb(var(--c-n-700-rgb));
    background: rgb(var(--c-n-900-rgb) / 0.6);
    border-left-width: 3px;
    border-left-color: rgb(var(--c-n-600-rgb));
}

/* 開封済み。今クリアした章だけがこの見た目になり、下に続く機密の行と対比される */
.dossier-row.is-declassified {
    border-left-color: rgb(var(--c-support-400-rgb));
    background: rgb(var(--c-support-400-rgb) / 0.06);
}

.dossier-row-head {
    display: flex;
    align-items: baseline;
    gap: 12px;
    margin-bottom: 4px;
}

.dossier-code {
    font-size: 0.72rem;
    letter-spacing: 0.18em;
    color: rgb(var(--c-n-500-rgb));
    white-space: nowrap;
}

.dossier-name {
    font-size: 0.95rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    color: rgb(var(--c-n-100-rgb));
}

.dossier-chapter-title {
    font-size: 0.8rem;
    color: rgb(var(--c-n-400-rgb));
}

/* 判子。傾けて押した体裁にする */
.dossier-stamp {
    margin-left: auto;
    flex-shrink: 0;
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.14em;
    padding: 2px 8px;
    border: 1px solid currentColor;
    transform: rotate(-4deg);
    white-space: nowrap;
    color: rgb(var(--c-danger-400-rgb));
    opacity: 0.85;
}

.dossier-stamp.is-declassified {
    color: rgb(var(--c-support-400-rgb));
}

.dossier-synopsis {
    font-size: 0.8rem;
    line-height: 1.5;
    color: rgb(var(--c-n-300-rgb));
}

/* 黒塗り。⚠️ 中の文字は DOM に入れない。幅は文字数から決める
   （CSS で隠すだけでは開発者ツールで読めてしまい、機密という体裁が崩れる） */
.dossier-redacted {
    display: inline-block;
    vertical-align: -0.12em;
    height: 1.05em;
    background-color: #000000;
    border: 1px solid rgb(var(--c-n-700-rgb));
    margin: 0 2px;
}

.dossier-themes {
    margin-top: 4px;
    font-size: 0.72rem;
    letter-spacing: 0.04em;
    color: rgb(var(--c-accent-400-rgb) / 0.85);
}

.dossier-footer {
    flex-shrink: 0;
    margin-top: 14px;
    text-align: center;
}

.dossier-closing {
    font-size: 0.8rem;
    line-height: 1.5;
    color: rgb(var(--c-n-300-rgb));
    margin-bottom: 10px;
}

/* ========== 戦闘中 (in-combat) 画面のcanvas縦横比補正 ========== */

/* Phaser の Scale.FIT が計算したサイズをそのまま使う。
   以前は object-fit: fill で縦横比を無視して引き伸ばしていた。
   戦闘中は #game-container が右パネルぶん狭められていたため、
   1920x1080 の描画がそこへ押し込まれて横に約24%潰れていた
   （実測 1220x900 = 比1.356。正しい16:9なら1600x900）。
   右パネルを畳んだので、その補正はもう要らない。 */
#game-container canvas {
    max-width: 100%;
    max-height: 100%;
    margin: auto;
    object-fit: contain;
}

/* ハッキング用クイズボタン（ブロック幅と美化） */
.hack-btn {
    width: 100% !important;
    display: block !important;
    box-sizing: border-box !important;
    border-radius: 4px;
    margin-bottom: 8px;
}

#game-wrapper.in-combat #dialogue-box {
    bottom: 24px !important;
}

/* ダメージ・ヴィネット（HP低下時に画面の縁が赤くなる演出）は、
   ここでの位置補正が不要になった。以前は上部バーの下端まで押し下げる
   補正だったが、全幅の上部バーを撤去したので inset-0（画面全体）を
   そのまま使える。左右クラスタは pointer-events: none の上に乗るだけで、
   ヴィネット自体を覆い隠さない。 */

/* ========== 回復キット / スキル演出（CSSエフェクト） ========== */

/* 広がる緑の波紋（足元） */
.heal-ripple {
    position: absolute;
    width: 60px;
    height: 60px;
    border: 3px solid rgb(var(--c-support-400-rgb) / 0.85); /* 明るいネオングリーン */
    border-radius: 50%;
    transform: translate(-50%, -50%);
    box-shadow: 0 0 20px rgb(var(--c-support-400-rgb) / 0.7), inset 0 0 12px rgb(var(--c-support-400-rgb) / 0.5);
    animation: heal-ripple-ani 0.9s cubic-bezier(0.1, 0.8, 0.3, 1) forwards;
}

@keyframes heal-ripple-ani {
    0% {
        transform: translate(-50%, -50%) scale(0.2);
        opacity: 1;
    }
    100% {
        transform: translate(-50%, -50%) scale(2.2);
        opacity: 0;
    }
}

/* 浮かび上がる「+」粒子 */
.heal-plus-particle {
    position: absolute;
    color: rgb(var(--c-support-500-rgb)); /* 緑 */
    font-size: 26px;
    font-weight: 900;
    font-family: 'IBM Plex Sans JP', sans-serif;
    text-shadow: 0 0 10px rgb(var(--c-support-500-rgb) / 0.9), 0 0 20px rgb(var(--c-support-500-rgb) / 0.5);
    transform: translate(-50%, -50%);
    animation: heal-particle-ani 1.3s cubic-bezier(0.25, 1, 0.5, 1) forwards;
}

@keyframes heal-particle-ani {
    0% {
        transform: translate(-50%, -50%) translateY(15px) scale(0.4) rotate(0deg);
        opacity: 0;
    }
    15% {
        opacity: 1;
    }
    100% {
        transform: translate(-50%, -50%) translateY(-75px) scale(1.3) rotate(15deg);
        opacity: 0;
    }
}

/* ========== 画面中央警告カットイン用スタイル ========== */
#center-warning-cutin {
    pointer-events: none;
    z-index: 99;
}

.center-cutin-bg-flash {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgb(var(--c-danger-500-rgb) / 0.25);
    animation: center-cutin-flash-ani 0.5s ease-out forwards;
}

.center-cutin-container {
    position: relative;
    width: 620px;
    background: rgb(var(--c-n-950-rgb) / 0.95);
    border: 2px solid rgb(var(--c-danger-500-rgb));
    box-shadow: 0 0 35px rgb(var(--c-danger-500-rgb) / 0.7), inset 0 0 15px rgb(var(--c-danger-500-rgb) / 0.3);
    padding: 22px;
    display: flex;
    flex-direction: column;
    gap: 15px;
    transform: scaleX(0);
    transform-origin: center;
    animation: center-cutin-slide-ani 0.35s cubic-bezier(0.16, 1, 0.3, 1) forwards;
    backdrop-filter: blur(12px);
    overflow: hidden;
}

/* L字型ネオンアクセントの追加 */
.center-cutin-container::before,
.center-cutin-container::after {
    content: '';
    position: absolute;
    width: 16px;
    height: 16px;
    border-color: rgb(var(--c-danger-500-rgb));
    border-style: solid;
    pointer-events: none;
}
.center-cutin-container::before {
    top: 4px; left: 4px;
    border-width: 3px 0 0 3px;
}
.center-cutin-container::after {
    bottom: 4px; right: 4px;
    border-width: 0 3px 3px 0;
}

.center-cutin-warning-bar {
    background: rgb(var(--c-danger-500-rgb));
    color: #ffffff;
    font-weight: 900;
    font-family: 'Anton', 'IBM Plex Sans JP', sans-serif;
    text-align: center;
    padding: 5px;
    letter-spacing: 6px;
    font-size: 14px;
    text-shadow: 0 0 8px rgba(255, 255, 255, 0.6);
    animation: center-cutin-blink-ani 0.4s infinite alternate;
}

.center-cutin-content {
    display: flex;
    gap: 24px;
    align-items: center;
    position: relative;
    z-index: 2;
}

.center-cutin-portrait-frame {
    width: 84px;
    height: 84px;
    border: 2px solid rgb(var(--c-danger-500-rgb) / 0.8);
    background: rgb(var(--c-n-900-rgb));
    box-shadow: 0 0 15px rgb(var(--c-danger-500-rgb) / 0.4);
    padding: 2px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

#center-cutin-portrait {
    width: 100%;
    height: 100%;
    object-fit: cover;
    filter: brightness(1.1) contrast(1.05);
}

.center-cutin-message-box {
    flex: 1;
    display: flex;
    flex-direction: column;
}

#center-cutin-name {
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    font-size: 13px;
    font-weight: 900;
    letter-spacing: 2px;
    text-shadow: 0 0 5px rgb(var(--c-accent-400-rgb) / 0.5);
}

#center-cutin-text {
    font-size: 17px;
    font-weight: 800;
    line-height: 1.5;
    text-shadow: 0 0 10px rgba(255, 255, 255, 0.3);
}

.center-cutin-scanline {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: linear-gradient(
        rgb(var(--c-n-950-rgb) / 0) 50%, 
        rgba(0, 0, 0, 0.3) 50%
    );
    background-size: 100% 4px;
    pointer-events: none;
    z-index: 1;
    opacity: 0.45;
}

/* アニメーションキーフレーム */
@keyframes center-cutin-flash-ani {
    0% { background: rgb(var(--c-danger-500-rgb) / 0.55); }
    100% { background: rgb(var(--c-danger-500-rgb) / 0.05); }
}

@keyframes center-cutin-slide-ani {
    0% { transform: scaleX(0); opacity: 0; }
    100% { transform: scaleX(1); opacity: 1; }
}

@keyframes center-cutin-blink-ani {
    0% { opacity: 0.75; }
    100% { opacity: 1; }
}

/* ========== スキル発動・目元カットイン演出 (ペルソナ風) ========== */
#eye-cutin-overlay {
    z-index: 100;
    background: transparent;
}

.eye-cutin-flash {
    position: absolute;
    top: 0; left: 0; width: 100%; height: 100%;
    background: #ffffff;
    opacity: 0;
    pointer-events: none;
    animation: eye-cutin-flash-ani 0.28s ease-out forwards;
}

.eye-cutin-strip {
    position: relative;
    width: 160%;
    height: 230px;
    background: linear-gradient(90deg, rgb(var(--c-n-800-rgb)) 0%, rgb(var(--c-danger-500-rgb)) 35%, rgb(var(--c-danger-600-rgb)) 65%, rgb(var(--c-n-900-rgb)) 100%);
    border-top: 4px solid #ffffff;
    border-bottom: 4px solid #ffffff;
    box-shadow: 0 0 50px rgb(var(--c-danger-500-rgb) / 0.85);
    display: flex;
    align-items: center;
    justify-content: center;
    transform: skewX(-18deg) translateX(-130%);
    transform-origin: center;
    animation: eye-cutin-strip-ani calc(var(--eye-cutin-ms) * 1ms) cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

/* ストリップ背景のサイバーライン柄 */
.eye-cutin-lines {
    position: absolute;
    top: 0; left: 0; width: 100%; height: 100%;
    background: repeating-linear-gradient(
        45deg,
        rgba(255, 255, 255, 0.05),
        rgba(255, 255, 255, 0.05) 10px,
        rgba(0, 0, 0, 0.1) 10px,
        rgba(0, 0, 0, 0.1) 20px
    );
    pointer-events: none;
}

/* 目元画像を真っ直ぐにクリッピングして拡大表示 */
.eye-cutin-portrait-container {
    position: absolute;
    left: 20%;
    width: 420px;
    height: 190px;
    overflow: hidden;
    border: 3px solid #ffffff;
    background: rgb(var(--c-n-950-rgb));
    transform: skewX(18deg);
    box-shadow: 0 0 25px rgba(255, 255, 255, 0.45);
}

.eye-cutin-portrait-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center 22%;
    transform: scale(1.0);
    filter: contrast(1.1) brightness(1.05) drop-shadow(0 0 8px rgb(var(--c-danger-500-rgb) / 0.3));
}

/* スキル名テキストエリア */
.eye-cutin-text-container {
    position: absolute;
    right: 22%;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    transform: skewX(18deg);
}

.eye-cutin-skill-name {
    font-size: 52px;
    font-weight: 950;
    color: #ffffff;
    font-family: 'Anton', 'IBM Plex Sans JP', sans-serif;
    letter-spacing: 8px;
    text-shadow: 6px 6px 0px #000000, 0 0 20px rgba(255, 255, 255, 0.85);
    text-transform: uppercase;
    line-height: 1.0;
}

.eye-cutin-sub {
    font-size: 17px;
    font-weight: bold;
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    background: #ffffff;
    color: #000000;
    padding: 6px 14px;
    margin-top: 10px;
    letter-spacing: 1px;
    box-shadow: 3px 3px 0px #000000;
    max-width: 500px;
    white-space: normal;
    line-height: 1.4;
    word-break: break-all;
}

/* ガラス反射のようなグローバー */
.eye-cutin-glass-reflection {
    position: absolute;
    top: 0; left: 0; width: 100%; height: 100%;
    background: linear-gradient(
        135deg,
        rgba(255, 255, 255, 0.2) 0%,
        rgba(255, 255, 255, 0) 30%,
        rgba(255, 255, 255, 0.1) 70%,
        rgba(255, 255, 255, 0) 100%
    );
    pointer-events: none;
}

/* アニメーションキーフレーム定義 */
/* カットインの全画面フラッシュ。
   以前は 0.95 で 0.4秒だった。ほぼ不透明な白が全画面を覆うため、
   同じ瞬間に出るスキル固有の色付きフラッシュ（js/utils/vfx.js の flashScreen）と
   画面シェイクが完全に塗り潰されていた。
   全画面フラッシュが2枚重なっても情報は増えないので、こちらは輪郭を示す程度に弱める。 */
@keyframes eye-cutin-flash-ani {
    0% { opacity: 0.45; background: #ffffff; }
    30% { opacity: 0.38; background: rgb(var(--c-danger-500-rgb)); }
    100% { opacity: 0; }
}

@keyframes eye-cutin-strip-ani {
    0% {
        transform: skewX(-18deg) translateX(-130%);
        opacity: 0;
    }
    10% {
        /* 高速で画面を横切り、僅かに行き過ぎてから戻る（スナップ効果） */
        transform: skewX(-18deg) translateX(-6%);
        opacity: 1;
    }
    12% {
        transform: skewX(-18deg) translateX(-8%);
    }
    82% {
        /* 静止期間中に右へゆっくり僅かに流れる（ホバー効果） */
        transform: skewX(-18deg) translateX(-11%);
        opacity: 1;
    }
    100% {
        /* 右側へバサッと高速スライドアウト */
        transform: skewX(-18deg) translateX(130%);
        opacity: 0;
    }
}

/* 煙幕オーバーレイ（軽量版） */
#smoke-screen-overlay {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 30;
    background: radial-gradient(ellipse at 50% 50%, rgb(var(--c-n-400-rgb) / 0.25) 0%, rgb(var(--c-n-600-rgb) / 0.35) 60%, rgb(var(--c-n-800-rgb) / 0.45) 100%);
    opacity: 0;
    transition: opacity 1.0s ease-in-out;
    will-change: opacity;
}

#smoke-screen-overlay.active-smoke {
    display: block !important;
    opacity: 1;
    animation: smoke-drift-ani 6s ease-in-out infinite alternate;
}

@keyframes smoke-drift-ani {
    0%   { opacity: 0.85; }
    50%  { opacity: 1; }
    100% { opacity: 0.9; }
}

/* 回復フラッシュ演出 */
#heal-effect-layer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 20;
    background: transparent;
    opacity: 0;
    transition: opacity 0.3s ease-out;
}

#heal-effect-layer.active-heal {
    animation: heal-flash-ani 1.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes heal-flash-ani {
    0% {
        opacity: 0;
        box-shadow: inset 0 0 100px rgb(var(--c-support-500-rgb) / 0.4);
        background: rgb(var(--c-support-500-rgb) / 0.1);
    }
    15% {
        opacity: 1;
        box-shadow: inset 0 0 150px rgb(var(--c-support-500-rgb) / 0.65);
        background: rgb(var(--c-support-500-rgb) / 0.18);
    }
    100% {
        opacity: 0;
        box-shadow: inset 0 0 50px rgb(var(--c-support-500-rgb) / 0);
        background: rgb(var(--c-support-500-rgb) / 0);
    }
}

/* ===== 戦術ポイント（TP）のフィードバック ===== */

@keyframes tp-pulse {
    0%   { transform: scale(1);    text-shadow: none; }
    35%  { transform: scale(1.28); text-shadow: 0 0 12px rgb(var(--c-accent-400-rgb) / 0.9); }
    100% { transform: scale(1);    text-shadow: none; }
}

.tp-pulse {
    display: inline-block;
    animation: tp-pulse 260ms ease-out;
}

@keyframes placement-unlock {
    0%   { box-shadow: 0 0 0 0 rgb(var(--c-accent-400-rgb) / 0.85); }
    60%  { box-shadow: 0 0 18px 6px rgb(var(--c-accent-400-rgb) / 0.55); }
    100% { box-shadow: 0 0 0 0 rgb(var(--c-accent-400-rgb) / 0); }
}

.placement-unlock {
    animation: placement-unlock 700ms ease-out;
}

/* ========== 端末接続カットイン ========== */

/* 画面を上から下へ走る走査線。1本だけ通す。
   複数本にすると「壊れている」ように見え、接続の演出にならない。 */
.terminal-cutin-scan {
    position: absolute;
    left: 0;
    width: 100%;
    height: 3px;
    background: linear-gradient(90deg,
        transparent 0%,
        rgb(var(--c-support-400-rgb)) 50%,
        transparent 100%);
    box-shadow: 0 0 24px rgb(var(--c-support-400-rgb));
    animation: terminal-scan calc(var(--terminal-cutin-ms, 1400) * 1ms) linear forwards;
}

@keyframes terminal-scan {
    0%   { top: 0%;   opacity: 0; }
    12%  { opacity: 1; }
    88%  { opacity: 1; }
    100% { top: 100%; opacity: 0; }
}

/* ========== 解析完了カットイン ========== */

/* 端末を「開く」ときのカットインと対になる、「制圧した」ときのカットイン。
   開始側は接続中の不安げな演出（暗い緑のログが淡々と流れる）なので、
   完了側は同じ緑でも輝度と字の大きさで差をつけないと同じものに見える。
   走査線は下から上へ抜ける（開始側は上から下）。方向を逆にすることで、
   同じ部品を使いながら「終わった」ことが体感で分かる。 */
.hack-complete-scan {
    position: absolute;
    left: 0;
    width: 100%;
    height: 4px;
    background: linear-gradient(90deg,
        transparent 0%,
        rgb(var(--c-support-400-rgb)) 50%,
        transparent 100%);
    box-shadow: 0 0 32px rgb(var(--c-support-400-rgb));
    animation: hack-complete-scan calc(var(--hack-complete-cutin-ms, 1200) * 1ms) linear forwards;
}

@keyframes hack-complete-scan {
    0%   { top: 100%; opacity: 0; }
    12%  { opacity: 1; }
    88%  { opacity: 1; }
    100% { top: 0%;   opacity: 0; }
}

/* 見出しは一度だけ弾ませてから静止させる。点滅させ続けると
   「警告」に見えてしまい、成功の合図にならない。
   ⚠️ 動かすのは transform と opacity だけ。letter-spacing を動かすと
      毎フレーム中央寄せの再レイアウトが走るうえ、開始時の字間で横幅が
      706px まで伸びる（実測）。字間は静的に固定しておく。 */
.hack-complete-title {
    letter-spacing: 0.3em;
    animation: hack-complete-pop 420ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
    text-shadow: 0 0 18px rgb(var(--c-support-400-rgb) / 0.9);
}

@keyframes hack-complete-pop {
    0%   { transform: scale(1.6); opacity: 0; }
    100% { transform: scale(1);   opacity: 1; }
}

/* ========== ハッキング端末モーダル ========== */

/* コンソールの座標から中央へ開く。
   JS が --terminal-origin-x / -y に発生源の画面座標を書き込む。
   「コンソールに触れたから端末が開いた」ことを、位置で伝えるための演出。 */
#terminal-modal-panel {
    transform-origin: var(--terminal-origin-x, 50%) var(--terminal-origin-y, 50%);
}

#terminal-modal.opening #terminal-modal-panel {
    animation: terminal-open calc(var(--terminal-open-ms, 420) * 1ms) cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes terminal-open {
    0% {
        opacity: 0;
        transform: scale(0.15);
        filter: brightness(2.5);
    }
    100% {
        opacity: 1;
        transform: scale(1);
        filter: brightness(1);
    }
}

/* 選択肢ボタン。長い日本語が3つ並ぶので、行間と当たり判定を広く取る。 */
.terminal-option {
    text-align: left;
    padding: 14px 16px;
    border: 1px solid rgb(var(--c-support-500-rgb) / 0.5);
    background: rgb(var(--c-support-950-rgb) / 0.5);
    color: rgb(var(--c-support-300-rgb));
    font-size: 15px;
    line-height: 1.7;
    border-radius: 3px;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
    cursor: pointer;
}

.terminal-option:hover {
    background: rgb(var(--c-support-700-rgb) / 0.5);
    border-color: rgb(var(--c-support-400-rgb));
    color: #ffffff;
}

.terminal-option:disabled {
    opacity: 0.45;
    cursor: default;
}

/* ========== スキル（右クラスタの2段目） ==========
   右サイドパネルの中にあったものを外へ出し、さらに #hud-right-cluster の
   中へ移した。位置は親（#hud-right-cluster）が持つため、ここでは持たない。 */

/* 右クラスタ: TP行 + 設置物タイル + スキルタイル。すべて右揃え。
   上部バーの中と外に分かれていた操作UIを1箇所へまとめたもの。 */
#hud-right-cluster {
    position: absolute;
    top: var(--hud-edge-gap);
    right: var(--hud-edge-gap);
    z-index: 20;
    width: calc(var(--hud-right-budget) * 1px);
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 6px;
    /* クラスタ自身はクリックを透かし、中身だけ受ける。
       透かさないと 400px 幅の見えない板がマップを覆う */
    pointer-events: none;
}
#hud-right-cluster > * { pointer-events: auto; }

/* 左クラスタ: 進行状況 + 隊員カード + 部隊の持ち物。
   幅は --hud-side-budget（240px）で、これは以前から隊員パネルに割いていた枠。
   進行行と弾薬行が増えるぶん縦に約44px伸びるが、幅は変わらない。 */
#hud-left-cluster {
    position: absolute;
    top: var(--hud-edge-gap);
    left: var(--hud-edge-gap);
    z-index: 20;
    width: calc(var(--hud-side-budget) * 1px);
    display: flex;
    flex-direction: column;
    gap: 6px;
    /* クラスタ自身はクリックを透かし、中身だけ受ける。
       透かさないと 240px 幅の見えない板がマップを覆う */
    pointer-events: none;
}
#hud-left-cluster > * { pointer-events: auto; }
/* #squad-panel だけは例外。id セレクタ（上の `> *`）はクラス1個ぶんの
   .pointer-events-none（index.html 側）より詳細度が高く、素通りさせるつもりの
   none を auto で踏みつぶしてしまう。#squad-panel はカード同士の間に8pxの
   隙間があり、そこがクリックを吸ってマップへ抜けなくなるので、
   id 2個ぶんの詳細度でここだけ none に戻す。カード自身は個別に
   pointer-events-auto を持つ（js/scenes/main/hud.js）ので、この上書きでも
   NPC選択のクリックは効いたままになる。 */
#hud-left-cluster > #squad-panel { pointer-events: none; }
/* ⚠️ #hud-left-cluster.hidden の詳細度上書きは無い（意図的）。
   左クラスタは #hud-ui の子（#squad-panel や #damage-vignette と同じ）で、
   誰も class="hidden" を付けない。id 規則が display: flex を持つ点は
   #hud-right-cluster と同じだが、右クラスタは game-wrapper 直下の
   トップレベルなので自分で hidden を持つ必要があるのに対し、左クラスタは
   親（#hud-ui）が hidden になれば一緒に消えるため不要。 */

/* ⚠️ #hud-right-cluster の id 規則が display: flex を持つため、Tailwind の
   .hidden（.hidden{display:none}、クラス1個ぶんの詳細度）だけでは勝てず
   隠れない（id セレクタの詳細度がクラスより高いため）。id + class の
   組み合わせで詳細度を逆転させて確実に隠す。 */
#hud-right-cluster.hidden {
    display: none;
}

/* ⚠️ .hud-tile-row（#deploy-tiles / #hack-skills-ui）も同じ罠を踏む。
   Tailwind の .hidden（詳細度 0,1,0）と .hud-tile-row の display: flex
   （こちらも 0,1,0、かつ tailwind.css より後に読む style.css 側）が同詳細度で
   衝突し、後勝ちの style.css が勝つ。付けないと hideUI('hack-skills-ui') が
   classList に hidden を足しても display: flex のまま消えない
   （#hud-right-cluster の id 詳細度で一度踏んだのと同じ罠の2回目）。 */
.hud-tile-row.hidden {
    display: none;
}

/* ========== コマンドタイル（右クラスタ） ==========
   設置物とスキルで骨格は共通。違いは .hud-tile-deploy / .hud-tile-skill。
   色はすべて :root のトークンから取る（16進の直書きを増やさない）。

   寸法は仕様どおり 幅74px / アイコン25px / 文字11px。
   タイル5枚 = 80×5 + gap 5×4 = 420px で --hud-right-budget: 420 に収まる。

   ⚠️ 幅は文字から逆算している。DOM の HUD は canvas と一緒に縮小されないので
      フォントは実寸で効き、11px では実機で読みにくかった。12px へ上げるには
      英語の "RECLAIM TP"（実測71px）が入る内寸が要る。
      80px − 左右パディング2px×2 = 76px。 */
.hud-tile {
    position: relative;
    width: 80px;
    padding: 7px 2px 5px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    border-radius: 6px;
    border: 1px solid rgb(var(--c-n-500-rgb) / 0.4);
    background: rgb(var(--c-n-800-rgb) / 0.6);
    overflow: hidden;
    cursor: pointer;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

/* 押せない（disabled）スキルタイルはポインタ形状も「押せない」に合わせる。
   旧マークアップの disabled:pointer-events-none disabled:opacity-50 相当。
   減光自体は .is-unusable（状態クラス）が担うので、ここではカーソルだけ直す。 */
.hud-tile:disabled {
    cursor: default;
}

/* アイコンは白1枚のPNGをマスクとして使い、色をここで与える。
   画像に色を焼き込むと、状態ごとに画像が要るうえ、
   css/style.css から追い出した16進の直書きがPNGの中で復活する。
   --icon-url は js/ui/hudTiles.js が要素へ書き込む。 */
/* ⚠️ mask-image はここに書かない。js/ui/hudTiles.js が要素へ直接設定する。
   カスタムプロパティに入れてここで var() で受けると、相対パスが
   **このファイル（/css/style.css）を基準に**解決され、
   /css/assets/images/ui/icons/… を読みに行って全アイコンが 404 になる。
   実測で確認済み（Chromium 120）。色（background-color）はここが持つ。 */
.hud-tile-icon {
    width: 25px;
    height: 25px;
    background-color: rgb(var(--c-n-300-rgb));
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
}

/* ⚠️ フォントサイズはここが画面上の実寸になる。
   DOM の HUD は canvas と一緒に縮小されない（#game-wrapper に transform は無く、
   Phaser が canvas 要素だけを 1920→ウインドウ幅へ縮める）。
   つまりウインドウを広げても文字は大きくならないので、
   「小さい画面で潰れないか」ではなく「この実寸で読めるか」で決めること。

   幅の上限は実測で決めている（Chromium 120、既定のサンセリフ）:
     12px … BARRICADE 69px / RECLAIM TP 71px / バリケード 60px
     13px … BARRICADE 75px / RECLAIM TP 77px  ← タイル内寸 76px を超える
   タイル幅80px − 左右パディング2px×2 = 内寸76px なので、12px が上限。 */
.hud-tile-label {
    font-size: 12px;
    line-height: 1.2;
    color: rgb(var(--c-n-100-rgb));
    white-space: nowrap;
}

.hud-tile-cost {
    font-size: 12px;
    line-height: 1.2;
    color: rgb(var(--c-support-300-rgb));
}

/* ホットキー番号は補助情報なので小さいままでよいが、n-500 では暗すぎて
   実機でほぼ見えなかった。1段明るくする。 */
.hud-tile-key {
    position: absolute;
    top: 2px;
    left: 5px;
    font-size: 11px;
    line-height: 1;
    color: rgb(var(--c-n-400-rgb));
}

/* --- 設置物タイル --- */
.hud-tile-deploy:hover {
    border-color: rgb(var(--c-accent-400-rgb) / 0.6);
    background: rgb(var(--c-accent-900-rgb) / 0.5);
}
.hud-tile-deploy:hover .hud-tile-icon { background-color: rgb(var(--c-accent-300-rgb)); }
.hud-tile-deploy:hover .hud-tile-label { color: rgb(var(--c-accent-100-rgb)); }

/* 選択中: 設置モードに入っているタイル。これまでボタン側には何の表示も無く、
   どれを押したのか分からなかった。下辺の発光で「ここから設置が伸びている」を示す。 */
.hud-tile.is-selected {
    border: 2px solid rgb(var(--c-accent-400-rgb));
    background: rgb(var(--c-accent-900-rgb) / 0.85);
    box-shadow: 0 0 12px rgb(var(--c-accent-400-rgb) / 0.55);
}
.hud-tile.is-selected .hud-tile-icon { background-color: rgb(var(--c-accent-100-rgb)); }
.hud-tile.is-selected .hud-tile-label { color: #ffffff; }

/* TP不足: 一律の減光にはしない。アイコンと名前だけ沈め、コスト数値を赤に残して
   「足りないのは金額だ」と示す。 */
.hud-tile.is-insufficient {
    border-color: rgb(var(--c-n-500-rgb) / 0.25);
    background-color: rgb(var(--c-n-800-rgb) / 0.35);
}
.hud-tile.is-insufficient .hud-tile-icon { background-color: rgb(var(--c-n-600-rgb)); }
.hud-tile.is-insufficient .hud-tile-label { color: rgb(var(--c-n-600-rgb)); }
.hud-tile.is-insufficient .hud-tile-cost { color: rgb(var(--c-danger-400-rgb)); }

/* ⚠️ 「爆薬だけ通常時から赤くする」規則はここにあったが撤去した（2026-08-12）。
   この画面では赤が既に「TP不足」の意味を持っている（すぐ上の .is-insufficient が
   コスト数値を赤にする）。危険物にも赤を使うと意味が二重になり、
   TPが足りていて押せる爆薬タイルが常時赤いせいで「使えない」と誤読される。
   実機で実際にそう見えたため、赤は不足の意味だけに使う。
   test/uiConsistency.test.mjs「タイルの赤はTP不足の意味だけに使われている」が固定している。 */

/* --- スキルタイル --- */
.hud-tile-skill { border-color: rgb(var(--c-accent-400-rgb) / 0.35); }

/* 使用不可（ハッキング中でない／TP不足）。押せないことだけを伝えればよいので、
   理由は分けない。 */
.hud-tile.is-unusable {
    border-color: rgb(var(--c-n-500-rgb) / 0.2);
    background: rgb(var(--c-n-800-rgb) / 0.3);
}
.hud-tile.is-unusable .hud-tile-icon { background-color: rgb(var(--c-n-700-rgb)); }
.hud-tile.is-unusable .hud-tile-label,
.hud-tile.is-unusable .hud-tile-cost,
.hud-tile.is-unusable .hud-tile-key { color: rgb(var(--c-n-700-rgb)); }

/* クールダウン: 遮蔽がタイルの下端から立ち上がり、残り時間に比例した高さになる。
   高さは js/ui/hudTiles.js の applySkillState が style.height へ % で書く。
   以前は全面の黒幕＋数字で、残量が数字を読まないと分からなかった。 */
.hud-tile-gauge {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 0;
    background: rgb(var(--c-n-950-rgb) / 0.82);
    border-top: 1px solid rgb(var(--c-accent-400-rgb) / 0.7);
    pointer-events: none;
}
/* クールダウン中でないときは、高さ0でも border-top の1px線が残る。消す */
.hud-tile:not(.is-cooldown) .hud-tile-gauge { display: none; }

.hud-tile-timer {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 6px;
    font-size: 11px;
    line-height: 1;
    color: rgb(var(--c-accent-300-rgb));
    pointer-events: none;
}

/* タイルを横に並べる棚。設置物は5枚固定、スキルは1〜4枚の可変なので右揃え */
.hud-tile-row {
    display: flex;
    gap: 5px;
    justify-content: flex-end;
}

/* ===== ブリーフィングの隊形選択 ===== */
/*
 * 隊形図は StrategyScene が FORMATIONS の表から SVG を組む。
 * ここでは色と大きさだけを持つ（座標は JS 側の1箇所に集める）。
 */
/*
 * ブリーフィング内の小見出し。Tailwind のユーティリティで書くと text-[11px] などの
 * 新規クラスが要り、生成物 css/tailwind.css の作り直しが必要になる。
 *
 * 色は隣の説明文（.text-slate-300）と揃える。n-500 では実寸11pxで暗くて読めない。
 */
.briefing-caption {
    margin: 0 0 6px;
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    font-size: 11px;
    letter-spacing: 0.14em;
    color: rgb(var(--c-n-300-rgb));
}

.formation-card {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 8px 4px 7px;
    border: 1px solid rgb(var(--c-n-700-rgb));
    border-radius: 4px;
    background: rgb(var(--c-n-950-rgb) / 0.8);
    cursor: pointer;
    transition: border-color 0.15s, background-color 0.15s;
}

.formation-card:hover:not(:disabled) {
    border-color: rgb(var(--c-accent-400-rgb));
    background: rgb(var(--c-accent-900-rgb) / 0.35);
}

/* ⚠️ ここも色替えだけにしない（.supply-card.is-current と同じ理由）。
   陣形カードは中身が中央揃えなので、左端のバーではなく枠の太さで示す。
   padding を 1px 減らして、選択で外形が動かないようにする。 */
.formation-card.is-selected {
    border: 2px solid rgb(var(--c-warn-400-rgb));
    padding: 7px 3px 6px;
    box-shadow: inset 0 0 0 1px rgb(var(--c-warn-400-rgb) / 0.35);
    background: rgb(var(--c-warn-900-rgb) / 0.3);
}

.formation-card:disabled {
    cursor: not-allowed;
    opacity: 0.4;
    filter: grayscale(1);
}

.formation-diagram {
    width: 46px;
    height: 63px;
}

/* 少尉。編成スロットの枠と同じ系統の色で「これが自分」と分かるようにする */
.formation-diagram .fm-lead { fill: rgb(var(--c-accent-400-rgb)); }
.formation-diagram .fm-npc { fill: rgb(var(--c-support-300-rgb)); }

/* 進行方向を示す矢印。隊形図は上が前 */
.formation-diagram .fm-axis {
    fill: none;
    stroke: rgb(var(--c-n-600-rgb));
    stroke-width: 8;
}

.formation-name {
    font-size: 12px;
    font-weight: bold;
    line-height: 1.1;
    color: rgb(var(--c-n-100-rgb));
}

/* 英語の略記。日本語名の横に小さく添える */
.formation-name em {
    font-style: normal;
    font-size: 9px;
    letter-spacing: 0.08em;
    color: rgb(var(--c-n-300-rgb));
}

/*
 * 効果の1行。以前は support-300（緑）だったが、緑は「良いこと」に見えるため
 * 楔形の「被弾 +10%」まで利点のように読めてしまう。中立の n-300 にして、
 * 選択中だけ warn-400 にする。
 */
.formation-effect {
    font-size: 10px;
    line-height: 1.25;
    text-align: center;
    color: rgb(var(--c-n-300-rgb));
}

.formation-card.is-selected .formation-effect { color: rgb(var(--c-warn-400-rgb)); }

/* ===== ブリーフィングのパネルを画面内に収める ===== */
/*
 * パネルには min-h-[700px] があるだけで上限が無く、中身が増えた分だけ縦に伸びる。
 * 強化タブは中のスクロール領域を max-h-[420px] で抑えているので気づかなかったが、
 * 部隊編成タブに隊形の欄を足したところ、ウインドウ高さ870pxで941pxまで伸びて
 * 上下に36pxずつはみ出した（実機で指摘を受けた）。
 *
 * ⚠️ 上限を付けるだけでは直らない。flex/grid の子は既定が min-height:auto で
 *    中身より縮まないため、パネルだけ縮めると右カラムの overflow:hidden に
 *    切られ、隊形の欄が見えないまま消える（870px で70px切れていた）。
 *    パネルから roster-list までの連鎖すべてに min-height:0 を通すこと。
 */
#strategy-ui > div {
    max-height: calc(100vh - 24px);
}

#strategy-ui > div > .relative,
#strategy-ui .grid,
#strategy-ui .grid > div,
#strategy-ui .flex-1,
#content-mission,
#content-squad,
#content-supply,
#roster-list {
    min-height: 0;
}

/*
 * ロスターと説明の幅の配分。
 *
 * ⚠️ **説明カラムを固定幅にしないこと。** 固定にするとロスターの取り分が先に
 *    決まり、下士官が増えたとき破綻する。下士官は現在4人だが将来2名ほど増える予定。
 *
 * 最小ウインドウ 1296px（electron-main.js の minWidth）での実測:
 *
 *   説明カラム    ロスターの箱   116px カードが入る数
 *   無し（旧）    1069px         8枚
 *   240px 固定     817px         6枚（余裕ゼロ）
 *   340px 固定     717px         5枚 ← 6人で破綻する
 *
 * そこで**ロスター側に下限**を与え、説明は残り全部を使う。
 *   782px = カード116 × 6 ＋ gap12 × 5 ＋ padding12 × 2 ＋ **枠線1 × 2**
 *
 * ⚠️ 枠線を数え忘れないこと。box-sizing: border-box なので、枠線のぶんだけ
 *    内側が狭くなる。780px にしていたときは 2px 足りず、6人目が2行目へ落ちた
 *    （ブラウザの実測で発覚。計算だけでは気づけなかった）。
 *
 * 7人目以降は flex-wrap で2行へ流れる。幅が足りない＝破綻ではない。
 */
/* ⚠️ flex-grow は 0。1 にするとロスターが余白を説明カラムと山分けしてしまい、
   最小ウインドウで説明が 139px まで痩せた（実測）。ロスターは 780px で
   6人ぶん足りるので、余りは全部**説明へ回す**（テキストは幅があるほど読みやすい）。 */
/*
 * ⚠️ **この規則を上の `min-height: 0` のグループへ混ぜないこと。**
 *    `#roster-list` はもともとあのグループの**最後の1つ**で、そこを置換した結果
 *    `min-width` と `flex` が `#strategy-ui .flex-1` 全部へ効き、
 *    **タブボタンが1つ 782px に膨らんで画面が横に 1178px 溢れた**
 *    （実機で「画面がめちゃくちゃ」と指摘を受けた）。
 *    グループセレクタの末尾は、単独の規則に見えるので狙って踏む。
 */
#roster-list {
    min-width: 782px;
    flex: 0 1 782px;
}

/*
 * キャラ説明。ロスターと**横に並ぶ対の箱**なので、枠・余白・角丸を揃える。
 *
 * ⚠️ min-width: 0 が要る。flex 項目の既定は min-width: auto で、
 *    中身（長い説明文）より縮まないため、ロスターを押し出してしまう。
 *
 * ⚠️ **見た目をここに置く理由**: 以前は StrategyScene が描画のたびに
 *    className を丸ごと書き込んでいた。その文字列は説明がロスターの「下」に
 *    あった頃のもので、`mt-2` が入っていたため横並びにした途端**上端が
 *    ロスターと 8px ずれた**（実機で指摘を受けた）。加えて index.html で
 *    付けたクラスまで毎回消えていた。JS は中身だけを入れること。
 */
#roster-info {
    flex: 1 1 0;
    min-width: 0;
    padding: 12px;
    border: 1px solid rgb(var(--c-n-700-rgb));
    border-radius: 4px;
    background: rgb(var(--c-n-900-rgb) / 0.8);
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    font-size: 12px;
    line-height: 1.6;
    color: rgb(var(--c-n-300-rgb));
}

/* ===== 補給画面（個人装備） ===== */

/*
 * 出撃する4名を縦に並べる。1行がそのまま「誰に何を持たせるか」の単位。
 * 顔写真を左端に置くのは、名前だけだと部隊編成タブで選んだ顔と結び付かないため。
 */
.gear-unit {
    display: grid;
    grid-template-columns: 34px 76px 1fr 96px;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 7px 9px;
    border: 1px solid rgb(var(--c-n-700-rgb));
    border-radius: 4px;
    background-color: rgb(var(--c-n-950-rgb) / 0.8);
    cursor: pointer;
    text-align: left;
    transition: border-color 0.15s, background-color 0.15s;
}

.gear-unit:hover { border-color: rgb(var(--c-accent-400-rgb)); }

/* 選択中の隊員。ここへ装備が入る */
.gear-unit.is-selected {
    border-color: rgb(var(--c-warn-400-rgb));
    background: rgb(var(--c-warn-900-rgb) / 0.3);
}

/* 顔写真。部隊編成タブの枠と同じ丸めと縁で「同じ人」と分かるようにする */
.gear-unit-face {
    width: 34px;
    height: 34px;
    object-fit: cover;
    border: 1px solid rgb(var(--c-n-700-rgb));
    border-radius: 3px;
    background: rgb(var(--c-n-800-rgb));
}

.gear-unit.is-selected .gear-unit-face { border-color: rgb(var(--c-warn-400-rgb)); }

.gear-unit-name {
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    font-size: 12px;
    font-weight: 700;
    color: rgb(var(--c-accent-400-rgb));
}

.gear-unit-slots {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

/*
 * 装備スロット。**アイコンだけ**を入れる小さな枠。名前は title（ホバー）で出す。
 *
 * ⚠️ 以前は装備名を文字で並べていた。何も買っていない初期状態では
 *    「標準支給 標準支給 標準支給」と同じ語が3つ並び、情報量がゼロだった
 *    （実機で指摘を受けた）。
 *
 * ⚠️ 下士官は3スロットが横に並ぶ。大きくすると flex-wrap で折り返して
 *    行が高くなるので、30px を超えないこと。補給カードの 36px は入らない。
 *
 * ⚠️ 枠とアイコンは**別の要素**。applyGearIcon はアイコンが無いとき要素ごと
 *    隠すので、同じ要素に枠を持たせると空きスロットの破線まで消える。
 */
.gear-slot-chip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border: 1px solid rgb(var(--c-n-700-rgb));
    border-radius: 3px;
}

.gear-slot-icon {
    width: 22px;
    height: 22px;
    background-color: rgb(var(--c-n-200-rgb));
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
}

/* 標準支給。購入品より一段沈めて「まだ手を入れていない枠」と分かるようにする。
   ⚠️ n-500 まで落とすと形が読めない（文字だったころ実機で指摘を受けた）。 */
.gear-slot-chip.is-default .gear-slot-icon { background-color: rgb(var(--c-n-400-rgb)); }

/* 空きスロット。補助の標準支給は**中身が「なし」**（support_none）で
   アイコンを持たない。破線にして「ここは空いている」と示す。 */
.gear-slot-chip.is-empty {
    border-style: dashed;
    border-color: rgb(var(--c-n-600-rgb));
}

/* 移動力。重量の影響がその場で読めないと、重い装備を選ぶ理由が分からない */
.gear-unit-mobility {
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    font-size: 12px;
    text-align: right;
    color: rgb(var(--c-n-200-rgb));
}

.gear-unit-mobility.is-slow { color: rgb(var(--c-warn-400-rgb)); }
.gear-unit-mobility.is-fast { color: rgb(var(--c-support-400-rgb)); }

/*
 * 調達／配備の切替。
 *
 * ⚠️ **全幅に置くこと。** 以前は右カラムの中に置いていたため、
 *    「調達＝左カラムを見ない」「配備＝左カラムが全て」という意味の違いが
 *    伝わらず、左カラムの意味が黙って変わっていた（実機で指摘を受けた）。
 *    段の境界は画面の境界に合わせる。
 */
/* 下線を敷く帯。選択中のタブがこの線を色付きで上書きして、どちらが開いているかを示す */
.supply-stage-tabs {
    display: flex;
    border-bottom: 1px solid rgb(var(--c-n-700-rgb));
}

/*
 * ⚠️ **角丸の箱にしない。** 以前は角丸・1px枠・暗い背景・左にラベル／右に数値
 *    という形にしていたが、それは下の補給カードと**まったく同じ意匠**で、
 *    「もう1枚のカード」に見えて押せると分からなかった（実機で指摘を受けた）。
 *
 * ブリーフィング上部のタブ（作戦説明／部隊編成／補給）と同じ
 * 「下線＋色付き文字」を使う。プレイヤーは既にその形をタブとして学習しているので、
 * 覚え直しが要らない。階層は文字サイズと位置（パネルの内側）で示す。
 */
.supply-stage-tab {
    flex: 1 1 0;
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: 10px;
    padding: 8px 10px;
    border: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;   /* 親の下線に重ねて、選択中を地続きに見せる */
    background: transparent;
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    font-size: 14px;
    font-weight: 700;
    /* ⚠️ 上のタブは非選択に n-500 を使っているが、この画面は文字が暗いという
       指摘を受けて 6:1 を下限に揃えた経緯がある。ここだけ 4.5:1 に落とさない。
       選択中との区別は色・下線・背景の3つで付いているので、
       非選択を沈めきる必要は無い。 */
    color: rgb(var(--c-n-400-rgb));
    cursor: pointer;
    transition: border-color 0.15s, background-color 0.15s, color 0.15s;
}

.supply-stage-tab:hover:not(.is-active) { color: rgb(var(--c-n-200-rgb)); }

.supply-stage-tab.is-active {
    border-bottom-color: rgb(var(--c-accent-400-rgb));
    background: rgb(var(--c-accent-900-rgb) / 0.2);
    color: rgb(var(--c-accent-400-rgb));
}

/*
 * 残っている仕事の数。
 *
 * ⚠️ 数だけを出さない。「8」とだけ書かれても何の数か分からない（実機で指摘を
 *    受けた）。「未調達 8」「未配備 3」と、**何が残っているか**を必ず添える。
 *    どちらも 0 なら「この段でやることは無い」という一貫した読み方になる。
 *
 * ⚠️ ラベルから離して右端へ飛ばさない。左端と右端に分けると「見出しと値」の
 *    データ行に見え、押せるものに見えなくなる。ラベルの直後に置く。
 */
.supply-stage-todo {
    font-size: 11px;
    font-weight: 400;
    white-space: nowrap;
    color: rgb(var(--c-warn-400-rgb));
}

/* 残りが無い段。色で「終わっている」と伝えるが、
   n-500 まで落とすと読めない（同じ画面で一度直した失敗を繰り返さない） */
.supply-stage-todo.is-done { color: rgb(var(--c-n-400-rgb)); }

/* 調達したばかりの品目。配備側へ移ったとき、どれが増えたのか分かるようにする */
.supply-card.is-new {
    border-color: rgb(var(--c-support-400-rgb));
    box-shadow: 0 0 0 1px rgb(var(--c-support-400-rgb) / 0.35);
}

/* 段が空のときの案内。空欄のままだと「壊れている」ように見える */
.gear-empty {
    padding: 14px 10px;
    border: 1px dashed rgb(var(--c-n-700-rgb));
    border-radius: 4px;
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    font-size: 12px;
    line-height: 1.6;
    text-align: center;
    color: rgb(var(--c-n-300-rgb));
}

/*
 * 補給品目のカテゴリ見出し（武器・防具・補助・端末）。
 *
 * ⚠️ 10px / n-500 では背景とのコントラストが 4.5:1 しかなく、実機で
 *    「小さくて暗い」と指摘を受けた。見出しは中身より読みやすくあるべきで、
 *    ここを沈めるとカテゴリの切れ目そのものが見えなくなる。
 */
.gear-cat-label {
    margin: 12px 0 5px;
    padding-bottom: 3px;
    border-bottom: 1px solid rgb(var(--c-n-700-rgb) / 0.7);
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.12em;
    color: rgb(var(--c-n-200-rgb));
}

/* 先頭のカテゴリだけは上の余白が要らない（見出しの直下に来るため） */
.gear-cat-label:first-child { margin-top: 0; }

/* 調達の段では品目を2列に並べる。カテゴリ見出しは両列にまたがる。
   またがせないと、見出しが1列目に入って品目とずれる */
#gear-catalog .gear-cat-label { grid-column: 1 / -1; }

/*
 * 補給カード。**部隊強化と個人装備で同じ見た目を使う**。
 *
 * ⚠️ 以前は部隊強化だけが大きな Tailwind のカード（アイコン＋説明文＋
 *    CURRENT/NEXT ＋ボタン、1枚251px）で、個人装備は小さな行だった。
 *    同じ「買う」操作なのに見た目も押し方も違い、同じ画面に並ぶと
 *    別の機能に見える（実機で指摘を受けた）。
 *
 * 押し方も揃えてある: **カードそのものを押すと買う**。
 * 部隊強化だけカードの中のボタンを押させる、という差を作らない。
 */
.supply-card {
    display: grid;
    grid-template-columns: 36px 1fr;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 6px 9px;
    border: 1px solid rgb(var(--c-n-700-rgb));
    border-radius: 4px;
    /* ⚠️ ショートハンドの `background:` を使わないこと。background-image を none へ
       リセットするため、順序に関わらず .surface-card のグラデーションが消える。 */
    background-color: rgb(var(--c-n-950-rgb) / 0.8);
    cursor: pointer;
    text-align: left;
    transition: border-color 0.15s, background-color 0.15s;
}

.supply-card:hover:not(:disabled) {
    border-color: rgb(var(--c-accent-400-rgb));
    background-color: rgb(var(--c-accent-900-rgb) / 0.35);
}

/*
 * 今の状態を示す。装備なら「着けている」、強化なら「MAX」。
 * どちらも押しても何も起きないので、カーソルとホバーで手を止める。
 * 灰色にはしない（押せないのではなく、もう到達しているという意味なので）。
 */
/*
 * ⚠️ 色替えだけにしない。色は既に**状態の意味を担って飽和している**
 *    （緑＝配備中・成功／赤＝WP不足／黄＝選択中）。左端の縦バーという
 *    「形」を足すことで、色に頼らずに区別でき、色覚特性にも左右されない。
 *
 * ⚠️ border-left を太くしたぶん padding-left を減らすこと（9px → 5px）。
 *    減らさないと、装備を着け替えるたびにカードの中身が4px横へ跳ねる。
 */
.supply-card.is-current {
    border-color: rgb(var(--c-warn-400-rgb));
    border-left: 5px solid rgb(var(--c-warn-400-rgb));
    padding-left: 5px;
    background-color: rgb(var(--c-warn-900-rgb) / 0.3);
    cursor: default;
}

.supply-card.is-current:hover {
    border-color: rgb(var(--c-warn-400-rgb));
    background-color: rgb(var(--c-warn-900-rgb) / 0.3);
}

/*
 * WP が足りない。
 *
 * ⚠️ 一律の減光（opacity + grayscale）にしない。戦闘画面の設置タイルは
 *    `.hud-tile.is-insufficient` で「アイコンと名前だけ沈め、コスト数値を赤に残す」
 *    形にしてあり、**足りないのは金額だ**と示している。同じ意味を同じ見た目で
 *    伝えるため、補給画面もそれに揃える（実機で指摘を受けた）。
 */
.supply-card:disabled {
    cursor: not-allowed;
    border-color: rgb(var(--c-n-500-rgb) / 0.25);
    background: rgb(var(--c-n-800-rgb) / 0.35);
}

/* ⚠️ n-600 まで沈めると背景とのコントラストが 2.3:1 になり、何を買えなかったのか
   すら読めなくなる（実機で指摘を受けた）。押せないと分かる程度に留める。 */
.supply-card:disabled .supply-card-name,
.supply-card:disabled .supply-card-body,
.supply-card:disabled .supply-card-dots { color: rgb(var(--c-n-400-rgb)); }

.supply-card:disabled .supply-card-price { color: rgb(var(--c-danger-400-rgb)); }

/*
 * 装備アイコン。素材は白1枚のマスクで、色はここが background-color で与える。
 *
 * ⚠️ **カテゴリで色を分けないこと。** 色は既に「状態」の意味を担って飽和して
 *    いる（緑＝配備中・成功／赤＝WP不足／黄＝選択中）。カテゴリにも色を割り
 *    当てると、同じ緑が「配備中」と「防具」の2つを意味することになる。
 *    カテゴリの区別はアイコンの**形**とカード内のラベルが担う。
 *
 * ⚠️ 36px。24px にしないこと。既存の icon-sheet.png では、機関銃座のような
 *    密度の高い図像が 24px で既に潰れている。
 */
.supply-card-icon {
    width: 36px;
    height: 36px;
    background-color: rgb(var(--c-n-200-rgb));
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
}

/* WP が足りないカードでは、アイコンも名前と同じだけ沈める。
   ⚠️ ただし価格は赤のまま残す（足りないのは金だ、と示すため）。 */
.supply-card:disabled .supply-card-icon { background-color: rgb(var(--c-n-400-rgb)); }

.supply-card-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 8px;
}

/*
 * カード内のカテゴリ表示。**全幅の見出しを廃した代わり**。
 *
 * ⚠️ 見出しを廃したのは、品目が1つのカテゴリだと右列が空くため。調達が進むほど
 *    品目が減るので、終盤ほど画面が痩せていく作りだった。
 *
 * 補助情報なので小さくするが、12px は割らない（過去に「小さくて暗い」と指摘）。
 */
.supply-card-cat {
    flex: 0 0 auto;
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    font-size: 12px;
    letter-spacing: 0.08em;
    color: rgb(var(--c-n-400-rgb));
}

/*
 * 品名。⚠️ **効果説明より2px以上大きく保つこと。** 以前は 13px で、説明(12px)と
 *    ほぼ同じ大きさだったため、カードに視線の入口が無かった。
 *
 * ⚠️ 差をつけるのに説明側を小さく・暗くしてはいけない。10px / n-300 にしていた頃、
 *    実機で「小さくて暗い」と2回指摘を受けている。上げる方向だけで差をつける。
 */
.supply-card-name {
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    font-size: 15px;
    font-weight: 700;
    color: rgb(var(--c-n-100-rgb));
}

.supply-card-price {
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    font-size: 12px;
    color: rgb(var(--c-warn-400-rgb));
    white-space: nowrap;
}

/* 解禁済み・MAX。もう買う必要が無いことを価格と同じ位置で伝える */
.supply-card-price.is-owned { color: rgb(var(--c-support-400-rgb)); }

/*
 * 効果と説明。**カードの中で最も読ませたい行**なので、名前の次に強くする。
 * ⚠️ 10px / n-300 では実機で「小さくて暗い」と指摘を受けた。
 *    ここが読めないと、何を買うかの判断そのものができない。
 */
.supply-card-body {
    margin-top: 3px;
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    font-size: 12px;
    line-height: 1.55;
    color: rgb(var(--c-n-200-rgb));
}

/* 強化の段階。個人装備には段階が無いので装備側では使わない。
   ⚠️ 未取得の ○ も**数えられる**濃さが要る。n-600 では見えず、
      あと何段残っているのか読めない。埋まった段は色相（琥珀）で区別する。 */
.supply-card-dots {
    margin-left: 6px;
    letter-spacing: 1px;
    color: rgb(var(--c-n-400-rgb));
}

.supply-card-dots .is-filled { color: rgb(var(--c-warn-400-rgb)); }

/* 効果の変化。「今 → 次」を1行で見せる */
.supply-card-next { color: rgb(var(--c-accent-400-rgb)); }

.gear-item-weight { color: rgb(var(--c-n-300-rgb)); }
.gear-item-weight.is-heavy { color: rgb(var(--c-warn-400-rgb)); }
.gear-item-weight.is-light { color: rgb(var(--c-support-400-rgb)); }

/* ===== 部隊編成のロスター ===== */
/*
 * ⚠️ 顔写真は割合（w-[22%]）ではなく固定幅にする。ブリーフィングを全幅の
 *    3画面にしたとき、22% が 231px まで膨らんで出撃メンバーの枠（159px）より
 *    大きくなり、キャラ説明が画面外へ押し出された（実機で指摘を受けた）。
 *    出撃メンバーより小さく保つこと。
 */
.roster-card {
    width: 116px;
    flex: 0 0 auto;
}

/*
 * キャラ説明はロスターの**外**に置く。中に入れると、ロスターを
 * スクロールしないと読めない位置へ流れていく。
 */
#roster-info:empty { display: none; }

/* ===== 部隊戦力のレーダーチャート ===== */
/*
 * 内側の六角形が「何も投資していない状態」、塗りが現在。
 * 内側の線より外にあれば伸びている、内にあれば落ちている。
 *
 * ⚠️ 色は :root のトークンから引く（16進の直書きは禁止）。
 *    基礎の線は控えめな中立色、現在はアクセント、伸びた軸の数値は支援色。
 */
/*
 * 隊形カードと部隊戦力の見出しの間。
 *
 * ⚠️ Tailwind の `mt-8` では効かない。`.briefing-caption` が `margin: 0 0 6px` と
 *    **ショートハンド**で書いており、style.css が tailwind.css より後に読まれるので
 *    margin-top ごと打ち消される。実測すると隙間が 0px だった。
 *    見出しの間隔はここで指定すること。
 */
#power-caption { margin-top: 26px; }

.power-chart {
    width: 210px;
    height: 210px;
    max-width: 100%;
}

/* 少尉の講評。チャートの右に並べる */
.power-analysis-title {
    margin: 4px 0 6px;
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.08em;
    color: rgb(var(--c-accent-400-rgb));
}

.power-analysis-line {
    margin: 0 0 5px;
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    font-size: 11px;
    line-height: 1.6;
    color: rgb(var(--c-n-300-rgb));
}

/* 講評の各行に見出し記号を付ける。段落が続くと箇条書きに見えないため */
.power-analysis-line::before {
    content: '> ';
    color: rgb(var(--c-n-500-rgb));
}

/* 目盛りの輪。読み取りの補助でしかないので、ほとんど見えない濃さでよい */
.power-ring {
    fill: none;
    stroke: rgb(var(--c-n-700-rgb) / 0.45);
    stroke-width: 1;
}

.power-spoke {
    stroke: rgb(var(--c-n-700-rgb) / 0.5);
    stroke-width: 1;
}

/* 基礎の線。破線にして「現在」と取り違えられないようにする */
.power-base {
    fill: none;
    stroke: rgb(var(--c-n-400-rgb) / 0.8);
    stroke-width: 1.5;
    stroke-dasharray: 4 3;
}

.power-current {
    fill: rgb(var(--c-accent-400-rgb) / 0.22);
    stroke: rgb(var(--c-accent-400-rgb));
    stroke-width: 2;
    stroke-linejoin: round;
}

.power-label {
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    font-size: 10px;
    fill: rgb(var(--c-n-300-rgb));
}

.power-value {
    font-family: 'Fira Code', 'IBM Plex Sans JP', monospace;
    font-size: 10px;
    font-weight: 700;
    fill: rgb(var(--c-n-400-rgb));
}

/* 伸びた軸だけ色を変える。全部に色を付けると、どこが伸びたのか読めない */
.power-value.is-grown { fill: rgb(var(--c-support-400-rgb)); }
