:root {
  --color-red: #c70025;
  --color-orange: #ee781f;
  --color-yellow: #fabe00;
  --color-green: #22ac38;
  --color-blue: #1bb8ce;
  --color-pink: #f19ec2;
  --color-purple: #915da3;
  --color-white: #e2e8f0;
  --color-black: #1e293b;
  /* エイドス物語戦専用の黒い駒(noir)の手番グロー色。7色には含まれない見た目専用の色。 */
  --color-noir: #4b3f66;
  --color-rainbow: linear-gradient(135deg, #c70025, #ee781f, #fabe00, #22ac38, #1bb8ce, #f19ec2, #915da3);
}

/* 縦向き警告オーバーレイ（index.htmlの#rotate-device-overlay）。既定は非表示。
   タッチ端末（hover:none かつ pointer:coarse ＝スマホ/タブレット）が縦向きの時だけ
   全面に「横向きにしてください」を出す。デスクトップやランドスケープでは決して出さない。
   盤面の座標計算・描画には一切干渉しない（CSSのメディアクエリで出し分けるだけ）。 */
#rotate-device-overlay {
  display: none;
}
@media (orientation: portrait) and (hover: none) and (pointer: coarse) {
  #rotate-device-overlay {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: 2147483000; /* すべてのモーダル・演出より前面 */
    align-items: center;
    justify-content: center;
    padding: 2rem;
    box-sizing: border-box;
    background: #0b1016;
    color: #e2e8f0;
    text-align: center;
    font-family: sans-serif;
  }
  /* ユーザー要望2026-08-08「なるべく画面全体に大きく表示させたい」。縦向きスマホの広い余白を
     埋めるよう、内容を画面幅・高さに追従して大きくする（clamp+vw/vmin）。 */
  #rotate-device-overlay .rotate-device-inner {
    max-width: min(92vw, 34rem);
    width: 100%;
  }
  #rotate-device-overlay .rotate-device-icon {
    font-size: clamp(5rem, 34vw, 13rem);
    line-height: 1;
    margin-bottom: clamp(1rem, 5vw, 2.5rem);
    display: inline-block;
    animation: rotate-device-tilt 1.8s ease-in-out infinite;
  }
  #rotate-device-overlay .rotate-device-title {
    font-size: clamp(1.6rem, 8vw, 3rem);
    font-weight: bold;
    margin-bottom: clamp(0.8rem, 3.5vw, 1.6rem);
  }
  #rotate-device-overlay .rotate-device-sub {
    font-size: clamp(1rem, 4.4vw, 1.5rem);
    line-height: 1.7;
    color: #94a3b8;
  }
}
/* 端末を横に倒す動きを示すアイコンのゆらぎ。 */
@keyframes rotate-device-tilt {
  0%, 100% { transform: rotate(0deg); }
  55%, 80% { transform: rotate(-90deg); }
}

/* 【続き574】起動時の案内モーダル（opening-setup-modal.js）。言語と「ホーム画面に追加」を
   最初にしっかり案内する。**タッチ端末にだけ出す**ので、実質ここのスマホ／タブレット用の
   寸法が本番の見え方になる。1600x900 のステージを縮めて表示する作りなので、iPhone の横持ち
   （932x430）では 1rem が実画面で 7〜8px にしかならない——だから文字は 2 倍前後に拡大する
   （続き566・572 で 6px 台のまま出してしまった件の再発防止。必ず実画面のpxで測ってから決める）。 */
#opening-setup-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(30rem, 92vw);
  z-index: 100061; /* backdrop(100060)より前面 */
  box-sizing: border-box;
  padding: 1.3rem;
  border-radius: 0.6rem;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(251, 191, 36, 0.5);
  color: #e2e8f0;
  font-family: sans-serif;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
  max-height: 52rem;
  overflow-y: auto;
}
.opening-setup-title {
  font-size: 1.15rem;
  font-weight: bold;
  color: #fbbf24;
  margin-bottom: 0.8rem;
  text-align: center;
}
.opening-setup-section {
  margin-top: 0.9rem;
  padding-top: 0.9rem;
  border-top: 1px solid rgba(148, 163, 184, 0.25);
}
.opening-setup-section:first-of-type {
  border-top: none;
  padding-top: 0;
}
.opening-setup-label {
  font-size: 0.9rem;
  font-weight: 600;
  margin-bottom: 0.4rem;
}
/* 説明・注記の色は決め打ちせず、周りから継いで薄めるだけにする（明暗2テーマがあるため
   ＝教訓15。決め打ちすると片方のテーマで文字が見えなくなる）。 */
.opening-setup-why,
.opening-setup-steps,
.opening-setup-note {
  font-size: 0.8rem;
  line-height: 1.6;
  opacity: 0.85;
}
.opening-setup-steps {
  margin: 0.5rem 0 0;
  padding-left: 1.4rem;
}
.opening-setup-note {
  font-size: 0.72rem;
  margin-top: 0.4rem;
}
.opening-setup-lang-row {
  display: flex;
  gap: 0.5rem;
}
.opening-setup-lang-btn {
  flex: 1 1 0;
  padding: 0.5rem 0.6rem;
  border-radius: 0.4rem;
  border: 1px solid rgba(148, 163, 184, 0.5);
  background: rgba(2, 6, 12, 0.35);
  color: inherit;
  font-family: sans-serif;
  font-size: 0.9rem;
  cursor: pointer;
}
.opening-setup-lang-btn.is-current {
  border-color: rgba(251, 191, 36, 0.9);
  background: rgba(251, 191, 36, 0.18);
  font-weight: 700;
}
.opening-setup-primary,
.opening-setup-secondary {
  display: block;
  width: 100%;
  margin-top: 0.6rem;
  padding: 0.6rem 1rem;
  border-radius: 0.4rem;
  border: none;
  font-family: sans-serif;
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
}
.opening-setup-primary {
  background: #f59e0b;
  color: #201400;
}
.opening-setup-secondary {
  background: rgba(148, 163, 184, 0.25);
  color: inherit;
  border: 1px solid rgba(148, 163, 184, 0.5);
}
.opening-setup-primary:disabled {
  opacity: 0.6;
  cursor: default;
}
.opening-setup-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  margin-top: 1rem;
}
.opening-setup-play {
  flex: 1 1 0;
  padding: 0.5rem 1rem;
  border-radius: 0.4rem;
  border: 1px solid rgba(148, 163, 184, 0.5);
  background: rgba(2, 6, 12, 0.35);
  color: inherit;
  font-family: sans-serif;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
}
.opening-setup-never {
  flex: none;
  padding: 0.4rem 0.6rem;
  border: none;
  background: transparent;
  color: inherit;
  opacity: 0.6;
  font-family: sans-serif;
  font-size: 0.75rem;
  text-decoration: underline;
  cursor: pointer;
}
/* ライトテーマ（既定）でも読めるように（first-run-bgm.js のモーダルと同じ扱い）。 */
body.theme-light #opening-setup-modal {
  background: #fdfbf5;
  border-color: rgba(176, 141, 63, 0.6);
  color: #3a3326;
}
body.theme-light .opening-setup-title {
  color: #8a6d2f;
}
body.theme-light .opening-setup-lang-btn,
body.theme-light .opening-setup-play {
  background: rgba(176, 141, 63, 0.12);
  border-color: rgba(176, 141, 63, 0.45);
}
/* スマホ・タブレット（＝実際に出る端末）での拡大。 */
body.is-phone-device #opening-setup-modal,
body.is-tablet-device #opening-setup-modal {
  width: min(60rem, 94vw);
  padding: 2rem 2.2rem;
  border-radius: 1rem;
}
body.is-phone-device .opening-setup-title,
body.is-tablet-device .opening-setup-title {
  font-size: 2.1rem;
  margin-bottom: 1.2rem;
}
body.is-phone-device .opening-setup-label,
body.is-tablet-device .opening-setup-label {
  font-size: 1.7rem;
  margin-bottom: 0.7rem;
}
body.is-phone-device .opening-setup-why,
body.is-tablet-device .opening-setup-why,
body.is-phone-device .opening-setup-steps,
body.is-tablet-device .opening-setup-steps {
  font-size: 1.5rem;
  line-height: 1.7;
}
body.is-phone-device .opening-setup-note,
body.is-tablet-device .opening-setup-note {
  font-size: 1.15rem;
  margin-top: 0.7rem;
}
body.is-phone-device .opening-setup-lang-btn,
body.is-tablet-device .opening-setup-lang-btn {
  font-size: 1.7rem;
  padding: 0.8rem 1rem;
  border-radius: 0.6rem;
}
body.is-phone-device .opening-setup-primary,
body.is-tablet-device .opening-setup-primary,
body.is-phone-device .opening-setup-secondary,
body.is-tablet-device .opening-setup-secondary {
  font-size: 1.7rem;
  padding: 0.9rem 1.2rem;
  border-radius: 0.6rem;
  margin-top: 0.9rem;
}
body.is-phone-device .opening-setup-play,
body.is-tablet-device .opening-setup-play {
  font-size: 1.5rem;
  padding: 0.8rem 1.2rem;
  border-radius: 0.6rem;
}
body.is-phone-device .opening-setup-never,
body.is-tablet-device .opening-setup-never {
  font-size: 1.4rem;
  padding: 0.7rem 0.8rem;
}
/* 【2026-10-12】**画面が低い端末で、この案内が読めなかった。**
   実測（本番・932x320＝iPhone横持ちで Safari の上下のバーが出ている状態。ステージ倍率0.356）:
   11行すべてが11px未満で、「このまま遊ぶ」が **8.5px**、手順が 8.5px、注記が 6.5px だった
   （932x430 なら 11.5px）。**10/17 に来た人が最初に見る画面がこれ**で、いちばん伝えたい
   「ホーム画面に追加してください」が読めない。

   🔴 **字を大きくするだけでは入らない**——320px の画面でモーダルは既に 268px（84%）を使っていた。
   **長い説明を隠してから字を大きくする**（続き574 で同じ判断をした。「字を大きくする」は
   文章を削ることと一緒に考える）。横幅も広げる＝手順の折り返しが減って、そのぶん縦が縮む。
   `max-height` の境目は**ビューポートの高さ**で見る（`vh` を長さに使うのとは別の話で、
   メディアクエリの判定はステージ倍率の影響を受けない）。 */
@media (max-height: 380px) {
  body.is-phone-device #opening-setup-modal,
  body.is-tablet-device #opening-setup-modal {
    width: 88rem; /* 94vw は実画面312pxにしかならない（vwの罠）。rem で広げる＝約501px */
    padding: 1.2rem 1.6rem;
  }
  /* 長い説明と注記は隠す。手順そのものは残すので、やることは分かる。 */
  body.is-phone-device .opening-setup-why,
  body.is-tablet-device .opening-setup-why,
  body.is-phone-device .opening-setup-note,
  body.is-tablet-device .opening-setup-note {
    display: none;
  }
  body.is-phone-device .opening-setup-title,
  body.is-tablet-device .opening-setup-title { font-size: 2.8rem; margin-bottom: 0.8rem; }
  body.is-phone-device .opening-setup-label,
  body.is-tablet-device .opening-setup-label { font-size: 2.3rem; margin-bottom: 0.5rem; }
  body.is-phone-device .opening-setup-steps,
  body.is-tablet-device .opening-setup-steps { font-size: 2rem; line-height: 1.5; }
  body.is-phone-device .opening-setup-lang-btn,
  body.is-tablet-device .opening-setup-lang-btn { font-size: 2.3rem; padding: 0.6rem 1rem; }
  body.is-phone-device .opening-setup-primary,
  body.is-tablet-device .opening-setup-primary,
  body.is-phone-device .opening-setup-secondary,
  body.is-tablet-device .opening-setup-secondary { font-size: 2.3rem; padding: 0.7rem 1.2rem; margin-top: 0.6rem; }
  body.is-phone-device .opening-setup-play,
  body.is-tablet-device .opening-setup-play { font-size: 2.1rem; padding: 0.7rem 1.2rem; }
  body.is-phone-device .opening-setup-never,
  body.is-tablet-device .opening-setup-never { font-size: 1.8rem; padding: 0.5rem 0.8rem; }
  body.is-phone-device .opening-setup-section,
  body.is-tablet-device .opening-setup-section { margin-top: 0.8rem; }
}

/* 初回起動時のBGM設定モーダル（first-run-bgm.js）。 */
#first-run-bgm-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(26rem, 92vw);
  z-index: 100051; /* backdrop(100050)より前面 */
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.6rem;
  padding: 1.4rem 1.4rem 1.2rem;
  box-sizing: border-box;
  color: #e2e8f0;
  font-family: sans-serif;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
  text-align: center;
  /* BGM・効果音・カード拡大サイズの3項目に増えて縦に長くなったため、小さい画面でも
     はみ出さず中でスクロールできるようにする。 */
  max-height: 90vh;
  overflow-y: auto;
}
.first-run-bgm-title {
  font-size: 1.2rem;
  font-weight: bold;
  margin-bottom: 0.7rem;
}
.first-run-bgm-desc {
  font-size: 0.85rem;
  line-height: 1.7;
  color: #cbd5e1;
  margin-bottom: 1.1rem;
  text-align: left;
}
.first-run-bgm-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 1rem;
}
.first-run-bgm-label {
  font-size: 0.85rem;
  white-space: nowrap;
}
.first-run-bgm-row input[type="range"] {
  flex: 1;
  min-width: 0;
}
.first-run-bgm-value {
  font-size: 0.85rem;
  min-width: 2.8rem;
  text-align: right;
  color: #94a3b8;
}
.first-run-bgm-preview {
  display: inline-block;
  width: 100%;
  padding: 0.55rem 1rem;
  margin-bottom: 0.8rem;
  background: rgba(56, 189, 248, 0.12);
  border: 1px solid rgba(56, 189, 248, 0.6);
  border-radius: 0.4rem;
  color: #e2e8f0;
  font-family: sans-serif;
  font-size: 0.9rem;
  cursor: pointer;
}
.first-run-bgm-preview:hover {
  background: rgba(56, 189, 248, 0.2);
}
.first-run-bgm-ok {
  display: inline-block;
  width: 100%;
  padding: 0.6rem 1rem;
  background: #0891b2;
  border: none;
  border-radius: 0.4rem;
  color: #fff;
  font-family: sans-serif;
  font-size: 0.95rem;
  font-weight: bold;
  cursor: pointer;
}
.first-run-bgm-ok:hover {
  background: #0ea5c4;
}
/* ライトテーマ対応。 */
body.theme-light #first-run-bgm-modal {
  background: #fdfbf5;
  border-color: rgba(176, 141, 63, 0.6);
  color: #3a3326;
}
body.theme-light .first-run-bgm-desc {
  color: #6b5c3c;
}
body.theme-light .first-run-bgm-value {
  color: #6b5c3c;
}
/* ライトテーマでは「試聴する」の薄い青地＋白文字がアイボリー地に埋もれて読めない
   （ユーザー報告）。濃いシアン文字＋やや濃い枠でコントラストを付ける。 */
body.theme-light .first-run-bgm-preview {
  background: rgba(8, 145, 178, 0.1);
  border-color: rgba(8, 145, 178, 0.55);
  color: #0e7490;
}
body.theme-light .first-run-bgm-preview:hover {
  background: rgba(8, 145, 178, 0.18);
}
/* 初回設定モーダルの「拡大サイズ」確認用デモカード（first-run-bgm.js）。実際のホバー拡大と
   同じ --card-preview-size を共有して画面中央に出す。モーダル自身（100051）より背面かつ
   backdrop（100050）より前面に置き、操作中はモーダルを薄くして後ろのこれを見せる。 */
#first-run-card-preview-demo {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: var(--card-preview-size);
  height: var(--card-preview-size);
  border-radius: 0.5rem;
  border: 2px solid rgba(255, 255, 255, 0.7);
  box-shadow: 0 1rem 2.5rem rgba(0, 0, 0, 0.6);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  pointer-events: none;
  z-index: 100050;
  display: none;
}

html, body {
  height: 100%;
  margin: 0;
}

/* 画面の縦横比を固定するための「ステージ」方式（ユーザー要望「画面の縦横比を固定したい。
   合わない画面は上下端か左右端に黒帯」）。bodyを固定の仮想解像度(--stage-width/-height、
   px単位)の箱にし、実際のウィンドウに収まる倍率でJS(applyViewportStage、main.js)が
   transform: translate() scale()を掛けて中央寄せする。CSSの仕様上、transformを持つ要素は
   position:fixed/absoluteな子孫にとって新しい基準（containing block）になるため、
   これまで「実画面に対してfixed」だったアイコンボタン・モーダル・ドラッグゴースト等
   （本プロジェクトのほぼ全てのオーバーレイUI）が、コードを1つも変えずに自動的に
   「このステージに対してfixed」に切り替わり、ステージの外側（黒帯部分）には一切はみ
   出さなくなる。合わない縦横比の画面では、ステージの外側がhtml自身の黒背景として
   見える。
   ハマりどころ: ステージにtransformが掛かることで、以降はマウス/タッチイベントの
   clientX/clientYが「実画面のピクセル」であり続ける一方、ステージ内の要素へその値を
   そのままstyle.left/top等に使うと倍率・オフセット分だけズレる。main.jsの
   stageClientToLocal()/stageDelta()で変換してから使うこと（ドラッグゴースト・
   コンテキストメニュー・各種ツールチップの位置決め、パン操作の移動量計算など）。
   elementsFromPoint()・getBoundingClientRect()同士の比較（当たり判定）は両方とも実画面
   座標のままで一貫しているため、変換不要。 */
:root {
  --stage-width: 1600px;
  --stage-height: 900px;
}
html {
  background: #000;
  overflow: clip;
}
/* ハマりどころ: overflow:hiddenだと「スクロールコンテナ」自体には変わりなく、フォーカス移動
   （Tabキーで画面外の入力欄にフォーカス等）やscrollIntoView()で見た目には現れないまま
   body.scrollLeft/scrollTopが動いてしまうことがある。bodyはtransformでposition:fixedな
   子孫全ての基準（containing block）になっているため、このスクロール量がその全ての
   子孫のstyle.left/top計算に混入し、画面のあちこちのUIが実際にズレて描画される
   （検証中に発見。overflow:clipなら仕様上そもそもスクロールコンテナにならず、
   scrollLeft/scrollTopは常に0のまま安全）。 */
body {
  width: var(--stage-width);
  height: var(--stage-height);
  position: fixed;
  top: 0;
  left: 0;
  transform-origin: 0 0;
  overflow: clip;
  background: radial-gradient(ellipse at center, #2a3441 0%, #0f1520 70%, #05070a 100%);
}

:root {
  --cell: 3.4rem;
}

.scene {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  perspective: var(--camera-perspective);
  perspective-origin: 50% var(--camera-perspective-origin-y);
  /* ブラウザ標準のピンチズーム/パン等のジェスチャー処理を無効化し、
     initCameraControls()の独自ピンチズーム実装に完全に委ねる。 */
  touch-action: none;
}

/* ハマりどころ（ユーザー報告「タブレットで画角によって手札・名前が消える」）:
   手札(A)自体のZ値・rotateXを触っても改善しなかったため、原因は手札単体の値ではなく、
   preserve-3d + perspectiveの入れ子構造をタブレットのGPUが特定の投影角度で正しく
   合成できていない（3D描画の精度起因のバグ）可能性が高いと考えている。この場合、
   手札個別の値ではなく「視点（透視投影）そのもの」を変えることで症状の出る角度を
   避けられる可能性があるため、タブレット専用の実験用として透視投影自体を
   調整できるようにする。 */
body.is-touch-device .scene {
  perspective: var(--camera-perspective-touch, var(--camera-perspective));
  perspective-origin: 50% var(--camera-perspective-origin-y-touch, var(--camera-perspective-origin-y));
}

/* ハマりどころ（ユーザー報告）: ゲーム画面に来て最初の操作がいきなり2本指ピンチだった
   場合、指の片方がposition:fixedな常設UI（アイコンボタン・自分専用ステータス等、
   .sceneの外＝直接のtouch-action:noneが効かない領域）の上に乗っていると、その指の分だけ
   ブラウザ標準のページ全体ピンチズームが依然として認識されてしまっていた（2本指ジェス
   チャーの可否は、触れている全ての指それぞれの位置の要素のtouch-actionの合成で決まる
   ため、片方でもtouch-action:auto=既定の領域があると、ブラウザはそちらを優先してネイ
   ティブズームとして扱ってしまう）。html/body自体に一律touch-action:noneを付ける方法も
   検討したが、touch-actionは「祖先が禁止した挙動は子孫が明示的に許可しても復活しない」
   仕様のため、それだと管理者モード等のスクロール可能なパネル(overflow-y:auto)まで
   タッチスクロールできなくなってしまう副作用がある。そのため、常時画面に表示される
   固定UIチャンパーツ（スクロールを必要としないもの）だけを個別に列挙してtouch-action:
   noneを付ける、より的を絞った対策にした。 */
.icon-action-button,
#self-hand-status,
#turn-round-counter,
#turn-timer-rope,
.turn-timer-warning,
#priority-transfer-buttons,
#phase-guide-bar,
#game-setup-toggle-button,
#quick-start-bar {
  touch-action: none;
}

:root {
  --table-tilt: 42deg; /* JS側(fitTableToViewport)もこの変数を参照してscaleだけ動的に上書きする */
  /* 「カメラ位置」（3D視点の初期値）。管理者モードから調整できる。perspectiveは値が小さいほど
     カメラが近く（遠近感が強い）、perspective-origin-yは0remに近いほど画面上寄りが視点の消失点に
     なる。ドラッグゴースト(.drag-ghost-piece-outer)のperspectiveもこの値を共有し、
     盤面上の駒と見た目の遠近感がズレないようにしている。
     ハマりどころ: perspective-origin-yは以前%指定（.sceneの高さ=100vhに対する割合）だった。
     %は「.sceneの実ピクセル高さ」に対する割合のため、ブラウザウィンドウの高さが変わると
     視点（消失点）の絶対位置自体が上下に動いてしまい、rotateXで傾いた盤面の見え方
     （3D投影後の見かけの縦幅、ひいては「盤面拡大」ボタンの上端・下端の位置）が
     ウィンドウサイズに応じて意図せず変わってしまっていた。remの絶対値にすることで、
     消失点の位置をウィンドウサイズに左右されない固定位置にした。 */
  --camera-perspective: 1090px;
  --camera-perspective-origin-y: 8.4rem;
  /* カメラの上下(Y軸)方向の平行移動。fitTableToViewport()内でtranslate()として
     rotateX/scale3dより先に足す（rem絶対量なので、盤面拡大時のscale3dの影響を受けない）。 */
  --camera-offset-y: -1.4rem;
  /* カメラのズーム。ビューポートに自動フィットさせた基準の倍率(applyNormalFit/
     applyBoardZoomFitがそれぞれ計算するscale)に、さらに掛け合わせる係数。1が等倍
     （今までの自動フィットのまま）。「盤面拡大」ボタンの拡大レベルとは独立していて、
     どの表示状態でも常に効く。 */
  --camera-zoom: 1.05;
  /* 「盤面拡大」ボタンのズーム位置微調整（applyBoardZoomFit参照）。管理者モードで調整可能。
     ボタンは 盤面拡大(1段階目) → もっと拡大(2段階目) → 元に戻す、の3段階トグル。 */
  --board-zoom-margin: 1;
  --board-zoom-offset-x: 0rem;
  --board-zoom-offset-y: 3.2rem;
  /* 拡大時の倍率計算の基準となる高さ（px）。実際のブラウザウィンドウの高さではなく、この
     固定値を使うことで、拡大時の上端・下端の画面上の位置がウィンドウサイズに左右されなく
     なる（applyBoardZoomFit参照）。 */
  --board-zoom-reference-height: 1000px;
  --board-zoom-2-margin: 2;
  --board-zoom-2-offset-x: 0rem;
  --board-zoom-2-offset-y: -2.5rem;
  --board-zoom-2-reference-height: 1000px;
  /* フェイズ案内板（画面右下）の位置。管理者モードで調整可能。 */
  --phase-guide-bottom: 0rem;
  /* ターンタイマー（画面中央のロープ・優先権譲渡ボタン）の位置。管理者モードで調整可能。 */
  --turn-timer-rope-pos-x: 0rem;
  --turn-timer-rope-pos-y: 0rem;
  --turn-timer-rope-width: 46rem;
  --priority-transfer-pos-x: -1.6rem;
  --priority-transfer-pos-y: 1.5rem;
}

/* ユーザー報告「正方形であるはずの盤面が少し横長に見える」への対応（2D表示ON時）。
   通常はperspectiveが効いているため.game-tableのrotateXは正しい正方形に投影される
   が、2D表示（body.diagnostic-flatten-3d、perspective:none）の下では遠近補正が
   無いままrotateXだけが残り、正射影的にY方向だけcos(角度)倍に潰れて見える（＝
   相対的に横長に見える）。傾き角度・拡大率・位置（実質的な「カメラ視点位置」）を
   2D表示専用に調整できるようにした（既定は傾き0deg＝正方形に戻る）。
   ハマりどころ: .game-tableのtransformはmain.jsのapplyNormalFit/applyBoardZoomFitが
   毎回インラインスタイル(table.style.transform)として直接書き込むため、ここに
   CSSルールを書いても常にインラインスタイルに負けて無効化される（実測で確認済み）。
   そのため実際の調整反映はCSSではなくmain.js側（getFlatTableAdjustments）で
   行っている。この変数は宣言だけここに残す。 */
.game-table {
  transform: rotateX(var(--table-tilt)) scale(0.85);
  transform-style: preserve-3d;
  display: grid;
  grid-template-columns: 12rem auto 12rem;
  /* 奥(C, 相手): 控えめな高さ / 手前(A, 自分): 大きい手札分の高さを確保 */
  grid-template-rows: 6.5rem auto 11rem;
  gap: 0.75rem;
  width: max-content;
  /* タブレット等のタッチ操作で、カード/駒を長押しした時にブラウザ標準の文字選択・
     コールアウトメニュー（「共有」「画像を保存」等）が出てしまうのを防ぐ
     （main.jsのstartTouchHoldOrDragによる拡大プレビュー機能と競合するため）。 */
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

/* ユーザー報告「スマホで画像を長押しすると『写真に保存/共有/コピー』のメニューが出て
   保存できてしまう」。.game-tableに -webkit-touch-callout: none を付けていても、iOSは
   <img>要素については祖先からの継承だけでは抑止しきれずネイティブのコールアウトメニューを
   出すことがあるため、全画像に直接指定して長押し保存・画像ドラッグを封じる。カードの
   長押し拡大（main.jsのstartTouchHoldOrByDragによる独自ハンドラ）はこの指定と競合しない。 */
img {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
  user-drag: none;
}

/* 【#350 ユーザー報告2026-09-15「iPhoneで画面を長押しすると、たまにスクショのやつになる」】
   添付画面では、左下の名前・手札枚数やお知らせの文字が**青く選択**されていた。長押しの抑止は
   盤面(.game-table)・画像・手札の固定オーバーレイ等に個別に付けてきたが、それ以外の場所
   （名前の欄・お知らせ・WebGLのキャンバス等）は素通しだった。ゲーム画面（このCSSを読むのは
   index.html だけ）では**ページ全体で**文字選択と長押しメニューを止め、入力欄だけ選べるように戻す。 */
body {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
input,
textarea,
select,
[contenteditable="true"] {
  -webkit-user-select: text;
  user-select: text;
  -webkit-touch-callout: default;
}

/* ユーザー報告2026-08-16「スマホでカードを拡大しようと長押しするとテキスト選択の青いやつになる」。
   スマホでは手札は#self-hand-overlay、ロック済みカードは#mini-lock-area(-top)という、.game-tableの
   外にある固定オーバーレイに移る。.game-tableに付けた文字選択・コールアウト抑止(user-select:none等)は
   これらには効かないため、長押しでネイティブのテキスト選択が始まってしまっていた。同じ抑止を
   これらのカード保持オーバーレイにも付ける（子孫のカードへは継承で効く）。 */
#self-hand-overlay,
#mini-lock-area,
#mini-lock-area-top {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

.zone {
  background: transparent;
  border: none;
  box-shadow: none;
}

.zone-top     { grid-column: 2; grid-row: 1; }
.zone-bottom  { grid-column: 2; grid-row: 3; }
.zone-left    { grid-column: 1; grid-row: 2; }
.zone-right   { grid-column: 3; grid-row: 2; }
.zone-deck    { grid-column: 1; grid-row: 1; }
.zone-eternal { grid-column: 1; grid-row: 3; }
.zone-first   { grid-column: 3; grid-row: 1; }
.zone-discard { grid-column: 3; grid-row: 3; }

.zone {
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}

.player-zone {
  position: relative; /* .hand-areaをposition:absoluteで中心配置するための基準 */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 0.5rem;
  font-size: 0.7rem;
  min-height: 0;
  overflow: visible; /* 親の.zoneはoverflow:hiddenだが、手札位置調整(--hand-*-pos-y)でゾーンの外に
                         はみ出すと見えない境界線で上端が切れてしまうため、pile-zoneと同様に上書きする */
}

/* ハマりどころ: .hand-areaは以前.player-zoneの通常のフローに置いていた（name labelの下に
   flexで積む）。すると手札の枚数が増えて--hand-a-sizeによる幅が大きくなった時、その幅が
   .player-zone自身の幅、さらには.game-tableのgridトラック(auto幅の中央列)にまで影響し、
   盤面(.arena)の方が押し出される形でズレる、というレイアウト崩れが起きていた。
   .hand-areaをposition:absoluteで通常のフローから外し、.player-zoneの中心(50%,50%)を
   基準にtranslateで自分自身を中心合わせすることで、手札がどれだけ幅を持っても周囲の
   グリッド/フレックスのサイズ計算に一切影響しないようにした。 */
.player-zone .hand-area {
  position: absolute;
  top: 50%;
  left: 50%;
  height: 5.2rem;
  transform-style: preserve-3d;
  overflow: visible;
  /* 明示的なz-indexを持たせる（詳細は.player-avatarのz-index:0コメント参照）。
     「z-index:auto/0の束はDOM順で決まる」という仕様上正しいはずの挙動は、深い3D transform
     階層ではブラウザ（特にモバイル系）が期待通りに合成しないことがこのプロジェクトで
     繰り返し確認されているため、あいまいさの残るDOM順頼みではなく、重なり得る要素全部に
     完全に明示的なz-indexを割り振って決着させる。Aの駒アバター(z-index:3)には勝てず
     （既存の見た目を維持）、B/C/Dの駒アバター(z-index:0)には勝つ（手札が手前になる）
     よう、間の値にしてある。 */
  z-index: 1;
}

/* 手札公開エリア: 手札からドラッグで手動配置した「宣言」カード、または「公開ドロー」
   ボタンで山から引いたカードを、扇状の手札とは別に表面全体が見えるよう左寄せの一列に
   並べる場所（main.jsのbuildPlayerZone参照、findDropTargetがドラッグの受け皿にもなる）。
   盤面のそば・プレイヤー名の下あたりがデフォルト位置（ユーザー要望）。
   ハマりどころ（ユーザー報告）: 当初.hand-areaと同じ「中心基準(-50%,-50%)+
   flex-wrap:wrap」にしていたため、1枚目の位置が枚数に応じて左右に動いてしまい、かつ
   4枚目で次の行に折り返してしまっていた。「1枚目の位置は固定・左端基準で右方向へ
   永続的に増えていく」という要望に合わせ、水平方向の自己センタリング(-50%)を外して
   左端をpos-xの位置に固定し（垂直方向だけ-50%で中央基準を維持）、flex-wrapもnowrapに
   変更した（何枚積んでも折り返さず右へ伸び続ける）。 */
.hand-reveal-area {
  position: absolute;
  top: 50%;
  left: 50%;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 0.5rem;
  z-index: 1;
  /* 盤面のマスと同じ見た目で常時可視化する（ユーザー要望）。中身が空の間もmin-width/
     min-heightで当たり判定の箱を確保しておくことで、手札からドラッグしてここへ
     直接置けるようにする（枠が無いと0x0に潰れてドロップ先として掴めなかった）。 */
  min-width: var(--hand-reveal-card-size, 2.9rem);
  min-height: var(--hand-reveal-card-size, 2.9rem);
  border-radius: 0.2rem;
  background: rgba(30, 41, 59, 0.15);
  border: 1px solid rgba(226, 232, 240, 0.15);
}
/* 中身が入ったら（＝子の.hand-reveal-slotが1つでもあれば）ラベルは不要になるので、
   :emptyで空の時だけ表示する（盤面マスの座標ラベルと同じ考え方）。 */
.hand-reveal-area:empty {
  justify-content: center;
}
.hand-reveal-area:empty::before {
  content: "手札公開エリア";
  font-size: 0.5rem;
  line-height: 1.15;
  text-align: center;
  color: rgba(226, 232, 240, 0.55);
  padding: 0.2rem;
}
.hand-reveal-bottom {
  transform: translate(0, -50%) translate(var(--hand-reveal-bottom-pos-x, -16.2rem), var(--hand-reveal-bottom-pos-y, 0.4rem));
}
/* ユーザー要望2026-08-08: ミニロックエリア表示中（盤面拡大でロックエリアが画面外＝画面下中央に
   ミニロック＋ミニ手札公開エリアを出している間）は、自分（下側）の通常の手札公開エリアを隠す
   （重複するため）。相手側（top/left/right）はそのまま。 */
body.mini-lock-active .hand-reveal-bottom {
  display: none;
}
/* ユーザー要望「各プレイヤーとも公開方向はロックエリアの向きに合わせる」により、
   B=時計回り90度・C=180度・D=反時計回り90度を追加した（position(translate)の後に
   付けることで、位置調整自体は回転の影響を受けない画面座標のまま行える）。 */
.hand-reveal-top {
  transform: translate(0, -50%) translate(var(--hand-reveal-top-pos-x, 13rem), var(--hand-reveal-top-pos-y, -2.2rem)) rotate(180deg);
}
.hand-reveal-left {
  transform: translate(0, -50%) translate(var(--hand-reveal-left-pos-x, -1rem), var(--hand-reveal-left-pos-y, -13.4rem)) rotate(90deg);
}
.hand-reveal-right {
  transform: translate(0, -50%) translate(var(--hand-reveal-right-pos-x, -2.4rem), var(--hand-reveal-right-pos-y, 13.5rem)) rotate(-90deg);
}
/* 1枚ごとに「カード＋捨てるボタン」を縦に積んだ単位。 */
.hand-reveal-slot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
}
.hand-reveal-card {
  position: relative;
  width: var(--hand-reveal-card-size, 2.9rem);
  height: var(--hand-reveal-card-size, 2.9rem);
  border-radius: 0.3rem;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
  flex-shrink: 0;
}
/* 手動配置（宣言）と公開ドローを、枠線の色で視覚的に区別する（ユーザー要望）。 */
.hand-reveal-card.is-manual {
  border: 2px solid rgba(56, 189, 248, 0.85); /* 水色 */
}
.hand-reveal-card.is-drawn {
  border: 2px solid rgba(250, 204, 21, 0.85); /* 黄色 */
}
.hand-reveal-badge {
  position: absolute;
  bottom: -0.35rem;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.5rem;
  white-space: nowrap;
  background: rgba(15, 23, 32, 0.9);
  border-radius: 0.2rem;
  padding: 0.05rem 0.25rem;
  pointer-events: none;
}
.hand-reveal-discard-btn {
  font-size: 0.6rem;
  background: rgba(153, 27, 27, 0.6);
  color: #fecaca;
  border: 1px solid rgba(248, 113, 113, 0.6);
  border-radius: 0.2rem;
  padding: 0.1rem 0.35rem;
  cursor: pointer;
  white-space: nowrap;
}
.hand-reveal-discard-btn:hover {
  background: rgba(153, 27, 27, 0.9);
}

/* 手札全体を持ち上げる。自分（is-self）はテーブルの傾きを打ち消してカメラ側を向かせ、
   扇状に大きく開く。他プレイヤー（is-opponent）は逆に盤面側へ伏せて、密集させて持つ。
   個々のカードの位置・角度はJS側でindexに応じて計算しinline transformで与える。
   ハマりどころ: この持ち上げtransformは、以前は子要素の.hand-fanに付けていた。すると
   .hand-fan（見た目）はカメラ側へ大きくリフトされて描画されるのに、ドラッグの当たり判定に
   使っている.hand-area自身は一切transformされない「素の（傾いた机の上に平らに置かれた）」
   位置のままになり、見た目上のカードの位置とクリック判定の位置がズレるバグになっていた
   （プレイヤーCの手札がロックエリアの帯とほぼ同じ画面位置に「当たり判定だけ」重なって
   干渉する、という形で発覚した）。transformを.hand-fanではなく.hand-area自身に付ける
   ことで、当たり判定の箱自体が見た目通りの位置に来るようにした。 */
.hand-fan {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
}

/* 「手札を画面下に固定する」ON（fixed-hand.js / body.fixed-hand-mode）時の、盤面ズームの外側に
   置く固定手札トレイ（main.jsのsyncFixedHandOverlayが自分の.hand-area/.hand-reveal-areaをここへ
   移す）。ステージ(body)基準でfixedなので盤面のズーム・パンの影響を受けない。3Dの傾きは打ち消して
   フラットに扇を並べる。位置・サイズは実機で調整予定。 */
#self-hand-overlay {
  position: fixed;
  left: 50%;
  /* 位置・全体サイズは管理者モードで調整可能（ユーザー要望2026-08-07）。 */
  bottom: var(--fixed-hand-bottom, 0.5rem);
  transform: translateX(calc(-50% + var(--fixed-hand-x-offset, 0rem))) scale(var(--fixed-hand-scale, 1));
  transform-origin: bottom center;
  z-index: 60;
  display: flex;
  flex-direction: column-reverse;
  align-items: center;
  gap: 0.4rem;
  pointer-events: none;
}
#self-hand-overlay .hand-area,
#self-hand-overlay .hand-reveal-area {
  pointer-events: auto;
}
/* スマホ専用の位置・全体サイズ・回転（管理者モードで調整。ユーザー要望2026-08-07）。
   PC/タブレット用の値とは別に、スマホだけこちらで上書きする。 */
body.is-phone-device #self-hand-overlay {
  /* ユーザー調整値2026-08-07（スマホは縦持ちで手札を左側に縦向きに寄せる）。管理者モードで再調整可。 */
  bottom: var(--fixed-hand-bottom-phone, 19.5rem);
  transform: translateX(calc(-50% + var(--fixed-hand-x-offset-phone, -33.4rem)))
    scale(var(--fixed-hand-scale-phone, 1)) rotate(var(--fixed-hand-rotate-phone, 64deg));
  /* ユーザー報告2026-08-16「スマホで手札が巨大半透明アバターの背面に行ってしまう」。固定手札トレイ
     (#self-hand-overlay, z-index:60) が自分専用ステータスエリア(#self-hand-status, z-index:1000＝
     巨大アバターを含む)より下だったため。スマホでは手札を前面に出す（両者ともbody直下のfixedなので
     z-indexで直接比較される。モーダル類は10000+なので1001でも下）。トレイはpointer-events:noneで
     カード以外はクリックを通すので、下のステータスアイコンのクリックは妨げない。 */
  z-index: 1001;
}
/* スマホ＋手札固定ON時の、自分の手札公開エリア（宣言カード/公開ドロー）の位置・サイズ・回転を
   トレイ全体(--fixed-hand-*-phone)とは別に個別調整（管理者モード、ユーザー要望2026-08-08）。
   トレイは既に64deg回転しているので、この回転はその上に乗る相対値（既定0＝トレイ通り）。
   .hand-reveal-bottom等が持つ既定transformを完全に置き換えるため!important。 */
body.is-phone-device.fixed-hand-mode #self-hand-overlay .hand-reveal-area {
  transform: translate(var(--hand-reveal-fixed-phone-x, 0rem), var(--hand-reveal-fixed-phone-y, 0rem))
    scale(var(--hand-reveal-fixed-phone-scale, 1))
    rotate(var(--hand-reveal-fixed-phone-rotate, 0deg)) !important;
  transform-origin: center center;
}
/* 盤面ゾーン用の「中心基準に絶対配置＋3D傾き」を解除し、トレイ内で通常フロー＋フラットにする。 */
#self-hand-overlay .hand-area {
  position: relative !important;
  left: auto !important;
  top: auto !important;
  transform: none !important;
  transform-style: flat;
  height: var(--hand-a-thickness);
  margin: 0 auto;
}
#self-hand-overlay .hand-fan {
  transform-style: flat;
}

/* ミニロックエリア（main.jsのupdateMiniLockArea）。盤面を拡大して自分のロックエリアが
   画面外に出た時だけ、画面下中央に7色スロットのロック状況を出す。ステージ(body)基準でfixed
   なので盤面ズームの影響を受けない。ユーザー要望2026-08-07。位置・サイズは実機で調整予定。 */
#mini-lock-area {
  position: fixed;
  left: 50%;
  /* 既定は画面最下部（ユーザー調整値2026-08-07: -0.2rem）。位置は管理者モードで調整可能。 */
  bottom: var(--mini-lock-bottom, -0.2rem);
  /* ユーザー報告2026-08-18: 窓を画面の半分にするとステージ倍率が小さくなり、ミニロックが
     一緒に縮んで読めなくなる。ステージ倍率(--stage-scale、applyViewportStageがJSで設定)が
     一定より小さい時だけ逆補正で拡大し、最低限の読める大きさ(--mini-lock-min-eff-scale)を
     保つ（通常窓＝倍率が十分大きい時は max()が1を選ぶ＝従来通り）。transform-originを
     bottom centerにして、拡大しても画面下端からはみ出さず上方向へ伸びるようにする。 */
  transform: translateX(calc(-50% + var(--mini-lock-x-offset, 0rem)))
    scale(max(1, calc(var(--mini-lock-min-eff-scale, 0.66) / var(--stage-scale, 1))));
  transform-origin: bottom center;
  z-index: 62;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  padding: 0.4rem 0.6rem;
  background: rgba(10, 16, 24, 0.86);
  border: 1px solid rgba(148, 163, 184, 0.5);
  border-radius: 0.5rem;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.5);
  pointer-events: none;
}
/* 「手札を画面下に固定する」ONの時は下部に手札トレイが来るので、その上へ重ねる。 */
body.fixed-hand-mode #mini-lock-area {
  bottom: var(--mini-lock-bottom-fixed-hand, 0rem);
}
/* スマホ専用: 自分のミニロックエリアの位置・サイズ（管理者モード、ユーザー要望2026-08-16）。
   未設定なら手札固定ON時の値(--mini-lock-bottom-fixed-hand)→共通値へフォールバック。上の
   fixed-hand ルールと同詳細度(0,1,1)なので、確実に上書きするためこの後に置く。 */
body.is-phone-device #mini-lock-area {
  bottom: var(--mini-lock-bottom-phone, var(--mini-lock-bottom-fixed-hand, var(--mini-lock-bottom, -0.2rem)));
  transform: translateX(calc(-50% + var(--mini-lock-x-offset-phone, var(--mini-lock-x-offset, 0rem))))
    scale(var(--mini-lock-scale-phone, 1));
  transform-origin: bottom center;
}
/* 相手プレイヤーのミニロックエリアは画面最上部に出す（ユーザー要望2026-08-07）。 */
#mini-lock-area-top {
  position: fixed;
  left: 50%;
  top: var(--mini-lock-top, 2.2rem);
  /* 相手のミニロックは独自の横ずれ(--mini-lock-top-x-offset)で調整できる。未設定なら
     従来通り自分と共通の--mini-lock-x-offsetにフォールバック（ユーザー要望2026-08-07）。 */
  /* 下(#mini-lock-area)と同じ逆補正で、狭い窓でも読める大きさを保つ。こちらは画面上端に
     あるので transform-origin を top center にして下方向へ伸ばす。 */
  transform: translateX(calc(-50% + var(--mini-lock-top-x-offset, var(--mini-lock-x-offset, 0rem))))
    scale(max(1, calc(var(--mini-lock-min-eff-scale, 0.66) / var(--stage-scale, 1))));
  transform-origin: top center;
  z-index: 62;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  padding: 0.4rem 0.6rem;
  background: rgba(10, 16, 24, 0.86);
  border: 1px solid rgba(148, 163, 184, 0.5);
  border-radius: 0.5rem;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.5);
  pointer-events: none;
}
/* スマホ専用: 相手のミニロックエリアの位置・サイズ（管理者モード、ユーザー要望2026-08-08）。
   未設定なら従来の共通値(--mini-lock-top / --mini-lock-top-x-offset / --mini-lock-x-offset)へ
   フォールバックする。詳細度がベース(#id)より高い(#id + .class)ので確実に上書きされる。 */
body.is-phone-device #mini-lock-area-top {
  top: var(--mini-lock-top-phone, var(--mini-lock-top, 2.2rem));
  transform: translateX(
      calc(-50% + var(--mini-lock-top-x-offset-phone, var(--mini-lock-top-x-offset, var(--mini-lock-x-offset, 0rem))))
    )
    scale(var(--mini-lock-top-scale-phone, 1));
  transform-origin: top center;
}
/* 自分のミニロックと、その右隣のミニ捨て場を横並びにする。 */
.mini-lock-self-wrap {
  display: flex;
  align-items: flex-end;
  gap: 0.6rem;
}
.mini-discard {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
}
.mini-discard-card {
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 0.25rem;
  border: 2px solid rgba(148, 163, 184, 0.7);
  background-color: rgba(0, 0, 0, 0.4);
  background-size: cover;
  background-position: center;
}
.mini-discard-card.has-card {
  border-color: rgba(226, 232, 240, 0.85);
}
.mini-lock-area-label {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.35rem;
  font-family: sans-serif;
  font-size: 0.72rem;
  font-weight: bold;
  color: #cbd5e1;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
  min-width: 5.5rem;
  max-width: 9rem;
  text-align: right;
  white-space: nowrap;
}
.mini-lock-area-name {
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: 0.85;
}
/* 手札枚数を大きく・アイコニックに（ユーザー要望）。カード風のバッジ＋大きめの数字。 */
.mini-lock-hand-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.15rem;
  flex: none;
  padding: 0.05rem 0.35rem;
  border-radius: 0.4rem;
  background: rgba(15, 23, 32, 0.72);
  border: 1px solid rgba(148, 163, 184, 0.45);
}
.mini-lock-hand-icon {
  font-size: 0.95rem;
  line-height: 1;
}
.mini-lock-hand-count {
  font-size: 1.15rem;
  font-weight: 800;
  line-height: 1;
  color: #fde68a;
  min-width: 0.9rem;
  text-align: center;
}
/* CPUが選ぶ番の選択モーダル（色宣言・選択肢・儀式ピック等）とその背景を丸ごと隠す用
   （ユーザー要望2026-08-07: CPUの選択モーダルは非表示。結果モーダルは別クラスなので出る）。
   各モーダル生成関数が、CPU選択中(isCpuSelectingNow)なら本体と背景にこのクラスを付ける。 */
.is-cpu-hidden {
  display: none !important;
}

/* プレイヤー1人分の行（相手も含め全員分を積む。ユーザー要望2026-08-07）。自分の行を強調。 */
.mini-lock-player {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
/* ミニ手札公開エリア（publicDrawの縮小版）。ミニロックの左隣に左揃えで並べる
   （ユーザー要望2026-08-07）。カードが無ければ幅0で場所を取らない。 */
.mini-hand-reveal {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 0.2rem;
}
.mini-hand-reveal:empty {
  display: none;
}
.mini-hand-reveal-card {
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 0.2rem;
  border: 1px solid rgba(226, 232, 240, 0.7);
  background-color: rgba(0, 0, 0, 0.4);
  background-size: cover;
  background-position: center;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}
.mini-lock-player.is-self .mini-lock-area-label {
  color: #fde68a;
}
.mini-lock-area-slots {
  display: flex;
  gap: 0.3rem;
}
.mini-lock-slot {
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 0.25rem;
  border: 2px solid var(--slot-color, rgba(148, 163, 184, 0.6));
  box-shadow: 0 0 0.35rem var(--slot-color, transparent);
  background-color: rgba(0, 0, 0, 0.35);
  background-size: cover;
  background-position: center;
  opacity: 0.55;
}
.mini-lock-slot.is-locked {
  opacity: 1;
}
/* ロック中でも使えるカード（ファースト/エターナル）はミニロックエリアでもクリックできる。
   #mini-lock-areaはpointer-events:noneなので、このスロットだけ有効化＋使えることを光って示す。 */
.mini-lock-slot.is-usable {
  pointer-events: auto;
  cursor: pointer;
  border-color: #fde68a;
  box-shadow: 0 0 0.5rem #fde68a, 0 0 0.9rem var(--slot-color, transparent);
  animation: mini-lock-usable-pulse 1.3s ease-in-out infinite;
}
@keyframes mini-lock-usable-pulse {
  0%, 100% { box-shadow: 0 0 0.4rem #fde68a, 0 0 0.7rem var(--slot-color, transparent); }
  50% { box-shadow: 0 0 0.8rem #fff7cc, 0 0 1.3rem var(--slot-color, transparent); }
}
body.reduce-glow .mini-lock-slot.is-usable {
  animation: none;
}
/* 「捨てる/奪うロックカードを選ぶ」ピッカー中、ミニロックエリアでも選べる候補スロット
   （ユーザー要望2026-08-08）。pointer-events:auto でクリック可能にし、赤系で選択対象を示す。 */
.mini-lock-slot.is-pick-target {
  pointer-events: auto;
  cursor: pointer;
  border-color: #f87171;
  box-shadow: 0 0 0.5rem #f87171, 0 0 0.9rem var(--slot-color, transparent);
  animation: mini-lock-pick-pulse 1.1s ease-in-out infinite;
}
@keyframes mini-lock-pick-pulse {
  0%, 100% { box-shadow: 0 0 0.4rem #f87171, 0 0 0.7rem var(--slot-color, transparent); }
  50% { box-shadow: 0 0 0.9rem #fca5a5, 0 0 1.4rem var(--slot-color, transparent); }
}
body.reduce-glow .mini-lock-slot.is-pick-target {
  animation: none;
}

/* 各プレイヤーの手札が盤面中心からどれだけ離れているか（＝手札ゾーンの画面上の位置）を
   独立に調整できるようにする。translateを先頭（＝最後に適用）に置くことで、
   カメラ目線に傾けた後の画面座標系でシフトするようにしている。 */
:root {
  --hand-a-pos-x: 0rem;
  --hand-a-pos-y: 4.3rem;
  --hand-b-pos-x: -0.3rem;
  --hand-b-pos-y: 0rem;
  --hand-c-pos-x: 0rem;
  --hand-c-pos-y: -3rem;
  --hand-d-pos-x: 0rem;
  --hand-d-pos-y: 0rem;
  /* 手札エリア(.hand-area)の「扇が伸びる方向」のサイズ（手札3枚の時を基準値とする）。
     .hand-areaはカードをドロップする際の当たり判定(findDropTarget内の.hand-area)にも
     使われており、以前はwidth:100%（盤面と同じ幅）で常に固定されていたため、実際に
     見えている手札の範囲よりずっと広い当たり判定になり、ロックエリアの帯と干渉していた。
     枚数に応じてJS側(main.js buildPlayerZone)で自動的に比例拡大/縮小する。 */
  --hand-a-size: 17rem;
  --hand-b-size: 7rem;
  --hand-c-size: 7rem;
  --hand-d-size: 7rem;
  /* 手札エリアの「扇が伸びない方向」の厚み（固定値。枚数では変わらない）。デフォルトの
     5.2rem/9rem/100%幅では、この厚みの分だけ当たり判定がロックエリア側にはみ出して
     干渉することが分かったため、カード1枚分程度に絞れるよう調整可能にした。 */
  --hand-a-thickness: 10rem;
  --hand-b-thickness: 5rem;
  --hand-c-thickness: 4rem;
  --hand-d-thickness: 5rem;
}

/* translate(-50%, -50%)を先頭に置き、.hand-areaが（幅がどれだけ変わっても）常に
   .player-zoneの中心を基準に配置されるようにする。 */
.zone-bottom .hand-area {
  height: var(--hand-a-thickness);
  transform: translate(-50%, -50%) translate(var(--hand-a-pos-x), var(--hand-a-pos-y)) rotateX(-40deg)
    translateZ(2.4rem);
}
/* ユーザー要望「タブレット専用の位置調整に自分の手札位置も追加」。他のプレイヤー用ボタンと
   同じ「タブレット専用オーバーライド未設定ならPC値にフォールバック」の仕組みにする。 */
body.is-touch-device .zone-bottom .hand-area {
  transform: translate(-50%, -50%)
    translate(var(--hand-a-pos-touch-x, var(--hand-a-pos-x)), var(--hand-a-pos-touch-y, var(--hand-a-pos-y)))
    rotateX(var(--hand-a-rotate-x-touch, -40deg)) translateZ(var(--hand-a-translate-z-touch, 2.4rem));
}
/* ユーザー報告「スマホ用の手札位置・サイズ・回転が全く反映されない」の原因: 通常の
   3D表示のまま(--hand-a-pos-phone-x等)を効かせようとしていたが、既存の2D表示用ルール
   （下のbody.diagnostic-flatten-3d.is-touch-device、2クラスセレクタ）の方が詳細度が
   高く、2D表示がONの端末では常にそちらが勝ってしまい上書きできていなかった。ユーザー
   確認の上、そもそも「自分の手札の位置・サイズ・回転はスマホ・2D表示時限定でよい」と
   方針変更したため、このルール自体は下の2D表示用ブロックへ統合した
   （body.diagnostic-flatten-3d.is-phone-device参照）。 */
/* ユーザー報告「タブレットで手札が高速に点滅する」への対応（未検証・実機のGPUでしか
   起きない現象のため、この環境では再現確認できていない）。手札のクリッピング対策
   （applyNormalFit）でタブレット等の縦幅が狭い画面では自動フィットのscaleが以前より
   小さくなったことで、preserve-3d階層内の手札(.hand-area、rotateX+translateZで持ち上げ
   ている)と他の要素との画面上のZ値の差が縮み、GPUの深度バッファの精度限界で
   「どちらが手前か」の判定がフレームごとに揺れる、いわゆるz-fighting（ユーザーの
   仮説「レイヤーが中途半端な位置にいる」と同じ現象）が起きやすくなった可能性がある。
   will-changeで手札を独立した合成レイヤーに昇格させ、周囲の要素との3D深度比較の
   あいまいさを避ける（見た目には影響しない、purely rendering-layerのヒント）。
   PCでのレイアウト・見た目に影響しないよう、タッチデバイスに限定して適用する。 */
body.is-touch-device .hand-area {
  will-change: transform;
}
/* ユーザー報告「タブレットでBDの名前表示もチカチカ高速点滅する」への追加対応
   （前回のhand-areaへのwill-change単体では解決しなかった）。B/Dの.hand-areaは
   translateZ(0.5rem)しか無く（Aの2.4remよりずっと浅い）、translateZ無し(実質0)の
   同じpreserve-3d階層内の兄弟である.labelとのZ差がごくわずかなため、この2つの間で
   z-fightingが起きている可能性が高いと判断した。.labelの方にも同じwill-change対策を
   施し、独立した合成レイヤーに分離することで深度比較のあいまいさを避ける
   （こちらも実機でしか再現できず未検証）。 */
body.is-touch-device .label {
  will-change: transform;
}
/* ユーザーの仮説「背景にレイヤーか手札レイヤーがいたずらしてそう」に基づく追加対応。
   .table-background-bg（背景画像）と.playmat-bg（プレイマット画像）はどちらも
   translateZ無し（実質同じZ=0）で、z-indexだけで前後を分けている。preserve-3d階層内では
   z-indexだけでは確実な前後判定にならず、この2枚も.labelと同様のz-fighting候補になり得る
   ため、同じ対策を施す。 */
body.is-touch-device .table-background-bg,
body.is-touch-device .playmat-bg {
  will-change: transform;
}
/* ユーザー報告「手札・名前ラベルの点滅は大分減ったが、アバター・手札公開エリア・
   ロックエリアの帯付近も同じタイミングで一瞬透明になる（Bのアバターは下半分だけ）」。
   これらも.hand-area/.labelと同じ「preserve-3d階層内でtranslateZ無し・z-indexだけで
   前後を分けている」要素（.player-avatarのB/C/Dはz-index:0、.hand-reveal-areaは
   z-index:1、.lock-area-barはz-index:2、いずれもtranslateZは無し）で、既存の
   will-change対策がまだ適用されていなかった箇所だった。同じ対策を横展開する。 */
body.is-touch-device .player-avatar,
body.is-touch-device .hand-reveal-area,
body.is-touch-device .lock-area-bar {
  will-change: transform;
}

.zone-top .hand-area {
  height: var(--hand-c-thickness);
  transform: translate(-50%, -50%) translate(var(--hand-c-pos-x), var(--hand-c-pos-y)) rotateX(18deg) translateZ(0.5rem);
}

.zone-right .hand-area {
  width: var(--hand-d-thickness);
  transform: translate(-50%, -50%) translate(var(--hand-d-pos-x), var(--hand-d-pos-y)) rotateX(18deg) translateZ(0.5rem);
}

/* プレイヤーBの手札は180度回転させる */
.zone-left .hand-area {
  width: var(--hand-b-thickness);
  transform: translate(-50%, -50%) translate(var(--hand-b-pos-x), var(--hand-b-pos-y)) rotateX(18deg) translateZ(0.5rem) rotate(180deg);
}
/* ユーザー要望「タブレットで手札/名前が画角によって見えなくなる。Z値が原因では」への
   対応。B/C/Dの手札はもともとtranslateZ(0.5rem)しか無く、Z値無し(実質0)の.labelと
   ほぼ同じ深さのため、GPUの深度バッファ精度によってはz-fightingが起きやすい
   （will-changeでのレイヤー分離だけでは解決しなかったとの報告があったため、根本的な
   Z値そのものをユーザーが微調整できるようにする）。PC版はこれらのルールの対象外
   （body.is-touch-deviceスコープ）のため、Z値・見た目とも一切変わらない。 */
body.is-touch-device .zone-top .hand-area {
  transform: translate(-50%, -50%) translate(var(--hand-c-pos-x), var(--hand-c-pos-y)) rotateX(18deg) translateZ(var(--hand-c-translate-z-touch, 0.5rem));
}
body.is-touch-device .zone-right .hand-area {
  transform: translate(-50%, -50%) translate(var(--hand-d-pos-x), var(--hand-d-pos-y)) rotateX(18deg) translateZ(var(--hand-d-translate-z-touch, 0.5rem));
}
body.is-touch-device .zone-left .hand-area {
  transform: translate(-50%, -50%) translate(var(--hand-b-pos-x), var(--hand-b-pos-y)) rotateX(18deg) translateZ(var(--hand-b-translate-z-touch, 0.5rem)) rotate(180deg);
}

/* 「2D表示に切り替える」（tablet-2d-mode.js、body.diagnostic-flatten-3d）ON時、
   上のタッチ用ルールがrotateX+translateZを持ったままだと、.sceneのperspective:noneと
   合わさって「奥行きの無いrotateXがそのまま縦方向を潰す」形になり、手札が不自然に
   潰れて見えることがある。管理者モード「📱 タブレット2D位置調整」で、2D表示時だけの
   回転・オフセットを個別に上書きできるようにする（セレクタの詳細度をタッチ用ルール
   より高くして確実に上書きする）。既定値はrotateX(0)（潰れを解消する素直な平置き）＋
   オフセット無しにしてあるので、値を変えるまでは「回転だけ無くなった」状態になる。 */
body.diagnostic-flatten-3d.is-touch-device .zone-bottom .hand-area {
  transform: translate(-50%, -50%) translate(var(--hand-a-pos-touch-x, var(--hand-a-pos-x)), var(--hand-a-pos-touch-y, var(--hand-a-pos-y)))
    translate(var(--hand-a-flat-offset-x, 0rem), var(--hand-a-flat-offset-y, 0rem)) rotateX(var(--hand-a-rotate-x-flat, 0deg));
}
/* ユーザー要望「管理者モードにスマホ用の調整項目を追加。自分の手札については全体を
   回転させれるようにもしてください」→「自分の手札位置サイズ回転は2D表示時限定に
   してください」。上のタブレット/2D表示用ルールと同じ2クラスセレクタ（詳細度を揃え、
   ソース順で確実に上書きする）で、body.is-phone-deviceもtrueの時だけさらに上書きする。
   位置は上の--hand-a-flat-offset-x/yへフォールバックし、回転はrotate()（Z軸、盤面に
   対して平面内での回転、既存のrotateX＝傾き角度とは別軸）を新規に追加する。サイズ
   （--hand-a-size-phone）はJS側（main.jsのbuildPlayerZone、diagnostic-flatten-3dかつ
   is-phone-deviceの時だけ適用）で読んでいるためここには変数が出てこない。 */
body.diagnostic-flatten-3d.is-phone-device .zone-bottom .hand-area {
  transform: translate(-50%, -50%)
    translate(var(--hand-a-pos-touch-x, var(--hand-a-pos-x)), var(--hand-a-pos-touch-y, var(--hand-a-pos-y)))
    translate(
      var(--hand-a-pos-phone-x, var(--hand-a-flat-offset-x, 0rem)),
      var(--hand-a-pos-phone-y, var(--hand-a-flat-offset-y, 0rem))
    )
    rotate(var(--hand-a-rotate-z-phone, 0deg))
    rotateX(var(--hand-a-rotate-x-flat, 0deg));
}
body.diagnostic-flatten-3d.is-touch-device .zone-top .hand-area {
  transform: translate(-50%, -50%) translate(var(--hand-c-pos-x), var(--hand-c-pos-y))
    translate(var(--hand-c-flat-offset-x, 0rem), var(--hand-c-flat-offset-y, 0rem)) rotateX(var(--hand-c-rotate-x-flat, 0deg));
}
body.diagnostic-flatten-3d.is-touch-device .zone-right .hand-area {
  transform: translate(-50%, -50%) translate(var(--hand-d-pos-x), var(--hand-d-pos-y))
    translate(var(--hand-d-flat-offset-x, 0rem), var(--hand-d-flat-offset-y, 0rem)) rotateX(var(--hand-d-rotate-x-flat, 0deg));
}
body.diagnostic-flatten-3d.is-touch-device .zone-left .hand-area {
  transform: translate(-50%, -50%) translate(var(--hand-b-pos-x), var(--hand-b-pos-y))
    translate(var(--hand-b-flat-offset-x, 0rem), var(--hand-b-flat-offset-y, 0rem)) rotateX(var(--hand-b-rotate-x-flat, 0deg)) rotate(180deg);
}

.hand-card {
  position: absolute;
  top: 50%;
  left: 50%;
  /* カード1枚自体の見た目の大きさ（手札エリア全体のサイズ--hand-{a,b,c,d}-sizeとは別物、
     管理者モードの「手札カード自体のサイズ」グループで調整可能）。中心基準の配置のため、
     marginをcalc()で連動させ、サイズを変えても中心がズレないようにしている。 */
  width: var(--hand-card-opponent-size, 2.6rem);
  height: var(--hand-card-opponent-size, 2.6rem);
  margin-top: calc(var(--hand-card-opponent-size, 2.6rem) / -2);
  margin-left: calc(var(--hand-card-opponent-size, 2.6rem) / -2);
  border-radius: 0.3rem;
  background-image: linear-gradient(160deg, #334155, #1e293b);
  border: 1px solid rgba(226, 232, 240, 0.4);
  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.55);
}

/* 【ユーザー要望2026-09-05・⑧「奪う」】相手の手札から自分の手札へ走る光の筋
   （スリカエ・接触・ゲート侵攻に共通）。始点で細く光り、終点へ向かって明るい塊が抜けていく。
   起点を左端・中心に固定して rotate で向きを合わせるので、JS側は長さと角度を渡すだけでよい。 */
.steal-beam {
  position: fixed;
  height: 0.34rem;
  transform-origin: 0 50%;
  margin-top: -0.17rem;
  pointer-events: none;
  z-index: 10450;
  border-radius: 999px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    color-mix(in srgb, var(--steal-beam-color, #f6c945) 85%, transparent) 45%,
    #ffffff 50%,
    color-mix(in srgb, var(--steal-beam-color, #f6c945) 85%, transparent) 55%,
    transparent 100%
  );
  background-size: 220% 100%;
  box-shadow: 0 0 0.8rem color-mix(in srgb, var(--steal-beam-color, #f6c945) 70%, transparent);
  animation: steal-beam 0.85s cubic-bezier(0.35, 0.8, 0.3, 1) forwards;
}
@keyframes steal-beam {
  0% {
    opacity: 0;
    background-position: 100% 0;
  }
  15% {
    opacity: 1;
  }
  80% {
    opacity: 0.9;
  }
  100% {
    opacity: 0;
    background-position: -20% 0;
  }
}
body.reduce-glow .steal-beam {
  animation-duration: 0.5s;
  box-shadow: none;
}


/* 【ユーザー要望2026-09-05】マイデッキ戦: 自分の手札に「これは他人のデッキの札」の印を付ける。
   自分の手札は右へ右へと重ねて並ぶ（後のカードほど手前）ので、**左上は必ず見えている**。
   そこへ印を置く。印の種類は管理者モードで切り替えられる。 */
/* ① 角がめくれて、持ち主の裏面がのぞく（既定）。三角形に切り抜いた裏面画像＋折り目の線。 */
.hand-card-mydeck-dogear {
  position: absolute;
  left: 0;
  top: 0;
  width: 34%;
  height: 34%;
  border-top-left-radius: 0.3rem;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  clip-path: polygon(0 0, 100% 0, 0 100%);
  pointer-events: none;
  z-index: 3;
}
/* 折り目。めくれた紙の断面に見えるよう、持ち主の色の細い線を斜めに置く。 */
.hand-card-mydeck-dogear::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    135deg,
    transparent 0 calc(70.7% - 0.09rem),
    var(--mydeck-mark-color, #f6c945) calc(70.7% - 0.09rem) 70.7%,
    transparent 70.7%
  );
  filter: drop-shadow(0 0 0.12rem rgba(0, 0, 0, 0.6));
}
/* 【ユーザー指示2026-09-06・#309】共有の山札から来た札は**角の折れ自体を出さない**
   （main.js の buildMyDeckHandMark が印を作らない）。この行はもう通らないが、
   共有の札にも印を戻す判断をした時のために残してある。 */
.hand-card-mydeck-dogear.is-shared::after {
  display: none;
}

/* ② 持ち主のアバターの小さな丸（ふちは持ち主の駒の色）。 */
.hand-card-mydeck-badge {
  position: absolute;
  left: 4%;
  top: 4%;
  width: 30%;
  height: 30%;
  border-radius: 50%;
  border: 0.14rem solid var(--mydeck-mark-color, #f6c945);
  background: rgba(15, 23, 42, 0.85);
  box-shadow: 0 0 0.35rem rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  font-size: 0.9rem;
  line-height: 1;
  pointer-events: none;
  z-index: 3;
}
.hand-card-mydeck-badge .avatar-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* 【ユーザー要望2026-09-06】共有の山札から引いた札には持ち主が居ないので、アバターの代わりに
   カードの裏を表す記号を出す。色も銀色（どのプレイヤーの色でもないことが色でも分かるように）。 */
.hand-card-mydeck-badge.is-shared {
  color: rgba(226, 232, 240, 0.95);
  font-size: 1.05rem;
}

/* 自分の手札は視認性優先で大きく表示する */
.hand-card.is-self {
  width: var(--hand-card-self-size, 9rem);
  height: var(--hand-card-self-size, 9rem);
  margin-top: calc(var(--hand-card-self-size, 9rem) / -2);
  margin-left: calc(var(--hand-card-self-size, 9rem) / -2);
  font-size: 0.62rem;
  /* ホバー(PC)/タップ(タブレット)で1枚だけ「ひょこっと」持ち上がる演出
     （main.jsのsetPeekedCard参照。JS側がtransformを直接書き換えるため、ここでは
     滑らかにするtransitionだけを持たせる）。 */
  transition: transform 0.2s ease-out;
}

/* 【ユーザー要望2026-09-05・⑥】手札に差し込まれる動き（applyHandInsertMotion）。位置は
   インラインの transform で決まるので、動かすのはトランジションに任せる。この2つの値だけを
   動かすので、扇の並び自体（レイアウト）には一切影響しない。 */
.hand-card.is-hand-inserting {
  transition: transform 0.42s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.3s ease;
}

/* ユーザー要望2026-08-28: 自動処理モードで自分の公開カード（publicDraw）を手札の扇の中に
   並べる時、通常の手札と区別できるよう明るいハイライト（金色の縁取り＋発光）を付ける。 */
.hand-card.is-public-in-hand {
  border-color: rgba(250, 204, 21, 0.95);
  box-shadow: 0 0 0 2px rgba(250, 204, 21, 0.85), 0 0 14px 3px rgba(250, 204, 21, 0.5), 0 6px 12px rgba(0, 0, 0, 0.55);
}
/* 公開されていることを示す👁アイコン（カード左上に小さく重ねる）。 */
.hand-card-public-eye {
  position: absolute;
  top: 4%;
  left: 4%;
  font-size: 0.62rem;
  line-height: 1;
  background: rgba(15, 23, 32, 0.85);
  border: 1px solid rgba(250, 204, 21, 0.9);
  border-radius: 0.25rem;
  padding: 0.06rem 0.12rem;
  pointer-events: none;
  z-index: 2;
}

/* 【#344】手札から複数枚まとめて捨てる時、押した順に付く番号（①②③…）。
   捨て場に積まれる順そのものなので、カードの中央に大きめに出して読み違えないようにする
   （左上の小さな印＝公開ドローの👁 と場所も見た目も分ける）。 */
.hand-card-discard-order {
  position: absolute;
  /* 【#362】以前は中央(top:50%)に置いていたが、扇状に重なった手札では中央はほぼ隠れる
     （実測: 6枚の手札で中央が見えているのは1枚だけ）。上端へ寄せ、下の .is-discard-picked で
     カード自体を前面へ出すことで、何枚重なっていても番号が読めるようにする。 */
  top: 0.15rem;
  left: 50%;
  transform: translate(-50%, 0);
  min-width: 1.9rem;
  height: 1.9rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.15rem;
  font-weight: bold;
  color: #06202e;
  background: linear-gradient(160deg, #fde68a, #f59e0b);
  border: 2px solid rgba(255, 255, 255, 0.9);
  border-radius: 50%;
  box-shadow: 0 0.1rem 0.35rem rgba(0, 0, 0, 0.5);
  pointer-events: none;
  z-index: 6;
}
/* 選び済みのカードは少し沈ませて「もう決まっている」ことを分かるようにする。
   【#362】あわせて**前面へ出す**（重なっている隣の札に番号を隠されないように）。
   手札の各カードは .hand-fan の中の兄弟要素なので、z-index を上げれば確実に前へ出る。 */
.hand-card.is-discard-picked,
.hand-reveal-card.is-discard-picked {
  filter: brightness(0.72);
  z-index: 30;
}

/* ユーザー要望「白と黒のカードは自分の手札内でそれぞれの色の湯気のような神秘的な
   オーラで纏われている演出を入れたい」への対応（main.jsのbuildPlayerZone参照、自分の
   手札のみ対象）。カードの縁からはみ出す2層のぼかしたグラデーションを、それぞれ別の
   周期でゆっくり漂わせる（速度差で単調な繰り返しに見えにくくする）。mix-blend-mode:
   screenで、カード本体の絵柄を暗くせず光だけを足す。 */
.hand-card.has-mystic-aura::before,
.hand-card.has-mystic-aura::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 15%;
  width: 75%;
  height: 75%;
  border-radius: 50%;
  filter: blur(0.6rem);
  pointer-events: none;
  mix-blend-mode: screen;
}
.hand-card.aura-white::before {
  background: radial-gradient(circle, rgba(255, 255, 255, 0.55), transparent 70%);
  animation: mystic-aura-drift-a 3.2s ease-in-out infinite;
}
.hand-card.aura-white::after {
  background: radial-gradient(circle, rgba(226, 232, 240, 0.4), transparent 70%);
  animation: mystic-aura-drift-b 4s ease-in-out infinite;
}
.hand-card.aura-black::before {
  background: radial-gradient(circle, rgba(126, 34, 206, 0.5), transparent 70%);
  animation: mystic-aura-drift-a 3.6s ease-in-out infinite;
}
.hand-card.aura-black::after {
  background: radial-gradient(circle, rgba(15, 23, 42, 0.6), transparent 70%);
  animation: mystic-aura-drift-b 4.4s ease-in-out infinite;
}
@keyframes mystic-aura-drift-a {
  0%,
  100% {
    transform: translate(-50%, 0) scale(1);
    opacity: 0.7;
  }
  50% {
    transform: translate(-62%, -35%) scale(1.15);
    opacity: 1;
  }
}
@keyframes mystic-aura-drift-b {
  0%,
  100% {
    transform: translate(-50%, -10%) scale(1.05);
    opacity: 0.6;
  }
  50% {
    transform: translate(-38%, -42%) scale(0.92);
    opacity: 0.9;
  }
}
/* ユーザー報告「10年前のタブレットで盤面下半分が白く欠け、ぼやけた色のシミが出る」
   への対応（原因調査）。この演出はmix-blend-mode（GPU側で専用の合成レイヤーを
   要求する）を、常に3D変形(preserve-3d/perspective)の中にある.hand-cardへ常時
   アニメーションさせながら適用している。CLAUDE.mdに既存の記録がある「タブレットの
   点滅はpreserve-3d+perspectiveの3D合成負荷が原因」という診断と同じ性質の負荷を
   さらに増やす組み合わせのため、疑わしい順に真っ先に切り分ける対象とした。
   「常時光る演出を無効にする」（body.reduce-glow）と「2D表示に切り替える」
   （body.diagnostic-flatten-3d、preserve-3dが効かなくなり3D合成の対象外になる）の
   どちらでも、このpseudo要素自体を生成しない（content: noneでmix-blend-modeの
   合成レイヤーごと無くす。既存のキラ加工([style*="rainbow-shard.webp"])はアニメーションだけ
   止める設計だが、こちらは新しい演出でまだ実機検証できていないため、より確実な
   「生成しない」を選んだ）。 */
body.reduce-glow .hand-card.has-mystic-aura::before,
body.reduce-glow .hand-card.has-mystic-aura::after,
body.diagnostic-flatten-3d .hand-card.has-mystic-aura::before,
body.diagnostic-flatten-3d .hand-card.has-mystic-aura::after {
  content: none;
}

/* ハマりどころ: ここは元々`background:`ショートハンドで指定していたが、ショートハンドは
   指定していないサブプロパティ(background-size/position/repeat)も暗黙に初期値へリセットする。
   このルールは.hand-cardより詳細度が高い(クラス2つ)ため、下の.hand-card,.board-card,.stack-top
   で指定したbackground-size:coverを打ち消してしまい、実物のカード裏面画像が「auto(原寸)サイズ・
   左上起点・タイル(repeat)」で描画される＝小さな枠の中に画像の左上の角だけが原寸大で
   表示される、というバグになっていた（ユーザー報告「裏面画像の角だけがアップして描画」）。
   background-imageのロングハンドに変えることでこの問題を回避した。 */
.hand-card.is-facedown {
  background-image: repeating-linear-gradient(45deg, #1e293b, #1e293b 5px, #2a3b52 5px, #2a3b52 10px);
  border-color: rgba(148, 163, 184, 0.45);
  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.5);
}

/* 実物のカード画像は、JS側でbackground-imageとしてinline styleで敷く（.hand-card/.board-card/
   .stack-top/.stack-front）。画像自体にタイトル・色・効果テキストまで描かれているため、
   background-size:coverで枠いっぱいに表示するだけでよい。山の残り枚数などの補足テキストは
   画像の上に重ねるので、text-shadowで可読性を確保する。 */
.hand-card,
.board-card,
.stack-top,
.hand-reveal-card {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0.2rem;
  font-size: 0.5rem;
  line-height: 1.15;
  overflow: hidden;
  color: #e2e8f0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9), 0 0 6px rgba(0, 0, 0, 0.7);
}

.stack-front {
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

/* --- ドラッグ操作 ---------------------------------------------------------
   駒・手札カード・山札等はマウス/タッチでドラッグして動かせる。touch-action:noneは
   タッチ操作時にドラッグ中にページ自体がスクロールしてしまうのを防ぐため。 */
.piece,
.hand-card,
.stack[data-pile],
.board-card {
  cursor: grab;
  touch-action: none;
}

/* 盤面マス・ロックスロットに直接置かれたカード（手札の外に出たカード）の見た目。
   手札の扇のような複雑な配置は不要なので、置かれたマス/スロットにフィットするだけの
   シンプルな見た目にする。色・柄は.hand-cardと揃える。 */
.board-card {
  position: absolute;
  inset: 3%;
  border-radius: 0.2rem;
  background-image: linear-gradient(160deg, #334155, #1e293b);
  border: 1px solid rgba(226, 232, 240, 0.4);
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
}

.board-card.is-facedown {
  background-image: repeating-linear-gradient(45deg, #1e293b, #1e293b 5px, #2a3b52 5px, #2a3b52 10px);
  border-color: rgba(148, 163, 184, 0.45);
}

/* ユーザー要望「『なないろの欠片』の表面をキラ仕様にできますか？」への対応。
   このカードの実物画像(background-image)は手札・盤面・ロックエリア・山札の一番上・
   ドラッグ中のゴースト・ホバー拡大プレビュー等、非常に多くの箇所へ個別に
   `url("assets/cards/rainbow-shard.webp")`という同じ形で埋め込まれている
   （main.js等の各所を1つずつ書き換えるのは大掛かりなため）。background-imageの
   値そのもの（決まったファイル名）を属性セレクタで狙い撃ちすることで、この1箇所の
   追加だけで表示箇所すべてに一律で効くようにした。斜めに流れる虹色の光沢
   （mix-blend-modeでカード自体の絵柄と重ねる）で、トレーディングカードのキラ加工を
   表現する。ほとんどの表示箇所は元々position:absolute/fixedなので、position自体は
   変更せず（position:relativeを強制すると、position:absolute等に依存した既存の
   配置が壊れる恐れがあるため）、::after疑似要素はそれぞれの既存のpositioned
   ancestorにそのまま乗せる。 */
[style*="rainbow-shard.webp"] {
  overflow: hidden;
}
[style*="rainbow-shard.webp"]::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(
    115deg,
    transparent 25%,
    rgba(255, 255, 255, 0.2) 38%,
    rgba(255, 0, 128, 0.45) 44%,
    rgba(255, 200, 0, 0.45) 49%,
    rgba(0, 220, 120, 0.45) 54%,
    rgba(0, 170, 255, 0.45) 59%,
    rgba(190, 0, 255, 0.45) 65%,
    rgba(255, 255, 255, 0.2) 76%,
    transparent 90%
  );
  background-size: 260% 260%;
  mix-blend-mode: overlay;
  animation: kira-shine 3.2s ease-in-out infinite;
  pointer-events: none;
}
@keyframes kira-shine {
  0% {
    background-position: 0% 0%;
  }
  50% {
    background-position: 100% 100%;
  }
  100% {
    background-position: 0% 0%;
  }
}
body.reduce-glow [style*="rainbow-shard.webp"]::after {
  animation: none !important;
  background-position: 40% 40%;
}

/* ロックしていても手札効果が使える特別なカード（ファーストカード・エターナルカード）を、
   ロックエリア内にある間だけ定期的に強調する。管理者モードで2種類の演出を切り替えられる
   （main.jsのbuildFlatCardがeffect-shine/effect-orbitのどちらかを付与し、--usable-locked-colorに
   そのロックスロットの色を設定する）。 */

/* 演出1: 斜めの光の帯（::after）が定期的にカードを横切り、あわせて金色のグローが明滅する。
   以前は帯の幅が35%あって存在感が強すぎたため、大幅に細くした。 */
@keyframes usable-locked-shine-sweep {
  0%, 85% {
    transform: translateX(-800%) skewX(-20deg);
  }
  100% {
    transform: translateX(800%) skewX(-20deg);
  }
}
@keyframes usable-locked-glow-pulse {
  0%, 80% {
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
  }
  90% {
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5), 0 0 0.7rem 0.15rem rgba(250, 204, 21, 0.9);
  }
  100% {
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
  }
}
.board-card.is-usable-while-locked.effect-shine,
.board-3d-glow.is-usable-while-locked.effect-shine {
  overflow: hidden;
  animation: usable-locked-glow-pulse 4s ease-in-out infinite;
}
.board-card.is-usable-while-locked.effect-shine::after,
.board-3d-glow.is-usable-while-locked.effect-shine::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 6%;
  height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.95), transparent);
  animation: usable-locked-shine-sweep 4s ease-in-out infinite;
  pointer-events: none;
}

/* 演出2（デフォルト）: そのロックスロットの色をした小さな球が、カードのふちに沿って
   ゆっくり周回する。4隅を等間隔のキーフレームにして直線的に結ぶことで、辺に沿って
   進むように見せている（offset-path等のモーションパスAPIより確実に動作するため）。 */
@keyframes usable-locked-orbit {
  0%   { top: 0%; left: 0%; }
  25%  { top: 0%; left: 100%; }
  50%  { top: 100%; left: 100%; }
  75%  { top: 100%; left: 0%; }
  100% { top: 0%; left: 0%; }
}
.board-card.is-usable-while-locked.effect-orbit::before,
.board-3d-glow.is-usable-while-locked.effect-orbit::before {
  content: "";
  position: absolute;
  width: 0.4rem;
  height: 0.4rem;
  margin: -0.2rem;
  border-radius: 50%;
  background: var(--usable-locked-color, #facc15);
  box-shadow: 0 0 0.35rem 0.1rem var(--usable-locked-color, #facc15);
  animation: usable-locked-orbit 3.5s linear infinite;
  pointer-events: none;
}

/* ロックエリアに置いたカードは、そのロックエリアの持ち主（座席）から見て正しい向きに
   読めるよう回転させる。手前(A)は元々正しい向きなので回転不要。奥(C)は真逆に座っているので
   本来180度、左(B)・右(D)はそれぞれの席から見て正しい向きになるよう90度/-90度。
   奥(C)・右(D)は、それぞれ.lock-top/.lock-right自体に7色スロットの並び順を正すための
   180度回転を追加したため（不具合修正）、カードの向きを打ち消し分だけ再調整している
   （Cは親の180度と相殺して0度、Dは-90度のままだと親の180度回転と合成されて上下逆さまに
   なってしまうため90度に変更）。 */
.lock-top .board-card {
  transform: rotate(0deg);
}
.lock-left .board-card {
  transform: rotate(90deg);
}
.lock-right .board-card {
  transform: rotate(90deg);
}

/* カードが新しくロックされた瞬間だけの使い捨て演出（画像素材/ロック/、assets/lock-icon.webp
   にコピー）。ロックされている間ずっと表示され続けるものではなく、ロックした瞬間に
   カード中心からカードより大きく拡大しながらフェードアウトする一発演出（main.jsの
   spawnLockStamp/triggerLockEffect参照。到達演出の柱状オーラと同じ「使い捨てDOM要素を
   setTimeoutで消す」パターン）。白黒（無色）カードは呼び出し元(maybeAnnounceLock)側で
   既に除外済みなので、ここでは色の判定はしない。 */
.lock-stamp-burst {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 55%;
  height: 55%;
  transform: translate(-50%, -50%) scale(0.4);
  background-image: url("../assets/lock-icon.webp");
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  filter: drop-shadow(0 0.1rem 0.3rem rgba(0, 0, 0, 0.7));
  pointer-events: none;
  z-index: 31;
  opacity: 0;
  animation: lock-stamp-burst 0.9s cubic-bezier(0.16, 0.84, 0.44, 1) forwards;
}
@keyframes lock-stamp-burst {
  0% {
    transform: translate(-50%, -50%) scale(0.4);
    opacity: 0;
  }
  25% {
    opacity: 1;
  }
  60% {
    transform: translate(-50%, -50%) scale(1.9);
    opacity: 0.85;
  }
  100% {
    transform: translate(-50%, -50%) scale(2.6);
    opacity: 0;
  }
}

/* 続き218・ロック演出「A：焼き付く刻印」。ロックするスロット(.cell/.lock-slot)に重ねる。
   --crest-color＝カードの色（虹は金）。収束リング→焼き付く閃光＋衝撃波。 */
/* ===== 黒のカードがロックエリアに置かれた瞬間（ユーザー要望2026-09-06）=====================
   通常のロック（金色の刻印 .lock-sear）とは別物に見えるよう、暗い紫〜黒で「呪印が焼き付く」
   形にしてある。真っ黒だと盤面に埋もれるので、ふちは明るい紫で縁取る。 */
.black-seal {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 100%;
  height: 100%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 32;
  --seal-color: #a855f7;
}
/* 外から染み出してくる暗い霧。 */
.black-seal-smoke {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 260%;
  height: 260%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(24, 6, 38, 0.92) 0%, rgba(38, 10, 58, 0.55) 42%, transparent 72%);
  opacity: 0;
  animation: black-seal-smoke-kf 1.2s ease-out forwards;
}
@keyframes black-seal-smoke-kf {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.35);
  }
  35% {
    opacity: 0.95;
    transform: translate(-50%, -50%) scale(1);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1.28);
  }
}
/* 締まっていく封印のリング（2枚を逆向きに回す）。 */
.black-seal-ring {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 190%;
  height: 190%;
  transform: translate(-50%, -50%);
  border-radius: 22%;
  border: 0.2rem solid var(--seal-color);
  box-shadow: 0 0 0.8rem var(--seal-color), inset 0 0 0.7rem rgba(15, 3, 26, 0.9);
  opacity: 0;
  animation: black-seal-ring-kf 1.05s cubic-bezier(0.5, 0, 0.75, 0) forwards;
}
.black-seal-ring.is-inner {
  width: 140%;
  height: 140%;
  border-width: 0.12rem;
  animation-duration: 1.2s;
  animation-direction: reverse;
}
@keyframes black-seal-ring-kf {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(2.2) rotate(60deg);
  }
  30% {
    opacity: 0.9;
  }
  100% {
    opacity: 0.15;
    transform: translate(-50%, -50%) scale(0.8) rotate(0deg);
  }
}
/* 焼き付く瞬間の暗い閃光（明るい白ではなく紫の芯）。 */
.black-seal-flash {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 210%;
  height: 210%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, #f5e8ff 0%, var(--seal-color) 26%, rgba(30, 6, 48, 0.85) 55%, transparent 72%);
  mix-blend-mode: screen;
  opacity: 0;
  animation: black-seal-flash-kf 0.6s 0.5s ease-out forwards;
}
@keyframes black-seal-flash-kf {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.4);
  }
  25% {
    opacity: 0.95;
    transform: translate(-50%, -50%) scale(1.02);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1.4);
  }
}

/* ===== なないろの欠片の2枚ロック（ユーザー要望2026-09-06「壮大な演出でもいいかも」）=======
   このゲームで一番派手なロックなので、スロットの中（光条＋3重のリング＋白い芯）に加えて
   画面全体にも七色の帯を走らせる（.rainbow-lock-sweep、body直下＝盤面の3D階層の外）。 */
.rainbow-lock {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 100%;
  height: 100%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 33;
}
/* 回る七色の光条。 */
.rainbow-lock-rays {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 420%;
  height: 420%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: repeating-conic-gradient(
    from 0deg,
    rgba(199, 0, 37, 0.55) 0deg 8deg,
    transparent 8deg 22deg,
    rgba(250, 190, 0, 0.55) 22deg 30deg,
    transparent 30deg 44deg,
    rgba(34, 172, 56, 0.55) 44deg 52deg,
    transparent 52deg 66deg,
    rgba(27, 184, 206, 0.55) 66deg 74deg,
    transparent 74deg 88deg,
    rgba(145, 93, 163, 0.55) 88deg 96deg,
    transparent 96deg 120deg
  );
  mask-image: radial-gradient(circle, transparent 18%, #000 34%, #000 62%, transparent 78%);
  -webkit-mask-image: radial-gradient(circle, transparent 18%, #000 34%, #000 62%, transparent 78%);
  opacity: 0;
  animation: rainbow-lock-rays-kf 1.6s ease-out forwards;
}
@keyframes rainbow-lock-rays-kf {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.25) rotate(0deg);
  }
  25% {
    opacity: 0.95;
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1.15) rotate(150deg);
  }
}
/* 外へ広がる七色のリング（3枚を時間差で）。 */
.rainbow-lock-ring {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 130%;
  height: 130%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 0.24rem solid transparent;
  border-image: conic-gradient(
      #c70025,
      #ee781f,
      #fabe00,
      #22ac38,
      #1bb8ce,
      #f19ec2,
      #915da3,
      #c70025
    )
    1;
  opacity: 0;
  animation: rainbow-lock-ring-kf 1.25s cubic-bezier(0.2, 0.7, 0.3, 1) forwards;
}
.rainbow-lock-ring.is-2 {
  animation-delay: 0.22s;
}
.rainbow-lock-ring.is-3 {
  animation-delay: 0.44s;
}
@keyframes rainbow-lock-ring-kf {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.35);
  }
  20% {
    opacity: 0.95;
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(3.4);
  }
}
/* 中央の白い芯。 */
.rainbow-lock-flash {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 260%;
  height: 260%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, #fff 0%, rgba(255, 255, 255, 0.85) 18%, transparent 58%);
  mix-blend-mode: screen;
  opacity: 0;
  animation: rainbow-lock-flash-kf 0.7s 0.62s ease-out forwards;
}
@keyframes rainbow-lock-flash-kf {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.3);
  }
  22% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.05);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1.6);
  }
}
/* 画面全体を横切る七色の帯（turn-announce.js の光の帯と同じ作り＝body直下・fixed）。 */
.rainbow-lock-sweep {
  position: fixed;
  inset: 0;
  z-index: 8998;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(
    100deg,
    transparent 0%,
    rgba(199, 0, 37, 0.22) 12%,
    rgba(250, 190, 0, 0.22) 20%,
    rgba(34, 172, 56, 0.22) 28%,
    rgba(27, 184, 206, 0.22) 36%,
    rgba(145, 93, 163, 0.22) 44%,
    transparent 58%
  );
  background-size: 300% 100%;
  animation: rainbow-lock-sweep-kf 1.7s cubic-bezier(0.25, 0.8, 0.3, 1) forwards;
}
@keyframes rainbow-lock-sweep-kf {
  0% {
    opacity: 0;
    background-position: 100% 50%;
  }
  22% {
    opacity: 1;
  }
  75% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    background-position: 0% 50%;
  }
}
/* 「ずっと光り続ける演出をやめる」設定では、画面全体の帯は出さない（一発の演出だが、
   眩しさを避けたい人の意図に沿う。スロットの中の演出はそのまま）。 */
body.reduce-glow .rainbow-lock-sweep {
  display: none;
}

/* ===== 結ばれの一本桜 コノハナサクヤ「手繰り寄せる」（main.js playKonohanaPull）=====
   マスチェンジの入れ替えには駒と駒をつなぐ電撃のアークがあるが、こちらは説明モーダルが
   出るだけだった。桜のカードなので電撃は流用せず、引き寄せ先から相手の駒へ桜の筋が伸び、
   花びらが巻き取られるように戻ってくる形にした。盤面の3D階層の外（body直下）に置くので、
   盤面のWebGL描画のON/OFFに左右されない。 */
.konohana-pull {
  position: fixed;
  height: 0;
  z-index: 10440;
  pointer-events: none;
  transform-origin: 0 50%; /* 起点＝引き寄せ先。あとは rotate で相手の駒の方へ向ける */
}
/* 引き寄せの筋。先（相手の駒側）から手前（発動者側）へ巻き取られていく。 */
.konohana-thread {
  position: absolute;
  left: 0;
  top: -1px;
  width: 100%;
  /* 【#333】スマホでは実画面 1.4px しかなく見えなかった（ステージの縮小率は約0.46）。太くする。 */
  height: 6px;
  border-radius: 6px;
  transform-origin: 0 50%;
  background: linear-gradient(90deg,
    rgba(255, 214, 232, 0) 0%,
    rgba(var(--konohana-rgb, 241, 158, 194), 0.95) 45%,
    rgba(255, 255, 255, 1) 100%);
  box-shadow: 0 0 0.5rem 0.06rem rgba(var(--konohana-rgb, 241, 158, 194), 0.75);
  animation: konohana-thread 1250ms cubic-bezier(0.25, 0.8, 0.3, 1) forwards;
}
@keyframes konohana-thread {
  0% { opacity: 0; transform: scaleX(0.05); }
  30% { opacity: 1; transform: scaleX(1); }
  100% { opacity: 0; transform: scaleX(0.12); }
}
/* 花びら。--start（筋のどこから流れ出すか）・--sway（横ぶれ）・--spin（回転）は JS が1枚ずつ決める。 */
.konohana-petal {
  position: absolute;
  left: 0;
  top: 0;
  /* 【重要】--start は「筋のどこから流れ出すか」の割合。translate() の % は**自分自身の
     大きさ**に対する割合なので、大きさを持たない箱に入れると常に 0px になり、花びらが全部
     起点に重なって出ない（実測でそうなっていた）。筋と同じ幅を持たせて、% が筋の長さに
     対する割合として効くようにする。高さは 0 のままで、中の花びらが負のマージンで 中央に来る。 */
  width: 100%;
  height: 0;
  opacity: 0;
  /* both にするのは、遅らせている間も 0% の姿勢（＝流れ出す位置で待つ）を保つため。
     forwards だけだと、順番待ちの花びらが起点に戻った状態で待機する。 */
  animation: konohana-petal 1250ms cubic-bezier(0.3, 0.7, 0.35, 1) both;
}
.konohana-petal-inner {
  /* 筋の傾きを打ち消して、花びらが横倒しにならないようにする（JS が rotate を入れる）。 */
  /* 盤面（明るいプレイマット）の上でも見えるよう、白より桃を強く・輪郭に濃い縁を置く。 */
  /* 【#333】0.72rem はスマホでは実画面 5px 程度で「演出が全然見えなかった」（ユーザー報告）。 */
  width: 1.25rem;
  height: 1.5rem;
  margin: -0.75rem 0 0 -0.62rem;
  border-radius: 60% 60% 55% 55% / 70% 70% 40% 40%;
  background: linear-gradient(160deg, #ffe6f1 0%, rgba(var(--konohana-rgb, 241, 158, 194), 1) 45%, #d2649a 100%);
  box-shadow: 0 0 0.45rem 0.05rem rgba(var(--konohana-rgb, 241, 158, 194), 0.9),
    0 0 0 0.5px rgba(160, 60, 105, 0.55);
}
@keyframes konohana-petal {
  0% { opacity: 0; transform: translate(var(--start), 0) rotate(0deg) scale(0.6); }
  18% { opacity: 1; transform: translate(calc(var(--start) * 0.82), var(--sway)) rotate(calc(var(--spin) * 0.3)) scale(1); }
  100% { opacity: 0; transform: translate(0, calc(var(--sway) * -0.4)) rotate(var(--spin)) scale(0.5); }
}
/* 引き寄せ先で花びらが渦を巻く（着地点の目印） */
.konohana-swirl {
  position: fixed;
  width: 4.8rem;
  height: 4.8rem;
  margin: -2.4rem 0 0 -2.4rem;
  border-radius: 50%;
  z-index: 10439;
  pointer-events: none;
  border: 0.12rem solid rgba(var(--konohana-rgb, 241, 158, 194), 0.85);
  box-shadow: 0 0 0.9rem 0.1rem rgba(var(--konohana-rgb, 241, 158, 194), 0.55),
    inset 0 0 0.7rem rgba(255, 255, 255, 0.75);
  animation: konohana-swirl 1350ms 260ms cubic-bezier(0.2, 0.8, 0.3, 1) forwards;
  opacity: 0;
}
@keyframes konohana-swirl {
  0% { opacity: 0; transform: rotate(0deg) scale(0.35); }
  35% { opacity: 1; transform: rotate(140deg) scale(1); }
  100% { opacity: 0; transform: rotate(300deg) scale(1.5); }
}

/* ===== 盤面・ロックエリアのカードが捨てられる瞬間（main.js spawnBoardCardDiscardBurst）=====
   手札のカードには前から霧散演出があったが、盤面とロックエリアのカードは音もなく消えていた。
   白の意思の覚醒のように一度に何枚も捨てられることがあるので、全画面のキャンバスを使う手札側の
   演出は流用せず、**その場で小さく散る**軽い作りにしてある（1枚あたり DOM 数個）。
   盤面の3D階層の外（body直下）に置くので、盤面のWebGL描画のON/OFFに左右されない。 */
.board-discard-ghost {
  position: fixed;
  z-index: 10440; /* 盤面より手前・勝利演出(10450)より下 */
  pointer-events: none;
  transform-origin: 50% 60%;
  animation: board-discard-fall 820ms cubic-bezier(0.3, 0, 0.4, 1) forwards;
}
.board-discard-face {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  border-radius: 0.18rem;
  box-shadow: 0 0 0.5rem 0.05rem rgba(var(--discard-rgb, 226, 232, 240), 0.55);
}
/* 火（ワイナウエア）は下から炙られたように、光（白の意思）は白く飛ばす。 */
.board-discard-ghost.is-fire .board-discard-face {
  box-shadow: 0 0 0.7rem 0.1rem rgba(255, 96, 48, 0.8), inset 0 -0.5rem 0.7rem rgba(255, 60, 0, 0.6);
}
.board-discard-ghost.is-light .board-discard-face {
  box-shadow: 0 0 1rem 0.22rem rgba(255, 255, 255, 0.95);
}
@keyframes board-discard-fall {
  0% { opacity: 1; transform: translate(0, 0) rotate(0deg) scale(1); filter: brightness(1); }
  22% { opacity: 1; transform: translate(0, -6%) rotate(-3deg) scale(1.05); filter: brightness(1.5); }
  100% { opacity: 0; transform: translate(0, 26%) rotate(9deg) scale(0.72); filter: brightness(0.7); }
}
/* 散る火の粉。--ex/--ey は JS が1粒ずつ向きを決める。 */
.board-discard-ember {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 0.22rem;
  height: 0.22rem;
  margin: -0.11rem 0 0 -0.11rem;
  border-radius: 50%;
  opacity: 0;
  background: rgba(var(--discard-rgb, 226, 232, 240), 0.95);
  box-shadow: 0 0 0.35rem 0.06rem rgba(var(--discard-rgb, 226, 232, 240), 0.8);
  animation: board-discard-ember 640ms ease-out forwards;
}
@keyframes board-discard-ember {
  0% { opacity: 0; transform: translate(0, 0) scale(0.5); }
  25% { opacity: 1; transform: translate(calc(var(--ex) * 0.4), calc(var(--ey) * 0.4)) scale(1); }
  100% { opacity: 0; transform: translate(var(--ex), var(--ey)) scale(0.3); }
}

.lock-converge {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 150%;
  height: 150%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 30;
  --crest-color: #f6c945;
}
.lock-converge::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 16%;
  border: 0.22rem solid var(--crest-color);
  box-shadow: 0 0 0.9rem var(--crest-color), inset 0 0 0.9rem var(--crest-color);
  opacity: 0;
  animation: lock-converge-kf 1.2s cubic-bezier(0.5, 0, 0.75, 0) forwards;
}
@keyframes lock-converge-kf {
  0% {
    transform: scale(2.5) rotate(-50deg);
    opacity: 0;
  }
  30% {
    opacity: 0.9;
  }
  100% {
    transform: scale(0.82) rotate(0deg);
    opacity: 0.25;
  }
}
.lock-sear {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 100%;
  height: 100%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 32;
  --crest-color: #f6c945;
}
.lock-sear-flash {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 200%;
  height: 200%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, #fff 0%, var(--crest-color) 32%, transparent 66%);
  mix-blend-mode: screen;
  opacity: 0;
  animation: lock-sear-flash-kf 0.42s ease-out forwards;
}
@keyframes lock-sear-flash-kf {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.5);
  }
  22% {
    opacity: 0.95;
    transform: translate(-50%, -50%) scale(1.05);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1.35);
  }
}
.lock-sear-shock {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 70%;
  height: 70%;
  transform: translate(-50%, -50%);
  border-radius: 16%;
  border: 0.22rem solid var(--crest-color);
  box-shadow: 0 0 0.7rem var(--crest-color);
  opacity: 0;
  animation: lock-sear-shock-kf 0.6s ease-out forwards;
}
@keyframes lock-sear-shock-kf {
  0% {
    opacity: 0.9;
    transform: translate(-50%, -50%) scale(0.45);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(2.5);
  }
}

/* ドラッグ中、今離すとどこに置かれるかを示すハイライト。findDropTarget()が返す実際の対象
   要素(.cell/.lock-slot/.hand-area/.pile-zone)にmain.js側でこのクラスを付け外しするだけで、
   レイアウトには一切影響しない。ゲームの色(--color-*)と紛れないよう、黄金色で統一する。 */
.drop-target-active {
  outline: 3px solid rgba(250, 204, 21, 0.9);
  outline-offset: 2px;
  box-shadow: 0 0 0.9rem 0.2rem rgba(250, 204, 21, 0.65);
}

/* マウスカーソルが今乗っている「つかめる/対象になる」要素のハイライト（ドラッグ中の黄金色の
   ドロップ先ハイライトとは別役割なので、混同しないよう水色にしている）。駒がカードの上に
   乗っている時、どちらが掴めるかを視覚的に示すために使う。
   ハマりどころ: box-shadowは.piece/.lock-slot等、対象自身が既に別のbox-shadowを持っている
   場合に上書きされて消えてしまう（box-shadowは複数ルールの値を合成しない）。outlineは
   他のどのルールも使っていないプロパティなので、確実に見えるようoutlineだけで表現する。 */
.hover-active {
  outline: 2px solid rgba(56, 189, 248, 0.85);
  outline-offset: 1px;
}

/* 駒は平らな外枠ではなく、立方体の各面（上面+4つの壁）を個別に光らせて、
   立体のまま辺が光っているように見せる。.piece-faceはbox-shadowを他で使っていないので
   ここで問題なく使える。 */
.piece.hover-active {
  outline: none;
}
.piece.hover-active .piece-face {
  box-shadow: 0 0 0.4rem 0.12rem rgba(56, 189, 248, 0.9);
}

/* 表向きカードにカーソルを乗せた時の拡大プレビュー。main.jsのupdatePreview()が
   カーソル位置に応じてleft/topとbackground-imageを書き換える。3D空間の外(body直下)に
   置く単純な2D要素なので、盤面の傾き等は一切気にしなくてよい。 */
:root {
  --card-preview-size: 20rem; /* 管理者モードで調整可能 */
}

#card-preview {
  position: fixed;
  width: var(--card-preview-size);
  height: var(--card-preview-size);
  border-radius: 0.5rem;
  border: 2px solid rgba(255, 255, 255, 0.7);
  box-shadow: 0 1rem 2.5rem rgba(0, 0, 0, 0.6);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  pointer-events: none;
  /* ユーザー報告「スマホで手札を拡大している最中に『ロックしますか？』モーダルが拡大より
     手前に出て、拡大内容が読めない」。カードの拡大プレビューは読むための最前面表示なので、
     操作確認モーダル(#touch-action-confirm-modal, z-index:10611)より前に出す。
     pointer-events:none のためモーダルのボタン操作は透過して妨げない。 */
  z-index: 10700;
  display: none;
}

/* 拡大プレビューの下に付ける一行の注意書き（main.jsのapplyPreviewNote）。今は
   「奇跡の森 ヴァーディアンの公開ドローで引いたカードは、このターン使わなければ
   ターン終了時に捨てられる」の告知に使う。プレビュー自体は固定サイズなので、
   注意書きはその下（外側）へはみ出す形で置く。 */
.card-preview-note {
  position: absolute;
  top: calc(100% + 0.35rem);
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: calc(var(--card-preview-size) * 1.35);
  padding: 0.25rem 0.6rem;
  border-radius: 0.35rem;
  background: rgba(24, 20, 14, 0.94);
  border: 1px solid rgba(250, 204, 21, 0.7);
  color: #fde68a;
  font-size: 0.72rem;
  line-height: 1.35;
  text-align: center;
  white-space: normal;
}

/* ユーザー要望「駒にカーソルをかざすと全部の駒にプレイヤー名が吹き出すようにしたい」。
   main.jsのshowAllPieceNameBubblesが、各駒の画面上の位置に合わせてこの吹き出しを差し込む。
   #card-previewと同じ position:fixed・pointer-events:none 方式なので、盤面の3Dの傾きに
   影響されず常に正対して読める。駒の中心の少し上に、下向きの三角（吹き出しの尻尾）付きで出す。 */
/* お知らせ／更新情報モーダル（changelog.js）。ホーム画面の「お知らせ」タイルから開く。 */
/* ユーザー要望「お知らせ更新情報ウィンドウが上下で見切れる→全画面表示に」。
   中央寄せの箱だと項目が増えるほどステージ外へはみ出て見切れるため、全画面ページ
   （#profile-page等と同じ inset:0）にし、タイトルを上に固定して一覧だけを内側スクロール
   させる。読みやすさのため一覧本文は中央に max-width で寄せる。 */
#changelog-modal {
  position: fixed;
  inset: 0;
  transform: none;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: rgba(15, 23, 32, 0.98);
  border: none;
  border-radius: 0;
  padding: 1.4rem clamp(1rem, 5vw, 5rem) 1.6rem;
  z-index: 2401;
  font-family: sans-serif;
  color: #e2e8f0;
}
#changelog-back {
  flex-shrink: 0;
  align-self: flex-start;
  background: none;
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.4rem;
  color: #e2e8f0;
  font-family: sans-serif;
  font-size: 0.85rem;
  padding: 0.45rem 0.9rem;
  cursor: pointer;
  margin-bottom: 0.9rem;
}
#changelog-back:hover {
  border-color: rgba(56, 189, 248, 0.7);
}
body.theme-light #changelog-back {
  color: #3a3326;
  border-color: rgba(176, 141, 63, 0.5);
}
.changelog-modal-title {
  flex-shrink: 0;
  font-weight: bold;
  font-size: 1.15rem;
  margin-bottom: 0.9rem;
  padding-right: 1.6rem;
  color: #fbbf24;
}
.changelog-list {
  flex: 1 1 auto;
  overflow-y: auto;
  width: min(50rem, 100%);
  margin: 0 auto;
}
.changelog-entry {
  border-top: 1px solid rgba(148, 163, 184, 0.18);
  padding: 0.7rem 0;
}
.changelog-entry:first-of-type {
  border-top: none;
  padding-top: 0;
}
.changelog-date {
  font-size: 0.8rem;
  font-weight: bold;
  color: #7dd3fc;
  font-family: ui-monospace, monospace;
  margin-bottom: 0.35rem;
}
.changelog-items {
  margin: 0;
  padding-left: 1.1rem;
}
.changelog-items li {
  font-size: 0.85rem;
  line-height: 1.6;
  margin-bottom: 0.25rem;
}
/* 続き484: 管理者だけに出る「開発者向けの詳細」の見出しと項目。一般向けの項目と
   見分けが付くよう、見出しを付けて本文を一段落ち着かせる（changelog.js参照）。 */
.changelog-dev-head {
  margin-top: 0.5rem;
  margin-bottom: 0.25rem;
  font-size: 0.72rem;
  font-weight: bold;
  color: #a5b4fc;
  opacity: 0.9;
}
.changelog-dev-items li {
  color: #cbd5e1;
  opacity: 0.85;
  font-size: 0.78rem;
}
/* 【2026-09-28】不具合の直しを畳んだ1行と、その開閉ボタン（changelog.js参照）。項目の列と違って
   「見出しっぽく」見えてほしいので、箇条書きの点は付けず1行に並べる。 */
.changelog-fixes-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.35rem;
}
/* 色は決め打ちしない。#changelog-modal 自体が明暗テーマで文字色を持っており、一般の項目
   (.changelog-items li) も色を指定せず継承している。ここで薄い色を決め打ちすると、明るいテーマの
   背景（羊皮紙）で文字が見えなくなる——実際に描いて撮ったら「不具合を修正しました」もボタンの文字も
   消えていた（2026-09-28）。 */
.changelog-fixes-label {
  font-size: 0.85rem;
  line-height: 1.6;
}
.changelog-fixes-toggle {
  flex: 0 0 auto;
  padding: 0.12rem 0.5rem;
  font-size: 0.72rem;
  color: inherit;
  background: rgba(148, 163, 184, 0.18);
  border: 1px solid rgba(148, 163, 184, 0.45);
  border-radius: 0.3rem;
  cursor: pointer;
}
.changelog-fixes-toggle:hover {
  background: rgba(148, 163, 184, 0.3);
}
.changelog-fixes-detail {
  margin-top: 0.3rem;
}
/* 【2026-09-29・続き544】お知らせの良いニュースを「短い1行＋詳細」に畳む。
   文字色は決め打ちしない——#changelog-modal 側が明暗テーマごとの色を持っており、
   .changelog-items li も色を指定せず継承している（続き535で実害）。 */
.changelog-summary-detail {
  margin-top: 0.3rem;
  padding-left: 0.6rem;
  border-left: 2px solid rgba(148, 163, 184, 0.45);
  font-size: 0.92em;
  opacity: 0.85;
}
.changelog-more-row {
  display: flex;
  justify-content: flex-end;
  margin-top: 0.35rem;
}
/* 畳んだ中身は「詳しい方」なので、一般向けの項目より少し控えめにする。 */
.changelog-fix-items li {
  color: #cbd5e1;
  font-size: 0.8rem;
}
/* 英語表示で、その回のお知らせに英訳(itemsEn)が無い時の一言（changelog.js参照）。 */
.changelog-ja-only {
  color: #94a3b8;
  font-size: 0.72rem;
  opacity: 0.85;
  margin-bottom: 0.3rem;
}
.changelog-empty {
  color: #94a3b8;
  font-size: 0.85rem;
  padding: 0.5rem 0;
}

/* デプロイ検知＆更新案内バナー（update-checker.js）。画面上部中央に固定。閉じられる。 */
#update-available-banner {
  position: fixed;
  top: 0.5rem;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  padding: 0.35rem 0.6rem 0.35rem 0.9rem;
  background: rgba(190, 24, 93, 0.95);
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: 0.6rem;
  color: #fff;
  font-size: 0.85rem;
  font-weight: bold;
  /* タイトル/オープニング画面(#opening-screen)は z-index:50000 の全画面オーバーレイなので、
     以前(10050)ではバナーがその裏に隠れ、ホームに移って初めて見える状態だった（ユーザー
     指摘2026-08-10「タイトル画面から出せないか／ホームでだけ出る理由は？」）。タイトルでも
     見えるよう、オープニングより前面へ出す。 */
  z-index: 50001;
  box-shadow: 0 0.4rem 1rem rgba(0, 0, 0, 0.5);
}
/* バナー本体（ラベル・更新ボタン・閉じる）の横並び。ハードリフレッシュ案内(update-banner-hint)を
   下に積めるよう、外側は縦積み(flex-direction:column)にしてこの行だけ横並びにする。 */
#update-available-banner .update-banner-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
/* 前回の更新が空振りだった時だけ出すハードリフレッシュ手順（update-checker.js参照）。 */
#update-available-banner .update-banner-hint {
  font-size: 0.72rem;
  font-weight: normal;
  line-height: 1.4;
  color: rgba(255, 255, 255, 0.92);
  padding: 0 0.2rem 0.1rem;
}
.update-banner-reload {
  font-size: 0.78rem;
  padding: 0.2rem 0.7rem;
  background: #fff;
  color: #be185d;
  border: none;
  border-radius: 0.4rem;
  cursor: pointer;
  font-weight: bold;
}
.update-banner-close {
  font-size: 1rem;
  line-height: 1;
  padding: 0 0.3rem;
  background: transparent;
  border: none;
  color: rgba(255, 255, 255, 0.85);
  cursor: pointer;
}
/* スマホでは「更新する」が小さくて押しづらいので、大きく＆少し下げる（ユーザー要望）。 */
body.is-phone-device #update-available-banner {
  top: 2.6rem;
  padding: 0.5rem 0.7rem 0.5rem 1rem;
  font-size: 1rem;
}
body.is-phone-device .update-banner-reload {
  font-size: 1.05rem;
  padding: 0.5rem 1.3rem;
  border-radius: 0.5rem;
}
body.is-phone-device .update-banner-close {
  font-size: 1.4rem;
  padding: 0 0.5rem;
}

/* 観戦中バナー（main.jsのupdateSpectatorBanner）。画面上部中央に固定。 */
#spectator-banner {
  position: fixed;
  top: 0.5rem;
  left: 50%;
  transform: translateX(-50%);
  display: none;
  align-items: center;
  gap: 0.6rem;
  padding: 0.3rem 0.8rem;
  background: rgba(14, 116, 144, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 0.6rem;
  color: #fff;
  font-size: 0.85rem;
  font-weight: bold;
  z-index: 9600;
  box-shadow: 0 0.4rem 1rem rgba(0, 0, 0, 0.5);
}
.spectator-banner-exit {
  font-size: 0.75rem;
  padding: 0.15rem 0.55rem;
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: 0.4rem;
  color: #fff;
  cursor: pointer;
}
/* 【2026-09-21・ユーザー要望】観戦中の「視点」切り替え（参加者ごとのボタン）。
   今見ている人のボタンだけ明るくする。 */
.spectator-banner-seats {
  display: flex;
  align-items: center;
  gap: 0.3rem;
}
.spectator-banner-seats-label {
  font-size: 0.7rem;
  opacity: 0.85;
  font-weight: normal;
}
.spectator-banner-seat {
  font-size: 0.72rem;
  padding: 0.12rem 0.5rem;
  max-width: 7rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 0.4rem;
  color: #fff;
  cursor: pointer;
}
.spectator-banner-seat.is-current {
  background: rgba(255, 255, 255, 0.92);
  border-color: #fff;
  color: #0e7490;
}

.piece-name-bubble {
  position: fixed;
  transform: translate(-50%, calc(-100% - 0.55rem));
  padding: 0.15rem 0.55rem;
  background: rgba(15, 23, 32, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 0.7rem;
  color: #f4f7fb;
  font-size: 0.8rem;
  font-weight: bold;
  white-space: nowrap;
  pointer-events: none;
  z-index: 9800;
  box-shadow: 0 0.4rem 1rem rgba(0, 0, 0, 0.5);
}
.piece-name-bubble::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -0.35rem;
  transform: translateX(-50%);
  border-left: 0.35rem solid transparent;
  border-right: 0.35rem solid transparent;
  border-top: 0.35rem solid rgba(15, 23, 32, 0.92);
}

/* options-menu.jsの「カード拡大プレビューのサイズ」スライダー用デモ表示。#card-preview
   と見た目・サイズは全く同じにしつつ、画面中央に固定して操作中だけ出す（main.js参照不要）。 */
#options-menu-card-preview-demo {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: var(--card-preview-size);
  height: var(--card-preview-size);
  border-radius: 0.5rem;
  border: 2px solid rgba(255, 255, 255, 0.7);
  box-shadow: 0 1rem 2.5rem rgba(0, 0, 0, 0.6);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  pointer-events: none;
  z-index: 10000;
  display: none;
}

/* 同じマス/ロックスロットに複数枚重なっている時、一番上のカードの左上に出す「+N」バッジ。 */
.stack-badge {
  position: absolute;
  top: -0.35rem;
  left: -0.35rem;
  min-width: 1.1rem;
  height: 1.1rem;
  padding: 0 0.25rem;
  border-radius: 0.6rem;
  background: rgba(15, 23, 32, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.6);
  color: #fff;
  font-size: 0.55rem;
  font-weight: bold;
  font-family: sans-serif;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 60;
  cursor: pointer;
}

/* 山札・エターナル・捨て場・ファーストカードにカーソルを乗せた時だけ出す「名前 N枚」表示。
   常時表示だった名前・枚数テキストを撤去した代わりに、ホバー時のみ少し大きめの文字で示す。 */
#pile-tooltip {
  position: fixed;
  display: none;
  z-index: 10000;
  pointer-events: none;
  background: rgba(15, 23, 32, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.5);
  border-radius: 0.4rem;
  padding: 0.3rem 0.6rem;
  color: #fff;
  font-size: 1rem;
  font-weight: bold;
  font-family: sans-serif;
  white-space: nowrap;
}

/* 「重なっているカードを見る」で開く、要クローズの一覧モーダル。ホバー式の横一列プレビューは
   「何枚も重なっていると表示が難しい」という理由で廃止し、これに置き換えた。
   flex-wrap + max-height + overflow-yで、枚数がいくら増えても画面内に収まるようにする。 */
#stack-modal,
/* 【続き546】ロックカードを選ぶモーダルも同じ見た目・同じ縮小にする */
#lock-pick-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(40rem, 92vw);
  max-height: 85vh;
  overflow-y: auto;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.5rem;
  padding: 1rem;
  z-index: 10002;
  font-family: sans-serif;
  color: #e2e8f0;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
.stack-modal-title {
  font-weight: bold;
  margin-bottom: 0.6rem;
  font-size: 0.9rem;
  padding-right: 1.6rem;
}
.stack-modal-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  max-height: 60vh;
  overflow-y: auto;
  margin-bottom: 0.8rem;
}
.stack-modal-card {
  position: relative;
  width: 8rem;
  height: 8rem;
  border-radius: 0.5rem;
  border: 2px solid rgba(255, 255, 255, 0.7);
  box-shadow: 0 0.4rem 1rem rgba(0, 0, 0, 0.5);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  flex-shrink: 0;
}
/* クリックで選べる版（pickStackedLockCard、ロック2枚重なりからハンド使用するカードを選ぶ）。 */
/* 【#357・2026-09-28】盤面のカードを「捨てる順」に選ぶモーダル（requestBoardCardsOrderedForEffect）。
   #stack-modal の見た目をそのまま使い、案内文と確定ボタンだけ足す。 */
.stack-modal-note {
  margin: 0.2rem 0 0.6rem;
  font-size: 0.78rem;
  line-height: 1.5;
  opacity: 0.85;
  text-align: center;
}
.stack-modal-confirm {
  display: block;
  margin: 0.9rem auto 0;
  padding: 0.4rem 1.2rem;
  border: 1px solid rgba(250, 204, 21, 0.7);
  border-radius: 0.5rem;
  background: rgba(28, 16, 4, 0.95);
  color: #fde68a;
  font-family: sans-serif;
  font-size: 0.85rem;
  font-weight: bold;
  cursor: pointer;
}
.stack-modal-confirm:hover {
  filter: brightness(1.12);
}
/* 選んだカードは、手札の時と同じ見た目（前へ出して縁取り）にする。 */
.stack-modal-card.is-discard-picked {
  outline: 3px solid #fbbf24;
  outline-offset: 2px;
  transform: translateY(-0.3rem);
}
.stack-modal-card.is-pickable {
  cursor: pointer;
  transition: transform 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
}
.stack-modal-card.is-pickable:hover {
  transform: translateY(-0.3rem) scale(1.04);
  border-color: rgba(251, 191, 36, 0.95);
  box-shadow: 0 0.7rem 1.4rem rgba(0, 0, 0, 0.6);
}
/* 【#412・2026-10-12】ユーザー報告「複数捨てるモーダルの時、**下のボタンが見切れてます**！
   カードの上部も少し見切れてる気がします！」
   報告に入っていた画面の記録で**その場で確定した**: モーダルの箱は `160,40,612,349` ＝下端389で
   画面(430)には収まっているのに、**中身が476ある**（はみ出し112）。`#stack-modal` 自身が
   `overflow-y: auto` なので、**確定ボタンが巻かれて見えなくなっていた**。巻けば出てくるが、
   2倍に拡大されたモーダルの中をさらに巻くとは普通は思わない。手元でも再現した
   （手札4枚＝ボタンは見える／**8枚から見えなくなる**。箱は報告と同じ 160,40,612,349）。

   直し方: 中身を縦に3段（題と案内／カードの一覧／確定ボタン）に分け、**巻くのは一覧だけ**にする。
   こうすれば枚数がいくら増えても**確定ボタンは必ず見える**。 */
#stack-modal.is-order-picker {
  display: flex;
  flex-direction: column;
  overflow: hidden; /* 巻くのは中の一覧だけ（下の .stack-modal-list） */
}
#stack-modal.is-order-picker .stack-modal-title,
#stack-modal.is-order-picker .stack-modal-note,
#stack-modal.is-order-picker .stack-modal-confirm {
  flex: 0 0 auto;
}
#stack-modal.is-order-picker .stack-modal-list {
  /* 残りの高さを全部使う。`min-height: 0` が無いと flex の子は縮まず、結局はみ出す。 */
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
  overflow-y: auto;
}
/* スマホ: `max-height: 85vh` は実画面349pxにしかならない（`vh` の罠＝続き575 の★。しかも
   このモーダルは --center-modal-scale-phone で2倍に拡大される）。ステージ基準の rem にして、
   画面の9割ほどまで使う（26rem ＝ 実画面約398px / 430px）。画面が低い端末でも同じ割合になる
   ——rem はステージ倍率と一緒に縮むが、`vh` は縮まないので割合がずれる。 */
body.is-phone-device #stack-modal.is-order-picker,
body.is-tablet-device #stack-modal.is-order-picker {
  max-height: 26rem;
}
/* カードを少し小さくして1行に多く並べる（巻く量そのものを減らす）。8rem は実画面122pxで、
   一覧の幅612pxに4枚しか並ばなかった。 */
body.is-phone-device #stack-modal.is-order-picker .stack-modal-card,
body.is-tablet-device #stack-modal.is-order-picker .stack-modal-card {
  width: 6rem;
  height: 6rem;
}
/* OKボタン1つだけの汎用確認モーダル（山札切れの補充確認等に使う）。 */
#confirm-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(24rem, 90vw);
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.5rem;
  padding: 1.2rem;
  z-index: 10002;
  font-family: sans-serif;
  color: #e2e8f0;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
.confirm-modal-title {
  font-weight: bold;
  margin-bottom: 0.6rem;
}
.confirm-modal-body {
  font-size: 0.9rem;
  line-height: 1.6;
  margin-bottom: 1rem;
  white-space: pre-wrap;
}
.confirm-modal-ok {
  padding: 0.4rem 1.4rem;
  background: #0891b2;
  color: #fff;
  border: none;
  border-radius: 0.25rem;
  cursor: pointer;
}

/* 接触の誤押下防止確認モーダル（キャンセル/OKの2択。#confirm-modalのOK単独版と違い
   キャンセルもあるため専用ブロックにしてある）。 */
#contact-confirm-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(26rem, 90vw);
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.5rem;
  padding: 1.2rem;
  z-index: 10601;
  font-family: sans-serif;
  color: #e2e8f0;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
.contact-confirm-title {
  font-weight: bold;
  margin-bottom: 0.6rem;
}
.contact-confirm-body {
  font-size: 0.9rem;
  line-height: 1.6;
  margin-bottom: 1rem;
}
.contact-confirm-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 0.6rem;
}
.contact-confirm-buttons button {
  padding: 0.4rem 1.2rem;
  border: none;
  border-radius: 0.25rem;
  cursor: pointer;
  font-family: sans-serif;
  font-size: 0.85rem;
  font-weight: bold;
}
.contact-confirm-cancel {
  background: linear-gradient(160deg, #64748b, #334155);
  color: #fff;
}
.contact-confirm-cancel:hover {
  background: linear-gradient(160deg, #94a3b8, #475569);
}
.contact-confirm-ok {
  background: linear-gradient(160deg, #22c55e, #15803d);
  color: #fff;
}
.contact-confirm-ok:hover {
  background: linear-gradient(160deg, #4ade80, #16a34a);
}
.contact-confirm-ok:disabled,
.contact-confirm-cancel:disabled {
  opacity: 0.5;
  cursor: default;
}

/* 接触の結果（何を奪った/奪われたか）モーダル（ユーザー要望）。#contact-confirm-modalと
   同じ見た目のパターンに、奪ったカードの画像プレビューを追加した。 */
/* ハンドフェイズで手札効果を持つカードを掴んだ時の中央ヒント（ユーザー要望）。ドラッグの
   邪魔をしないpointer-events:none。掴んでいる間だけ.showで表示。 */
#hand-drop-hint {
  position: fixed;
  top: 38%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.96);
  width: max-content;
  max-width: min(26rem, 90vw);
  background: rgba(30, 41, 59, 0.96);
  border: 1px solid rgba(251, 191, 36, 0.7);
  border-radius: 0.6rem;
  padding: 0.8rem 1.2rem;
  z-index: 10600;
  pointer-events: none;
  font-family: sans-serif;
  font-size: 0.95rem;
  font-weight: 700;
  color: #fde68a;
  text-align: center;
  box-shadow: 0 0.6rem 1.8rem rgba(0, 0, 0, 0.55);
  opacity: 0;
  transition: opacity 0.18s ease, transform 0.18s ease;
}
#hand-drop-hint.show {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
#contact-result-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(24rem, 90vw);
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.5rem;
  padding: 1.2rem;
  z-index: 10621;
  font-family: sans-serif;
  color: #e2e8f0;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
  text-align: center;
}
.contact-result-title {
  font-weight: bold;
  margin-bottom: 0.6rem;
  padding-right: 1.6rem;
}
.contact-result-body {
  font-size: 0.88rem;
  line-height: 1.7;
  white-space: pre-wrap;
  margin-bottom: 0.8rem;
}
.contact-result-card-image {
  position: relative;
  width: 8rem;
  height: 8rem;
  object-fit: cover;
  overflow: hidden;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border-radius: 0.4rem;
  border: 2px solid rgba(255, 255, 255, 0.5);
  box-shadow: 0 0.4rem 1rem rgba(0, 0, 0, 0.5);
  margin: 0 auto 1rem;
  display: block;
}
.contact-result-ok {
  padding: 0.4rem 1.4rem;
  background: #0891b2;
  color: #fff;
  border: none;
  border-radius: 0.25rem;
  cursor: pointer;
  font-family: sans-serif;
  font-size: 0.85rem;
  font-weight: bold;
}
.contact-result-ok:hover {
  background: #0ea5c4;
}
/* ライトテーマ（body.theme-light）: 接触の結果モーダルがダークのままだったので、他の
   ライトモーダル（#bug-report-modal等）と同じアイボリー地＋ゴールド枠＋濃い文字へ。
   タイトル/本文の文字色はこのモーダルからの継承なので、ここで上書きすれば一緒に暗くなる。
   「閉じる」ボタンは色付き（シアン）＋白文字でライト地でも読めるためそのまま。 */
body.theme-light #contact-result-modal {
  background: #fdfbf5;
  border-color: rgba(176, 141, 63, 0.6);
  color: #3a3326;
}

/* カード右クリックの専用メニューと、その中の「カード補足を見る」で開くモーダル。 */
#card-context-menu {
  position: fixed;
  z-index: 10001;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.4rem;
  padding: 0.3rem;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.6);
}
#card-context-menu button {
  display: block;
  width: 100%;
  padding: 0.4rem 0.8rem;
  background: transparent;
  color: #e2e8f0;
  border: none;
  text-align: left;
  cursor: pointer;
  font-size: 0.8rem;
  font-family: sans-serif;
  border-radius: 0.25rem;
  white-space: nowrap;
}
#card-context-menu button:hover {
  background: rgba(56, 189, 248, 0.2);
}

/* モーダル外クリックで閉じられるようにするための透明な全画面オーバーレイ。
   デッキビューアのカードモーダルと同じ「backdropのclickでclose()」パターン。 */
#card-note-modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 10001;
}

#card-note-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(38rem, 92vw);
  max-height: 80vh;
  overflow-y: auto;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.5rem;
  padding: 1rem;
  z-index: 10002;
  font-family: sans-serif;
  color: #e2e8f0;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
/* 拡大画像の右隣に補足テキストを並べることで、縦スクロールをなるべく減らす。
   画面が狭い場合はflex-wrapで縦積みにフォールバックする。 */
.card-note-content {
  display: flex;
  gap: 0.8rem;
  margin-bottom: 0.8rem;
  flex-wrap: wrap;
}
.card-note-image {
  display: block;
  position: relative;
  width: 14rem;
  flex-shrink: 0;
  border-radius: 0.4rem;
  /* <img> から <div>（テキスト合成/画像）へ置換したため、正方形・クリップ・画像敷き設定を明示。 */
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
.card-note-text-col {
  flex: 1;
  min-width: 12rem;
}
.card-note-title {
  font-weight: bold;
  margin-bottom: 0.6rem;
  font-size: 0.95rem;
}
.card-note-body {
  font-size: 0.85rem;
  line-height: 1.6;
  white-space: pre-wrap;
}
/* カード補足モーダル（右クリック・山札一覧の両方）共通の、右上に浮かせる丸型✕閉じるボタン。
   下部に「閉じる」テキストボタンを置く形から変更（画像+テキスト横並びレイアウトと相性が良い）。
   モーダル本体がposition:fixedのため、この絶対配置はモーダルを基準にできる。 */
.modal-close-x {
  position: absolute;
  top: 0.6rem;
  right: 0.6rem;
  width: 1.8rem;
  height: 1.8rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  border: none;
  color: #e2e8f0;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.modal-close-x:hover {
  background: rgba(255, 255, 255, 0.2);
}

/* フェイズ案内板。以前は画面最下部中央にテキストラベル3項目を表示していたが、ユーザー
   提供のアイコン画像（ロック/ハンド/ムーブフェイズ.webp）に合わせて、他のアイコンボタン
   （手札シャッフル等）と同じ見た目に統一し、画面右下（既存のアイコンボタン列の近く）へ
   引っ越した。現時点では「今どのフェイズか」を示す状態が無いため純粋な案内表示だが、
   将来フェイズ状態を持たせた時に該当ボタンを光らせられるよう、dataset.phaseで識別できる
   構造にしてある（phase-guide.js参照）。位置は管理者モードで調整可能
   （--phase-guide-bottom/-right）。 */
#phase-guide-bar {
  position: fixed;
  bottom: var(--phase-guide-bottom, 0rem);
  right: var(--phase-guide-right, 2.3rem);
  z-index: 1000;
  display: flex;
  gap: 0.6rem;
  font-family: sans-serif;
}
/* ユーザー要望「タブレット専用位置調整にフェイズ案内を追加」。他のタブレット専用
   オーバーライドと同じ「未設定ならPC値にフォールバック」の仕組み。 */
body.is-touch-device #phase-guide-bar {
  bottom: var(--phase-guide-bottom-touch, var(--phase-guide-bottom, 0rem));
  right: var(--phase-guide-right-touch, var(--phase-guide-right, 2.3rem));
}
body.diagnostic-flatten-3d.is-touch-device #phase-guide-bar {
  bottom: calc(var(--phase-guide-bottom-touch, var(--phase-guide-bottom, 0rem)) + var(--phase-guide-bottom-flat, 0rem));
  right: calc(var(--phase-guide-right-touch, var(--phase-guide-right, 2.3rem)) + var(--phase-guide-right-flat, 0rem));
}
/* ユーザー要望（続き81）「フェイズ案内エリア（フェイズ案内板、スキップボタン、
   基本時間、相手のターン自分のターン表示）についてスマホ画面での一括サイズ位置調整を
   管理者モードに追加して」。#phase-guide-bar自体が既にこれら全て（基本時間の
   丸アイコン・タイマートグルのポップオーバー・スキップボタン・ターン表示）の
   共通の親要素になっている（turn-timer.js/phase-automation.jsがこのバーへ
   appendChildしている）ため、#option-areaと同じ「親要素ごとtranslate+scale」の
   手法だけで一括調整が実現できる。アンカー（bottom/right基準）を基準点に保つため
   transform-originは右下にする。
   続き87でユーザー要望「PC、タブレット用にも同じ一括調整を追加してほしい」への
   対応として、PC（無条件）・タブレット（body.is-touch-device、未設定ならPC値へ
   フォールバック）の2段を追加した。scale(1)・translate(0,0)が既定のため、
   従来のPC/タブレットの見た目には一切影響しない。 */
#phase-guide-bar {
  transform-origin: bottom right;
  transform: translate(var(--phase-guide-pos-x, 0rem), var(--phase-guide-pos-y, 0rem)) scale(var(--phase-guide-scale, 1));
}
body.is-touch-device #phase-guide-bar {
  transform: translate(
      var(--phase-guide-pos-touch-x, var(--phase-guide-pos-x, 0rem)),
      var(--phase-guide-pos-touch-y, var(--phase-guide-pos-y, 0rem))
    )
    scale(var(--phase-guide-scale-touch, var(--phase-guide-scale, 1)));
}
body.is-phone-device #phase-guide-bar {
  transform: translate(var(--phase-guide-pos-phone-x, 0rem), var(--phase-guide-pos-phone-y, 0rem))
    scale(var(--phase-guide-scale-phone, 1));
}
/* ターンタイマーの基本時間（無音・ロープ非表示の窓）の残り秒数を、ロック/ハンド/ムーブと
   同じ丸いアイコンボタンの見た目（icon-action-button.js共通CSS）で表示する（turn-timer.js
   側がこの箱をbar内へappendする）。アイコン画像の代わりに数字を中央に置き、優先権保持者の
   駒の色で縁取りして「誰の分か」を示す（ロープと同じ色分けの考え方）。残りが少なくなるに
   つれ黄色→赤に変わり、最後は点滅する。 */
.turn-timer-base-clock {
  /* 続き68: タイマーオン/オフのポップオーバーを開閉するトリガーを兼ねるようになった
     ため、クリック可能に見えるカーソルへ変更した。 */
  cursor: pointer;
}
.turn-timer-base-clock .icon-action-button-icon-wrap {
  border-color: var(--turn-timer-base-clock-color, #38bdf8);
}
.turn-timer-base-clock-value {
  font-variant-numeric: tabular-nums;
  font-size: 1.1rem;
  font-weight: bold;
  font-family: sans-serif;
  color: #e2e8f0;
}
.turn-timer-base-clock.is-warning .icon-action-button-icon-wrap {
  border-color: rgba(234, 179, 8, 0.8);
}
.turn-timer-base-clock.is-warning .turn-timer-base-clock-value {
  color: #eab308;
}
.turn-timer-base-clock.is-critical .icon-action-button-icon-wrap {
  border-color: rgba(239, 68, 68, 0.9);
  animation: turn-timer-warning-blink 1s ease-in-out infinite;
}
.turn-timer-base-clock.is-critical .turn-timer-base-clock-value {
  color: #ef4444;
}
/* ユーザー要望「砂時計残機数を基本時間の隣に表示させたい」。ロープ先端の砂時計バッジ
   （.turn-timer-rope-hourglass-count）と同じ「丸枠の右下に小さく添える」見た目にする。 */
.turn-timer-base-clock-hourglass {
  position: absolute;
  bottom: -0.35rem;
  right: -0.7rem;
  display: flex;
  align-items: center;
  gap: 0.1rem;
  background: rgba(15, 23, 32, 0.9);
  border: 1px solid var(--turn-timer-base-clock-color, #38bdf8);
  border-radius: 1rem;
  padding: 0 0.25rem;
}
.turn-timer-base-clock-hourglass-icon {
  display: block;
  width: 0.75rem;
  height: 0.75rem;
  object-fit: contain;
}
.turn-timer-base-clock-hourglass-count {
  font-size: 0.65rem;
  font-family: sans-serif;
  color: #e2e8f0;
}
.phase-guide-tooltip {
  position: absolute;
  bottom: calc(100% + 0.5rem);
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: 16rem;
  background: rgba(15, 23, 32, 0.95);
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: 0.4rem;
  padding: 0.4rem 0.6rem;
  color: #fff;
  font-size: 0.75rem;
  font-weight: normal;
  line-height: 1.4;
  white-space: normal;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.15s ease;
  /* ユーザー報告「ホバー表示がアイコンの裏に行ってしまっている」への対応。以前は
     z-indexを指定しておらず(auto)、隣接する他のアイコンボタン等（多くがz-index:900〜1002）
     の描画順次第でツールチップの方が奥に埋もれてしまうことがあった。ホバー中は常に
     最前面に出したいので、既存モーダル群（最大でも10701付近）より確実に高い値にする。 */
  z-index: 20000;
}
/* オンライン状態アイコンは画面右上にあるため、簡易説明を上に出すと画面外で見切れる
   （ユーザー報告）。このアイコンと、その左隣の不具合報告アイコンだけ下側に出す。 */
#option-area #self-status-online .phase-guide-tooltip,
#self-status-bug-report .phase-guide-tooltip {
  bottom: auto;
  top: calc(100% + 0.5rem);
}
/* 不具合報告アイコンの絵文字（imgアイコンと同じ枠内に収める）。 */
#self-status-bug-report .bug-report-icon-emoji {
  /* 他のSVGアイコンと見た目の大きさを揃える（絵文字は大きく見えがちなので少し小さく）。
     大きさは管理者モードの --bug-report-icon-size で調整（絵文字は枠に連動、既定2.6remで0.44倍）。 */
  font-size: calc(var(--bug-report-icon-size, 1.9rem) * 0.44);
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--bug-report-icon-size, 1.9rem);
  height: var(--bug-report-icon-size, 1.9rem);
}
/* #option-areaは全画面の器で、子は各自 position:absolute で配置する決まり（flow配置では
   ないので位置指定が無いと左上に落ちる＝ユーザー報告の不具合）。ログイン中アイコン
   (#self-status-online, right:21.3rem)の左隣に置く。ラベルは隠してアイコンのみにする。 */
/* 不具合報告ボタンは #option-area ではなく body 直下に置く（main.js参照）。ゲーム中の
   モーダルより常に前面(高いz-index)にして、詰み時でも必ず押して報告できるようにするため
   （ユーザー要望「不具合報告ボタンだけ常に最前面に」）。見た目位置は従来と同じ top/right。 */
#self-status-bug-report {
  position: fixed;
  /* 位置・大きさは管理者モード（不具合報告アイコンのサイズ・位置）で調整可能。 */
  top: var(--bug-report-icon-top, 0.6rem);
  /* ユーザー要望「残金(19.5→22.1rem)と2D切替(16.9rem)の間へ」。これでオンラインのピルの
     部屋名を省略せずに伸ばせる（左隣にアイコンが無くなるため）。 */
  right: var(--bug-report-icon-right, 19.5rem);
  /* カード拡大(100060)等の“本当に最前面”オーバーレイの直下。通常のモーダル(〜10700)・
     全画面ページ(50000台)より前面。これでどのモーダルが出ていても押せる。 */
  z-index: 100055;
  transform: none;
}
#self-status-bug-report .icon-action-button-caption {
  display: none;
}
#self-status-bug-report .icon-action-button-icon-wrap {
  width: var(--bug-report-icon-size, 1.9rem);
  height: var(--bug-report-icon-size, 1.9rem);
}
/* 不具合報告・行動ログアイコンは丸枠・背景塗りつぶし・影を出さず、画像単体で見せる
   （ユーザー要望2026-08-10）。ID+classでベースの.icon-action-button-icon-wrapに勝つ。 */
#self-status-bug-report .icon-action-button-icon-wrap,
#self-status-action-log .icon-action-button-icon-wrap {
  background: none;
  border: none;
  box-shadow: none;
  border-radius: 0;
}
#self-status-bug-report:hover .icon-action-button-icon-wrap,
#self-status-action-log:hover .icon-action-button-icon-wrap {
  background: none;
  border-color: transparent;
  box-shadow: none;
}

/* アイコン+キャプション形式のボタン共通部品（icon-action-button.js参照）。アイコンは丸い
   バッジ、キャプションはその下に小さく表示するクリック可能な文字（点線下線でクリックできる
   ことを示す）。 */
.icon-action-button {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: sans-serif;
}
.icon-action-button-icon-wrap {
  position: relative;
  width: 2.6rem;
  height: 2.6rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(15, 23, 32, 0.85);
  border: 1px solid rgba(148, 163, 184, 0.4);
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.4);
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.icon-action-button:hover .icon-action-button-icon-wrap {
  background: rgba(56, 189, 248, 0.18);
  border-color: rgba(56, 189, 248, 0.6);
  transform: scale(1.06);
}
/* ユーザー報告「ホバー簡易説明がアイコンの裏に行ってしまっている」（再発）。
   .phase-guide-tooltip自体は既にz-index:20000を持つが効いていなかった。真因:
   #draw-button・#end-turn-button・#hand-shuffle-button等は個々に
   position:fixed + transform（--icon-pos-*の位置調整用）+ 同じz-index:1000を持つため、
   それぞれが独立した合成レイヤー（スタッキングコンテキスト）を形成する。ツールチップの
   z-index:20000は「自分の属するボタンの中」でしか比較されず、隣のボタン（同じz-index:1000の
   別のスタッキングコンテキスト）とはDOM順でしか優劣が決まらない——ツールチップ自身の
   z-indexがどれだけ高くても、属するボタンごと隣のボタンの後ろに埋もれ得る。ホバー中の
   ボタン自身のz-indexを引き上げ、ツールチップごと最前面に出す（各アイコンボタンの
   z-index:1000はID詳細度を持つルールのため、!important無しでは負けてしまう）。 */
.icon-action-button:hover {
  z-index: 20000 !important;
}
/* 各アイコンボタン（手札シャッフル・盤面拡大・1枚ドロー・ターン終了・オプション・
   誤操作防止）は基本サイズを共有していたが、管理者モードから個別にサイズ調整したいという
   要望があったため、ボタンごとに専用のCSS変数を割り振った（位置調整の--icon-pos-*と
   同じ命名規則）。フォールバック値は元の共通サイズ(2.6rem)のまま。 */
#hand-shuffle-button .icon-action-button-icon-wrap {
  width: var(--icon-size-hand-shuffle, 3.5rem);
  height: var(--icon-size-hand-shuffle, 3.5rem);
}
#board-zoom-button .icon-action-button-icon-wrap {
  width: var(--icon-size-board-zoom, 3.5rem);
  height: var(--icon-size-board-zoom, 3.5rem);
}
#draw-button .icon-action-button-icon-wrap {
  width: var(--icon-size-draw, 3.5rem);
  height: var(--icon-size-draw, 3.5rem);
}
#end-turn-button .icon-action-button-icon-wrap {
  width: var(--icon-size-end-turn, 3.5rem);
  height: var(--icon-size-end-turn, 3.5rem);
}
#options-menu-button .icon-action-button-icon-wrap {
  width: var(--icon-size-options, 2.6rem);
  height: var(--icon-size-options, 2.6rem);
}
#piece-hide-button .icon-action-button-icon-wrap {
  width: var(--icon-size-piece-hide, 2.6rem);
  height: var(--icon-size-piece-hide, 2.6rem);
}
#card-hide-button .icon-action-button-icon-wrap {
  width: var(--icon-size-card-hide, 2.6rem);
  height: var(--icon-size-card-hide, 2.6rem);
}
/* PC/タブレットでサイズを分けたい場合の対応（位置の--icon-pos-*-touch-x/yと同じ
   フォールバックチェーン方式。誤操作防止ボタンはタッチ専用のため対象外）。 */
body.is-touch-device #hand-shuffle-button .icon-action-button-icon-wrap {
  width: var(--icon-size-hand-shuffle-touch, var(--icon-size-hand-shuffle, 3.5rem));
  height: var(--icon-size-hand-shuffle-touch, var(--icon-size-hand-shuffle, 3.5rem));
}
body.is-touch-device #board-zoom-button .icon-action-button-icon-wrap {
  width: var(--icon-size-board-zoom-touch, var(--icon-size-board-zoom, 3.5rem));
  height: var(--icon-size-board-zoom-touch, var(--icon-size-board-zoom, 3.5rem));
}
body.is-touch-device #draw-button .icon-action-button-icon-wrap {
  width: var(--icon-size-draw-touch, var(--icon-size-draw, 3.5rem));
  height: var(--icon-size-draw-touch, var(--icon-size-draw, 3.5rem));
}
body.is-touch-device #end-turn-button .icon-action-button-icon-wrap {
  width: var(--icon-size-end-turn-touch, var(--icon-size-end-turn, 3.5rem));
  height: var(--icon-size-end-turn-touch, var(--icon-size-end-turn, 3.5rem));
}
body.is-touch-device #options-menu-button .icon-action-button-icon-wrap {
  width: var(--icon-size-options-touch, var(--icon-size-options, 2.6rem));
  height: var(--icon-size-options-touch, var(--icon-size-options, 2.6rem));
}
/* ハマりどころ（ユーザー往復の経緯）: 最初「オプションアイコンだけスマホで大きい
   （隣のマイページ・ヘルプに揃えて）」との報告を受け、スマホ限定でPCと同じ
   基準サイズ(2.6rem)まで戻す修正をしたが、今度は「小さすぎて管理者モードが
   開けない（オプションボタン自体がこのアイコン）」という逆方向の報告になった。
   --icon-size-options-touch(6rem)がそもそもタブレット/スマホ問わずタッチ端末の
   タップ領域を確保するために用意されていた値だったことを踏まえ、フォールバック
   チェーンを他のアイコン（ヘルプ等）と同じ「スマホ専用値→タッチ専用値→PC値」の
   順に統一した。これでスマホの既定サイズは（未調整なら）タッチ用の6remになり
   タップしやすさを確保しつつ、--icon-size-options-phoneで個別にさらに調整も
   できる（管理者モードのスライダー参照）。 */
body.is-phone-device #options-menu-button .icon-action-button-icon-wrap {
  width: var(--icon-size-options-phone, var(--icon-size-options-touch, var(--icon-size-options, 2.6rem)));
  height: var(--icon-size-options-phone, var(--icon-size-options-touch, var(--icon-size-options, 2.6rem)));
}
/* ホバーで簡易説明（phase-guide-tooltip、フェイズ案内板・優先権譲渡と共用）を表示する。 */
.icon-action-button:hover .phase-guide-tooltip {
  opacity: 1;
  visibility: visible;
}
.icon-action-button-icon-img {
  width: 62%;
  height: 62%;
  object-fit: contain;
}
.icon-action-button-caption {
  font-size: 0.65rem;
  color: #f1f5f9;
  white-space: nowrap;
  cursor: pointer;
  text-decoration: underline dotted rgba(226, 232, 240, 0.4);
  text-underline-offset: 0.15rem;
  /* ユーザー報告「盤面背景が白系だとアイコン下のテキスト（白文字）が読みづらい」。盤面の
     色に依存せず読めるよう、文字の後ろに半透明の暗いバッジを敷き、さらに影を足す。 */
  background: rgba(15, 23, 32, 0.72);
  padding: 0.05rem 0.4rem;
  border-radius: 0.4rem;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.65);
}
.icon-action-button-caption:hover {
  color: #7dd3fc;
  text-decoration-color: #7dd3fc;
}
.icon-action-button:disabled .icon-action-button-icon-wrap {
  opacity: 0.5;
}
.icon-action-button:disabled {
  cursor: not-allowed;
}
.icon-action-button:disabled .icon-action-button-caption {
  cursor: not-allowed;
}
/* 管理者モードの「アイコン再配置モード」がONの間、対象の5つのアイコンボタンに
   掴めることが分かる点線の枠＋grabカーソルを付ける（icon-rearrange.js参照）。
   ドラッグ中は枠を強調しgrabbingカーソルに切り替える。 */
body.icon-rearrange-mode #hand-shuffle-button,
body.icon-rearrange-mode #board-zoom-button,
body.icon-rearrange-mode #draw-button,
body.icon-rearrange-mode #end-turn-button,
body.icon-rearrange-mode #options-menu-button {
  cursor: grab;
  outline: 2px dashed rgba(56, 189, 248, 0.7);
  outline-offset: 3px;
  border-radius: 50%;
}
body.icon-rearrange-mode #hand-shuffle-button.is-rearranging,
body.icon-rearrange-mode #board-zoom-button.is-rearranging,
body.icon-rearrange-mode #draw-button.is-rearranging,
body.icon-rearrange-mode #end-turn-button.is-rearranging,
body.icon-rearrange-mode #options-menu-button.is-rearranging {
  cursor: grabbing;
  outline-color: #38bdf8;
}

/* 管理者モードの「自分専用ステータスエリア再配置モード」がONの間、対象の要素に
   掴めることが分かる点線の枠＋grabカーソルを付ける（self-status-rearrange.js参照）。
   ドラッグで位置、マウスホイールでサイズを変えられる（アイコン再配置モードと同じ
   考え方だが、サイズ調整もできる点が異なるため別モードとして用意した）。 */
body.self-status-rearrange-mode .self-status-large-avatar,
body.self-status-rearrange-mode .self-status-piece-thumb,
body.self-status-rearrange-mode .self-status-card-back-thumb,
body.self-status-rearrange-mode .self-status-playmat-thumb,
body.self-status-rearrange-mode #self-status-online {
  cursor: grab;
  outline: 2px dashed rgba(56, 189, 248, 0.7);
  outline-offset: 3px;
}
body.self-status-rearrange-mode .self-status-large-avatar.is-rearranging,
body.self-status-rearrange-mode .self-status-piece-thumb.is-rearranging,
body.self-status-rearrange-mode .self-status-card-back-thumb.is-rearranging,
body.self-status-rearrange-mode .self-status-playmat-thumb.is-rearranging,
body.self-status-rearrange-mode #self-status-online.is-rearranging {
  cursor: grabbing;
  outline-color: #38bdf8;
}

/* 盤面拡大ボタン: 拡大中は色を、2段階目「もっと拡大」はさらに別の色を、というアイコン版。 */
#board-zoom-button.is-active .icon-action-button-icon-wrap {
  background: rgba(34, 197, 94, 0.25);
  border-color: rgba(74, 222, 128, 0.7);
}
#board-zoom-button.is-zoom-2 .icon-action-button-icon-wrap {
  background: rgba(249, 115, 22, 0.25);
  border-color: rgba(251, 146, 60, 0.7);
}

/* 「拡大率登録」ボタン（盤面拡大機能の再設計、main.jsのbuildBoardZoomRegisterButton
   参照）。手動ズーム/移動中（hasManualView）だけ、盤面拡大ボタンの真上に浮かんで
   現れる。位置はJS側で盤面拡大ボタンの実際の座標から毎回計算し直す（ドラッグ再配置
   されても追従するため）ので、ここではleft/topの基準からの中央下寄せだけ指定する。
   常に点滅させて「まだ登録されていない設定」であることを目立たせる。 */
#board-zoom-register-button {
  position: fixed;
  transform: translate(-50%, -100%);
  z-index: 1001;
}
#board-zoom-register-button .icon-action-button-icon-wrap {
  width: 2rem;
  height: 2rem;
  animation: board-zoom-register-pulse 1.2s ease-in-out infinite;
}
@keyframes board-zoom-register-pulse {
  0%, 100% {
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.4), 0 0 0 0.1rem rgba(250, 204, 21, 0.9);
    transform: scale(1);
  }
  50% {
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.4), 0 0 0 0.4rem rgba(250, 204, 21, 0.15);
    transform: scale(1.12);
  }
}
/* 登録した瞬間だけ、点滅の代わりに緑色でしっかり止める（「保存できた」というフィード
   バック）。0.6秒後にmain.js側でクラスを外し、通常の点滅表示に戻る。 */
#board-zoom-register-button.is-just-registered .icon-action-button-icon-wrap {
  animation: none;
  background: rgba(74, 222, 128, 0.35);
  border-color: #4ade80;
  transform: scale(1.2);
}

#phase-guide-modal,
#priority-transfer-modal,
#icon-action-detail-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(30rem, 92vw);
  max-height: 80vh;
  overflow-y: auto;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.5rem;
  padding: 1rem;
  z-index: 10101;
  font-family: sans-serif;
  color: #e2e8f0;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
.phase-guide-modal-title {
  font-weight: bold;
  font-size: 1rem;
  margin-bottom: 0.6rem;
}
.phase-guide-modal-body {
  font-size: 0.85rem;
}

/* 画面中央、プレイマットを横断するスケールで出現する神秘的なオーラのロープ。基本時間
   （管理者モードのropeBaseSeconds、無音・無表示）が切れて初めて、延長時間（同
   ropeExtensionSeconds）ぶんだけ出現する——「1回の行動」の基本時間そのものにはロープを
   出さない、というユーザー要望を反映。色は優先権を持つプレイヤーの駒の色
   （turn-timer.js側が--turn-timer-rope-colorへ動的に設定、手番プレイヤーの駒の発光と
   同じ「神秘的なオーラ」のトンマナ）。誰の延長中でも全プレイヤーに見える（自分のロープ
   だけではない）。右端（火花＋砂時計）が左へ進みながら燃え尽きていく＝ロープ本体
   (.turn-timer-rope-strand)の幅を右側から縮める。 */
#turn-timer-rope {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(calc(-50% + var(--turn-timer-rope-pos-x)), calc(-50% + var(--turn-timer-rope-pos-y)));
  z-index: 960;
  width: var(--turn-timer-rope-width);
  max-width: 90vw;
  pointer-events: none;
  --turn-timer-rope-color: #eab308;
}
.turn-timer-rope-track {
  position: relative;
  height: 1.6rem;
}
.turn-timer-rope-strand {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  height: 0.5rem;
  width: 100%;
  border-radius: 0.3rem;
  background: linear-gradient(
    90deg,
    color-mix(in srgb, var(--turn-timer-rope-color) 35%, transparent),
    var(--turn-timer-rope-color)
  );
  box-shadow:
    0 0 1.2rem 0.25rem color-mix(in srgb, var(--turn-timer-rope-color) 65%, transparent),
    0 0 0.4rem color-mix(in srgb, var(--turn-timer-rope-color) 90%, white);
  animation: turn-timer-rope-aura-pulse 1.4s ease-in-out infinite;
}
@keyframes turn-timer-rope-aura-pulse {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(1.4); }
}
.turn-timer-rope-tip {
  position: absolute;
  top: 50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  transform: translate(-50%, -50%);
}
.turn-timer-rope-spark {
  width: 0.9rem;
  height: 0.9rem;
  border-radius: 50%;
  background: radial-gradient(circle, #fff, var(--turn-timer-rope-color) 55%, transparent 75%);
  animation: turn-timer-rope-spark-flicker 0.22s steps(2) infinite;
  margin-bottom: 0.2rem;
}
@keyframes turn-timer-rope-spark-flicker {
  0% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.55; transform: scale(0.65) rotate(25deg); }
  100% { opacity: 1; transform: scale(1.15) rotate(-15deg); }
}
.turn-timer-rope-hourglass {
  position: relative;
  font-size: 1.5rem;
  line-height: 1;
  filter: drop-shadow(0 0 0.5rem var(--turn-timer-rope-color));
}
/* ユーザーがプレイヤー色別の砂時計アイコン画像を追加してくれたため、絵文字(⏳)から
   差し替えた。優先権保持者の駒の色に応じて表示するアイコンをJS側(updateRope)で切り替える。 */
.turn-timer-rope-hourglass-icon {
  display: block;
  width: 1.5rem;
  height: 1.5rem;
  object-fit: contain;
}
.turn-timer-rope-hourglass-count {
  position: absolute;
  bottom: -0.35rem;
  right: -0.7rem;
  background: rgba(15, 23, 32, 0.9);
  border: 1px solid var(--turn-timer-rope-color);
  border-radius: 50%;
  min-width: 1.1rem;
  padding: 0 0.15rem;
  font-size: 0.65rem;
  font-family: sans-serif;
  font-weight: bold;
  color: #fff;
  text-align: center;
}
.turn-timer-rope-name {
  margin-top: 0.9rem;
  text-align: center;
  font-family: sans-serif;
  font-size: 0.75rem;
  font-weight: bold;
  color: var(--turn-timer-rope-color);
  text-shadow: 0 0 0.5rem var(--turn-timer-rope-color);
}

/* 優先権譲渡ボタン。参加座席全員分（自分を含む）を、実際のテーブルの位置関係と同じ
   「＋」形（上/左/右/自分=下）に配置する。自分のボタンは「自分に優先権を戻す」ボタンを
   兼ねる。誰でも押せる自己申告制（このゲーム全体の設計方針通り、押した本人が今優先権を
   持っているかは問わない）。円の中はそのプレイヤーのアバター、枠の色はそのプレイヤーの
   駒の色。ボタン列の下に「優先権譲渡」という小さなラベルを置き、フェイズ案内板と同じ
   「ホバーで簡易説明・クリックで詳細説明」を付けてある。 */
/* 「手札シャッフル」ボタン（縦積みボタン列の一番上、bottom:10.8rem）のすぐ上に置く
   （ユーザー指定）。右揃えでその列と揃える。 */
#priority-transfer-buttons {
  position: fixed;
  bottom: 13.7rem;
  right: 1.2rem;
  transform: translate(var(--priority-transfer-pos-x), var(--priority-transfer-pos-y));
  z-index: 950;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  font-family: sans-serif;
}
/* ユーザー要望「タブレット専用位置調整に優先権譲渡ボタンを追加」。他のタブレット専用
   オーバーライドと同じ「未設定ならPC値にフォールバック」の仕組み。
   ユーザー要望「優先権譲渡ボタンの一括サイズ調整を追加」に対応し、自分専用ステータス
   エリア（--self-status-scale-touch）と同じ考え方でscale()を追加した。 */
body.is-touch-device #priority-transfer-buttons {
  transform: translate(
      var(--priority-transfer-pos-touch-x, var(--priority-transfer-pos-x)),
      var(--priority-transfer-pos-touch-y, var(--priority-transfer-pos-y))
    )
    scale(var(--priority-transfer-scale-touch, 1));
}
body.diagnostic-flatten-3d.is-touch-device #priority-transfer-buttons {
  transform: translate(
      calc(var(--priority-transfer-pos-touch-x, var(--priority-transfer-pos-x)) + var(--priority-transfer-flat-x, 0rem)),
      calc(var(--priority-transfer-pos-touch-y, var(--priority-transfer-pos-y)) + var(--priority-transfer-flat-y, 0rem))
    )
    scale(var(--priority-transfer-scale-touch, 1));
}
.priority-transfer-grid {
  display: grid;
  grid-template-columns: repeat(3, 2.6rem);
  grid-template-rows: repeat(3, 2.6rem);
  gap: 0.3rem;
  position: relative;
}
/* ユーザー要望「優先権譲渡のアイコン群に4アイコンを囲むようにひし形の枠をつけたい」
   への対応。上下左右4アイコンの中心同士をちょうど結ぶ正方形(グリッド中心から見て
   1セル+隙間分=2.9rem四方)を45度回転させることで、頂点が4アイコンの中心に一致する
   ひし形になる。装飾のみでクリックを奪わないようpointer-events:noneにしてある。 */
.priority-transfer-diamond {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 5.9rem;
  height: 5.9rem;
  transform: translate(-50%, -50%) rotate(45deg);
  border: 2px solid rgba(148, 163, 184, 0.55);
  border-radius: 0.4rem;
  pointer-events: none;
  z-index: 0;
}
.priority-transfer-btn {
  /* ハマりどころ: グリッドセルのサイズ(2.6rem)任せにしていたところ、アバターが絵文字
     テキストの座席では中身の文字幅次第でボタン自体が横に伸び、正円ではなく縦長の楕円に
     なってしまうことがあった。width/heightを明示して常に正円になるようにする。 */
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.5);
  background: rgba(15, 23, 32, 0.85);
  color: #e2e8f0;
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  overflow: hidden;
  position: relative;
}
.priority-transfer-btn img.avatar-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
}
.priority-transfer-btn:hover {
  filter: brightness(1.3);
}
/* 実際のテーブルの位置関係（自分から見て奥/左/右、自分は手前）と同じ「＋」形に配置する。
   turn-timer.js側がdata-pos属性（top/left/right/self）を各ボタンに付ける。 */
.priority-transfer-btn[data-pos="top"] { grid-column: 2; grid-row: 1; }
.priority-transfer-btn[data-pos="left"] { grid-column: 1; grid-row: 2; }
.priority-transfer-btn[data-pos="right"] { grid-column: 3; grid-row: 2; }
.priority-transfer-btn[data-pos="self"] { grid-column: 2; grid-row: 3; }
/* 自分のボタン（＝優先権を自分に戻すボタン）は、他人へ渡すボタンと見分けられるよう
   金色のリングを重ねる。 */
.priority-transfer-btn.is-self::after {
  content: "";
  position: absolute;
  inset: -0.2rem;
  border-radius: 50%;
  border: 2px solid rgba(250, 204, 21, 0.8);
  pointer-events: none;
}
/* ユーザー要望「優先権を誰が持っているのか明確にしたい」への対応。今まさに優先権を
   持っている座席のボタンだけ、ゆっくり明滅する水色の光でひと目で分かるようにする
   （is-selfの金色リングとは別レイヤーなので、自分が優先権を持っている時は両方重なって見える）。 */
@keyframes priority-transfer-glow {
  0%, 100% { box-shadow: 0 0 0.3rem 0.05rem rgba(56, 189, 248, 0.6); }
  50% { box-shadow: 0 0 0.6rem 0.2rem rgba(56, 189, 248, 0.95); }
}
.priority-transfer-btn.has-priority {
  border-color: #38bdf8;
  animation: priority-transfer-glow 1.6s ease-in-out infinite;
}

/* ユーザー要望「4アイコンの中央に『優先権譲渡』のテキストを持ってきてほしい」への対応。
   グリッドの中心セル（元々空き）に配置し、ひし形の枠(.priority-transfer-diamond)より
   手前に表示する。背景を少し敷いて、枠線と文字が重なっても読みやすくしてある
   （fieldsetのlegendと同じ発想）。 */
.priority-transfer-label {
  position: relative;
  grid-column: 2;
  grid-row: 2;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(15, 23, 32, 0.85);
  border: none;
  border-radius: 0.3rem;
  color: #94a3b8;
  font-size: 0.6rem;
  line-height: 1.15;
  white-space: nowrap;
  cursor: pointer;
  padding: 0.1rem 0.25rem;
}
.priority-transfer-label:hover {
  color: #e2e8f0;
}
.priority-transfer-label:hover .phase-guide-tooltip {
  opacity: 1;
  visibility: visible;
}
/* .phase-guide-tooltipは既定でleft:50%＋中央寄せだが、優先権譲渡ボタンは画面右端近く
   （right:1.2rem）に置いてあるため、中央寄せのままだとツールチップが画面右端で見切れて
   いた。右端をラベルの右端に合わせ、常に左方向にだけ伸びるようにする。 */
.priority-transfer-label .phase-guide-tooltip {
  left: auto;
  right: 0;
  transform: none;
}

/* 同じ理由（画面右端近くのボタンでツールチップが中央寄せのまま右へ見切れる）で、
   ターン終了ボタン(#end-turn-button、right:1.2rem)にも同じ右寄せ補正を適用する。 */
#end-turn-button .phase-guide-tooltip {
  left: auto;
  right: 0;
  transform: none;
}
/* 逆に画面左端近く（#piece-hide-button/#card-hide-button、left:1.2rem付近）では中央寄せの
   ツールチップが左へ見切れるため、左端をアイコンの左端に合わせ、常に右方向にだけ伸びる
   ようにする。 */
#piece-hide-button .phase-guide-tooltip,
#card-hide-button .phase-guide-tooltip {
  left: 0;
  transform: none;
}
/* ユーザー報告（続き92）「駒消し・カード消し・優先権譲渡アイコンの長押し簡易ホバー説明が
   小さいので他の簡易ホバー説明のサイズに合わせて大きくしてほしい」。優先権譲渡は
   #priority-transfer-buttons自体がbody.is-touch-deviceで--priority-transfer-scale-touch
   分だけ縮小されており、親のtransform:scaleは子孫のツールチップの見た目も一緒に
   縮小してしまう。駒消し/カード消しには同じ縮小の仕組みは無いが、3つまとめて
   スマホ限定でフォントサイズ・パディングを引き上げ、他の簡易説明と見比べやすい
   サイズに揃える。 */
body.is-phone-device #piece-hide-button .phase-guide-tooltip,
body.is-phone-device #card-hide-button .phase-guide-tooltip,
body.is-phone-device .priority-transfer-label .phase-guide-tooltip {
  font-size: 1rem;
  padding: 0.6rem 0.9rem;
  max-width: 20rem;
}

/* ユーザー報告「ホバー表示が画面の端で見切れていないか確認してほしい」で見つかった
   2件。#phase-guide-bar（画面右下、right:2.3rem）内のボタンも中央寄せのままだと
   ツールチップが右へ見切れるため、上記2つと同じ右寄せ補正を適用する。 */
#phase-guide-bar .phase-guide-tooltip {
  left: auto;
  right: 0;
  transform: none;
}

/* #options-menu-button（画面右上、top:0.3rem/right:1rem）は右端に近いだけでなく画面
   最上部にもあるため、既定の「ボタンの上に出す」(bottom:calc(100% + 0.5rem))のままだと
   画面上端でも見切れてしまう。右寄せに加え、下方向に出すよう上下も反転させる。 */
#options-menu-button .phase-guide-tooltip {
  left: auto;
  right: 0;
  top: calc(100% + 0.5rem);
  bottom: auto;
  transform: none;
}
/* ユーザー報告「オプションアイコン以外、ホバーしても簡易説明が出ない」。原因は、これらの
   右上オプションエリアのアイコンも簡易説明のテキスト自体は持っているのに、既定の
   phase-guide-tooltipが「ボタンの上」(bottom:calc(100%+0.5rem))に出るため、画面最上部の
   これらでは上端の外に描画されて見えなかったこと。options-menu-buttonと同じく下方向へ
   出し、右端側で見切れないよう右寄せにする（テキストは左へ伸びる）。 */
#board-view-toggle-button .phase-guide-tooltip,
#discord-link-button .phase-guide-tooltip,
#ranking-page-button .phase-guide-tooltip,
#help-button .phase-guide-tooltip,
#my-page-button .phase-guide-tooltip {
  left: auto;
  right: 0;
  top: calc(100% + 0.5rem);
  bottom: auto;
  transform: none;
}

/* #end-turn-buttonのそばに出す、タイムアウト警告バッジ（点滅）。stateは一切変更しない、
   あくまで「ムーブフェイズを終えてターンを終了してください」という視覚的な促し。 */
.turn-timer-warning {
  position: fixed;
  transform: translateY(-50%);
  z-index: 1001;
  background: rgba(239, 68, 68, 0.95);
  color: #fff;
  font-family: sans-serif;
  font-size: 0.7rem;
  font-weight: bold;
  padding: 0.4rem 0.7rem;
  border-radius: 0.4rem;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
  animation: turn-timer-warning-blink 1s ease-in-out infinite;
  pointer-events: none;
  /* 文中の明示的な<br>（turn-timer.js参照）で2行に区切っているため、自動折返しが
     割り込まないよう各行の実際の幅より少し広めに確保する。 */
  max-width: 12rem;
  white-space: nowrap;
  text-align: right;
  line-height: 1.5;
}
/* 警告テキストだけでなく、対象の#end-turn-button自体にも点滅する縁取りを付ける。
   画面が狭くテキストバッジが見切れる／被る場合でも、光っているボタン自体で気付ける
   ようにするための保険。 */
#end-turn-button.turn-timer-warning-glow {
  animation: turn-timer-button-glow 1s ease-in-out infinite;
}
/* 「優先権を手番プレイヤーに返してください」警告用。優先権譲渡ボタン群自体を同じ
   点滅する縁取りで光らせる（updatePriorityReturnWarning参照）。 */
#priority-transfer-buttons.turn-timer-warning-glow {
  animation: turn-timer-button-glow 1s ease-in-out infinite;
  border-radius: 0.6rem;
}
@keyframes turn-timer-button-glow {
  0%, 100% { box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4), 0 0 0 0.15rem rgba(239, 68, 68, 0.9); }
  50% { box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4), 0 0 0 0.3rem rgba(239, 68, 68, 0.3); }
}
/* ユーザー要望「到達効果の処理が終わったら原則ターンを終了します。なので
   ターン終了アイコンを強調してターン終了を促そう」。turn-timer-warning-glow
   （赤、タイムアウト警告＝何か問題があるような見た目）とは別に、「そろそろ
   終了してよいですよ」という穏やかな促しのための青系パルス
   （updateEndTurnButton参照）。 */
#end-turn-button.is-emphasized {
  animation: end-turn-emphasis-glow 1.2s ease-in-out infinite;
}
@keyframes end-turn-emphasis-glow {
  0%, 100% { box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4), 0 0 0 0.15rem rgba(56, 189, 248, 0.9); }
  50% { box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4), 0 0 0 0.35rem rgba(56, 189, 248, 0.3); }
}
@keyframes turn-timer-warning-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

/* ドラッグ中はカーソルに1:1追従する「ゴースト」を3D空間の外(document.body直下)に表示する。
   left:0;top:0;を基準にtransform: translate()だけで位置決めするので、駒/カードの実寸
   (remベースの固定サイズ)をそのまま流用でき、ドラッグ開始時にサイズを測る必要がない。 */
.drag-ghost {
  position: fixed;
  left: 0;
  top: 0;
  margin: 0;
  z-index: 9999;
  pointer-events: none;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.5);
}

/* 駒をドラッグ中も立方体のまま見せるための入れ子構造。外側(outer)はカーソル追従の位置決めだけ
   を担当し、内側(inner)にperspective+盤面と同じ傾き(--table-tilt)を与えることで、盤面の上に
   乗っている時と同じような立体的な見た目を、3D空間の外(document.body直下)でも再現する。 */
.drag-ghost-piece-outer {
  position: fixed;
  left: 0;
  top: 0;
  width: var(--cell);
  height: var(--cell);
  perspective: var(--camera-perspective);
  pointer-events: none;
  z-index: 9999;
}

.drag-ghost-piece-inner {
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
}

/* 実物のプレイマット画像（画像素材/プレイマット.png、assets/playmat.pngにコピー）を敷く。
   以前は`.arena`の背景画像として実装していたが、2つの問題があった：
   (1) background-sizeで拡大すると、はみ出した部分が要素の外側として切り取られ見切れる。
   (2) 背景のサイズ基準が「アリーナ全体（ロック帯を含む可変サイズの箱）」だったため、
       ロック帯の太さを変えるとアリーナの箱ごと変わり、画像も道連れで動いてしまう。
   これを解決するため、画像は`.playmat-bg`という独立した要素にし、
   - サイズ基準を「盤面(.board)の固定サイズ」にする（ロック帯の太さに影響されない）
   - background-sizeではなくtransform: scale()で拡大する（はみ出しても切れない。
     transform-originを中心にしているので盤面中央が拡大縮小の起点になる） */
:root {
  /* プレイマット画像の位置合わせ用パラメータ。管理者モード（src/admin.js）から調整できる。
     scaleは盤面サイズ(.board)を100%とした拡大率、pos-x/pos-yは中心からのずれ（%）。 */
  --playmat-scale: 1.42;
  --playmat-pos-x: 0%;
  --playmat-pos-y: 0%;
  /* .boardの実サイズ（枠のpadding込み）。ロック帯・プレイマット双方の位置計算の基準として共通化した。 */
  --board-size: calc(7 * var(--cell) + 6 * 0.2rem + 2 * 0.3rem);
  /* ロック帯の「長さ」方向のサイズ＝マス7個分（盤面自体の枠paddingは含まない、マスの並びと同じ長さ）。 */
  --lock-band-length: calc(7 * var(--cell) + 6 * 0.2rem);
  /* ロックエリアは盤面のマスと同じ大きさ（--cell四方）の帯として、盤面の外側にデフォルトで
     隙間なく密着配置する。太さ調整は行わず、盤面中心からの距離（位置）だけを辺ごとに独立して
     ずらせるようにする。position:absolute + transformによる見た目上の移動のみなので、
     グリッドのトラックサイズには一切影響せず、他の要素（手札・山札・他の辺のロック帯など）は
     連動して動かない（以前は帯の「太さ」をCSS Gridのトラックサイズとして扱っていたため、
     1辺だけ太さを変えると盤面全体・他の全要素が連動して動いてしまうバグがあった）。 */
  --lock-top-pos-x: 0rem;
  --lock-top-pos-y: -1.8rem;
  --lock-bottom-pos-x: 0rem;
  --lock-bottom-pos-y: 1.8rem;
  --lock-left-pos-x: -1.9rem;
  --lock-left-pos-y: 0rem;
  --lock-right-pos-x: 1.8rem;
  --lock-right-pos-y: 0rem;
  --lock-slot-border-width: 0.01rem; /* ロックスロットの枠線の太さ（管理者モードで調整可） */
  --lock-slot-glow-scale: 0.25; /* ロックスロットの色付きグローの強さ（管理者モードで調整可） */
}

.arena {
  grid-column: 2;
  grid-row: 2;
  display: grid;
  width: max-content;
  transform-style: preserve-3d;
  padding: 1rem;
  border-radius: 0.75rem;
  position: relative;
}

/* 背景画像（ユーザー提供、プレイマットよりさらに大きい「背景」イメージ）。プレイマット
   (.playmat-bg、z-index:1)よりさらに背面(z-index:0)に、同じ「.boardの固定サイズを基準に
   transform:scaleで拡大」の考え方で配置する。中心を合わせる基準はプレイマットと同じ
   .board中心なので、拡大率だけ変えれば自然とプレイマットの背後に大きくはみ出して見える。
   ハマりどころ（ユーザー報告「もともと横長だったのにトリミングされてしまう」）: 元画像は
   横長(実測1672x941、約16:9)だが、箱自体は.board基準の正方形(--board-size四方)に固定し、
   単一のscale()で均等に拡大していたため、background-size:100% 100%（枠いっぱいに引き
   伸ばす指定）と組み合わさって、正方形の枠に押し込められる形で縦方向に潰れて見えていた。
   横幅方向・縦方向を別々の倍率（--table-background-scale-x/-scale-y）で拡大できるように
   し、background-sizeも100% 100%（引き伸ばし）からcontain（トリミングもせず、箱の縦横比に
   関わらず画像本来の縦横比のまま収める）に変更した。これにより、たとえ箱の縦横比が画像と
   完全に一致していなくても画像が変形・トリミングされることは無い。 */
:root {
  --table-background-scale-x: 4.75;
  --table-background-scale-y: 4.85;
  --table-background-pos-x: 0%;
  --table-background-pos-y: 108.5%;
}
.table-background-bg {
  position: absolute;
  z-index: 0;
  top: 50%;
  left: 50%;
  width: var(--board-size);
  height: var(--board-size);
  margin-top: calc(-1 * var(--board-size) / 2);
  margin-left: calc(-1 * var(--board-size) / 2);
  background-repeat: no-repeat;
  background-size: contain;
  transform: translate(var(--table-background-pos-x), var(--table-background-pos-y))
    scale(var(--table-background-scale-x), var(--table-background-scale-y));
  transform-origin: center center;
  pointer-events: none;
}

.playmat-bg {
  position: absolute;
  z-index: 1;
  top: 50%;
  left: 50%;
  width: var(--board-size);
  height: var(--board-size);
  margin-top: calc(-1 * var(--board-size) / 2);
  margin-left: calc(-1 * var(--board-size) / 2);
  /* 画像自体はJS側(buildArena)がplaymat.jsの選択状態に応じてinline styleで敷く
     （セットアップウィザードで白/青/黒を選べるようにしたため、固定のurlをやめた）。 */
  background-repeat: no-repeat;
  background-size: 100% 100%;
  transform: translate(var(--playmat-pos-x), var(--playmat-pos-y)) scale(var(--playmat-scale));
  transform-origin: center center;
  pointer-events: none;
  /* 経緯: 最初z-indexを明示指定したら、position指定のないロックエリアや盤面（通常のフロー要素）
     より手前に描画され隠れるバグが起きたため、一旦z-indexを外してDOM順に任せる方式にした。
     しかしそれでも「.lock-areaだけ描画されない」問題が再発した（DevTools上は正しい位置・色・
     最前面のはずなのに画面に出ない、というブラウザ側の合成の問題と思われる不具合）。
     最終的に、プレイマット(1) < 盤面(2) < ロックエリア(3)の順で全要素に明示的なz-indexを
     揃えて割り振ることで解決した。z-indexを「全く使わない」のではなく「関係する全要素に
     一貫した値を必ず割り振る」のが安全、という教訓。 */
}

/* ロックエリアは.arenaの中心（＝盤面の中心。.arenaのpaddingは全辺均等なので中心は一致する）
   を基準に、盤面の実サイズ(--board-size)の半分だけ外側へmarginでずらして「盤面の外側に密着」
   をデフォルト位置にする。そこからのユーザー調整分はtransformで加える（レイアウトに影響しない）。 */
.lock-area {
  position: absolute;
  z-index: 3;
  display: flex;
  gap: 0.2rem;
  align-items: center;
  justify-content: center;
  border-radius: 0.4rem;
}

/* 現在のターンプレイヤーのロックエリア全体を、ゆっくり柔らかく明滅させて目立たせる
   （box-shadowは.lock-area自体に背景が無くても外側に描画されるため、これだけで光って見える）。
   色は--turn-glow-rgb（管理者モードで白/黄色を切り替え可能）を参照する。 */
:root {
  --turn-glow-rgb: 255, 224, 130; /* デフォルトは黄色寄り。管理者モードのチェックボックスで白(255,255,255)に切り替え可 */
}
@keyframes lock-area-turn-glow {
  0%, 100% { box-shadow: 0 0 0.6rem 0.1rem rgba(var(--turn-glow-rgb), 0.25); }
  50% { box-shadow: 0 0 2.2rem 0.7rem rgba(var(--turn-glow-rgb), 0.75); }
}
.lock-area.is-turn-player {
  animation: lock-area-turn-glow 2.6s ease-in-out infinite;
}

.lock-top, .lock-bottom {
  left: 50%;
  width: var(--lock-band-length);
  height: var(--cell);
  margin-left: calc(-1 * var(--lock-band-length) / 2);
  flex-direction: row;
}
.lock-top {
  top: 50%;
  margin-top: calc(-1 * var(--board-size) / 2 - var(--cell));
  /* プレイヤーCのロックエリアも、Dの.lock-right同様に7色スロットの並び順が逆だった
     バグ修正。.lock-bottom(A)と同じDOM順のまま同じ左→右で描画すると、対面に座る
     Cから見ると自分の右→左になってしまうため、rotate(180deg)でその場反転させ、
     Bと同じ考え方でAと正しくミラーになるようにする（既存のtranslateには影響しない）。
     ロックしたカードの向き(.lock-top .board-card)もこれに合わせて再調整済み。 */
  transform: translate(var(--lock-top-pos-x), var(--lock-top-pos-y)) rotate(180deg);
}
.lock-bottom {
  top: 50%;
  margin-top: calc(var(--board-size) / 2);
  transform: translate(var(--lock-bottom-pos-x), var(--lock-bottom-pos-y));
}

.lock-left, .lock-right {
  top: 50%;
  height: var(--lock-band-length);
  width: var(--cell);
  margin-top: calc(-1 * var(--lock-band-length) / 2);
  flex-direction: column;
}
.lock-left {
  left: 50%;
  margin-left: calc(-1 * var(--board-size) / 2 - var(--cell));
  transform: translate(var(--lock-left-pos-x), var(--lock-left-pos-y));
}
.lock-right {
  left: 50%;
  margin-left: calc(var(--board-size) / 2);
  /* プレイヤーDから見て7色スロットの並び順が逆になっていたバグ修正。rotate(180deg)は
     rightmost（内側）に置くことで、translate(先頭)はこれまで通り画面座標系での
     単純な位置調整のままにし、回転は箱自身の中心を軸にした「その場での180度反転」だけに
     留めている（180度回転は中心が動かないため、既存のtuned位置には影響しない）。
     ロックしたカードの向き(main.js/style.css内.lock-right .board-card)もこれに合わせて
     再調整済み。 */
  transform: translate(var(--lock-right-pos-x), var(--lock-right-pos-y)) rotate(180deg);
}

/* ロックエリアバー: ロックエリアと盤面の間に置く装飾画像（画像素材/ロックエリアバー/、
   assets/lock-area-bar.webpにコピー）。画像自体は横長の1枚絵なので、外側の位置決め用ボックス
   （top/bottomはそのまま横長、left/rightは幅高さを入れ替えた縦長）と、中の画像用要素
   （常に横長のまま、left/rightだけCSSでrotate(90deg)）を分けている。回転による見た目上の
   ズレを、外側ボックスの位置決め計算に混ぜずに済むようにするため。表示/非表示はオプション
   メニューの「基本設定」から（main.jsのbuildLockAreaBarがdisplayを切り替える）。 */
:root {
  --lock-bar-scale: 1.13;
  --lock-bar-thickness: 1.2rem;
  --lock-bar-top-pos-x: 0rem;
  --lock-bar-top-pos-y: 0rem;
  --lock-bar-bottom-pos-x: 0rem;
  --lock-bar-bottom-pos-y: 0rem;
  --lock-bar-left-pos-x: 0rem;
  --lock-bar-left-pos-y: 0rem;
  --lock-bar-right-pos-x: 0rem;
  --lock-bar-right-pos-y: 0rem;
}
.lock-area-bar {
  position: absolute;
  z-index: 2;
  pointer-events: none;
}
.lock-area-bar-image {
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(var(--lock-band-length) * var(--lock-bar-scale));
  height: calc(var(--lock-bar-thickness) * var(--lock-bar-scale));
  /* 画像は main.js の buildLockAreaBar がインラインstyleで敷く（WebGL描画がインラインしか
     読まないため）。ここで url() を書くと指定元が2か所に散るので書かない。 */
  background-size: 100% 100%;
  background-repeat: no-repeat;
  transform: translate(-50%, -50%);
}
.lock-area-bar-left .lock-area-bar-image,
.lock-area-bar-right .lock-area-bar-image {
  transform: translate(-50%, -50%) rotate(90deg);
}
/* プレイヤーD（右辺）の画像は柄の向きが逆さまだったため、90degに180deg足して反転させる。
   他の3辺とは絵柄の向きが独立して調整できるよう、あえてrightだけ別ルールに分けている。 */
.lock-area-bar-right .lock-area-bar-image {
  transform: translate(-50%, -50%) rotate(270deg);
}
.lock-area-bar-top, .lock-area-bar-bottom {
  left: 50%;
  width: calc(var(--lock-band-length) * var(--lock-bar-scale));
  height: calc(var(--lock-bar-thickness) * var(--lock-bar-scale));
  margin-left: calc(-1 * var(--lock-band-length) * var(--lock-bar-scale) / 2);
}
.lock-area-bar-top {
  top: 50%;
  margin-top: calc(-1 * var(--board-size) / 2 - var(--lock-bar-thickness) * var(--lock-bar-scale));
  transform: translate(var(--lock-bar-top-pos-x), var(--lock-bar-top-pos-y));
}
/* プレイヤーC（奥）の画像も柄の向きが逆さまだったため180deg回転させる（右辺と同じ理由、
   外側の位置決めボックス側ではなく中の画像要素だけを回転させることで既存の位置調整には
   影響しない）。 */
.lock-area-bar-top .lock-area-bar-image {
  transform: translate(-50%, -50%) rotate(180deg);
}
.lock-area-bar-bottom {
  top: 50%;
  margin-top: calc(var(--board-size) / 2);
  transform: translate(var(--lock-bar-bottom-pos-x), var(--lock-bar-bottom-pos-y));
}
.lock-area-bar-left, .lock-area-bar-right {
  top: 50%;
  width: calc(var(--lock-bar-thickness) * var(--lock-bar-scale));
  height: calc(var(--lock-band-length) * var(--lock-bar-scale));
  margin-top: calc(-1 * var(--lock-band-length) * var(--lock-bar-scale) / 2);
}
.lock-area-bar-left {
  left: 50%;
  margin-left: calc(-1 * var(--board-size) / 2 - var(--lock-bar-thickness) * var(--lock-bar-scale));
  transform: translate(var(--lock-bar-left-pos-x), var(--lock-bar-left-pos-y));
}
.lock-area-bar-right {
  left: 50%;
  margin-left: calc(var(--board-size) / 2);
  transform: translate(var(--lock-bar-right-pos-x), var(--lock-bar-right-pos-y));
}

.lock-slot {
  position: relative; /* 中に置かれた.board-cardをposition:absoluteで配置するための基準 */
  width: var(--cell);
  height: var(--cell);
  border-radius: 0.2rem;
  border-width: var(--lock-slot-border-width);
  border-style: solid;
  border-color: rgba(226, 232, 240, 0.6);
  background: transparent; /* うっすら枠だけの色分けにする（塗りつぶしは無し） */
  /* 3D遠近感で帯が薄く潰れて見えなくなる問題への対策として、色付きのグロー(box-shadow)を追加。
     JS側でcolorプロパティにその色スロットの色を設定し、currentColorで参照する（buildLockArea参照）。
     ぼかしが強すぎると隣接する色同士が混ざって見えるため、控えめな値にしている。
     --lock-slot-glow-scaleで枠線とは別にグローの強さだけ管理者モードから調整できる
     （枠線を細くしても、グローだけ強いと結局「存在感」が残ってしまうため）。 */
  box-shadow: 0 0 calc(0.15rem * var(--lock-slot-glow-scale)) calc(0.05rem * var(--lock-slot-glow-scale)) currentColor;
}

.board {
  grid-column: 3;
  grid-row: 3;
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: repeat(7, var(--cell));
  grid-template-rows: repeat(7, var(--cell));
  gap: 0.2rem;
  border-radius: 0.4rem;
  padding: 0.3rem;
  transform-style: preserve-3d;
}

.cell {
  border-radius: 0.2rem;
  background: rgba(30, 41, 59, 0.15); /* プレイマット画像を透かして見せる薄いティント */
  border: 1px solid rgba(226, 232, 240, 0.15);
  transform-style: preserve-3d;
  position: relative;
}

.cell.is-gate {
  background: rgba(250, 204, 21, 0.12);
  border-color: rgba(250, 204, 21, 0.6);
}

/* ゲートマスの台座演出（管理者モードでオンオフ可能）。駒(.piece)と同じ「床+壁」の
   preserve-3d技法（buildCubePieceと同じ考え方、main.jsのbuildGatePedestal参照）だが、
   .cell自身は動かさず、この装飾専用の子要素だけを浮かせる。pointer-events:noneのため
   駒/カードの当たり判定(elementsFromPoint)をこの上に乗っていても一切邪魔しない。 */
:root {
  --gate-pedestal-height: 0.35rem;
}
.gate-pedestal {
  position: absolute;
  inset: 0;
  pointer-events: none;
  transform-style: preserve-3d;
}
.gate-pedestal-face {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: #cbd5e1; /* ライトグレー */
  backface-visibility: hidden;
}
.gate-pedestal-top {
  transform: translateZ(var(--gate-pedestal-height));
  border-radius: 0.15rem;
}
.gate-pedestal-wall-back {
  height: var(--gate-pedestal-height);
  transform-origin: 50% 100%;
  transform: translateY(-100%) rotateX(90deg) rotateZ(180deg) scaleX(-1);
  filter: brightness(0.5);
}
.gate-pedestal-wall-front {
  bottom: 0;
  height: var(--gate-pedestal-height);
  transform-origin: 50% 100%;
  transform: rotateX(-90deg);
  filter: brightness(0.95);
}
.gate-pedestal-wall-left {
  height: var(--gate-pedestal-height);
  transform-origin: 0% 0%;
  transform: rotateZ(90deg) rotateX(-90deg) scaleX(-1) translateX(-100%) translateY(-100%);
  filter: brightness(0.65);
}
.gate-pedestal-wall-right {
  top: 0;
  right: 0;
  height: var(--gate-pedestal-height);
  transform-origin: 100% 0%;
  transform: rotateZ(-90deg) rotateX(-90deg) translateY(-100%);
  filter: brightness(0.8);
}

/* 他プレイヤーの駒/カードの移動を、remote-move-animator.jsが移動元・移動先のマス自体を
   一瞬点滅させて知らせる（駒・カードそのものの飛翔演出とは別に、操作していないプレイヤーが
   「どこで何が起きたか」を見落としにくくするための合図）。マスの縁が2回明滅するだけの
   短い演出のため、到達演出（マス中央から立ち上る光の柱、.arrival-effect-burst）と同時に
   起きても視覚的に競合しない。 */
/* ユーザー要望「点滅はマスの枠だけでなく面もにしたい、色はそれを行ったプレイヤーの色に
   したい」。--move-blink-color（remote-move-animator.jsが実行者の駒の色で都度セット）を
   使い、縁の光彩(box-shadow)だけでなく面(background-color)も一緒に明滅させる。
   color-mix()で塗りつぶしを半透明にし、下のマス目・駒・カードが透けて見えるようにする
   （完全な不透明色で塗ると駒/カードが隠れてしまうため）。 */
@keyframes move-highlight-blink {
  0%, 100% { box-shadow: none; background-color: transparent; }
  25%, 75% {
    box-shadow: inset 0 0 0 0.2rem var(--move-blink-color, #ffffff), 0 0 0.7rem 0.2rem var(--move-blink-color, #ffffff);
    background-color: color-mix(in srgb, var(--move-blink-color, #ffffff) 45%, transparent);
  }
  50% { box-shadow: none; background-color: transparent; }
}
:root {
  /* 点滅の長さ（秒）。ユーザー要望「その秒数は管理者モードで調整できるようにしたい」。
     keyframes自体は25/75%地点で明滅する比率のままなので、この値を伸ばすほどゆっくり
     2回光るようになる（回数は変わらない）。 */
  --move-blink-duration: 3;
}
.cell.move-highlight-blink,
.lock-slot.move-highlight-blink {
  animation: move-highlight-blink calc(var(--move-blink-duration, 3) * 1s) ease-in-out;
}

/* オンラインで他プレイヤーがカードを場に置いた/取った時、点滅と一緒に方向を示す矢印を
   一瞬表示する（ユーザー要望）。「↓」=置いた、「↑」=取った。マス自体の点滅
   （move-highlight-blink）と重ねて出す、控えめなオーバーレイ。ユーザー要望により、
   矢印をそれを行ったプレイヤーの色・太く大きくし、そのプレイヤーのミニアバターも
   セットで表示する（.move-blink-arrow-glyphが矢印本体、.move-blink-arrow-avatarが
   アバター、どちらも親.move-blink-arrowのvar(--move-blink-color)を共有する）。 */
/* ユーザー報告「単独設置の場合、マスの点滅は正常だがミニアバター（矢印）は1回しか
   点滅しない、また7秒に設定したはずが実際には5秒くらいで消える」への対応。以前はここが
   15%〜70%だけ表示のシンプルな「1回フェードイン→保持→フェードアウト」だったため、
   (a) マス目のmove-highlight-blink（25%/75%の2山、50%で一旦消える）と違って1回しか
   明滅しないように見え、(b) 70%地点（7秒なら約4.9秒）で完全に消えるため、マス目が
   まだ7秒目まで点滅を続けているのに矢印・アバターだけ先に消えて「実際は5秒では」と
   感じさせていた。マス目と同じ25%/75%山・50%谷のリズムに合わせ、100%まで使い切るように
   変更した。 */
@keyframes move-blink-arrow-fade {
  0%, 100% { opacity: 0; transform: translateY(0.2rem) scale(0.7); }
  25%, 75% { opacity: 1; transform: translateY(0) scale(1); }
  50% { opacity: 0; transform: translateY(0) scale(0.85); }
}
.move-blink-arrow {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.1rem;
  pointer-events: none;
  z-index: 6;
  animation: move-blink-arrow-fade calc(var(--move-blink-duration, 3) * 1s) ease-in-out;
}
/* ユーザーが用意した色別の矢印画像（既に着色済み）に差し替えたため、テキストグリフ
   時代のcolor/text-stroke/text-shadowは不要になった。--move-blink-arrow-strokeは
   「輪郭線の太さ」から「発光（drop-shadow）の強さ」へ役割を変えて再利用する
   （admin.jsのCSS変数名は変えずラベルだけ更新、既存の同期の手間を増やさないため）。 */
.move-blink-arrow-glyph {
  width: var(--move-blink-arrow-size, 5rem);
  height: var(--move-blink-arrow-size, 5rem);
  object-fit: contain;
  filter:
    drop-shadow(0 0 var(--move-blink-arrow-stroke, 0.09rem) var(--move-blink-color, #ffffff))
    drop-shadow(0 0.1rem 0.2rem rgba(0, 0, 0, 0.6));
}
.move-blink-arrow-avatar {
  width: 1.8rem;
  height: 1.8rem;
  border-radius: 50%;
  overflow: hidden;
  border: 0.12rem solid var(--move-blink-color, #ffffff);
  box-shadow: 0 0 0.4rem rgba(0, 0, 0, 0.6);
  background: #1e293b;
  /* ユーザー要望「ミニアバターの傾きも管理者モードで調整したい」。 */
  transform: rotate(var(--move-blink-avatar-rotate, 0deg));
}
.move-blink-arrow-avatar .avatar-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* 立方体の駒。ユドナリウム(TK11235/udonarium)のterrain（地形）コンポーネントの技法を
   そのまま移植した。「床」を駒の高さ分translateZで持ち上げ、4枚の壁を角ごとに異なる
   transform-origin + rotateZ/rotateX の組み合わせで側面に貼り付ける、真のpreserve-3d構成。
   （以前試した独自のpreserve-3d立方体は組み方が甘く崩れたが、こちらは実績のある組み方）
   どの面がどれだけ見えるかはブラウザの3D計算に任せる（セル位置によるJS側の手動計算は不要）。
   注意（ハマりどころ）: `.piece`に`filter: drop-shadow()`を付けると、CSSの仕様上
   要素とその子が強制的に2Dへ平坦化され（transform-style: preserve-3dが無視される）、
   壁が全て高さゼロに潰れるバグが発生した。影は`filter`ではなく`box-shadow`で代用する。 */
:root {
  --piece-size: 2.1rem;
  --piece-height: 2rem;
  --piece-hitbox-scale: 1; /* 駒の見た目とは独立に、ホバー/掴む判定エリアの広さだけ調整する（管理者モード） */
}

.piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--piece-size);
  height: var(--piece-size);
  margin-top: calc(var(--piece-size) / -2);
  margin-left: calc(var(--piece-size) / -2);
  transform-style: preserve-3d;
  backface-visibility: hidden;
  box-shadow: 0 0.3rem 0.4rem rgba(0, 0, 0, 0.5);
  /* ハマりどころ（ユーザー報告、タブレット）: セットアップ配布アニメーション終了時、
     .piece.is-setup-pendingのtransform:scale(0.2)から通常表示へtransitionするタイミングで、
     preserve-3dの立方体が「ペっちゃんこ」（壁が高さ0に潰れたように）に描画されたまま
     固まってしまうことがあった。駒を一度掴んで動かす（＝render()で作り直される）と
     正しい立体に戻ることから、初回の合成(compositing)レイヤー確立に失敗するタイプの
     WebKit/モバイルブラウザの既知の不具合と判断し、will-change: transformでブラウザに
     3D合成レイヤーを事前に確保させる、この種の不具合に対する定番の対策を施した。
     一時的にopacityも合わせてヒントする対策を試したが、実機PCで駒自体が描画されなく
     なる（真っ黒/非表示になる）重大な副作用が判明したため、will-change: transformのみに
     戻した（transformだけならタブレットでのペっちゃんこ問題を直したまま、この副作用は
     出ていなかったため）。preserve-3dな立方体（.piece-face×5枚の子要素を持つ）に対しては、
     will-changeへopacityを追加するだけでも合成方法が変わり、GPU/ドライバの組み合わせに
     よっては駒ごと消えてしまうことがある、という教訓。 */
  will-change: transform;
}

.piece-face {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  backface-visibility: hidden;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

/* 見た目には影響しない、ホバー/掴む判定専用の透明な当たり判定エリア。.pieceの中心を基準に
   --piece-hitbox-scaleで拡大縮小する（3D変形の無い平面のままでよい。findDraggableAt/
   findHoverTargetは.closest(".piece")で判定するため、この子要素がクリックされても
   正しく親の.pieceに解決される）。 */
.piece-hitbox {
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(var(--piece-size) * var(--piece-hitbox-scale));
  height: calc(var(--piece-size) * var(--piece-hitbox-scale));
  transform: translate(-50%, -50%);
}

/* 床（上面）: 駒の高さ分持ち上げる */
.piece-top {
  transform: translateZ(var(--piece-height));
}

/* 奥の壁（カメラから見て奥・最も見えにくい面） */
.piece-wall-back {
  height: calc(var(--piece-height) + 0.15rem); /* 上面との継ぎ目の隙間を隠すため少し重ねる */
  transform-origin: 50% 100%;
  transform: translateY(-100%) rotateX(90deg) rotateZ(180deg) scaleX(-1);
  filter: brightness(0.35);
}

/* 手前の壁（カメラに正対する面。最も明るい） */
.piece-wall-front {
  bottom: 0;
  height: calc(var(--piece-height) + 0.15rem); /* 上面との継ぎ目の隙間を隠すため少し重ねる */
  transform-origin: 50% 100%;
  transform: rotateX(-90deg);
}

/* 左の壁 */
.piece-wall-left {
  height: calc(var(--piece-height) + 0.15rem); /* 上面との継ぎ目の隙間を隠すため少し重ねる */
  transform-origin: 0% 0%;
  transform: rotateZ(90deg) rotateX(-90deg) scaleX(-1) translateX(-100%) translateY(-100%);
  filter: brightness(0.55);
}

/* 右の壁 */
.piece-wall-right {
  top: 0;
  right: 0;
  height: calc(var(--piece-height) + 0.15rem); /* 上面との継ぎ目の隙間を隠すため少し重ねる */
  transform-origin: 100% 0%;
  transform: rotateZ(-90deg) rotateX(-90deg) translateY(-100%);
  filter: brightness(0.8);
}

.pile-zone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  font-size: 0.65rem;
  overflow: visible; /* 親の.zoneはoverflow:hiddenだが、拡大縮小したスタックが見切れないよう上書きする */
  /* .stack-frontが3Dで正しく折り畳まれるために必要。この要素自体はtransformを持たないが、
     preserve-3dの既定値flatのままだと、間に挟まっているだけでも子孫(.stack-front)の
     3D変形が壊れて高さ0に潰れてしまう（駒(.piece)側は.cell/.board/.arenaが全てpreserve-3d
     なのに対し、山(.stack)側だけこの.pile-zoneが素通りできていなかったのが原因）。 */
  transform-style: preserve-3d;
}

/* 山札・捨て場・エターナルをそれぞれ独立に拡大縮小・位置調整できるようにする。
   プレイマット(.playmat-bg)と同じ考え方：中心基準のtransform: scale/translateなので見切れない。
   管理者モード（src/admin.js）から調整できる。 */
:root {
  --deck-scale: 1;
  --deck-pos-x: 4.1rem;
  --deck-pos-y: 1.5rem;
  --eternal-scale: 1;
  --eternal-pos-x: 4.1rem;
  --eternal-pos-y: -3.7rem;
  --discard-scale: 1;
  --discard-pos-x: -4.1rem;
  --discard-pos-y: -3.7rem;
  --first-scale: 1;
  --first-pos-x: -4.1rem;
  --first-pos-y: 1.5rem;
}

.zone-deck .stack {
  transform: translate(var(--deck-pos-x), var(--deck-pos-y)) scale(var(--deck-scale));
  transform-origin: center center;
}

.zone-eternal .stack {
  transform: translate(var(--eternal-pos-x), var(--eternal-pos-y)) scale(var(--eternal-scale));
  transform-origin: center center;
}

.zone-first .stack {
  transform: translate(var(--first-pos-x), var(--first-pos-y)) scale(var(--first-scale));
  transform-origin: center center;
}

.zone-discard .stack {
  transform: translate(var(--discard-pos-x), var(--discard-pos-y)) scale(var(--discard-scale));
  transform-origin: center center;
}

.pile-deck { background-image: linear-gradient(160deg, #334155, #1e293b); }
/* 捨て場は空の時、中身の分からない塗りつぶしではなく透明にする（ロックスロットと同じ考え方）。
   カードが入れば.stack-topに実物画像がinline styleで敷かれ、これは上書きされて見えなくなる。 */
.pile-discard { background-image: none; background-color: transparent; }
.pile-eternal { background-image: var(--color-rainbow); }
.pile-first { background-image: linear-gradient(160deg, #334155, #1e293b); }

/* 枚数に応じて厚みを持つカードの山。駒(.piece)と同じ「床+壁」の考え方だが、面は上面と正面の2枚のみ。
   --stack-height（枚数から計算）が高さになる。将来、盤面マスのカードスタックにも流用する想定。 */
.stack {
  position: relative;
  width: 3.2rem;
  height: 3.2rem;
  transform-style: preserve-3d;
  /* ユーザー報告「2D表示の時、山札と捨て場のカードとエターナルカードの束、ファースト
     カードの束が見えなくなってしまっている」（続き62）。.pieceの`will-change:
     transform`コメント（このファイル内、駒のセットアップ配布アニメーション終了時に
     preserve-3dの立方体が「ペっちゃんこ」に描画されたまま固まる不具合の対策）と全く
     同じ「床+壁」の3D構築技法を使っているにもかかわらず、こちらにはこの対策が
     漏れていた。駒と違い山札は通常のプレイ中ほとんど作り直されない（render()での
     再構築機会が少ない）ため、一度固まると自然には直りにくい。 */
  will-change: transform;
}

.stack-top {
  position: absolute;
  inset: 0;
  border-radius: 0.25rem;
  border: 1px solid rgba(226, 232, 240, 0.35);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  line-height: 1.2;
  text-align: center;
  transform: translateZ(var(--stack-height));
  backface-visibility: hidden;
}

/* 【ユーザー要望2026-09-05・⑥】山へカードが積まれた瞬間の「トン」。少し上から落ちてきて、
   一度わずかに沈んでから戻る。山の天面は translateZ(--stack-height) で厚みのぶん持ち上げて
   いるので、キーフレーム側でもその持ち上げを必ず含める（含めないと天面が根元まで落ちる）。 */
.stack.is-settling .stack-top {
  animation: stack-settle 0.42s cubic-bezier(0.3, 0.9, 0.35, 1);
}
@keyframes stack-settle {
  0% {
    transform: translateZ(calc(var(--stack-height) + 0.55rem));
  }
  55% {
    transform: translateZ(calc(var(--stack-height) - 0.12rem));
  }
  100% {
    transform: translateZ(var(--stack-height));
  }
}

.stack-top .stack-count {
  font-size: 0.85em;
  opacity: 0.8;
}

/* 山の側面。以前は.stack-front（手前の1面）しか無く、見る角度によっては他の面（特に右側）が
   まるごと透明に見えてしまっていた（そもそも面自体が存在しないため）。駒(.piece)と同じ
   「床+4面の壁」の技法で、奥・左・右の面も追加し、正しい直方体にした。
   側面はカード柄を貼らず無地のライトグレーにする（薄い帯に絵柄を貼ると引き伸ばされて
   見苦しいため）。filterはpreserve-3dを無効化してしまう（駒の壁面で判明済みの教訓）ため、
   暗くする表現はbox-shadowで代用する（面ごとに濃さを変えて陰影をつける）。 */
.stack-front,
.stack-back,
.stack-left,
.stack-right {
  position: absolute;
  width: 100%;
  height: calc(var(--stack-height) + 0.15rem);
  /* ユーザー要望「側面の色をカード裏面の色に対応した雰囲気の色に自動で変更したい」。
     main.jsのbuildCardStackが色テーマ付きの裏面セットを選んでいる間だけ
     --stack-side-colorを設定する（既定は今まで通りの薄いグレー）。 */
  background-color: var(--stack-side-color, #cbd5e1);
  /* 【ユーザー要望2026-09-05】「山札の側面がつるつるなのが気になる。本当は縞々になってる
     はず」。カード1枚ぶんの厚み（--stack-stripe、main.js が枚数から決める）ごとに細い線を
     入れて、重なった紙の断面に見せる。background-color は上で別に指定しているので、
     ここは必ず background-image（ロングハンド）で足す——background ショートハンドを使うと
     色の指定まで初期値へ戻ってしまう（カード裏面で踏んだのと同じ罠）。 */
  /* 線は1本ずつが実際に見える太さにする（サブピクセルの繰り返しはモアレになる。#289）。 */
  background-image: repeating-linear-gradient(
    to bottom,
    rgba(255, 255, 255, 0.22) 0,
    rgba(255, 255, 255, 0.22) 0.6px,
    rgba(0, 0, 0, 0.26) 0.6px,
    rgba(0, 0, 0, 0.26) 1.2px,
    transparent 1.2px,
    transparent var(--stack-stripe, 3px)
  );
  backface-visibility: hidden;
}

.stack-front {
  bottom: 0;
  left: 0;
  transform-origin: 50% 100%;
  transform: rotateX(-90deg);
  box-shadow: inset 0 0 0 999px rgba(0, 0, 0, 0.4);
}

.stack-back {
  top: 0;
  left: 0;
  transform-origin: 50% 100%;
  transform: translateY(-100%) rotateX(90deg) rotateZ(180deg) scaleX(-1);
  box-shadow: inset 0 0 0 999px rgba(0, 0, 0, 0.6);
}

.stack-left {
  top: 0;
  left: 0;
  transform-origin: 0% 0%;
  transform: rotateZ(90deg) rotateX(-90deg) scaleX(-1) translateX(-100%) translateY(-100%);
  box-shadow: inset 0 0 0 999px rgba(0, 0, 0, 0.5);
}

.stack-right {
  top: 0;
  right: 0;
  left: auto;
  transform-origin: 100% 0%;
  transform: rotateZ(-90deg) rotateX(-90deg) translateY(-100%);
  box-shadow: inset 0 0 0 999px rgba(0, 0, 0, 0.2);
}

/* 【ユーザー要望2026-09-05】カードがオープンする瞬間のめくれ演出。盤面の外（body直下）に
   浮かべる二面のカードを回す。駒の移動ゴーストと同じ「perspective + 盤面と同じ傾き」の
   入れ子なので、盤面のWebGL描画のON/OFFに関係なく同じに見える。 */
.card-flip-ghost {
  position: fixed;
  left: 0;
  top: 0;
  perspective: var(--camera-perspective);
  pointer-events: none;
  z-index: 9995;
}
.card-flip-inner {
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
}
.card-flip-flipper {
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  animation-name: card-flip-open;
  animation-timing-function: cubic-bezier(0.45, 0.05, 0.25, 1);
  animation-fill-mode: both;
}
.card-flip-face {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border-radius: 0.25rem;
  backface-visibility: hidden;
  box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.5);
}
/* 裏面は自分を180度回しておく。回転の前半は「flipper が180度＝裏面が正面」、
   後半は「0度＝表面が正面」になり、backface-visibility が裏返った方を隠す。 */
.card-flip-back {
  transform: rotateY(180deg);
}
@keyframes card-flip-open {
  0% {
    transform: rotateY(180deg) translateZ(0);
  }
  50% {
    transform: rotateY(90deg) translateZ(0.7rem);
  }
  100% {
    transform: rotateY(0deg) translateZ(0);
  }
}
/* 【ユーザー要望2026-09-05】到達が連鎖した回数。到達のオーラの上に重ねて出す。 */
.arrival-chain-badge {
  position: absolute;
  left: 50%;
  top: -0.2rem;
  transform: translate(-50%, -100%);
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  font-size: calc(0.62rem + var(--arrival-chain-level, 2) * 0.07rem);
  font-weight: 700;
  letter-spacing: 0.06em;
  white-space: nowrap;
  color: #fffdf2;
  background: rgba(20, 14, 4, 0.72);
  border: 1px solid rgba(250, 204, 21, 0.85);
  text-shadow: 0 0 0.4rem rgba(250, 204, 21, 0.9);
  box-shadow: 0 0 calc(0.2rem + var(--arrival-chain-level, 2) * 0.12rem) rgba(250, 204, 21, 0.75);
  pointer-events: none;
  animation: arrival-chain-badge 1.2s ease-out forwards;
}
@keyframes arrival-chain-badge {
  0% {
    opacity: 0;
    transform: translate(-50%, -60%) scale(0.6);
  }
  22% {
    opacity: 1;
    transform: translate(-50%, -110%) scale(1.15);
  }
  40% {
    transform: translate(-50%, -100%) scale(1);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -150%) scale(1);
  }
}
/* 【③】めくれて起きた到達は「N コンボ」。連鎖（金）と一目で区別できるよう水色にする。 */
.arrival-chain-badge.is-combo {
  border-color: rgba(56, 189, 248, 0.85);
  text-shadow: 0 0 0.4rem rgba(56, 189, 248, 0.9);
  box-shadow: 0 0 calc(0.2rem + var(--arrival-chain-level, 2) * 0.12rem) rgba(56, 189, 248, 0.75);
}

/* 連鎖中の到達は、オーラ自体も少し強くする。 */
.arrival-effect-burst.is-chained {
  filter: saturate(1.25) brightness(1.12);
}
/* 【ユーザー要望2026-09-05】あと1色で勝ち。残った1つの空きスロットだけを呼吸させる。 */
.lock-slot.is-one-away {
  animation: lock-slot-one-away 2.2s ease-in-out infinite;
}
@keyframes lock-slot-one-away {
  0%, 100% {
    box-shadow: 0 0 0.25rem currentColor;
  }
  50% {
    box-shadow:
      0 0 0.9rem currentColor,
      0 0 1.8rem currentColor;
  }
}
body.reduce-glow .lock-slot.is-one-away {
  animation: none;
}

/* 【#375・2026-10-06】ゴメンナサイで奪われて**空になった**ロックスロット。
   攻めた側の画面で「どの色が抜けたのか」を指し示すのが、この場面の一番の説明になる
   （ゴメンナサイは宣言したロックを取り消さないので、盤面は『成功したのに勝たない』
   という読み取りにくい絵になる）。is-one-away と同じ currentColor の光り方に揃えて
   あるが、こちらは一度だけ強く点滅して消える。 */
.lock-slot.is-just-robbed {
  animation: lock-slot-just-robbed 0.65s ease-in-out 4;
}
@keyframes lock-slot-just-robbed {
  0%, 100% {
    box-shadow: 0 0 0.25rem currentColor;
  }
  50% {
    box-shadow:
      0 0 1.1rem currentColor,
      0 0 2.4rem currentColor,
      inset 0 0 1.2rem currentColor;
  }
}
body.reduce-glow .lock-slot.is-just-robbed {
  animation: none;
  outline: 0.18rem dashed currentColor;
}

/* 【#383・2026-10-07】駒を選ばせる効果（マスチェンジ・強制移動等）で、対象になれる駒自身を
   光らせる。ユーザー要望「マスチェンジで対象のコマを選ぶ時、そのコマがわかりやすく
   ハイライトされているといい」。マスの枠（.card-effect-target-cell）だけだと「マスを選ぶのか
   駒を選ぶのか」が見た目で分からなかった。
   光らせるのは **.piece-face**（駒の各面）で、.piece 自身ではない——.piece は box-shadow を
   既に持っており、box-shadow は後のルールで丸ごと上書きになるため（教訓10）。また WebGL 描画中は
   .piece-face の box-shadow を剥がすルールがあるが、あれは !important を付けていない
   （ハイライトの光は3D描画中でも見せたいという意図）ので、詳細度の高いこちらが勝つ。 */
.piece.is-effect-target-piece .piece-face {
  box-shadow:
    0 0 0.55rem #ffd54a,
    0 0 1.3rem #ffd54a;
  animation: effect-target-piece 1.1s ease-in-out infinite;
}
@keyframes effect-target-piece {
  0%, 100% {
    box-shadow:
      0 0 0.45rem #ffd54a,
      0 0 1rem #ffd54a;
  }
  50% {
    box-shadow:
      0 0 0.9rem #ffd54a,
      0 0 2.1rem #ffd54a,
      0 0 3.2rem #ffd54a;
  }
}
body.reduce-glow .piece.is-effect-target-piece .piece-face {
  animation: none;
  box-shadow: 0 0 0.5rem #ffd54a;
}
/* 【ユーザー要望2026-09-05】相手のゲートに乗った瞬間。乗られた側の色で輪が広がる。 */
.gate-intrusion-ring {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 90%;
  height: 90%;
  border: 0.16rem solid var(--gate-intrusion-color, #f6c945);
  border-radius: 50%;
  pointer-events: none;
  box-shadow:
    0 0 0.7rem var(--gate-intrusion-color, #f6c945),
    inset 0 0 0.7rem var(--gate-intrusion-color, #f6c945);
  animation: gate-intrusion-ring 1.1s cubic-bezier(0.2, 0.8, 0.3, 1) forwards;
}
.gate-intrusion-ring.is-delayed {
  animation-delay: 0.22s;
}
@keyframes gate-intrusion-ring {
  0% {
    transform: translate(-50%, -50%) scale(0.2);
    opacity: 0;
  }
  18% {
    opacity: 1;
  }
  100% {
    transform: translate(-50%, -50%) scale(2.6);
    opacity: 0;
  }
}
/* 【ユーザー要望2026-09-05】自分の持ち時間が残りわずかな時、画面のふちを自分の色で脈打たせる。
   盤面の一番外側に薄い内向きの光を敷くだけなので、操作は一切邪魔しない。 */
body.time-critical::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 2000;
  box-shadow: inset 0 0 5rem 1rem var(--time-critical-color, #ef4444);
  animation: time-critical-edge 1.1s ease-in-out infinite;
}
@keyframes time-critical-edge {
  0%, 100% {
    opacity: 0.18;
  }
  50% {
    opacity: 0.6;
  }
}
body.reduce-glow.time-critical::after {
  animation: none;
  opacity: 0.35;
}

.label {
  position: relative;
  z-index: 5; /* 手札を大きくずらすと.hand-fanが上に重なって名前が隠れることがあるため、常に手前に出す */
  opacity: 0.7;
  letter-spacing: 0.05em;
  white-space: nowrap;
}

/* 現在のターンプレイヤーの名前ラベルを点滅させて目立たせる（.lock-areaの明滅と対になる演出）。
   opacityはアニメーション中はkeyframesの値が.labelの静的なopacity:0.7を上書きするため、
   ここで改めて基準値を書く必要はない。 */
@keyframes label-turn-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}
.label.is-turn-player {
  /* ロックエリアの明滅(lock-area-turn-glow)と速さを合わせ、同じリズムでゆっくり点滅させる */
  animation: label-turn-blink 2.6s ease-in-out infinite;
  font-weight: bold;
  color: #fde68a;
}

/* プレイヤー名ラベルの位置（管理者モードで調整可能）。B/Dは盤面の左右の辺と平行になるよう
   90度回転させた上で、その回転済みの向きのままtranslateで位置調整できるようにしている
   （回転を先に書くとtranslateがまだ回転していない軸基準になり、直感的な調整幅と一致するため、
   translateの方を後ろに書いて先に適用させている）。 */
:root {
  --label-a-pos-x: -14.7rem;
  --label-a-pos-y: -1.1rem;
  --label-b-pos-x: -4.5rem;
  --label-b-pos-y: -5.1rem;
  --label-c-pos-x: 7.3rem;
  --label-c-pos-y: -7rem;
  --label-d-pos-x: 3.8rem;
  --label-d-pos-y: -5.2rem;
}

/* ユーザー要望「BCD分の名前ラベルは全部画面上が頭の正の向きにしてください（位置は
   こちらで調整します）」により、以前あった辺ごとの回転（B=90deg/C=180deg/D=-90deg、
   盤面の辺に沿って読める向きにしていた）を撤去し、4つとも回転無しの通常表示にした。 */
.zone-bottom .label {
  transform: translate(var(--label-a-pos-x), var(--label-a-pos-y));
}
.zone-left .label {
  transform: translate(var(--label-b-pos-x), var(--label-b-pos-y));
}
.zone-top .label {
  transform: translate(var(--label-c-pos-x), var(--label-c-pos-y));
}
.zone-right .label {
  transform: translate(var(--label-d-pos-x), var(--label-d-pos-y));
}
/* ユーザー要望「タブレットで名前が見えなくなるのはZ値が原因では」への対応。PC版の.label
   はtranslateZ自体を持たない（常にZ=0）ため、この4ルールはbody.is-touch-deviceスコープで
   PCのtransformを完全に上書きし、そこにだけtranslateZを追加する（PC側の見た目は一切
   変わらない）。 */
body.is-touch-device .zone-bottom .label {
  transform: translate(var(--label-a-pos-x), var(--label-a-pos-y)) translateZ(var(--label-translate-z-touch, 0rem));
}
body.is-touch-device .zone-left .label {
  transform: translate(var(--label-b-pos-x), var(--label-b-pos-y)) translateZ(var(--label-translate-z-touch, 0rem));
}
body.is-touch-device .zone-top .label {
  transform: translate(var(--label-c-pos-x), var(--label-c-pos-y)) translateZ(var(--label-translate-z-touch, 0rem));
}
body.is-touch-device .zone-right .label {
  transform: translate(var(--label-d-pos-x), var(--label-d-pos-y)) translateZ(var(--label-translate-z-touch, 0rem));
}
/* ユーザー要望「盤面横のプレイヤー名BCDについてサイズを管理者モードで調整できるように
   したい」。Aは対象外（自分の名前ラベルは別トグルで表示/非表示のみ）。
   ユーザー要望「相手プレイヤー名の背面に黒色半透明の塗りつぶしを視認性向上のために」
   への対応であわせて背景も追加した。 */
.zone-left .label,
.zone-top .label,
.zone-right .label {
  font-size: var(--label-bcd-font-size, 1.6rem);
  background: rgba(0, 0, 0, 0.45);
  border-radius: 0.25rem;
  padding: 0.1rem 0.4rem;
}

/* 各プレイヤーのアバター（絵文字ダミー）。相手のアバターは「手札の後ろ側」に見えるよう、
   buildPlayerZone()内でhand-areaより先にDOMへ足している（重なった時は後から足した
   hand側が手前）。位置・サイズは管理者モードで調整できる（--avatar-{a,b,c,d}-size・
   --avatar-{a,b,c,d}-pos-x/y）。emoji自体は常に正立させたいので、名前ラベルと違い回転は
   付けない。 */
:root {
  --avatar-a-size: 5.7rem;
  --avatar-b-size: 8rem;
  --avatar-c-size: 8rem;
  --avatar-d-size: 8rem;
  --avatar-a-pos-x: 0rem;
  --avatar-a-pos-y: 1.9rem;
  --avatar-b-pos-x: -3.5rem;
  --avatar-b-pos-y: 0.1rem;
  --avatar-c-pos-x: 0.1rem;
  --avatar-c-pos-y: -6.8rem;
  --avatar-d-pos-x: 3.5rem;
  --avatar-d-pos-y: -0.1rem;
}
.player-avatar {
  position: absolute;
  top: 50%;
  left: 50%;
  line-height: 1;
  z-index: 3;
  pointer-events: none;
  filter: drop-shadow(0 0.2rem 0.3rem rgba(0, 0, 0, 0.6));
}
/* サイズは元々4人共通の--avatar-sizeだったが、プレイヤーごとに調整したいという要望を受けて
   位置(--avatar-{a,b,c,d}-pos-x/y)と同じ「表示位置ごと」の変数に分割した
   （bottom=a/left=b/top=c/right=d、hand-{a,b,c,d}-size等と同じ命名規則）。 */
.zone-bottom .player-avatar {
  font-size: var(--avatar-a-size);
  transform: translate(-50%, -50%) translate(var(--avatar-a-pos-x), var(--avatar-a-pos-y));
}
.zone-left .player-avatar {
  font-size: var(--avatar-b-size);
  transform: translate(-50%, -50%) translate(var(--avatar-b-pos-x), var(--avatar-b-pos-y));
}
.zone-top .player-avatar {
  font-size: var(--avatar-c-size);
  transform: translate(-50%, -50%) translate(var(--avatar-c-pos-x), var(--avatar-c-pos-y));
}
.zone-right .player-avatar {
  font-size: var(--avatar-d-size);
  transform: translate(-50%, -50%) translate(var(--avatar-d-pos-x), var(--avatar-d-pos-y));
}

/* エモートの吹き出し（emote.js、続き78）。.player-avatarが既にposition:absoluteのため、
   その子として絶対配置するだけでアバターの真上に出せる。ワンショット演出のため
   pointer-events不要（.player-avatar自体も終始pointer-events:noneの表示専用要素）。 */
.emote-speech-bubble {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-bottom: 0.4rem;
  background: rgba(255, 255, 255, 0.96);
  color: #1e293b;
  font-family: sans-serif;
  font-size: 0.9rem;
  font-weight: bold;
  white-space: nowrap;
  padding: 0.35rem 0.7rem;
  border-radius: 999px;
  box-shadow: 0 0.15rem 0.4rem rgba(0, 0, 0, 0.35);
  animation: emote-bubble-pop 0.2s ease-out;
  z-index: 10;
}
.emote-speech-bubble::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 0.35rem solid transparent;
  border-top-color: rgba(255, 255, 255, 0.96);
}
@keyframes emote-bubble-pop {
  from {
    opacity: 0;
    transform: translateX(-50%) scale(0.6);
  }
  to {
    opacity: 1;
    transform: translateX(-50%) scale(1);
  }
}
/* 盤面の奥側（zone-top＝相手が最も奥に表示される席）のエモート吹き出しは、アバターの上だと
   画面上端で見切れやすい（＋その画角では手札とも干渉する）ため、アバターの左側に出す。
   #123: 以前は座席（data-player="C"）ベースだったが、盤面は自視点で回転するため、奥に来る席は
   viewerによって変わる。表示ゾーン（.zone-top等）基準にすることで、どのviewer/座席でも
   「奥のアバターは左」になる。尻尾も右向き（アバター側）へ。 */
.zone-top .emote-speech-bubble {
  bottom: auto;
  top: 50%;
  left: auto;
  right: 100%;
  transform: translateY(-50%);
  margin-bottom: 0;
  margin-right: 0.4rem;
  animation: emote-bubble-pop-left 0.2s ease-out;
}
.zone-top .emote-speech-bubble::after {
  top: 50%;
  left: 100%;
  transform: translateY(-50%);
  border: 0.35rem solid transparent;
  border-left-color: rgba(255, 255, 255, 0.96);
}
@keyframes emote-bubble-pop-left {
  from {
    opacity: 0;
    transform: translateY(-50%) scale(0.6);
  }
  to {
    opacity: 1;
    transform: translateY(-50%) scale(1);
  }
}
/* 盤面の左右（zone-left / zone-right）に表示される席のエモートはアバターの“下”に出す
   （アバター上だとプレイヤー名表示と被って見づらいため）。尻尾は上向き（アバター側）にする。
   #123: 座席（data-player="B"/"D"）ではなく表示ゾーン基準にして、viewerに依らず「左右の席は下」に。 */
.zone-left .emote-speech-bubble,
.zone-right .emote-speech-bubble {
  bottom: auto;
  top: 100%;
  margin-bottom: 0;
  margin-top: 0.4rem;
}
.zone-left .emote-speech-bubble::after,
.zone-right .emote-speech-bubble::after {
  top: auto;
  bottom: 100%;
  border-top-color: transparent;
  border-bottom-color: rgba(255, 255, 255, 0.96);
}

/* エモート選択ポップアップ（emote.js、続き78）。他のモーダルと違い暗転させない
   （テンポよく連投したい操作のため）。backdropは外側クリック判定専用の透明な
   全画面レイヤー。 */
#emote-picker-backdrop {
  position: fixed;
  inset: 0;
  z-index: 10500;
  background: transparent;
}
#emote-picker {
  position: fixed;
  z-index: 10501;
  width: min(20rem, 80vw);
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.6rem;
  padding: 0.8rem;
  font-family: sans-serif;
  color: #e2e8f0;
  box-sizing: border-box;
  box-shadow: 0 0.4rem 1.2rem rgba(0, 0, 0, 0.5);
}
#emote-picker-title {
  font-weight: bold;
  font-size: 0.85rem;
  margin-bottom: 0.6rem;
  opacity: 0.85;
}
#emote-picker-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.5rem;
}
.emote-picker-btn {
  --emote-color: var(--color-red);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--emote-color);
  color: #e2e8f0;
  border-radius: 0.4rem;
  padding: 0.5rem 0.4rem;
  font-family: sans-serif;
  font-size: 0.85rem;
  cursor: pointer;
}
.emote-picker-btn:hover {
  background: color-mix(in srgb, var(--emote-color) 25%, transparent);
}

/* 相手のアバターをクリックした時の「エモートを非表示/表示」メニュー（ユーザー要望2026-08-17）。 */
#emote-mute-backdrop {
  position: fixed;
  inset: 0;
  z-index: 10500;
  background: transparent;
}
#emote-mute-menu {
  position: fixed;
  z-index: 10501;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.6rem;
  padding: 0.7rem;
  font-family: sans-serif;
  color: #e2e8f0;
  box-shadow: 0 0.4rem 1.2rem rgba(0, 0, 0, 0.5);
}
#emote-mute-menu-title {
  font-weight: bold;
  font-size: 0.8rem;
  margin-bottom: 0.5rem;
  opacity: 0.85;
}
.emote-mute-menu-btn {
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(148, 163, 184, 0.4);
  color: #e2e8f0;
  border-radius: 0.4rem;
  padding: 0.5rem 0.9rem;
  font-family: sans-serif;
  font-size: 0.85rem;
  cursor: pointer;
  white-space: nowrap;
}
.emote-mute-menu-btn:hover {
  background: rgba(255, 255, 255, 0.14);
}

/* ユーザー報告・要望への対応: BCD（自分＝画面手前/zone-bottom以外の3人）のアバターを
   手札より背面に描画する（Aは現状のまま=手札より手前を維持）。
   根本原因: .player-avatarには元々z-index:3（正の数）が付いており、.hand-area側は
   z-index:auto（無指定）だった。CSSのスタッキング順では「z-index:autoの位置指定要素」より
   「正のz-indexを持つ要素」の方が必ず手前に描画されるため、DOM順（ラベル→アバター→
   手札の順でappendChildしており、コード上の意図は「手札を後から足すことで手前にする」
   だった）は実際には無視され、常にアバターが手札より手前に出てしまっていた
   （アバターが3remだった頃は目立たなかったが、8remに拡大したことで顕在化した）。
   BCDだけz-index:0にすると、.hand-area（z-index:auto）と同じ「z-index:auto/0の束」に
   加わり、その束の中ではDOM順が効くため、意図通りアバター→手札の順（手札が手前）になる。 */
.zone-left .player-avatar,
.zone-top .player-avatar,
.zone-right .player-avatar {
  z-index: 0;
}

/* 手番プレイヤーのアバターも、ロックエリア/名前ラベルと同じ色(--turn-glow-rgb、管理者モードで
   白/黄色を切替可)・同じ2.6秒周期でゆっくり柔らかく発光させる。絵文字はdrop-shadowでないと
   グロー風の縁取りにならないため、既存の静的な影用drop-shadowに重ねる形でもう1つ追加する。 */
@keyframes avatar-turn-glow {
  0%, 100% { filter: drop-shadow(0 0.2rem 0.3rem rgba(0, 0, 0, 0.6)) drop-shadow(0 0 0.15rem rgba(var(--turn-glow-rgb), 0.4)); }
  50% { filter: drop-shadow(0 0.2rem 0.3rem rgba(0, 0, 0, 0.6)) drop-shadow(0 0 0.7rem rgba(var(--turn-glow-rgb), 0.95)); }
}
.player-avatar.is-turn-player {
  animation: avatar-turn-glow 2.6s ease-in-out infinite;
}

/* 手品師の技（ユーザー要望「駒ではなくアバターを選択して相手を選ぶ」）。マス選択
   （.cell.card-effect-target-cell）と同じ黄色パルスの見た目にして、カード効果の
   対象選択であることを一目で分かるようにする。
   ハマりどころ: .player-avatarは元々常にpointer-events:noneで、手札より背面に
   置くための純粋な装飾要素として扱われている（クリック判定を持たない）。この
   ままだとelementsFromPoint()が絶対に見つけられずクリックが一切効かないため、
   このクラスが付いている間だけpointer-events:autoで上書きする。 */
.player-avatar.card-effect-target-avatar {
  pointer-events: auto;
  outline: 3px solid rgba(250, 204, 21, 0.9);
  outline-offset: 3px;
  border-radius: 50%;
  animation: tutorial-move-range-pulse 1.4s ease-in-out infinite;
  cursor: pointer;
}
body.reduce-glow .player-avatar.card-effect-target-avatar {
  animation: none;
}

/* 「ターンを次のプレイヤーへ渡す」ボタン。プレイヤー自身が操作するゲームプレイ上のボタン
   なので、開発者向けツール（管理者モード＝左上、山札一覧/セットアップ＝右上）とは離し、
   画面右下に配置する。state.turnPlayerがnullの間（セットアップ手順3が未実行）は
   非表示にする（updateEndTurnButton()参照）。 */
#end-turn-button {
  position: fixed;
  bottom: 1.2rem;
  right: 1.2rem;
  z-index: 1000;
  display: none; /* is-active時のみflexに切り替える（updateEndTurnButton参照） */
  transform: translate(var(--icon-pos-end-turn-x, 0rem), var(--icon-pos-end-turn-y, -3.4rem));
}
/* PC/タブレットでUI位置を分けたい場合の対応（device-detect.jsのbody.is-touch-device）。
   管理者モードの「タブレット用の位置調整」で個別に触った分だけ、タッチ端末側の変数
   （--icon-pos-*-touch-x/y）が実際にセットされ、この方がPC用より優先される（var()の
   フォールバックチェーン: タッチ専用値→PC用値→ハードコードの初期値）。一度も調整して
   いなければ、タブレットでもPCと全く同じ位置のまま変わらない。 */
body.is-touch-device #end-turn-button {
  transform: translate(
    var(--icon-pos-end-turn-touch-x, var(--icon-pos-end-turn-x, 0rem)),
    var(--icon-pos-end-turn-touch-y, var(--icon-pos-end-turn-y, -3.4rem))
  );
}
body.diagnostic-flatten-3d.is-touch-device #end-turn-button {
  transform: translate(
    calc(var(--icon-pos-end-turn-touch-x, var(--icon-pos-end-turn-x, 0rem)) + var(--icon-pos-end-turn-flat-x, 0rem)),
    calc(var(--icon-pos-end-turn-touch-y, var(--icon-pos-end-turn-y, -3.4rem)) + var(--icon-pos-end-turn-flat-y, 0rem))
  );
}
/* 自分のターンでオンライン中、自分の手番でない間は押せなくする（アイコン全体を薄くする）。 */
#end-turn-button:disabled .icon-action-button-icon-wrap {
  background: rgba(71, 85, 105, 0.5);
  border-color: rgba(100, 116, 139, 0.5);
}
#end-turn-button:not(:disabled) .icon-action-button-icon-wrap {
  border-color: rgba(251, 191, 36, 0.6);
}

/* スポットライトモード（管理者モード「スタンダードモード/スポットライトモード」切替）。
   盤面付近だけ明るく、周辺が暗くなる演出。ボタン類(z-index:900以上)より手前に出ると
   操作の邪魔になるため、それより低いz-indexにして常に背面に留める。クリックを拾わない
   よう pointer-events:none も必須（無いと盤面の駒・カードのドラッグ操作を奪ってしまう）。 */
#spotlight-overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.5s ease;
  background: radial-gradient(
    ellipse var(--spotlight-width, 55%) var(--spotlight-height, 50%) at center,
    transparent var(--spotlight-inner-radius, 15%),
    rgba(0, 0, 0, var(--spotlight-opacity, 0.95)) var(--spotlight-outer-radius, 100%)
  );
}
body.spotlight-mode #spotlight-overlay {
  opacity: 1;
}

/* ゲームタイトル。画面左上（以前「⚙ 管理者モード」ボタンがあった場所。オプションメニューに
   統合して空いたスペースに表示する）。 */
#game-title {
  position: fixed;
  top: 1rem;
  left: 1rem;
  z-index: 900;
  font-family: sans-serif;
  font-size: 0.95rem;
  font-weight: bold;
  color: rgba(226, 232, 240, 0.85);
  letter-spacing: 0.02em;
  pointer-events: none;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.6);
}

/* 通算ターン数・ラウンド数。「📋 山札一覧」ボタン(top: 1rem)のすぐ上に、さりげなく
   小さく表示する。turnNumberが無い（まだ対戦が始まっていない）間は非表示。位置・文字
   サイズは管理者モードで調整できる（--turn-round-counter-top/-right/-font-size、
   ユーザー要望）。 */
/* ユーザー要望「オプション・マイページ・ヘルプ・金額表示・ターンラウンド数を『オプション
   エリア』としてまとめて位置・サイズ調整できるようにしてほしい」への対応。この5要素は
   全てここへappendChildされる（option-area.js参照）ようにし、画面右上の角(top:0;right:0)
   を基準点とするposition:fixedの0×0のアンカー要素にした。子要素は従来通りposition:absolute
   ＋同じtop/right値で全く同じ見た目になり（0×0の箱でも子のoffsetは箱の端＝この角を基準に
   計算されるため）、スマホ表示時だけこの要素自体にtranslate+scaleを掛けることで、
   5要素の相対位置関係を保ったまま「まとめて動かす・まとめて拡大縮小する」を実現する。
   z-index:900は元々5要素それぞれが個別に持っていた値と揃えてあり（position:fixedの
   要素は新しいスタッキングコンテキストを作るため、中の子のz-index:900はこの要素同士の
   比較にはもう使われないが、他のパネル・背景幕等（900未満〜1002等）との前後関係は
   この#option-area自身のz-indexで決まる）、タイトル画面表示中だけ他4要素を隠しつつ
   オプションボタンだけ引き上げる従来の挙動は下のbody.opening-screen-active側で別途
   対応している。 */
#option-area {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 900;
  /* ハマりどころ（ユーザー報告「オプションエリアの見た目がめちゃくちゃ崩れてる」）:
     最初は幅0×高さ0のアンカー要素にしていたが、#turn-round-counter（明示的な
     widthを持たないテキストdiv）のような「shrink-to-fitで自分の幅を決める」子要素は、
     containing blockの幅が0だとshrink-to-fit計算そのものが破綻し、1文字ずつ縦に
     折り返す崩れた表示になってしまった（アイコン勢のズレ・重なりも同じ原因）。
     containing blockの幅・高さを画面いっぱい(100%、position:fixedなのでbody基準＝
     従来この5要素が個別にposition:fixedだった時と同じ基準)にすることで、子要素の
     shrink-to-fit計算が正しく機能するようにした。画面いっぱいになった分、
     下の盤面のクリックを奪ってしまわないようpointer-events:noneにし、実際に押せる
     必要がある子要素（5つ）だけ子孫セレクタで再度autoに戻している。 */
  pointer-events: none;
}
#option-area > * {
  pointer-events: auto;
}
/* 駒に追従する飾りのペット（piece-pet.js）。画面全体を覆う操作を奪わないオーバーレイに、
   各駒の足元へ絵文字＋接地影を置く。盤面より前・UIパネル(900〜)より後ろに敷く。 */
#piece-pet-layer {
  position: fixed;
  inset: 0;
  z-index: 500;
  pointer-events: none;
  overflow: hidden;
}
.piece-pet {
  position: absolute;
  left: 0;
  top: 0;
  will-change: transform;
  line-height: 1;
}
.piece-pet-emoji {
  display: block;
  line-height: 1;
  filter: drop-shadow(0 0.04em 0.04em rgba(0, 0, 0, 0.45));
  will-change: transform;
}
/* 足元の楕円の影（絵文字だけがホップして影は接地に残るので生き物っぽく見える）。 */
.piece-pet-shadow {
  position: absolute;
  left: 50%;
  bottom: -0.06em;
  width: 0.7em;
  height: 0.2em;
  transform: translateX(-50%);
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.38), rgba(0, 0, 0, 0) 70%);
  pointer-events: none;
}
/* 左下ステータスエリアの「ペット変更」アイコン（現在の選択を絵文字で表示）。プレイマット
   アイコンと同じ器（.self-status-playmat-thumb）に、画像の代わりに絵文字を中央表示する。 */
.self-status-pet-thumb {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.4rem;
  line-height: 1;
  padding: 0;
}
/* ペット「なし」時のプレースホルダー（薄い肉球）。実際のペットと紛らわしくないよう
   淡く沈める（ひよこ🐥の誤フォールバック修正、ユーザー報告）。クリックは可能なまま。 */
.self-status-pet-thumb.is-empty {
  opacity: 0.4;
  filter: grayscale(1);
}
/* ペット選択ピッカーのマス（絵文字＋ラベル）。 */
.pet-picker-swatch {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
  overflow: hidden; /* 背面の1枚絵がマスからはみ出さないように */
}
/* 【続き497】マスの背面に敷く水彩画の1枚絵（ユーザー要望）。絵の主役は前面の追従
   スプライトのままなので、こちらは薄く・下端をフェードさせて名前を読みやすく保つ。 */
.pet-picker-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.3;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.95) 0%, rgba(0, 0, 0, 0.55) 55%, transparent 88%);
  mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.95) 0%, rgba(0, 0, 0, 0.55) 55%, transparent 88%);
  transition: opacity 0.16s ease-out;
}
.pet-picker-swatch:hover .pet-picker-bg,
.pet-picker-swatch.is-selected .pet-picker-bg {
  opacity: 0.48;
}
.pet-picker-emoji {
  font-size: 2.4rem;
  line-height: 1;
}
.pet-picker-label {
  font-size: 0.7rem;
  color: #cbd5e1;
}
/* 画像スプライトのペット（キュビット等、piece-pet.js）。サイズはJSがwidth/heightを
   インラインで指定する。絵文字と同じく画像だけがホップし、影は接地に残る。 */
.piece-pet-sprite {
  display: block;
  object-fit: contain;
  filter: drop-shadow(0 0.05em 0.08em rgba(0, 0, 0, 0.4));
  will-change: transform;
  pointer-events: none;
}
/* ペット選択ピッカーでのスプライトプレビュー（正面静止画）。 */
.pet-picker-sprite {
  width: 3rem;
  height: 3rem;
  object-fit: contain;
}
/* 対戦ロビー（online-ui.js、対局前の中央モーダル）。盤面の上に浮かぶ中央カード。カード以外は
   盤面を邪魔しないようクリック透過（着席していく他プレイヤーが周囲に見える）。 */
#online-lobby-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2200;
  pointer-events: none;
}
.online-lobby-card {
  pointer-events: auto;
  width: min(24rem, 92vw);
  max-height: 90vh;
  overflow-y: auto;
  background: rgba(15, 23, 32, 0.96);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.6rem;
  padding: 1rem;
  color: #e2e8f0;
  font-family: sans-serif;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
  text-align: center;
  box-sizing: border-box;
}
.online-lobby-title {
  font-weight: bold;
  margin-bottom: 0.4rem;
}
.online-lobby-count {
  font-size: 0.9rem;
  color: #cbd5e1;
  margin-bottom: 0.8rem;
}
.online-lobby-options {
  text-align: left;
  margin-bottom: 0.2rem;
}
.online-lobby-option-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  cursor: pointer;
  margin-bottom: 0.45rem;
  font-size: 0.85rem;
}
/* ロビーの開始オプションを「大きいアイコンをクリックでオン/オフ」に（ユーザー要望2026-08-10）。
   ON＝点灯（色つき枠＋発光、フル表示）／OFF＝グレーアウト（減光＋グレースケール）でひと目で分かる。 */
.lobby-icon-toggle-row {
  display: flex;
  justify-content: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-bottom: 0.6rem;
}
.lobby-icon-toggle {
  flex: 1 1 5rem;
  max-width: 7rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  padding: 0.6rem 0.4rem 0.45rem;
  background: rgba(255, 255, 255, 0.05);
  border: 1.5px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.6rem;
  cursor: pointer;
  font-family: sans-serif;
  color: #cbd5e1;
  transition: border-color 0.15s ease, background 0.15s ease, opacity 0.15s ease, box-shadow 0.15s ease;
  /* OFF（既定）＝減光＋グレースケール */
  opacity: 0.5;
  filter: grayscale(1);
}
.lobby-icon-toggle:hover {
  opacity: 0.75;
}
.lobby-icon-toggle.is-on {
  opacity: 1;
  filter: none;
  border-color: rgba(56, 189, 248, 0.85);
  background: rgba(56, 189, 248, 0.14);
  box-shadow: 0 0 0.7rem rgba(56, 189, 248, 0.35);
  color: #e2e8f0;
}
.lobby-icon-toggle-img {
  width: 2.9rem;
  height: 2.9rem;
  object-fit: contain;
  pointer-events: none;
}
.lobby-icon-toggle-label {
  font-size: 0.72rem;
  font-weight: bold;
  line-height: 1.2;
  text-align: center;
}
.lobby-icon-toggle-state {
  font-size: 0.62rem;
  font-weight: bold;
  letter-spacing: 0.06em;
  padding: 0.02rem 0.4rem;
  border-radius: 0.6rem;
  background: rgba(148, 163, 184, 0.3);
  color: #94a3b8;
}
.lobby-icon-toggle.is-on .lobby-icon-toggle-state {
  background: rgba(56, 189, 248, 0.9);
  color: #06202e;
}
/* ライトテーマ */
body.theme-light .lobby-icon-toggle {
  background: rgba(176, 141, 63, 0.08);
  border-color: rgba(176, 141, 63, 0.4);
  color: #6b5c3c;
}
body.theme-light .lobby-icon-toggle.is-on {
  border-color: rgba(176, 141, 63, 0.95);
  background: rgba(176, 141, 63, 0.18);
  box-shadow: 0 0 0.7rem rgba(176, 141, 63, 0.3);
  color: #3a3326;
}
body.theme-light .lobby-icon-toggle.is-on .lobby-icon-toggle-state {
  background: linear-gradient(160deg, #d9b45a, #b8933f);
  color: #3a2f15;
}
.online-lobby-waiting {
  font-size: 0.95rem;
  font-weight: bold;
  color: #fde68a;
  padding: 0.8rem 0.4rem;
  line-height: 1.5;
}
/* 巨大半透明アバター（my-page.js、最背面の飾り）。DOM先頭＝最背面。通常はクリックを
   透過し（下の要素を邪魔しない）、レイアウト編集中だけ掴めるようにする。 */
.my-page-bg-avatar {
  opacity: 0.15;
  pointer-events: none;
  line-height: 0;
}
.my-page-bg-avatar img {
  width: 12rem;
  height: 12rem;
  border-radius: 50%;
  object-fit: cover;
  display: block;
}
.profile-layout-editing .my-page-bg-avatar {
  pointer-events: auto; /* 編集中はドラッグできるように */
  opacity: 0.28; /* 編集中は少し見やすく */
}
/* 【続き497】背面の巨大半透明ペット（my-page.js、水彩画の1枚絵）。巨大アバターと同じ
   「最背面の飾り」で、位置・大きさはレイアウト編集モードから調整する。アバターと違って
   円形には切らない（1枚絵は正方形の余白ごと整えてあり、そのまま置くのが自然なため）。 */
.my-page-bg-pet {
  opacity: 0.16;
  pointer-events: none;
  line-height: 0;
}
.my-page-bg-pet img {
  width: 10rem;
  height: 10rem;
  object-fit: contain;
  display: block;
}
.profile-layout-editing .my-page-bg-pet {
  pointer-events: auto;
  opacity: 0.3;
}
/* マイページの表示アバター（本体・背面の巨大半透明の両方）も、左下ステータスの巨大アバターと
   同じ理由で縁を放射状マスクでフェードさせる。アバターのイラストには灰色/白の背景が焼き込まれて
   おり、円形クロップのハードな縁がそのまま“円盤の枠”として見える。特にライトテーマでは背景が
   白く読めるため「プロフィールの不要な白い枠」として目立っていた（ユーザー報告）。中央の
   キャラクターは残したまま、縁だけを背景へ溶け込ませる（.self-status-large-avatarと同一マスク）。 */
.my-page-avatar-img,
.my-page-bg-avatar img {
  -webkit-mask-image: radial-gradient(circle, #000 60%, rgba(0, 0, 0, 0.35) 74%, transparent 86%);
  mask-image: radial-gradient(circle, #000 60%, rgba(0, 0, 0, 0.35) 74%, transparent 86%);
}
/* マイページの着せ替え一式（my-page.js renderMyPageBody）。 */
.my-page-cosmetics {
  margin: 0.6rem 0 1rem;
}
.my-page-cosmetics-title {
  font-size: 0.85rem;
  color: #cbd5e1;
  margin-bottom: 0.4rem;
  text-align: center;
}
/* マイページのランク戦・段位バッジ（rank-badge.js、フェーズ4/6）。枠なし・テーマ対応。
   位置・サイズは「レイアウト編集モード」(profile-layout-editor.js) が絶対配置＋inline transform で
   管理する（アバター/名前/戦績等と同じ）。以前あった transform: translate/scale(var(--mypage-rank-*))
   は layout editor の inline transform に負けて効かないため撤去（ユーザー報告2026-08-16）。 */
.my-page-rank-group {
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  margin: 0.4rem 0 0.8rem;
}
.my-page-rank-title { font-size: 0.85rem; color: #ffe6a8; letter-spacing: 0.04em; }
.my-page-rank-name { font-size: 1rem; font-weight: 800; color: #ffe6a8; transform: translate(var(--mypage-rank-name-pos-x, 0rem), var(--mypage-rank-name-pos-y, 0rem)); }
body.theme-light .my-page-rank-name { color: #92660a; }
.my-page-rank-none { font-size: 0.78rem; color: #94a3b8; }
body.theme-light .my-page-rank-none { color: #64748b; }
/* ライトテーマ: マイページ背景が明るいので、金色の段位名・タイトルを濃い金に、
   ゲージの未点灯ドットを濃色faintにして読めるようにする。 */
body.theme-light .my-page-rank-title,
body.theme-light .my-page-rank-group .rank-badge-name { color: #92660a; }
body.theme-light .my-page-rank-group .rank-badge-dot { background: rgba(30, 41, 59, 0.16); border-color: rgba(30, 41, 59, 0.28); }
.my-page-cosmetics-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  justify-content: center;
}
/* ユーザー要望2026-08-16「着せ替えの項目たちの枠も不要。ないほうがかっこいい」＝
   ボタンの枠・背景を消して、サムネ画像とラベルだけを見せる。ホバーはサムネの縁だけ光らせる。 */
.my-page-cosmetic-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  padding: 0.3rem 0.4rem;
  background: none;
  border: none;
  color: #e2e8f0;
  cursor: pointer;
  font-size: 0.82rem;
}
.my-page-cosmetic-btn:hover .my-page-cosmetic-thumb {
  border-color: rgba(56, 189, 248, 0.7);
}
/* 「選択中」サムネ（ユーザー要望「それぞれ何に着せ替え中か分かるように」）。ボタン内に
   ラベルの下へ現在の選択の画像を小さく出す。ペット「なし」等は文字で示す。 */
.my-page-cosmetic-label {
  white-space: nowrap;
}
.my-page-cosmetic-thumb {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 0.35rem;
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid rgba(148, 163, 184, 0.25);
  overflow: hidden;
}
.my-page-cosmetic-thumb img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}
.my-page-cosmetic-thumb.is-none {
  font-size: 0.68rem;
  color: #94a3b8;
}

/* マイページ レイアウト編集モード（profile-layout-editor.js、管理者専用の製作者ツール）。 */
.profile-layout-editing .profile-layout-item {
  outline: 1px dashed rgba(56, 189, 248, 0.85);
  cursor: move;
}
.profile-layout-handle {
  position: absolute;
  width: 12px;
  height: 12px;
  background: #38bdf8;
  border: 1px solid #fff;
  border-radius: 2px;
  z-index: 5;
}
.handle-n { top: -6px; left: 50%; transform: translateX(-50%); cursor: ns-resize; }
.handle-s { bottom: -6px; left: 50%; transform: translateX(-50%); cursor: ns-resize; }
.handle-e { right: -6px; top: 50%; transform: translateY(-50%); cursor: ew-resize; }
.handle-w { left: -6px; top: 50%; transform: translateY(-50%); cursor: ew-resize; }
.handle-ne { top: -6px; right: -6px; cursor: nesw-resize; }
.handle-nw { top: -6px; left: -6px; cursor: nwse-resize; }
.handle-se { bottom: -6px; right: -6px; cursor: nwse-resize; }
.handle-sw { bottom: -6px; left: -6px; cursor: nesw-resize; }
.profile-layout-toolbar {
  position: fixed;
  top: 0.5rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 3000;
  display: flex;
  gap: 0.6rem;
  align-items: center;
  background: rgba(15, 23, 32, 0.95);
  border: 1px solid rgba(56, 189, 248, 0.6);
  border-radius: 0.5rem;
  padding: 0.3rem 0.8rem;
  color: #e2e8f0;
  font-size: 0.8rem;
}
.profile-layout-toolbar button {
  padding: 0.25rem 0.7rem;
  background: #38bdf8;
  color: #0f172a;
  border: none;
  border-radius: 0.35rem;
  cursor: pointer;
  font-weight: 700;
  font-size: 0.78rem;
}
#profile-layout-export {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 3100;
  width: min(90vw, 40rem);
  background: #0f1520;
  border: 1px solid rgba(148, 163, 184, 0.5);
  border-radius: 0.5rem;
  padding: 0.8rem;
  box-sizing: border-box;
}
#profile-layout-export textarea {
  width: 100%;
  height: 14rem;
  font-family: monospace;
  font-size: 0.75rem;
  background: #0b0f16;
  color: #e2e8f0;
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.4rem;
  padding: 0.5rem;
  box-sizing: border-box;
}
.profile-layout-export-row {
  display: flex;
  gap: 0.5rem;
  justify-content: flex-end;
  margin-top: 0.5rem;
}
.profile-layout-export-row button {
  padding: 0.3rem 0.9rem;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.35rem;
  color: #e2e8f0;
  cursor: pointer;
}
/* ユーザー要望「オプションエリアのある列全部に、右端から左端まで独自のおしゃれな帯を
   敷いてほしい（背景が白系だとアイコンが見にくいため）」。#option-area自体は画面いっぱいの
   透明なアンカーなので、その最上部に横いっぱいの装飾帯を疑似要素で敷く。上部を濃く下へ
   すっと消える縦グラデーションで、盤面の背景色に関係なくアイコン・通貨表示が読めるようにする
   （丸い縁取りを持たないアイコン=ヘルプ/Discord/ランキング等が白背景に埋もれる問題への対策）。
   ::before は #option-area > * に含まれず pointer-events:none のままなので盤面のクリックは
   奪わない。アイコン群(z-index:900)より背面に自然と回る。 */
#option-area::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: var(--option-area-band-height, 3rem);
  background: linear-gradient(
    180deg,
    rgba(13, 20, 30, 0.88) 0%,
    rgba(13, 20, 30, 0.7) 55%,
    rgba(13, 20, 30, 0) 100%
  );
  /* ユーザー要望「両端までしっかり背景を入れて」。以前は左右をフェードさせる横マスクを
     掛けていたが、端が届いて見えなかったため撤去し、画面の左端から右端まで隙間なく敷く。 */
  pointer-events: none;
}
/* オープニング画面中はアイコン群を隠している（下の opening-screen-active 参照）ので、
   帯も出さない（映画的な起動演出に横帯が乗らないように）。 */
body.opening-screen-active #option-area::before {
  display: none;
}
body.is-phone-device #option-area {
  transform-origin: top right;
  transform: translate(var(--option-area-pos-phone-x, 0rem), var(--option-area-pos-phone-y, 0rem))
    scale(var(--option-area-scale-phone, 1));
}
/* 不具合報告ボタンはbody直下の固定配置で（z-indexを常に最前面にするため。上の
   #self-status-bug-report参照）、スマホで#option-area全体が1.65倍(--option-area-scale-phone)に
   拡大されてもそれに乗らず、他アイコンより小さく・位置もズレていた（ユーザー報告2026-08-13
   「スマホでオプションエリアの不具合報告ボタンが小さく場所もおかしい」）。#option-areaは
   top-right基準で拡大されるため、固定配置のこのボタンも位置(top/right)とアイコンサイズを
   同じ倍率で拡大すれば、拡大後の他アイコンと大きさ・位置が揃う。 */
body.is-phone-device #self-status-bug-report {
  top: calc(var(--bug-report-icon-top, 0.6rem) * var(--option-area-scale-phone, 1.65));
  right: calc(var(--bug-report-icon-right, 19.5rem) * var(--option-area-scale-phone, 1.65));
}
body.is-phone-device #self-status-bug-report .icon-action-button-icon-wrap {
  width: calc(var(--bug-report-icon-size, 1.9rem) * var(--option-area-scale-phone, 1.65));
  height: calc(var(--bug-report-icon-size, 1.9rem) * var(--option-area-scale-phone, 1.65));
}
body.is-phone-device #self-status-bug-report .bug-report-icon-emoji {
  width: calc(var(--bug-report-icon-size, 1.9rem) * var(--option-area-scale-phone, 1.65));
  height: calc(var(--bug-report-icon-size, 1.9rem) * var(--option-area-scale-phone, 1.65));
  font-size: calc(var(--bug-report-icon-size, 1.9rem) * 0.44 * var(--option-area-scale-phone, 1.65));
}

#turn-round-counter {
  position: absolute;
  /* ユーザー要望により位置を変更（元はtop:0.4rem/right:3.9rem、
     オプションアイコンとの衝突回避を意図した値だった）。 */
  top: var(--turn-round-counter-top, 3.1rem);
  right: var(--turn-round-counter-right, 1.6rem);
  z-index: 900;
  font-family: sans-serif;
  font-size: var(--turn-round-counter-font-size, 0.65rem);
  line-height: 1;
  color: rgba(226, 232, 240, 0.6);
  letter-spacing: 0.02em;
  pointer-events: none;
  display: none;
}

/* 「⚙ オプション」ボタンとそのドロップダウン（管理者モードの呼び出し口をここに統合した）。
   山札一覧・セットアップと同じ右上の並びに、その下へ積む形で配置する。 */
/* ハマりどころ: z-indexを1001（山札一覧/セットアップと同じ並びのつもりで）にしていたところ、
   セットアップウィザードや管理者モードのパネル（z-index:999〜1000）を開くと、その上に
   オプションボタンが浮いて表示されてしまっていた（両パネルとも、自分自身のトグルボタンは
   開いている間non-displayにするが、無関係な他のボタンまでは考慮しないため）。
   オプションボタン自体は「自分の担当パネルさえ開いていなければ常に見える」でよいので、
   他のパネル群より明確に低いz-indexにして、開いているパネルの下に自然と隠れるようにした。 */
/* 右上に積むボタン群（山札一覧・セットアップ・オプション）の共通サイズ。ラベルの文字数が
   バラバラでも見た目の横幅が揃うよう、幅を固定して中央寄せにする（山札一覧/セットアップの
   トグルボタンはdeck-viewer.js/game-setup.jsでinline style.cssTextとして定義されているため、
   このクラスを付けて共通化する）。 */
/* ユーザー報告「『ゲームを開始する』ボタンがボタンっぽくなくて少しわかりにくい」
   への対応。#game-setup-toggle-buttonは自前のインラインスタイル（背景・枠線・
   角丸）で見た目を持っていたが、textButton()（online-ui.js、「ゲームを開始する」
   「部屋を作成」「この部屋を離れる」等）はこのクラスの見た目（幅・中央寄せのみ）
   にしか頼っておらず、ブラウザの素の<button>のままで押せることが分かりにくかった。
   共通クラス自体に見た目を持たせ、両方に同じ「押せそうな」外観を与える
   （インラインスタイルを個別に持つボタンは、そちらが優先されるため見た目は
   変わらない）。 */
.header-tool-button {
  min-width: 8rem;
  text-align: center;
  white-space: nowrap;
  padding: 0.4rem 0.7rem;
  background: rgba(15, 23, 32, 0.85);
  color: #e2e8f0;
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.4rem;
  cursor: pointer;
  font-family: sans-serif;
  font-size: 0.75rem;
}
.header-tool-button:hover {
  background: rgba(56, 189, 248, 0.15);
  border-color: rgba(56, 189, 248, 0.6);
}
.header-tool-button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
/* ライトテーマ: 既定は暗い地＋白文字で、ライトのモーダル上でも静止時は読めるが、ホバーの
   background: rgba(56,189,248,0.15)（ほぼ透明）だと下のライト地が透けて“薄い水色地＋白文字”に
   なり読めなくなる（ユーザー報告「ゲームを開始する等をホバーすると見にくい」）。ライトテーマでは
   白地＋濃い文字にし、ホバーも濃いシアン文字でコントラストを保つ。 */
body.theme-light .header-tool-button {
  background: #ffffff;
  color: #3a3326;
  border-color: rgba(176, 141, 63, 0.5);
}
body.theme-light .header-tool-button:hover {
  background: rgba(8, 145, 178, 0.12);
  border-color: rgba(8, 145, 178, 0.55);
  color: #0e7490;
}

/* 画面の最も右上（ラウンド数表示の右隣）に、歯車アイコンだけの小さいボタンとして配置する。
   以前は右側のボタン列（📋山札一覧・🎲セットアップ）と並んで「⚙ オプション」という
   テキスト付きボタンだったが、山札一覧をこのメニューの中へ統合したのに合わせて
   アイコンのみに整理した。 */
#options-menu-button {
  position: absolute;
  top: 0.3rem;
  right: 1rem;
  z-index: 900;
  transform: translate(var(--icon-pos-options-x, 0rem), var(--icon-pos-options-y, 0rem));
}
body.is-touch-device #options-menu-button {
  transform: translate(
    var(--icon-pos-options-touch-x, var(--icon-pos-options-x, 0rem)),
    var(--icon-pos-options-touch-y, var(--icon-pos-options-y, 0rem))
  );
}
/* ユーザー要望「スマホ用のオプションアイコンの個別の位置サイズ調整を復活させて」
   （オプションエリアの一括調整とは別に、この1つだけ自分で細かく動かしたいとのこと）。
   グループ全体の位置・拡大率（#option-area、body.is-phone-device #option-area参照）は
   引き続き効いたまま、その上でこのアイコン1つだけさらに個別に動かせるようにする。 */
body.is-phone-device #options-menu-button {
  transform: translate(
    var(--icon-pos-options-phone-x, var(--icon-pos-options-touch-x, var(--icon-pos-options-x, 0rem))),
    var(--icon-pos-options-phone-y, var(--icon-pos-options-touch-y, var(--icon-pos-options-y, 0rem)))
  );
}
/* ユーザー要望により、オプションボタンだけキャプション文字と丸い縁取りを非表示にする
   （他のアイコンボタンは従来通り）。captionをdisplay:noneにすると1要素だけのflex列に
   なりgapも自動的に効かなくなるため、ボタン自体の高さもアイコンぶんだけに縮む。 */
#options-menu-button .icon-action-button-caption {
  display: none;
}
#options-menu-button .icon-action-button-icon-wrap {
  background: none;
  border: none;
  box-shadow: none;
}
#options-menu-button:hover .icon-action-button-icon-wrap {
  background: none;
  border-color: transparent;
}
/* ユーザー要望「画面右上のオプションアイコンの隣に人マークのアイコンを作り、それを
   押すとマイページモーダルが開く」への対応。#options-menu-buttonと同じ高さ・同じ
   見た目（アイコンのみ、丸い縁取り無し）で、その左隣に並べる。 */
/* 行動ログのトグルアイコン（オプションアイコン right:1rem とマイページ right:6.5rem の間）。 */
#self-status-action-log {
  position: absolute;
  /* 位置・大きさは管理者モード（行動ログアイコンのサイズ・位置）で調整可能。 */
  top: var(--action-log-icon-top, 0.6rem);
  right: var(--action-log-icon-right, 3.9rem);
  z-index: 900;
  transform: none;
}
#self-status-action-log .icon-action-button-caption {
  display: none;
}
#self-status-action-log .icon-action-button-icon-wrap {
  width: var(--action-log-icon-size, 1.95rem);
  height: var(--action-log-icon-size, 1.95rem);
}
#self-status-action-log .action-log-icon-emoji {
  font-size: calc(var(--action-log-icon-size, 1.95rem) * 0.44);
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--action-log-icon-size, 1.95rem);
  height: var(--action-log-icon-size, 1.95rem);
}
#self-status-action-log.is-active .icon-action-button-icon-wrap {
  outline: 2px solid rgba(250, 204, 21, 0.8);
  border-radius: 0.4rem;
}
/* 行動ログのトグルは右上の端に近いので、簡易説明は下側かつ右寄せに出す（中央寄せだと
   右側が画面外で見切れる＝ユーザー報告）。 */
#option-area #self-status-action-log .phase-guide-tooltip {
  bottom: auto;
  top: calc(100% + 0.5rem);
  left: auto;
  right: 0;
  transform: none;
}
/* 行動ログウィンドウ本体（オプションエリアの下あたり、右上）。#option-area(position:fixedで
   ステージ基準の器)の子として絶対配置し、アイコン群と同じ座標系に揃える。 */
#action-log-window {
  position: absolute;
  top: 3.4rem;
  right: 1rem;
  width: 30rem;
  max-width: 92vw;
  max-height: 60vh;
  z-index: 901;
  background: rgba(15, 23, 32, 0.96);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.5rem;
  padding: 0.6rem 0.8rem;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.5);
  font-family: sans-serif;
  color: #e2e8f0;
  display: flex;
  flex-direction: column;
}
.action-log-window-title {
  flex-shrink: 0;
  font-weight: bold;
  color: #fbbf24;
  font-size: 0.85rem;
  margin-bottom: 0.4rem;
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.action-log-info-btn {
  background: none;
  border: 1px solid rgba(148, 163, 184, 0.5);
  color: inherit;
  border-radius: 50%;
  width: 1.15rem;
  height: 1.15rem;
  line-height: 1;
  font-size: 0.75rem;
  cursor: pointer;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.action-log-info-btn:hover {
  border-color: currentColor;
}
/* 行動ログウィンドウ右上の✕（ユーザー要望2026-08-08）。タイトル行の右端へ寄せる。 */
.action-log-window-close {
  margin-left: auto;
  background: none;
  border: 1px solid rgba(148, 163, 184, 0.5);
  color: inherit;
  border-radius: 50%;
  width: 1.15rem;
  height: 1.15rem;
  line-height: 1;
  font-size: 0.7rem;
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
}
.action-log-window-close:hover {
  border-color: currentColor;
  background: rgba(148, 163, 184, 0.2);
}
.action-log-window-body {
  overflow-y: auto;
  font-family: sans-serif;
  font-size: 0.85rem;
  line-height: 1.6;
  word-break: break-word;
}
/* ターン区切りの見出し（ラウンド・ターン・手番プレイヤー。--log-accent は座席色）。 */
.action-log-turn-header {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0.5rem 0 0.15rem;
  padding: 0.1rem 0.35rem;
  border-left: 3px solid var(--log-accent, #94a3b8);
  background: rgba(148, 163, 184, 0.12);
  border-radius: 0 0.25rem 0.25rem 0;
  font-weight: bold;
  font-size: 0.8rem;
  color: var(--log-accent, inherit);
}
.action-log-turn-header:first-child {
  margin-top: 0;
}
.action-log-turn-avatar {
  width: 1.3rem;
  height: 1.3rem;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}
.action-log-turn-label {
  white-space: nowrap;
}
/* 各行動。左に座席色の細いバー、右端に時刻。 */
.action-log-event {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.05rem 0.35rem 0.05rem 0.6rem;
  border-left: 3px solid var(--log-accent, #94a3b8);
}
.action-log-event-text {
  flex: 1;
}
.action-log-event-time {
  flex-shrink: 0;
  font-size: 0.72rem;
  opacity: 0.6;
  font-variant-numeric: tabular-nums;
}
/* 説明モーダル（ⓘ）。 */
#action-log-info-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 10621;
  width: min(30rem, 92vw);
  max-height: 80vh;
  overflow-y: auto;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.5rem;
  padding: 1rem 1.2rem;
  color: #e2e8f0;
  font-family: sans-serif;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
}
.action-log-info-modal-title {
  font-weight: bold;
  color: #fbbf24;
  margin-bottom: 0.6rem;
  padding-right: 1.6rem;
}
.action-log-info-modal-p {
  margin: 0 0 0.6rem;
  line-height: 1.7;
  font-size: 0.85rem;
}
body.theme-light #action-log-info-modal {
  background: #fdfbf5;
  border-color: rgba(176, 141, 63, 0.5);
  color: #3a3326;
}
body.theme-light .action-log-info-modal-title {
  color: #a67c1a;
}
body.theme-light #action-log-window {
  background: #fdfbf5;
  border-color: rgba(176, 141, 63, 0.5);
  color: #3a3326;
}
body.theme-light .action-log-window-title {
  color: #a67c1a;
}
/* マイページ以左のアイコンを、行動ログアイコンを差し込んだ分だけ左へずらす（+2.6rem）。 */
#my-page-button {
  position: absolute;
  top: 0.3rem;
  right: 6.5rem;
  z-index: 900;
}
#my-page-button .icon-action-button-caption {
  display: none;
}
#my-page-button .icon-action-button-icon-wrap {
  background: none;
  border: none;
  box-shadow: none;
}
#my-page-button:hover .icon-action-button-icon-wrap {
  background: none;
  border-color: transparent;
}

/* ランキングアイコン（ranking-page.js、ユーザー要望（続き77）「オプションエリアに
   ランキングアイコンを追加したいです。ヘルプアイコンとマイページアイコンの間に
   お願いします」）。#my-page-button（right:3.9rem）とヘルプの間、ヘルプが元々
   あった位置(6.5rem)に置き、ヘルプ以降（ヘルプ・Discord・通貨表示）はその分
   さらに左へ2.6remずつ押し出す。 */
#ranking-page-button {
  position: absolute;
  top: 0.3rem;
  right: 9.1rem;
  z-index: 900;
}
#ranking-page-button .icon-action-button-caption {
  display: none;
}
#ranking-page-button .icon-action-button-icon-wrap {
  background: none;
  border: none;
  box-shadow: none;
}
#ranking-page-button:hover .icon-action-button-icon-wrap {
  background: none;
  border-color: transparent;
}

/* ユーザー要望「オプションの横にヘルプボタンを創設したい」への対応。#options-menu-button・
   #my-page-buttonと同じ高さ・同じ見た目（アイコンのみ、丸い縁取り無し）で、その並びの
   さらに左隣に置く。位置・サイズは管理者モードで調整できる
   （--icon-pos-help-x/-y・--icon-size-help、help.js参照）。続き77でランキングアイコンを
   間に挟んだ分、right:6.5rem→9.1remへ押し出した。 */
#help-button {
  position: absolute;
  top: 0.3rem;
  right: 11.7rem;
  z-index: 900;
  transform: translate(var(--icon-pos-help-x, 0rem), var(--icon-pos-help-y, 0rem));
}
#help-button .icon-action-button-caption {
  display: none;
}
#help-button .icon-action-button-icon-wrap {
  width: var(--icon-size-help, 2.6rem);
  height: var(--icon-size-help, 2.6rem);
  background: none;
  border: none;
  box-shadow: none;
}
#help-button:hover .icon-action-button-icon-wrap {
  background: none;
  border-color: transparent;
}

/* Discordアイコン（discord-link.js、ユーザー要望「右上のオプションエリアにDiscord
   アイコンを追加してください。ヘルプマークの左隣に追加し、残金表示をその分左に
   ずらしてください」）。#help-button（続き77でright:9.1remへ）のさらに左隣、通貨表示が
   元々あった位置(11.7rem)に置き、通貨表示はさらに左（14.3rem）へ押し出す。 */
#discord-link-button {
  position: absolute;
  top: 0.3rem;
  right: 14.3rem;
  z-index: 900;
  transform: translate(var(--icon-pos-discord-x, 0rem), var(--icon-pos-discord-y, 0rem));
}
/* 2D/3D切り替えアイコン。Discord(11.7rem)と残金表示(16.9remへシフト)の間＝14.3rem。
   #option-areaの子は個別にposition指定するため（未指定だと左上に落ちる）ここで固定配置する。 */
#board-view-toggle-button {
  position: absolute;
  top: 0.3rem;
  right: 16.9rem;
  z-index: 900;
  transform: translate(var(--icon-pos-view-toggle-x, 0rem), var(--icon-pos-view-toggle-y, 0rem));
}
/* ユーザー要望「2D/3D切替アイコンは丸囲み不要・下の『2D/3D』テキストも不要・ホバーで
   簡易説明を出す」。他の縁取り無しアイコン（ヘルプ/Discord/オプション等）と同じ扱いにする。
   ホバーの簡易説明は .icon-action-button:hover .phase-guide-tooltip で従来どおり表示される
   （文言は board-view-toggle.js の applyState がツールチップへ設定）。 */
#board-view-toggle-button .icon-action-button-caption {
  display: none;
}
#board-view-toggle-button .icon-action-button-icon-wrap {
  background: none;
  border: none;
  box-shadow: none;
}
#board-view-toggle-button:hover .icon-action-button-icon-wrap {
  background: none;
  border-color: transparent;
}
/* 全画面表示アイコン（fullscreen-toggle.js）。不具合報告(22.1rem)と2D/3D切替(16.9rem)の間の
   19.5rem に置く（不具合報告を元の19.5→22.1へ、通貨表示を22.1→24.7へずらして間を空けた）。
   2D/3D切替と同じく縁取り・キャプション無し・ホバーで簡易説明。 */
#fullscreen-toggle-button {
  position: absolute;
  top: 0.3rem;
  right: 19.5rem;
  z-index: 900;
  transform: translate(var(--icon-pos-fullscreen-x, 0rem), var(--icon-pos-fullscreen-y, 0rem));
}
#fullscreen-toggle-button .icon-action-button-caption {
  display: none;
}
#fullscreen-toggle-button .icon-action-button-icon-wrap {
  background: none;
  border: none;
  box-shadow: none;
}
#fullscreen-toggle-button:hover .icon-action-button-icon-wrap {
  background: none;
  border-color: transparent;
}
#discord-link-button .icon-action-button-caption {
  display: none;
}
#discord-link-button .icon-action-button-icon-wrap {
  width: var(--icon-size-discord, 2.6rem);
  height: var(--icon-size-discord, 2.6rem);
  background: none;
  border: none;
  box-shadow: none;
}
#discord-link-button:hover .icon-action-button-icon-wrap {
  background: none;
  border-color: transparent;
}

/* ゲーム内通貨の所持金額表示（currency-display.js、ユーザー要望「ヘルプボタンの横に
   通貨アイコンと所持金額を表示させたい」）。続き77でランキングアイコンが割り込んだ分、
   #help-button（right:9.1rem）→#discord-link-button（right:11.7rem）のさらに左隣
   （right:14.3rem）へ押し出した。アイコンボタンでは無く常時表示のバッジのため、
   他のicon-action-buttonとは別の専用の見た目にしてある。位置は管理者モードで
   調整できる（--currency-display-pos-x/-y、admin.js参照）。 */
#currency-display {
  position: absolute;
  top: 0.5rem;
  /* 全画面ボタンを不具合報告(22.1)と2D/3D(16.9)の間に入れるため、通貨は22.1→24.7へ左送り。 */
  right: 24.7rem;
  z-index: 900;
  display: flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.3rem 0.7rem;
  background: rgba(15, 23, 32, 0.7);
  border: 1px solid rgba(250, 204, 21, 0.4);
  border-radius: 999px;
  font-family: sans-serif;
  font-size: 0.85rem;
  font-weight: bold;
  color: #fde68a;
  transform: translate(var(--currency-display-pos-x, 0rem), var(--currency-display-pos-y, 0rem));
}
#currency-display-icon {
  width: 1.2rem;
  height: 1.2rem;
  object-fit: contain;
}

/* オンライン状態ウィジェットをオプションエリア（残金アイコン#currency-displayの左隣）へ
   移設（ユーザー要望）。アイコン＋部屋名を横並びのピルにし、右端(right)基準で左へ伸ばす
   （部屋名が長くても右詰めで収まる）。ステータスエリア時代の位置transform（#self-status-online
   単体ルール／タブレット用ルール）は、より詳細度の高いこの #option-area #self-status-online が
   上書きする。 */
#option-area #self-status-online {
  position: absolute;
  top: 0.5rem;
  /* 残金表示(#currency-display, right:24.7rem。左へ伸びる)の左隣。続き195で残金が24.7remへ動いた
     ぶん、旧28.6remだと3桁以上の残金と部屋名ピルが重なった（ユーザー報告2026-08-18）。十分な
     ゆとりを取って左へ寄せる。部屋名は省略せず左へ伸ばせる（左に他アイコン無し）。 */
  right: 31.5rem;
  z-index: 900;
  transform: none;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.35rem;
  padding: 0.2rem 0.7rem 0.2rem 0.35rem;
  background: rgba(15, 23, 32, 0.7);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 999px;
}
#option-area #self-status-online .icon-action-button-icon-wrap {
  width: 1.5rem;
  height: 1.5rem;
  background: none;
  border: none;
  box-shadow: none;
  flex: none;
}
#option-area #self-status-online .icon-action-button-caption {
  display: inline-block;
  font-family: sans-serif;
  font-size: 0.8rem;
  font-weight: bold;
  color: #cbd5e1;
  white-space: nowrap;
}

/* ユーザー要望「対戦終了時にお金がもらえる演出を追加したい」への対応
   （currency-display.jsのshowCurrencyAwardEffect参照）。#currency-display自体は
   位置調整用のtransform（--currency-display-pos-x/-y）を既に持っているため、この
   パルスはtransformを使わずfilterだけで光らせる。 */
#currency-display.is-pulsing {
  animation: currency-display-pulse 0.9s ease-out;
}
@keyframes currency-display-pulse {
  0% {
    filter: none;
  }
  30% {
    filter: drop-shadow(0 0 0.7rem rgba(250, 204, 21, 0.95));
  }
  100% {
    filter: none;
  }
}
.currency-award-float {
  position: fixed;
  transform: translate(-50%, 0);
  z-index: 10600;
  font-family: sans-serif;
  font-weight: bold;
  font-size: 1.1rem;
  color: #fde68a;
  text-shadow: 0 0.15rem 0.4rem rgba(0, 0, 0, 0.7);
  pointer-events: none;
  animation: currency-award-float-rise 1.7s ease-out forwards;
}
@keyframes currency-award-float-rise {
  0% {
    opacity: 0;
    transform: translate(-50%, 0.5rem) scale(0.8);
  }
  15% {
    opacity: 1;
    transform: translate(-50%, 0) scale(1.15);
  }
  70% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -2.6rem) scale(1);
  }
}

/* ユーザー要望「所持金表示のアイコンをクリックすると『ショップ』『所持金について』の
   二つのプルダウンが出るようにしてほしい」（currency-display.js）。#currency-display
   の直下に出す小さな2択メニュー。 */
#currency-dropdown-menu {
  position: fixed;
  z-index: 9501;
  display: flex;
  flex-direction: column;
  min-width: 10rem;
  padding: 0.4rem;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(250, 204, 21, 0.4);
  border-radius: 0.6rem;
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.5);
}
#currency-dropdown-menu button {
  padding: 0.5rem 0.7rem;
  background: transparent;
  color: #e2e8f0;
  border: none;
  border-radius: 0.4rem;
  cursor: pointer;
  font-family: sans-serif;
  font-size: 0.85rem;
  text-align: left;
}
#currency-dropdown-menu button:hover {
  background: rgba(250, 204, 21, 0.15);
}

/* 「所持金について」説明パネル（currency-display.jsのshowCurrencyAboutPanel）。 */
#currency-about-panel {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 9601;
  width: min(24rem, 90vw);
  padding: 1.4rem 1.6rem;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(250, 204, 21, 0.4);
  border-radius: 0.7rem;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
  font-family: sans-serif;
  color: #e2e8f0;
}
.currency-about-title {
  font-weight: bold;
  font-size: 1.1rem;
  color: #fde68a;
  margin-bottom: 0.9rem;
}
.currency-about-list {
  margin: 0;
  padding-left: 1.2rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  font-size: 0.9rem;
  line-height: 1.5;
}

/* オンライン対戦の部屋一覧・部屋作成モーダル（online-ui.jsのopenOnlinePanel）。
   続き92でinline styleから移設した（body.is-phone-device #online-panelのスケール
   調整がinline styleに負けて効かなくなるため、他の画面中央モーダルと同じ構成に揃える）。 */
#online-panel {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  /* 部屋パネルは2カラム（左＝作成／右＝参加・観戦、ユーザー要望2026-08-17）にしたため広めにする。
     狭い画面では .online-room-columns が縦積みに折り返す。 */
  width: min(40rem, 94vw);
  max-height: 90vh;
  overflow-y: auto;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.5rem;
  padding: 1rem;
  z-index: 10002;
  font-family: sans-serif;
  color: #e2e8f0;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
/* 部屋選択の2カラム（左＝作成／右＝参加・観戦）。狭い画面では縦積みに折り返す。 */
.online-room-columns {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}
.online-room-col {
  flex: 1 1 15rem;
  min-width: 0;
}
/* オンライン対戦ウィンドウの「ホームに戻る」ボタン（ユーザー要望2026-08-10）。パネル上部・左寄せ。
   ✕（右上・絶対配置）とは重ならない。 */
#online-panel-home-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  margin-bottom: 0.6rem;
  padding: 0.3rem 0.75rem;
  background: rgba(148, 163, 184, 0.12);
  border: 1px solid rgba(148, 163, 184, 0.5);
  border-radius: 0.4rem;
  color: #e2e8f0;
  font-family: sans-serif;
  font-size: 0.8rem;
  font-weight: bold;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
#online-panel-home-btn:hover {
  border-color: rgba(56, 189, 248, 0.7);
  background: rgba(56, 189, 248, 0.15);
}
body.theme-light #online-panel-home-btn {
  background: rgba(176, 141, 63, 0.12);
  border-color: rgba(176, 141, 63, 0.5);
  color: #4a3f28;
}
body.theme-light #online-panel-home-btn:hover {
  border-color: rgba(176, 141, 63, 0.95);
  background: rgba(176, 141, 63, 0.2);
}

/* 管理者モード：位置合わせパネル（admin.jsのbuildPanel）。続き92でinline styleから
   移設した（理由は#online-panelと同じ）。表示/非表示はJS側（initAdminMode()の
   open()/close()）がstyle.displayを直接切り替える。 */
#admin-panel {
  position: fixed;
  top: 1rem;
  left: 1rem;
  /* ユーザー報告「ホーム画面で管理者モードを押してもウィンドウが出ない（背面に隠れる）」。
     ホーム画面(#home-screen)はz-index:1500のため、従来のz-index:1000だと背後に回っていた。
     ショップ/マイページ等の他パネル（2000〜2601）と同様、ホームより前に出す。 */
  z-index: 2600;
  background: rgba(15, 23, 32, 0.95);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.5rem;
  padding: 0.75rem;
  width: 19rem;
  max-height: 90vh;
  overflow-y: auto;
  box-sizing: border-box;
  font-family: sans-serif;
  font-size: 0.75rem;
  color: #e2e8f0;
}
/* ユーザー報告「タイトル画面の管理者パネルボタンを押しても何も起きない（背面に行っている）」。
   タイトル画面(#opening-screen)はz-index:50000のため、管理者パネル(2600)/バックドロップ(999)は
   その裏に隠れていた。タイトル画面が出ている間だけ、両方を#opening-screenより手前へ引き上げる。 */
body.opening-screen-active #admin-panel {
  z-index: 50200;
}
body.opening-screen-active #admin-panel-backdrop {
  /* createBackdrop がインラインで z-index:999 を付けるため、!important で上書きする。 */
  z-index: 50150 !important;
}
/* ユーザー報告2026-08-08「ショップページで管理者モードのウィンドウが背面に隠れて出ない」。
   ショップ(#shop-panel, z-index:2601)より管理者パネル(2600)がわずかに低く隠れていた。ショップを
   開いている間だけ、管理者パネル/バックドロップをショップ・オプションエリア(2700)より前へ出す。 */
body.shop-open #admin-panel {
  z-index: 2800;
}
body.shop-open #admin-panel-backdrop {
  z-index: 2799 !important;
}
/* ユーザー報告2026-08-16「マイページ（プロフィール全画面）で管理者モードのウィンドウが背面に隠れる」。
   #profile-pageは不具合#84でz-index:2650へ引き上げてあるため、管理者パネル(2600)が負けて隠れていた
   （ホーム/ランキング/図鑑=1500では隠れないが、プロフィール2650で発生）。ショップ時と同じく、全画面
   ページ（プロフィール含む）が開いている間だけ管理者パネル/バックドロップを2800/2799へ前面化する。 */
body.full-screen-page-active #admin-panel {
  z-index: 2800;
}
body.full-screen-page-active #admin-panel-backdrop {
  z-index: 2799 !important;
}
/* スマホでは管理者モーダル自体が小さすぎて操作しづらいとの報告（続き92）。
   左上固定のパネルのため、他の画面中央モーダル（translate(-50%,-50%)）とは
   別の専用の一括調整用変数にする（transform-originを左上に固定し、左上の
   位置は変えずに右下方向へ拡大する）。 */
body.is-phone-device #admin-panel {
  transform-origin: top left;
  transform: scale(var(--admin-panel-scale-phone, 1));
}

/* ログインボーナス（日次）の通知トースト（main.jsのshowDailyBonusToast参照）。数秒で
   自動的に消える、押せない通知専用の小さな表示。 */
/* ログインボーナスのモーダル（ユーザー要望「ログインでお金がもらえる時はモーダルで」）。 */
#daily-bonus-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 10061;
  width: min(22rem, 88vw);
  text-align: center;
  background: linear-gradient(160deg, #1e293b, #0f172a);
  border: 1px solid rgba(250, 204, 21, 0.6);
  border-radius: 0.8rem;
  padding: 1.4rem 1.2rem 1.2rem;
  font-family: sans-serif;
  color: #e2e8f0;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
.daily-bonus-modal-title {
  font-weight: bold;
  font-size: 1.1rem;
  color: #fbbf24;
  margin-bottom: 0.5rem;
}
.daily-bonus-modal-amount {
  font-size: 2rem;
  font-weight: 800;
  color: #fde68a;
  margin: 0.3rem 0 0.5rem;
}
.daily-bonus-modal-note {
  font-size: 0.8rem;
  color: #cbd5e1;
  line-height: 1.6;
  margin-bottom: 1rem;
}
.daily-bonus-modal-ok {
  padding: 0.5rem 1.6rem;
  border: none;
  border-radius: 0.4rem;
  background: #f59e0b;
  color: #201400;
  font-weight: 700;
  font-size: 0.95rem;
  cursor: pointer;
}
/* AFK時のCPU代行（ユーザー要望2026-08-08）。自席が代行中の間、画面下中央に「復帰する」バナーを出す。 */
#afk-cpu-banner {
  display: none;
  position: fixed;
  left: 50%;
  bottom: 1.2rem;
  transform: translateX(-50%);
  z-index: 100060;
  align-items: center;
  gap: 0.8rem;
  padding: 0.6rem 1rem;
  background: rgba(15, 23, 32, 0.96);
  border: 1px solid rgba(250, 204, 21, 0.7);
  border-radius: 999px;
  box-shadow: 0 0.6rem 1.6rem rgba(0, 0, 0, 0.5);
  font-family: sans-serif;
  color: #fde68a;
  font-weight: bold;
  font-size: 0.9rem;
}
#afk-cpu-banner.is-visible {
  display: flex;
}
.afk-cpu-banner-btn {
  padding: 0.4rem 1.1rem;
  border: none;
  border-radius: 999px;
  background: #22c55e;
  color: #062312;
  font-weight: 800;
  font-size: 0.9rem;
  cursor: pointer;
}
.afk-cpu-banner-btn:hover {
  background: #16a34a;
}
/* 相手席の名前に添える「🤖CPU操作中」タグ。 */
.afk-cpu-tag {
  color: #fbbf24;
  font-size: 0.8em;
  font-weight: bold;
}

/* エイドス会話パネル（eidos-dialogue-ui.js、ユーザー要望2026-08-08 / 決定稿）。物語用の会話UI。
   主人公=左 / エイドス=右 が向き合い、セプトは中央下。発話者は明るく前へ、聞き手は暗く。
   既存の説明モーダル/吹き出しとは別役割。 */
#eidos-dialogue-root {
  position: fixed;
  inset: 0;
  z-index: 10700; /* 盤面・多くのUIより前面。最上位のオープニング/回転案内よりは下。 */
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.35s ease;
}
#eidos-dialogue-root.is-open {
  opacity: 1;
}
#eidos-dialogue-root.is-closing {
  opacity: 0;
}
#eidos-dialogue-blocker {
  position: absolute;
  inset: 0;
  pointer-events: auto; /* 会話中は盤面クリックを吸収（盤面操作ロック）。 */
  background: rgba(0, 0, 0, 0.18);
  transition: background 0.6s ease;
}
/* SCENE1: 暗転から徐々に明るく（開始時は黒→is-openで薄暗いオーバーレイへ）。 */
#eidos-dialogue-root.from-black #eidos-dialogue-blocker {
  background: rgba(0, 0, 0, 0.96);
}
#eidos-dialogue-root.from-black.is-open #eidos-dialogue-blocker {
  background: rgba(0, 0, 0, 0.18);
}
/* 演出: 背景を少し暗くする（fx.bgDim）。 */
#eidos-dialogue-root.fx-bg-dim.is-open #eidos-dialogue-blocker {
  background: rgba(0, 0, 0, 0.45);
}
#eidos-dialogue-panel {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  pointer-events: auto;
  cursor: pointer;
  font-family: sans-serif;
  color: #e2e8f0;
}
/* 立ち絵の舞台。テキスト帯の上に、左右へ立ち絵を、中央下にセプトを配置する。 */
.eidos-dialogue-stage {
  position: relative;
  height: min(46vh, 26rem);
  pointer-events: none;
}
.eidos-dialogue-portrait {
  position: absolute;
  bottom: 0;
  width: min(24rem, 42vw);
  height: 100%;
  background-size: contain;
  background-repeat: no-repeat;
  transition: filter 0.28s ease, opacity 0.28s ease, transform 0.28s ease;
}
.eidos-dialogue-portrait-left {
  left: 1.5vw;
  background-position: left bottom;
  transform-origin: left bottom;
}
.eidos-dialogue-portrait-right {
  right: 1.5vw;
  background-position: right bottom;
  transform-origin: right bottom;
}
.eidos-dialogue-portrait.is-speaking {
  opacity: 1;
  filter: brightness(1) drop-shadow(0 0.5rem 1rem rgba(0, 0, 0, 0.55));
  transform: scale(1.04);
}
.eidos-dialogue-portrait.is-listening {
  opacity: 0.8;
  filter: brightness(0.75);
  transform: scale(1);
}
/* 演出: エイドス側の黒い気配 / 主人公側の灰色のオーラ（best-effort、控えめ）。 */
#eidos-dialogue-root.fx-aura-dark .eidos-dialogue-portrait-right {
  filter: brightness(0.95) drop-shadow(0 0 1.4rem rgba(88, 40, 160, 0.8));
}
#eidos-dialogue-root.fx-aura-gray .eidos-dialogue-portrait-left {
  filter: brightness(1) drop-shadow(0 0 1.2rem rgba(180, 180, 190, 0.7));
}
.eidos-dialogue-sept {
  position: absolute;
  bottom: 0;
  left: 50%;
  /* ユーザー要望2026-08-08「中央に出るセプトをもっと大きく」。 */
  width: 12rem;
  height: 12rem;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center bottom;
  opacity: 0;
  transform: translateX(-50%) scale(0.9);
  transition: opacity 0.28s ease, transform 0.28s ease;
  pointer-events: none;
  /* セプトは真っ黒なので、光背を敷いて背景に埋もれないようにする（ユーザー要望2026-08-08）。
     以前は::before（z-index:-1）で描いていたが、セプトの絵柄が要素の background-image のため、
     CSS仕様上「要素の背景 → 負のz-indexの疑似要素」の順で描かれ、光背がセプトより手前に来て
     しまっていた（ユーザー報告2026-08-15）。drop-shadowは常に要素の描画内容の背面に出るので、
     セプトのシルエットに沿った光背が確実に後ろへ回る。 */
  filter: drop-shadow(0 0 1.4rem rgba(255, 252, 245, 0.95)) drop-shadow(0 0 2.6rem rgba(255, 244, 214, 0.6));
}
.eidos-dialogue-sept.is-shown {
  opacity: 1;
  transform: translateX(-50%) scale(1);
}

/* 物語チュートリアルの締めくくり（本気戦勝利＋セプト獲得後）のフェード暗転＋中央テキスト。
   ユーザー要望2026-08-15。オープニング画面(z-index 50000)より上に出す。 */
.eidos-story-ending {
  position: fixed;
  inset: 0;
  z-index: 60000;
  background: #000;
  opacity: 0;
  transition: opacity 2.4s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.eidos-story-ending.is-black {
  opacity: 1;
}
.eidos-story-ending.is-fading-out {
  opacity: 0;
  transition: opacity 1s ease;
}
/* 枠無し・中央・白文字（ユーザー要望「画面中央に枠無しで」）。 */
.eidos-story-ending-text {
  color: #f5f5f5;
  font-family: "Hiragino Mincho ProN", "Yu Mincho", serif;
  font-size: 1.9rem;
  letter-spacing: 0.12em;
  text-align: center;
  opacity: 0;
  transition: opacity 1.6s ease;
}
.eidos-story-ending-text.is-shown {
  opacity: 1;
}

/* 全クリ後の物語メニュー／チャプター選択オーバーレイ（ユーザー要望2026-08-15）。 */
.eidos-story-menu {
  position: fixed;
  inset: 0;
  z-index: 55000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(6, 8, 14, 0.72);
  opacity: 0;
  transition: opacity 0.25s ease;
}
.eidos-story-menu.is-shown {
  opacity: 1;
}
.eidos-story-menu-panel {
  width: min(24rem, 92vw);
  max-height: 88vh;
  overflow-y: auto;
  background: linear-gradient(180deg, rgba(30, 27, 46, 0.98), rgba(18, 16, 28, 0.98));
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.8rem;
  padding: 1.2rem 1.1rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  box-shadow: 0 1.4rem 3rem rgba(0, 0, 0, 0.6);
}
.eidos-story-menu-title {
  color: #f1e9ff;
  font-family: "Hiragino Mincho ProN", "Yu Mincho", serif;
  font-size: 1.25rem;
  letter-spacing: 0.06em;
  text-align: center;
  margin-bottom: 0.4rem;
}
.eidos-story-menu-btn {
  padding: 0.7rem 0.9rem;
  border-radius: 0.5rem;
  border: 1px solid rgba(148, 163, 184, 0.4);
  background: rgba(51, 45, 74, 0.85);
  color: #e9e4f5;
  font-family: sans-serif;
  font-size: 0.95rem;
  cursor: pointer;
  transition: background 0.15s ease, transform 0.05s ease;
}
.eidos-story-menu-btn:hover {
  background: rgba(78, 66, 112, 0.95);
}
.eidos-story-menu-btn:active {
  transform: translateY(1px);
}
.eidos-story-menu-btn.is-primary {
  background: linear-gradient(90deg, #7c5cbf, #915da3);
  border-color: rgba(200, 180, 240, 0.6);
  color: #fff;
}
.eidos-dialogue-sept.is-shown.is-speaking {
  transform: translateX(-50%) scale(1.15);
}
/* テキスト帯。 */
.eidos-dialogue-body {
  position: relative;
  margin: 0 auto;
  width: min(52rem, 96vw);
  box-sizing: border-box;
  min-height: 8rem;
  padding: 0.9rem 1.2rem 1rem;
  background: linear-gradient(160deg, rgba(15, 23, 32, 0.97), rgba(4, 12, 20, 0.97));
  border: 2px solid #7c5cff;
  border-radius: 0.9rem 0.9rem 0 0;
  box-shadow: 0 0 1.6rem rgba(124, 92, 255, 0.4), 0 -6px 24px rgba(0, 0, 0, 0.5);
}
/* 話者名・本文は発話者側へ寄せる（決定稿）。 */
#eidos-dialogue-panel[data-speaker-side="right"] .eidos-dialogue-name,
#eidos-dialogue-panel[data-speaker-side="right"] .eidos-dialogue-text {
  text-align: right;
}
#eidos-dialogue-panel[data-speaker-side="left"] .eidos-dialogue-name,
#eidos-dialogue-panel[data-speaker-side="left"] .eidos-dialogue-text,
#eidos-dialogue-panel[data-speaker-side="sept"] .eidos-dialogue-name,
#eidos-dialogue-panel[data-speaker-side="sept"] .eidos-dialogue-text {
  text-align: left;
}
.eidos-dialogue-name {
  font-weight: bold;
  color: #c4b5fd;
  font-size: 0.95rem;
  margin-bottom: 0.3rem;
}
.eidos-dialogue-text {
  font-size: 1.05rem;
  line-height: 1.6;
  /* ユーザー報告2026-08-15「会話の度にウィンドウが微妙に上下する」。原因は本文の行数(1行/2行)で
     本文帯の高さが変わり、その上に載る立ち絵・窓が上下していたこと。台本は概ね2行以内なので、
     本文領域を2行分(=1.05rem×1.6×2≈3.4rem)で固定し、1行でも2行でも同じ高さにしてブレを無くす。 */
  min-height: 3.4rem;
  white-space: pre-wrap;
}
.eidos-dialogue-choices {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 0.6rem;
  justify-content: center;
  /* ヒント↔選択肢/入力の切替でも窓の高さが変わらないよう、フッター領域の高さを予約する
     （選択肢の無い会話ステップでは下の .eidos-dialogue-hint が同じ高さを占める）。 */
  min-height: 2.4rem;
  align-items: center;
}
.eidos-dialogue-choice {
  padding: 0.5rem 1.1rem;
  border: 1px solid #7c5cff;
  border-radius: 0.5rem;
  /* ユーザー報告2026-08-14: 以前は薄い紫地＋ほぼ白文字(#e9e5ff)で、ライトテーマ時にパネルが
     明るいと文字が見えなかった。ソリッドな紫地＋白文字にして両テーマで確実に読めるようにする。 */
  background: #7c5cff;
  color: #ffffff;
  font-weight: bold;
  font-size: 0.95rem;
  cursor: pointer;
}
.eidos-dialogue-choice:hover {
  background: #6a4cff;
}

/* 会話中の入力ステップ（名前入力など）。入力欄＋決定ボタンを横並び（狭い画面では縦積み）。 */
.eidos-dialogue-input-wrap {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  flex-wrap: wrap;
}
.eidos-dialogue-input {
  padding: 0.5rem 0.8rem;
  border: 1px solid #7c5cff;
  border-radius: 0.5rem;
  background: rgba(255, 255, 255, 0.95);
  color: #201a3a;
  font-size: 1rem;
  font-weight: bold;
  min-width: 9rem;
  max-width: 14rem;
}
.eidos-dialogue-input:focus {
  outline: 2px solid #7c5cff;
  outline-offset: 1px;
}

/* ユーザー報告2026-08-14: 会話中はステータスエリアのアバター（手前の自分・相手・半透明の
   プレースホルダ含む）を隠す。会話の立ち絵（#eidos-dialogue-panel内の別要素）と重複して
   不自然に見えるため。eidos-dialogue-open は eidos-dialogue-ui.js が開閉時に付け外しする。 */
body.eidos-dialogue-open .player-avatar,
body.eidos-dialogue-open .self-status-large-avatar,
body.eidos-dialogue-open .self-status-large-avatar-ghost {
  display: none;
}
/* ユーザー報告2026-08-15: 会話（特に導入SCENE1）の背後に見えている自分の手札が邪魔なので、
   会話中は手札も隠す。盤面下の手札(.zone-bottom .hand-area)と、手札固定オプション時の
   固定オーバーレイ(#self-hand-overlay)の両方。会話中は盤面操作をロックしているため手札を
   隠しても支障はない。 */
body.eidos-dialogue-open .zone-bottom .hand-area,
body.eidos-dialogue-open #self-hand-overlay {
  display: none;
}
.eidos-dialogue-hint {
  margin-top: 0.5rem;
  font-size: 0.72rem;
  color: #94a3b8;
  text-align: right;
  /* 選択肢が出る時と同じ高さを占め、フッターの高さを一定に保つ（窓の上下ブレ防止）。 */
  min-height: 2.4rem;
  display: flex;
  align-items: center;
  justify-content: flex-end;
}
@media (max-width: 768px) {
  .eidos-dialogue-stage {
    height: min(40vh, 20rem);
  }
  .eidos-dialogue-portrait {
    width: 46vw;
  }
  .eidos-dialogue-sept {
    width: 9rem;
    height: 9rem;
  }
  .eidos-dialogue-body {
    width: 100vw;
    border-radius: 0;
    min-height: 7rem;
    padding: 0.7rem 0.9rem 0.9rem;
  }
  .eidos-dialogue-text {
    font-size: 1rem;
    /* 2行分(1rem×1.6×2=3.2rem)で固定して行数によるブレを無くす（デスクトップと同方針）。 */
    min-height: 3.2rem;
  }
}
/* ライトテーマ: 会話パネルもアイボリー地＋紫アクセントに（他モーダルと系統を合わせる）。 */
body.theme-light .eidos-dialogue-body {
  background: linear-gradient(160deg, #fffdf7, #f3edff);
  color: #33304a;
}
body.theme-light .eidos-dialogue-name {
  color: #6d28d9;
}
body.theme-light .eidos-dialogue-hint {
  color: #6b6484;
}

/* ライトテーマ（body.theme-light）: ログインボーナスのモーダルがダークのままだったので、他の
   ライトモーダル（#contact-result-modal等）と同じアイボリー地＋ゴールド枠＋濃い文字へ
   （ユーザー報告2026-08-08）。金額/タイトルのゴールド系文字はライト地でも読めるためそのまま、
   薄すぎた注記文字だけ濃いめに上書きする。 */
body.theme-light #daily-bonus-modal {
  background: linear-gradient(160deg, #fffdf7, #f6efdd);
  border-color: rgba(176, 141, 63, 0.6);
  color: #3a3326;
}
body.theme-light .daily-bonus-modal-title {
  color: #b8860b;
}
body.theme-light .daily-bonus-modal-amount {
  color: #a9740a;
}
body.theme-light .daily-bonus-modal-note {
  color: #6b5f47;
}
#daily-bonus-toast {
  position: fixed;
  bottom: 1.5rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10500;
  background: rgba(15, 23, 32, 0.95);
  border: 1px solid rgba(250, 204, 21, 0.6);
  border-radius: 999px;
  padding: 0.5rem 1.2rem;
  font-family: sans-serif;
  font-weight: bold;
  color: #fde68a;
  box-shadow: 0 0.6rem 1.6rem rgba(0, 0, 0, 0.5);
  animation: daily-bonus-toast-fade 3.5s ease-in-out forwards;
  pointer-events: none;
}
@keyframes daily-bonus-toast-fade {
  0% {
    opacity: 0;
    transform: translateX(-50%) translateY(0.5rem);
  }
  10% {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }
  85% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

/* ヘルプページ本体（help.js）。my-page-panelと同じ「画面中央固定・縦スクロール」の
   パターンだが、文章量が多いため横幅は広めにしてある。tutorial.jsの説明文をそのまま
   読み物として並べるだけの静的なページ。 */
#help-panel {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(28rem, 92vw);
  max-height: 85vh;
  overflow-y: auto;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.6rem;
  padding: 1.2rem 1.4rem;
  z-index: 2501;
  font-family: sans-serif;
  color: #e2e8f0;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
#help-panel-title {
  font-weight: bold;
  font-size: 1.1rem;
  margin-bottom: 0.8rem;
  padding-right: 1.6rem;
}
/* ユーザー要望「オプション画面にあった『チュートリアルを見る』をヘルプ画面に移設」。 */
#help-panel-tutorial-btn {
  display: block;
  width: 100%;
  padding: 0.55rem;
  margin-bottom: 1rem;
  background: linear-gradient(160deg, #0891b2, #0e7490);
  color: #fff;
  border: none;
  border-radius: 0.4rem;
  cursor: pointer;
  font-size: 0.88rem;
  font-weight: bold;
  font-family: sans-serif;
}
#help-panel-tutorial-btn:hover {
  background: linear-gradient(160deg, #22d3ee, #0891b2);
}
/* ユーザー要望「ヘルプ画面のトップは見やすいように項目を並べ表示し、クリックすると
   そのモーダルが出るようにする」への対応。基本ルール/用語集/よくある質問の3大項目を
   折りたたみ（<details>）にし、中身は項目名だけのボタン一覧にする。 */
.help-index-section {
  margin-bottom: 0.6rem;
}
.help-index-section > summary {
  font-weight: bold;
  font-size: 0.95rem;
  color: #fde68a;
  cursor: pointer;
  padding: 0.3rem 0;
}
.help-index-list {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  padding: 0.4rem 0 0.2rem 0.6rem;
}
/* よくある質問のカテゴリ（ロック/手札/移動/接触/その他）は、大項目の中にさらに
   ネストした折りたたみにする。 */
.help-index-subsection > summary {
  font-weight: bold;
  font-size: 0.85rem;
  color: #93c5fd;
  cursor: pointer;
  padding: 0.25rem 0;
}
.help-index-subsection .help-index-list {
  padding-left: 0.8rem;
}
.help-index-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 0.4rem 0.6rem;
  background: rgba(255, 255, 255, 0.05);
  border: none;
  border-radius: 0.3rem;
  color: #e2e8f0;
  cursor: pointer;
  font-size: 0.82rem;
  font-family: sans-serif;
  line-height: 1.4;
}
.help-index-item:hover {
  background: rgba(255, 255, 255, 0.12);
}

/* ヘルプ内の各項目（基本ルール1件・用語集の1語・FAQ1件）の内容を表示する小さなモーダル。
   ヘルプ画面自身の裏に重ねて出すためz-indexをそれより高くしてある。 */
#help-item-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(26rem, 90vw);
  max-height: 80vh;
  overflow-y: auto;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.6rem;
  padding: 1.2rem 1.4rem;
  z-index: 2511;
  font-family: sans-serif;
  color: #e2e8f0;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
.help-item-modal-title {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: bold;
  font-size: 1rem;
  color: #fde68a;
  margin-bottom: 0.7rem;
  padding-right: 1.6rem;
}
.help-item-modal-icon {
  width: 1.5rem;
  height: 1.5rem;
  object-fit: contain;
  flex-shrink: 0;
}
.help-item-modal-paragraph {
  font-size: 0.85rem;
  line-height: 1.7;
  margin: 0 0 0.5rem 0;
}
.help-item-modal-paragraph:last-child {
  margin-bottom: 0;
}

/* ショップ（shop.js、ユーザー要望「ゲーム内通貨を実装し、駒スキンやアバター、カード裏面、
   プレイマット背景を購入できるようにする」＋「モーダルではなく画面全体を使って表示
   させよう」）。画面全体を覆うフルスクリーン表示に変更した。上から
   ヘッダー（タイトル・残高・達成率・閉じるボタン、常に見える）→ステータス行→
   タブ→商品グリッド（残り全高、縦スクロール）の縦積み構成。 */
#shop-panel {
  position: fixed;
  inset: 0;
  z-index: 2601;
  display: flex;
  flex-direction: column;
  /* ユーザー要望2026-08-08「陳列がダサい、もっとかっこよく」。上部に暖色の光、全体に
     奥行きのあるビネットを重ねた高級ストア風の背景。 */
  background:
    radial-gradient(1200px 420px at 50% -8%, rgba(245, 158, 11, 0.14), transparent 62%),
    radial-gradient(circle at 50% 12%, rgba(30, 41, 59, 0.98), rgba(7, 10, 16, 0.99) 72%);
  font-family: sans-serif;
  color: #e2e8f0;
}
#shop-panel-header {
  display: flex;
  align-items: center;
  gap: 1.2rem;
  flex-wrap: wrap;
  /* 上端のオプションエリア（黒帯）と重ならないよう、ヘッダーを少し下げる（ユーザー報告2026-08-08）。 */
  padding: 3.6rem 1.4rem 1rem;
  border-bottom: 1px solid rgba(148, 163, 184, 0.25);
  position: relative;
}
/* ユーザー要望（続き85）「ショップもほかの全画面ページ（プロフィール/ランキング）に
   合わせ、『戻る』ボタンを左上に設置してください」。#profile-page-back/
   #ranking-page-backと同じ見た目。 */
#shop-panel-back {
  background: none;
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.4rem;
  color: #e2e8f0;
  font-family: sans-serif;
  font-size: 0.8rem;
  padding: 0.4rem 0.8rem;
  cursor: pointer;
}
#shop-panel-back:hover {
  border-color: rgba(56, 189, 248, 0.7);
  background: rgba(56, 189, 248, 0.12);
}
#shop-panel-title {
  font-weight: bold;
  font-size: 1.45rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  letter-spacing: 0.04em;
}
/* タイトル文字をゴールドグラデーションに（アイコン画像は別要素なので影響しない）。 */
#shop-panel-title {
  background: linear-gradient(180deg, #fff3c4, #f59e0b 55%, #b45309);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  text-shadow: 0 0 1.2rem rgba(245, 158, 11, 0.25);
}
#shop-panel-title img {
  width: 1.6rem;
  height: 1.6rem;
  object-fit: contain;
}
#shop-panel-header-info {
  margin-left: auto;
  text-align: right;
}
#shop-panel-balance {
  font-size: 1rem;
  font-weight: bold;
  color: #fde68a;
}
/* ユーザー要望「ショップ画面とマイページにアイテムコンプリート率を表示したい」。 */
#shop-panel-completion {
  font-size: 0.78rem;
  color: #94a3b8;
}
#shop-panel-status {
  font-size: 0.8rem;
  min-height: 1.1rem;
  padding: 0.4rem 1.4rem 0;
  color: #86efac;
}
#shop-panel-status.is-error {
  color: #fca5a5;
}
/* ユーザー要望「ショップのビジュアルはMTGAストアの感じを目指したい。一つ一つの商品を
   目立たせてかっこよく並べたい」への対応。カテゴリはタブ切り替え、中身は大きめの
   画像カードで、所持状況はMTGAの「SALE」等と同じ発想のリボンバッジで見せる
   （旧デザインの小さい密集グリッドから拡大・リッチ化）。 */
#shop-panel-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.8rem 1.4rem;
}
.shop-tab {
  padding: 0.45rem 1rem;
  background: rgba(255, 255, 255, 0.05);
  color: #cbd5e1;
  border: 1px solid rgba(148, 163, 184, 0.3);
  border-radius: 999px;
  cursor: pointer;
  font-size: 0.85rem;
  font-family: sans-serif;
  white-space: nowrap;
}
.shop-tab:hover {
  background: rgba(255, 255, 255, 0.1);
}
.shop-tab.is-active {
  background: linear-gradient(160deg, #f59e0b, #b45309);
  color: #fff;
  border-color: transparent;
  font-weight: bold;
  box-shadow: 0 0 0.9rem rgba(245, 158, 11, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.35);
}
/* ユーザー要望2026-08-08「MTGAストアのように横スクロールで大きな商品カードを並べたい」。
   縦グリッド→横1列のスクロール帯にする（各カテゴリ内で左右にスクロール）。 */
#shop-panel-grid {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: row;
  align-items: stretch;
  overflow-x: auto;
  overflow-y: hidden;
  gap: 2rem;
  padding: 2rem 2.4rem 2.6rem;
  /* scroll-snapは横スクロールを不安定にしていたため撤去（ユーザー報告2026-08-08）。掴んで
     ドラッグ（shop.js wireHorizontalScroll）で自由に流せるよう cursor:grab に。 */
  cursor: grab;
  user-select: none;
}
#shop-panel-grid.is-dragging {
  cursor: grabbing;
}
/* 画像ドラッグ位置調整モード（body.shop-adjust-mode、admin.js/shop.js）: カードをドラッグで
   画像/背景を動かす。購入・スウォッチのクリックは無効化して誤操作を防ぐ。 */
body.shop-adjust-mode #shop-panel-grid {
  cursor: default;
}
body.shop-adjust-mode .shop-item-card {
  cursor: move;
}
body.shop-adjust-mode .shop-item-buy,
body.shop-adjust-mode .shop-item-color-swatch {
  pointer-events: none;
}
.shop-item-card {
  position: relative;
  /* 横スクロール用に固定幅・縮まない。MTGA風にゆったり大きめ。 */
  flex: 0 0 17rem;
  width: 17rem;
  display: flex;
  flex-direction: column;
  background: linear-gradient(160deg, rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0.02));
  border: 1px solid rgba(148, 163, 184, 0.28);
  border-radius: 0.9rem;
  padding: 0.9rem;
  gap: 0.7rem;
  overflow: hidden;
  box-shadow: 0 0.4rem 1rem rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.08);
  transition: transform 0.16s ease-out, box-shadow 0.16s ease-out, border-color 0.16s ease-out;
}
/* 商品背景（ユーザー案）: 商品画像そのものを拡大・半透明でカード背面に敷く。ユーザー要望2026-08-08で
   「ぼかさない」「縦長にしない（＝coverの切り抜きで縦に伸びて見えていた→containで自然な比率）」に変更。
   下側（ラベル・価格）に向けてマスクでフェードし、文字を読みやすく保つ。 */
.shop-item-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-size: contain;
  background-position: center center;
  background-repeat: no-repeat;
  /* 位置・サイズ・回転は管理者モードで全商品連動調整（--shop-bg-*、ユーザー要望2026-08-08）。
     カードは overflow:hidden なので、はみ出しは商品枠でわざと見切れる。 */
  transform: translate(var(--shop-bg-x, 0rem), var(--shop-bg-y, 0rem)) scale(var(--shop-bg-scale, 1.35))
    rotate(var(--shop-bg-rotate, 0deg));
  transform-origin: center center;
  opacity: 0.42;
  -webkit-mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.95) 0%, rgba(0, 0, 0, 0.5) 46%, transparent 80%);
  mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.95) 0%, rgba(0, 0, 0, 0.5) 46%, transparent 80%);
  pointer-events: none;
}
.shop-item-card:hover .shop-item-bg {
  opacity: 0.55;
}
/* 【続き497】ペット商品の背面だけ水彩画の1枚絵に差し替える（shop.js）。1枚絵は
   スプライトより情報量が多いので、少し薄めにしてカードの文字を邪魔しない。 */
.shop-item-bg.is-portrait {
  opacity: 0.32;
}
.shop-item-card:hover .shop-item-bg.is-portrait {
  opacity: 0.46;
}
/* 中身（サムネ・ラベル・価格・スウォッチ）は背景より前面へ。 */
.shop-item-card > *:not(.shop-item-bg) {
  position: relative;
  z-index: 1;
}
/* カード上端のゴールドの飾りバー（高級ストア風の見出し帯）。 */
.shop-item-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: linear-gradient(90deg, transparent, rgba(250, 204, 21, 0.85), transparent);
  opacity: 0.55;
  transition: opacity 0.16s ease-out;
}
.shop-item-card:hover {
  transform: translateY(-0.35rem);
  border-color: rgba(250, 204, 21, 0.65);
  box-shadow: 0 0.9rem 2rem rgba(0, 0, 0, 0.55), 0 0 1.4rem rgba(245, 158, 11, 0.25);
}
.shop-item-card:hover::before {
  opacity: 1;
}
.shop-item-thumb {
  position: relative;
  /* ユーザー要望2026-08-08「商品枠は正方形ではなく縦長の全範囲に」。1:1固定をやめ、カードの
     縦方向の余白いっぱいまで伸ばす（下のラベル・価格を除いた全域）。商品画像はこの縦長枠の
     中央に収める（img: object-fit:contain）ので、縦長枠の中央に配置できる。 */
  flex: 1 1 auto;
  min-height: 8rem;
  border-radius: 0.6rem;
  overflow: hidden;
  /* 商品の周りの黒い半透明枠は不要（透明にしてカード背景を透かす）。 */
  background: transparent;
}
.shop-item-thumb img {
  width: 100%;
  height: 100%;
  /* 縦長枠の中央に商品を収める（cropしないので縦長に伸びない）。ユーザー要望2026-08-08。 */
  object-fit: contain;
  object-position: center;
  transition: transform 0.32s ease;
  /* 商品画像の位置・サイズは管理者モードで全商品連動調整（--shop-thumb-*）。thumbは overflow:hidden
     なので、枠からはみ出したぶんは商品枠でわざと見切れる（ユーザー要望2026-08-08）。 */
  transform: translate(var(--shop-thumb-x, 0rem), var(--shop-thumb-y, 0rem)) scale(var(--shop-thumb-scale, 1));
  /* 商品のドロップシャドウ（ユーザー要望2026-08-09）。drop-shadowは透明部分を無視して商品の
     シルエットに影を落とす。強さ/ぼかし/下方向オフセットは管理者モードで調整（0で影なし）。 */
  filter: drop-shadow(
    0 var(--shop-thumb-shadow-y, 0.35rem) var(--shop-thumb-shadow-blur, 0.5rem)
      rgba(0, 0, 0, var(--shop-thumb-shadow-alpha, 0.4))
  );
}
/* ホバーで画像を少しズーム（陳列に動きを出す）。調整後のサイズに1.06倍を上乗せ。 */
.shop-item-card:hover .shop-item-thumb img {
  transform: translate(var(--shop-thumb-x, 0rem), var(--shop-thumb-y, 0rem))
    scale(calc(var(--shop-thumb-scale, 1) * 1.06));
}

/* 駒スキンを立方体（キューブ）で見せる（ユーザー要望2026-08-12）。同じ駒画像を上・前・右の
   3面に貼り、面ごとの明度差で立体的に。色スウォッチで3面ともまとめて差し替わる（shop.js）。 */
.shop-item-thumb.shop-thumb-cube {
  display: flex;
  align-items: center;
  justify-content: center;
  perspective: 820px;
  /* 影は3Dを潰さないよう、perspective親(=この要素、preserve-3dではない)側に置く。
     preserve-3d要素に filter を付けると仕様上flatに潰れ、キューブが1面になる（不具合報告）。 */
  filter: drop-shadow(0 0.55rem 0.5rem rgba(0, 0, 0, 0.4));
}
.shop-cube {
  position: relative;
  width: 4.8rem;
  height: 4.8rem;
  transform-style: preserve-3d;
  transform: rotateX(-24deg) rotateY(-36deg);
  transition: transform 0.4s ease;
}
.shop-item-card:hover .shop-cube {
  transform: rotateX(-20deg) rotateY(-52deg);
}
.shop-cube-face {
  position: absolute;
  width: 4.8rem;
  height: 4.8rem;
  background-size: cover;
  background-position: center;
  border-radius: 0.3rem;
  backface-visibility: hidden;
}
.shop-cube-front {
  transform: translateZ(2.4rem);
  filter: brightness(0.92);
}
.shop-cube-top {
  transform: rotateX(90deg) translateZ(2.4rem);
  filter: brightness(1.15);
}
.shop-cube-right {
  transform: rotateY(90deg) translateZ(2.4rem);
  filter: brightness(0.64);
}

/* ペット商品: 画像クリックでアクション再生（shop.js attachPetPlayground）。クリックできる
   ことが伝わるよう指カーソル＋ホバーで軽く拡大。jumpの時だけ .shop-pet-bounce でひと跳ね。 */
.shop-item-thumb.shop-thumb-pet img {
  cursor: pointer;
}
.shop-pet-bounce {
  animation: shop-pet-bounce 0.62s ease;
}
@keyframes shop-pet-bounce {
  0% { transform: translate(var(--shop-thumb-x, 0rem), var(--shop-thumb-y, 0rem)) scale(var(--shop-thumb-scale, 1)) translateY(0); }
  35% { transform: translate(var(--shop-thumb-x, 0rem), var(--shop-thumb-y, 0rem)) scale(var(--shop-thumb-scale, 1)) translateY(-26%); }
  60% { transform: translate(var(--shop-thumb-x, 0rem), var(--shop-thumb-y, 0rem)) scale(var(--shop-thumb-scale, 1)) translateY(0); }
  75% { transform: translate(var(--shop-thumb-x, 0rem), var(--shop-thumb-y, 0rem)) scale(var(--shop-thumb-scale, 1)) translateY(-10%); }
  100% { transform: translate(var(--shop-thumb-x, 0rem), var(--shop-thumb-y, 0rem)) scale(var(--shop-thumb-scale, 1)) translateY(0); }
}

/* マイデッキ枠(+2): 箱風のミニ画像を2つ並べる（ユーザー要望2026-08-12。＋2枠＝箱2つ）。 */
.shop-item-thumb.shop-thumb-slotboxes {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.7rem;
  perspective: 720px;
}
.shop-slot-box {
  position: relative;
  width: 3.1rem;
  height: 3.9rem;
  border-radius: 0.35rem;
  background: linear-gradient(160deg, #2a3a52, #0e1626);
  border: 1px solid rgba(148, 163, 184, 0.4);
  transform: rotateY(-17deg) rotateX(4deg);
  box-shadow: 3px 0 0 #101a2b, 6px 1px 0 #0a1120, -6px 8px 10px rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
}
.shop-slot-box:nth-child(2) {
  transform: rotateY(-17deg) rotateX(4deg) translateY(0.32rem);
}
.shop-slot-box::before {
  /* 少し開いた蓋風の上端。 */
  content: "";
  position: absolute;
  left: 6%;
  right: 6%;
  top: -0.28rem;
  height: 0.5rem;
  border-radius: 0.2rem 0.2rem 0 0;
  background: linear-gradient(180deg, #3a4d6b, #223046);
  border: 1px solid rgba(148, 163, 184, 0.45);
  border-bottom: none;
}
.shop-slot-box-face {
  width: 66%;
  height: 66%;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  opacity: 0.92;
}
/* ホバーで光沢が横切る演出（sheen）。バッジ類より下に敷く。 */
.shop-item-thumb::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(115deg, transparent 32%, rgba(255, 255, 255, 0.28) 46%, transparent 60%);
  transform: translateX(-130%);
  transition: transform 0.6s ease;
}
.shop-item-card:hover .shop-item-thumb::after {
  transform: translateX(130%);
}
/* ユーザー要望（続き74）「未所持のものをグレー表示しないでください。購入者は色味も
   見たいと思うので」。以前は.is-lockedにgrayscale+暗化フィルターをかけていたが、
   削除した。未所持であることは🔒バッジ（.shop-item-thumb-lock）だけで十分伝わる。 */
.shop-item-thumb-lock {
  position: absolute;
  top: 0.3rem;
  right: 0.4rem;
  z-index: 2; /* 光沢(sheen, z-index:1)より前面 */
  font-size: 1.1rem;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.8));
  /* 南京錠マークの位置・サイズは管理者モードで全商品連動調整（--shop-badge-*）。 */
  transform: translate(var(--shop-badge-x, 0rem), var(--shop-badge-y, 0rem)) scale(var(--shop-badge-scale, 1));
  transform-origin: top right;
}
/* MTGAストアの左上リボン（「大人気」等）と同じ発想の所持状況バッジ。 */
.shop-item-ribbon {
  position: absolute;
  top: 0.5rem;
  left: -0.4rem;
  z-index: 2; /* 光沢(sheen, z-index:1)より前面 */
  padding: 0.15rem 0.6rem;
  font-size: 0.68rem;
  font-weight: bold;
  color: #0f1520;
  border-radius: 0.2rem;
  box-shadow: 0 0.2rem 0.4rem rgba(0, 0, 0, 0.5);
  white-space: nowrap;
  /* 無料/所持済みリボンの位置・サイズも南京錠と同じ --shop-badge-* で全商品連動調整。 */
  transform: translate(var(--shop-badge-x, 0rem), var(--shop-badge-y, 0rem)) scale(var(--shop-badge-scale, 1));
  transform-origin: top left;
}
.shop-item-ribbon.is-free {
  background: linear-gradient(160deg, #86efac, #16a34a);
  color: #06280f;
}
.shop-item-ribbon.is-owned {
  background: linear-gradient(160deg, #93c5fd, #2563eb);
  color: #0b1f45;
}
/* 駒スキンの色スウォッチ（続き84、shop.js参照）。ユーザー報告「駒スキンは1種類に
   つき7色あるが今は赤しか見えていない」への対応。サムネイル下に7色の小さな丸を
   並べ、クリックでそのカードの画像だけを差し替える（見た目の確認用、実際の色は
   ファーストカードで決まる）。 */
.shop-item-color-swatches {
  display: flex;
  justify-content: center;
  gap: 0.3rem;
}
.shop-item-color-swatch {
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 50%;
  background: var(--swatch-color);
  border: 2px solid rgba(255, 255, 255, 0.25);
  padding: 0;
  cursor: pointer;
}
.shop-item-color-swatch:hover {
  border-color: rgba(255, 255, 255, 0.55);
}
.shop-item-color-swatch.is-active {
  border-color: #fff;
  box-shadow: 0 0 0 0.15rem rgba(255, 255, 255, 0.3);
}
.shop-item-card-label {
  font-size: 0.85rem;
  font-weight: bold;
  text-align: center;
  line-height: 1.3;
  min-height: 2.2rem;
}
.shop-item-card-footer {
  display: flex;
  justify-content: center;
  min-height: 1.9rem;
}
.shop-item-buy {
  position: relative;
  overflow: hidden;
  padding: 0.5rem 0.7rem;
  background: linear-gradient(160deg, #f7b733, #b45309);
  color: #fff;
  border: 1px solid rgba(255, 220, 150, 0.5);
  border-radius: 0.5rem;
  cursor: pointer;
  font-size: 0.9rem;
  font-weight: bold;
  letter-spacing: 0.02em;
  white-space: nowrap;
  font-family: sans-serif;
  width: 100%;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.35);
  box-shadow: 0 0.3rem 0.7rem rgba(180, 83, 9, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.4);
  /* 金額ボタンの位置・サイズは管理者モードで全商品連動調整（--shop-price-*）。transformは
     レイアウトに影響しないので、動かしても隣接要素はズレない（ユーザー要望2026-08-08）。 */
  transform: translate(var(--shop-price-x, 0rem), var(--shop-price-y, 0rem)) scale(var(--shop-price-scale, 1));
  transform-origin: center;
}
/* コイン風のツヤ（上半分のハイライト）。 */
.shop-item-buy::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 50%;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.38), transparent);
  pointer-events: none;
}
.shop-item-buy:hover {
  background: linear-gradient(160deg, #fcd34d, #d97706);
  box-shadow: 0 0.4rem 0.9rem rgba(180, 83, 9, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.5);
}
.shop-item-buy:disabled {
  opacity: 0.5;
  cursor: default;
}

/* カード開発モード（card-dev-mode.js、ユーザー要望「管理者モードを潜っていくのは
   大変なので、オプションの直下に専用のモードとして追加してほしい」への対応）。
   ユーザー要望「毎回開くのが面倒。位置・サイズをエクスプローラーみたいに動かせるように」
   への対応で、my-page-panel等と違い中央固定ではなく、タイトルバードラッグでの移動
   （admin-panelと同じtoStageLocalRect/stageDelta方式）とネイティブのresize:bothに
   対応させている。初期位置は画面中央寄せだが、left:50%+transform:translateXのみ
   （縦方向はtopの固定値）にしてあるのは、ドラッグ開始時にtransformを外して
   left/topの絶対値に切り替える処理と衝突しないようにするため。 */
#card-dev-mode-panel {
  position: fixed;
  top: 4rem;
  left: 50%;
  transform: translateX(-50%);
  width: min(34rem, 92vw);
  height: min(38rem, 85vh);
  min-width: 20rem;
  min-height: 14rem;
  max-width: 96vw;
  max-height: 92vh;
  overflow: auto;
  resize: both;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.6rem;
  padding: 1.2rem 1.4rem;
  z-index: 2701;
  font-family: sans-serif;
  color: #e2e8f0;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
#card-dev-mode-title {
  font-weight: bold;
  font-size: 1.1rem;
  margin-bottom: 0.6rem;
  padding-right: 3.4rem;
  cursor: move;
  user-select: none;
}
#card-dev-mode-minimize-btn {
  position: absolute;
  top: 0.6rem;
  right: 2.6rem;
  width: 1.8rem;
  height: 1.8rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  border: none;
  color: #e2e8f0;
  cursor: pointer;
  font-size: 1rem;
  display: flex;
  align-items: center;
  justify-content: center;
}
#card-dev-mode-minimize-btn:hover {
  background: rgba(255, 255, 255, 0.2);
}
/* ユーザー要望「開発モードウィンドウがどこに縮小されたかわからない。画面左上のゲーム
   タイトルテキストの下に固定して」への対応。#game-title（top:1rem; left:1rem）の
   すぐ下に固定表示する（サイズ変更・移動しても常にここに戻ってくるので迷わない）。 */
#card-dev-mode-mini-icon {
  position: fixed;
  top: 3.1rem;
  left: 1rem;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 50%;
  background: rgba(15, 23, 32, 0.95);
  border: 1px solid rgba(250, 204, 21, 0.6);
  color: #fde68a;
  font-size: 1.2rem;
  display: none;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 2701;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
}
#card-dev-mode-intro {
  font-size: 0.78rem;
  color: #94a3b8;
  line-height: 1.5;
  margin-bottom: 0.8rem;
}
.card-dev-mode-row {
  margin-bottom: 0.7rem;
  padding: 0.5rem 0.6rem;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(148, 163, 184, 0.25);
  border-radius: 0.3rem;
}
.card-dev-mode-row-name {
  font-weight: bold;
  margin-bottom: 0.3rem;
  font-size: 0.85rem;
}
.card-dev-mode-row-line {
  font-size: 0.78rem;
  line-height: 1.5;
}
.card-dev-mode-row-label {
  color: #94a3b8;
}
.card-dev-mode-row-label.is-generated {
  color: #86efac;
}
.card-dev-mode-summon-btn {
  margin-top: 0.4rem;
  padding: 0.3rem 0.6rem;
  font-size: 0.75rem;
  background: rgba(250, 204, 21, 0.15);
  border: 1px solid rgba(250, 204, 21, 0.5);
  border-radius: 0.35rem;
  color: #fde68a;
  cursor: pointer;
}
.card-dev-mode-summon-btn:hover {
  background: rgba(250, 204, 21, 0.28);
}

/* ユーザー要望「オプションも全画面表示にしよう」。以前は右上から吊り下がるドロップダウン
   だったが、他の全画面ページ（お知らせ等）と揃えて全画面(inset:0)にする。中身は読みやすい
   幅で中央寄せし、上部に「← 戻る」ボタンを置く（options-menu.js renderContent）。 */
#options-menu-panel {
  position: fixed;
  inset: 0;
  z-index: 1002;
  background: rgba(15, 23, 32, 0.98);
  border: none;
  border-radius: 0;
  padding: 1.2rem clamp(1rem, 5vw, 5rem) 2rem;
  overflow-y: auto;
  box-sizing: border-box;
}
#options-menu-panel > * {
  max-width: 42rem;
  margin-left: auto;
  margin-right: auto;
}
#options-menu-back {
  display: inline-block;
  background: none;
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.4rem;
  color: #e2e8f0;
  font-family: sans-serif;
  font-size: 0.85rem;
  padding: 0.45rem 0.9rem;
  cursor: pointer;
  margin-bottom: 0.9rem;
}
#options-menu-back:hover {
  border-color: rgba(56, 189, 248, 0.7);
}
body.theme-light #options-menu-back {
  color: #3a3326;
  border-color: rgba(176, 141, 63, 0.5);
}
/* ユーザー要望「オプションマークはタイトル画面から常に表示させてほしい（BGM音量など
   重要な設定が含まれているため）」。#opening-screen（z-index:50000）より確実に高くする。
   対局中の通常のz-index（900/1002、上のコメント参照）は崩さないよう、オープニング画面が
   実際に表示されている間だけ（body.opening-screen-active、opening-screen.js参照）
   スコープしてある。
   ハマりどころ: #options-menu-buttonは「オプションエリア」グループ化（#option-area、
   position:fixedで新しいスタッキングコンテキストを作る）により現在は#option-areaの
   子要素になっているため、子自身にどれだけ高いz-indexを設定しても親(#option-area)の
   スタッキング順位（通常900）を飛び越えて#opening-screen(50000)より手前には出せない。
   そのため引き上げ対象を親の#option-area自体に変更した。ただし#option-areaには
   ヘルプ・通貨表示・マイページも同居しているため、そのままではタイトル画面中に
   この3つまで一緒に表に出てきてしまう（従来は個別のz-index:900のままなので
   #opening-screenの裏に隠れて見えていなかった）。それを防ぐため、この3つだけは
   タイトル画面表示中はdisplay:noneで明示的に隠し、見た目を従来通りに保っている
   （ターン・ラウンド数表示は元々turnNumberがnullの間display:noneなので対象外で問題ない）。 */
body.opening-screen-active #option-area {
  z-index: 50100;
}
body.opening-screen-active #help-button,
body.opening-screen-active #discord-link-button,
body.opening-screen-active #board-view-toggle-button,
body.opening-screen-active #fullscreen-toggle-button,
body.opening-screen-active #currency-display,
body.opening-screen-active #self-status-online,
body.opening-screen-active #my-page-button,
body.opening-screen-active #ranking-page-button {
  display: none;
}
body.opening-screen-active #options-menu-panel {
  z-index: 50101;
}
/* #self-status-onlineは #option-area #self-status-online（id2つ）で位置指定しているため、
   上の一括hide（body.opening-screen-active #self-status-online＝id1つ）ではdisplay:flexに
   負けてタイトル/オープニングでも表示されてしまっていた（ユーザー報告）。同等以上の詳細度で
   隠し、表示はホーム画面以降からにする。 */
/* 不具合報告ボタン(#self-status-bug-report)だけはタイトル画面でも出す（ユーザー要望・不具合
   報告#420「不具合報告ボタンは設定アイコン同样にタイトル画面から表示させるようにしていたのに
   いつのまにか表示されなくなってます」2026-10-18）。このボタンはbody直下のz-index:100055なので、
   #opening-screen(50000)より手前に出る。起動時の案内モーダル(100060)より後ろなので、あちらが
   出ている間は覆われる＝従来どおり。 */
body.opening-screen-active #option-area #self-status-online,
body.opening-screen-active #option-area #self-status-action-log {
  display: none;
}
/* ユーザー要望（続き75）「ホーム画面やプロフ全画面でも上のオプションエリアのアイコン等は
   表示していてください」。opening-screen-activeと同じ「#option-area自体のz-indexを
   引き上げる」手法だが、こちらはヘルプ/通貨表示/マイページを隠さない（オープニング画面と
   違い、ホーム画面等では対局前でもこれらを見せて構わないため）。ホーム画面
   (home-screen.js)・プロフィール全画面(profile-page.js)・ランキング全画面
   (ranking-page.js)はいずれもz-index:1500の共通の器のため、この1つのbodyクラスを
   共有して使う（開いている画面が開く時に付与、閉じる時に外す。3つは同時に複数開かない
   設計のため、単純なON/OFFで足りる）。 */
/* 全画面ページ（ホーム/ランキング/図鑑=z1500）を開いている間、オプションエリア（上端の
   アイコン列）を前面に出す。ただしプロフィール全画面(#profile-page)は不具合#84でz-index:2650へ
   引き上げてあるため、1600では負けてオプションエリアがマイページの裏に隠れていた（ユーザー
   報告2026-08-16）。ショップ用と同じ2700まで上げ、全画面ページ（プロフィール2650含む）すべて
   より前面にする。#option-area自体はpointer-events:none（子アイコンだけauto）なのでクリックは
   妨げない。 */
body.full-screen-page-active #option-area {
  z-index: 2700;
}
body.full-screen-page-active #options-menu-panel {
  z-index: 2701;
}
/* ショップ(#shop-panel, z-index:2601)は他の全画面ページ(1500)より高いので、開いている間だけ
   オプションエリア（上端の黒帯）をショップより前面へ引き上げて見えるようにする（ユーザー報告
   2026-08-08）。#option-area自体は pointer-events:none（子アイコンだけauto）なので盤面/ショップの
   クリックは妨げない。 */
body.shop-open #option-area {
  z-index: 2700;
}
body.shop-open #options-menu-panel {
  z-index: 2701;
}
.options-menu-item {
  display: block;
  width: 100%;
  padding: 0.4rem 0.6rem;
  background: transparent;
  color: #e2e8f0;
  border: none;
  text-align: left;
  cursor: pointer;
  font-size: 0.75rem;
  font-family: sans-serif;
  border-radius: 0.25rem;
  white-space: nowrap;
}
.options-menu-item:hover {
  background: rgba(56, 189, 248, 0.2);
}
.options-menu-section-title {
  font-weight: bold;
  color: #7dd3fc;
  font-size: 0.72rem;
  padding: 0.3rem 0.6rem 0.2rem;
}
/* 続き299: 項目名を「何が起きるか」の分かりやすい言い方へ書き換えた結果、1行に収まらない
   ものが出てきた。以前は white-space: nowrap で横にはみ出していたので、折り返して読める
   ようにする（チェックボックス自体は縮まないよう flex:none、1行目に揃うよう上寄せ）。 */
.options-menu-checkbox-row {
  display: flex;
  align-items: flex-start;
  gap: 0.4rem;
  padding: 0.2rem 0.6rem 0.4rem;
  cursor: pointer;
  font-size: 0.75rem;
  line-height: 1.45;
  white-space: normal;
}
.options-menu-checkbox-row > input[type="checkbox"] {
  flex: none;
  margin-top: 0.15rem;
}
.options-menu-divider {
  border-top: 1px solid rgba(148, 163, 184, 0.25);
  margin: 0.3rem 0;
}
/* 項目数が増えて縦に長くなりすぎた基本設定を、性質の近い項目ごとに開閉できるように
   まとめる（admin.jsの<details>と同じ考え方）。デフォルトは折りたたみ。 */
.options-menu-details {
  margin: 0.2rem 0.3rem;
  border: 1px solid rgba(148, 163, 184, 0.2);
  border-radius: 0.3rem;
}
.options-menu-details > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.4rem;
  padding: 0.3rem 0.5rem;
  font-size: 0.72rem;
  font-weight: bold;
  color: #cbd5e1;
  cursor: pointer;
  list-style: none;
}
.options-menu-details > summary::-webkit-details-marker {
  display: none;
}
.options-menu-details-title::before {
  content: "▸ ";
  color: #7dd3fc;
}
.options-menu-details[open] .options-menu-details-title::before {
  content: "▾ ";
}
/* セクションごとの「戻す」ボタン（続き64、ユーザー要望「セクションごとの初期設定に
   戻すボタンも用意する」）。 */
.options-menu-section-reset-btn {
  flex: none;
  font-size: 0.65rem;
  font-weight: normal;
  padding: 0.1rem 0.4rem;
  border-radius: 0.3rem;
  border: 1px solid rgba(148, 163, 184, 0.3);
  background: rgba(255, 255, 255, 0.05);
  color: #94a3b8;
  cursor: pointer;
}
.options-menu-section-reset-btn:hover {
  background: rgba(56, 189, 248, 0.2);
  color: #e2e8f0;
}
.options-menu-details-content {
  padding: 0.1rem 0.2rem 0.4rem;
}
/* 続き299「基本設定のUIを整備」。折りたたまずベタ置きする「よく使う設定」ブロックの見出し。
   折りたたみのsummaryと同じ見た目にしつつ、開閉しないので ▸ の三角は出さない。 */
.options-menu-plain-group-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.4rem;
  margin: 0.5rem 0.3rem 0.1rem;
  padding: 0.3rem 0.3rem 0.2rem;
  border-bottom: 1px solid rgba(148, 163, 184, 0.2);
  font-size: 0.72rem;
  font-weight: bold;
  color: #cbd5e1;
}
.options-menu-plain-group-header .options-menu-details-title::before {
  content: "";
}
/* 続き300: 基本設定 →「詳細設定」ビューへの入口ボタン（ユーザー要望「よく使う設定より下の
   設定は詳細設定というボタンを作り格納するのはどうかな？」）。折りたたみのsummaryとは別物と
   分かるよう、1枚の押せるカードとして見せる。 */
.options-menu-detail-entry {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.15rem;
  width: calc(100% - 0.6rem);
  margin: 0.9rem 0.3rem 0.3rem;
  padding: 0.55rem 0.7rem;
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.4rem;
  background: rgba(255, 255, 255, 0.05);
  color: #e2e8f0;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
/* ===== 【#318】基本設定の1画面目の見た目を揃える（ユーザー報告2026-09-06「なんかスマホの
   基本設定画面の、uiがダサい感じがするのはきのせい？」）====================================
   ダサく見えていた正体は**同じ1画面に4種類の見た目が並んでいた**こと——言語だけ枠なし、
   音量/案内表示は細い枠、詳細設定は塗りつぶしの箱、山札一覧/タイトルに戻るは枠なしで
   左端の位置も違う。中身は変えず「同じ形のカードが縦に並ぶ」だけに揃える。
   detailMode（詳細設定ビュー）は項目が多く性質も違うので対象外＝is-basic-view で絞る。 */
#options-menu-panel.is-basic-view > .options-menu-basic-row,
#options-menu-panel.is-basic-view > .options-menu-details,
#options-menu-panel.is-basic-view > .options-menu-detail-entry,
#options-menu-panel.is-basic-view > .options-menu-item {
  box-sizing: border-box;
  width: 100%;
  margin: 0 auto 0.45rem;
  border: 1px solid rgba(148, 163, 184, 0.28);
  border-radius: 0.55rem;
  background: rgba(255, 255, 255, 0.045);
}
#options-menu-panel.is-basic-view > .options-menu-details:hover,
#options-menu-panel.is-basic-view > .options-menu-detail-entry:hover,
#options-menu-panel.is-basic-view > .options-menu-item:hover {
  border-color: rgba(56, 189, 248, 0.55);
  background: rgba(56, 189, 248, 0.1);
}
/* 中身の並びも揃える: 左に名前、右に操作。 */
#options-menu-panel.is-basic-view > .options-menu-item,
#options-menu-panel.is-basic-view > .options-menu-basic-row > .options-menu-volume-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
}
/* 折りたたみの見出しは「名前 … 戻す ›」の順に右へ詰める（space-between のままだと
   3つ目の矢印に押されて「戻す」が行の真ん中に浮いてしまう）。 */
#options-menu-panel.is-basic-view > .options-menu-details > summary {
  justify-content: flex-start;
}

/* 「押すと何かが開く」ものには、右端に同じ形の矢印を出す（押せることが一目で分かる）。 */
#options-menu-panel.is-basic-view > .options-menu-item::after,
#options-menu-panel.is-basic-view > .options-menu-detail-entry::after,
#options-menu-panel.is-basic-view > .options-menu-details > summary::after {
  content: "›";
  margin-left: auto;
  padding-left: 0.4rem;
  color: #7dd3fc;
  opacity: 0.85;
  font-size: 1.35em;
  line-height: 1;
}
#options-menu-panel.is-basic-view > .options-menu-details[open] > summary::after {
  content: "⌄";
}
/* 見出しの行だけは矢印側の margin-left:auto を外す。auto が2つあると余白を**半分ずつ
   分け合う**ので、そのままだと「戻す」が行の真ん中に浮く（実測: 右端まで 124px 残った）。
   ※このルールは上の矢印ルールより**後ろ**に置くこと（同じ詳細度なので順番で決まる）。 */
#options-menu-panel.is-basic-view > .options-menu-details > summary::after {
  margin-left: 0;
}
/* 開閉の三角は右端の矢印と役割が重なるので、基本設定では出さない。 */
#options-menu-panel.is-basic-view .options-menu-details-title::before {
  content: none;
}
/* 詳細設定だけは2行（名前＋説明）なので、矢印は右端の中央に重ねて置く。 */
#options-menu-panel.is-basic-view > .options-menu-detail-entry {
  position: relative;
  padding-right: 2rem;
}
#options-menu-panel.is-basic-view > .options-menu-detail-entry::after {
  position: absolute;
  right: 0.8rem;
  top: 50%;
  transform: translateY(-50%);
  margin-left: 0;
  padding-left: 0;
}
/* 言語の但し書きはカードの中へ（名前と左端をそろえる）。 */
#options-menu-panel.is-basic-view > .options-menu-basic-row > .options-menu-lang-note {
  margin: 0 !important;
  padding: 0 0.6rem 0.5rem;
}
/* 「戻す」は補助的な操作なので控えめに、右端の矢印の手前へ寄せる。 */
#options-menu-panel.is-basic-view .options-menu-section-reset-btn {
  margin-left: auto;
  opacity: 0.75;
}
/* 全部カードになったので区切り線は要らない。 */
#options-menu-panel.is-basic-view > .options-menu-divider {
  display: none;
}

.options-menu-detail-entry:hover {
  border-color: rgba(56, 189, 248, 0.7);
  background: rgba(56, 189, 248, 0.12);
}
.options-menu-detail-entry-title {
  font-size: 0.8rem;
  font-weight: bold;
}
.options-menu-detail-entry-hint {
  font-size: 0.68rem;
  color: #94a3b8;
  line-height: 1.4;
}
/* ユーザー報告2026-08-28「ホーム画面からオプションを開くと上の黒帯が消えちゃってます」。
   #options-menu-panel は inset:0 の全画面ページで、これまで #option-area（上端のアイコン列と
   その装飾帯 #option-area::before）より前面に置いていたため、開いている間だけ帯が隠れ、
   アイコンも半透明パネルの下に沈んで見えていた（ライト配色だと特に目立つ）。オプションを
   開いている間はアイコン列を最前面へ出し（opening/full-screen-page/shopのどのz-index体系より
   上）、代わりにパネルの上余白を帯の高さ分あけて、内容が帯の下に潜らないようにする。
   #option-areaはpointer-events:none（子アイコンだけauto）なので設定項目のクリックは奪わない。 */
body.options-menu-open #option-area {
  z-index: 50200;
}
body.options-menu-open #options-menu-panel {
  padding-top: calc(var(--option-area-band-height, 3rem) + 0.8rem);
}
/* 続き299: セクションの中の小見出し。以前はここが「折りたたみの中の折りたたみ」だったが、
   閉じた箱の中にさらに閉じた箱があると存在に気づけないため、開いたままの小見出しにした。 */
.options-menu-sub-label {
  margin: 0.7rem 0.6rem 0.1rem;
  font-size: 0.68rem;
  font-weight: bold;
  color: #7dd3fc;
}
/* 戦績システム連携カード（続き64、ユーザー要望「戦績システム連携は一番上に強調して
   ください」）。他の折りたたみ設定グループとは別枠にし、常に開いた状態で見える
   ようにする。連携済みの場合はis-linkedでグレー表示にする
   （ユーザー要望「連携済みであったり、戦績システムでプレイヤー承認されていれば
   グレー表示で」）。 */
.options-menu-stats-link-card {
  margin: 0.4rem 0.3rem 0.6rem;
  padding: 0.5rem 0.7rem;
  border: 1px solid rgba(250, 204, 21, 0.4);
  border-radius: 0.4rem;
  background: rgba(250, 204, 21, 0.08);
}
.options-menu-stats-link-card.is-linked {
  border-color: rgba(148, 163, 184, 0.25);
  background: rgba(255, 255, 255, 0.03);
  opacity: 0.75;
}
.options-menu-stats-link-title {
  font-size: 0.78rem;
  font-weight: bold;
  color: #fde68a;
}
.options-menu-stats-link-card.is-linked .options-menu-stats-link-title {
  color: #cbd5e1;
}
.options-menu-volume-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.6rem;
  font-size: 0.75rem;
  white-space: nowrap;
}
.options-menu-volume-row input[type="range"] {
  flex: 1;
  min-width: 0;
}
.options-menu-volume-value {
  min-width: 2.6rem;
  text-align: right;
  opacity: 0.8;
}
/* CPUの速さ等の3択セグメント（options-menu.js buildCpuSpeedRow）。 */
.options-menu-segment {
  display: flex;
  gap: 0.25rem;
  flex: 1;
  justify-content: flex-end;
}
.options-menu-segment-btn {
  font-size: 0.72rem;
  padding: 0.2rem 0.6rem;
  border-radius: 0.3rem;
  border: 1px solid rgba(148, 163, 184, 0.5);
  background: rgba(15, 23, 32, 0.5);
  color: #cbd5e1;
  cursor: pointer;
}
.options-menu-segment-btn.is-selected {
  background: #0891b2;
  border-color: #38bdf8;
  color: #fff;
  font-weight: bold;
}
.options-menu-shortcut-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.25rem 0.6rem;
  font-size: 0.75rem;
  white-space: nowrap;
  border-radius: 0.25rem;
  transition: background 0.3s ease;
}
.options-menu-shortcut-row span:first-child {
  flex: 1;
}
.options-menu-shortcut-row.is-highlighted {
  background: rgba(56, 189, 248, 0.3);
}
.options-menu-shortcut-key {
  padding: 0.2rem 0.5rem;
  background: rgba(255, 255, 255, 0.08);
  color: #e2e8f0;
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.3rem;
  cursor: pointer;
  font-size: 0.7rem;
  font-family: monospace;
}
.options-menu-shortcut-key:hover {
  background: rgba(56, 189, 248, 0.2);
}
.options-menu-shortcut-clear {
  width: 1.3rem;
  height: 1.3rem;
  padding: 0;
  background: rgba(255, 255, 255, 0.08);
  color: #e2e8f0;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  font-size: 0.75rem;
  line-height: 1;
}
.options-menu-shortcut-clear:hover {
  background: rgba(248, 113, 113, 0.3);
}
.options-menu-shortcut-preset {
  display: block;
  width: calc(100% - 1.2rem);
  margin: 0.4rem 0.6rem 0.2rem;
  padding: 0.3rem;
  background: rgba(250, 204, 21, 0.15);
  color: #fde68a;
  border: 1px solid rgba(250, 204, 21, 0.4);
  border-radius: 0.3rem;
  cursor: pointer;
  font-size: 0.72rem;
  font-weight: bold;
  font-family: sans-serif;
}
.options-menu-shortcut-preset:hover {
  background: rgba(250, 204, 21, 0.3);
}

/* ユーザー要望「駒スキンやプレイマット等のアカウントに紐づく設定を初期化する
   ボタンを設置したい」への対応（options-menu.jsのbuildResetAppearanceRow参照）。 */
.options-menu-reset-row {
  margin: 0.5rem 0.6rem 0.3rem;
}
.options-menu-reset-btn {
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding: 0.35rem;
  background: rgba(148, 163, 184, 0.1);
  color: #e2e8f0;
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.3rem;
  cursor: pointer;
  font-size: 0.72rem;
}
.options-menu-reset-btn:hover {
  background: rgba(148, 163, 184, 0.2);
}
.options-menu-reset-btn.is-armed {
  background: rgba(239, 68, 68, 0.2);
  border-color: rgba(239, 68, 68, 0.6);
  color: #fecaca;
}
.options-menu-reset-btn:disabled {
  opacity: 0.6;
  cursor: default;
}
.options-menu-reset-hint {
  font-size: 0.68rem;
  color: #94a3b8;
  margin-top: 0.25rem;
  line-height: 1.4;
}

/* 「1枚ドロー」「盤面拡大」「手札シャッフル」ボタン。#end-turn-buttonの上に積む形で
   同じ右下の並びに配置する（bottom値の実際の適用はplayer-buttons.jsのapplyPositions()が
   ドラッグ並び替えに応じて上書きする。ここはページ読み込み直後の初期値）。見た目は
   共通の.icon-action-button（アイコン+キャプション）に統一した。 */
#draw-button {
  position: fixed;
  bottom: 5rem;
  right: 1.2rem;
  z-index: 1000;
  display: none;
  /* 管理者モードの「アイコン再配置モード」でドラッグした分のズレ。基準位置は変えず
     translate()で上乗せする（icon-rearrange.js参照）。 */
  transform: translate(var(--icon-pos-draw-x, -8rem), var(--icon-pos-draw-y, -4.71rem));
}
/* 「公開ドロー」ボタン。ユーザー指定の通り「1枚ドロー」ボタンの左上に置く
   （#draw-buttonの基準位置bottom:5rem;right:1.2remから見て、上方向・左方向にずらした
   位置をデフォルトにしてある）。1枚ドロー等3ボタンのドラッグ並び替えグループ
   （player-buttons.js）には含めず、#end-turn-button等と同じ独立した固定ボタンとして扱う。 */
#public-draw-button {
  position: fixed;
  bottom: 8.2rem;
  right: 5.5rem;
  z-index: 1000;
  display: none;
  transform: translate(var(--icon-pos-public-draw-x, 0.3rem), var(--icon-pos-public-draw-y, 1rem));
}
#public-draw-button .icon-action-button-icon-wrap {
  width: var(--icon-size-public-draw, 3.5rem);
  height: var(--icon-size-public-draw, 3.5rem);
}
/* ユーザー要望「タブレット専用位置調整に公開ドローボタンを追加」。他のタブレット専用
   オーバーライドと同じ「未設定ならPC値にフォールバック」の仕組み。 */
body.is-touch-device #public-draw-button {
  transform: translate(
    var(--icon-pos-public-draw-touch-x, var(--icon-pos-public-draw-x, 0.3rem)),
    var(--icon-pos-public-draw-touch-y, var(--icon-pos-public-draw-y, 1rem))
  );
}
body.diagnostic-flatten-3d.is-touch-device #public-draw-button {
  transform: translate(
    calc(var(--icon-pos-public-draw-touch-x, var(--icon-pos-public-draw-x, 0.3rem)) + var(--icon-pos-public-draw-flat-x, 0rem)),
    calc(var(--icon-pos-public-draw-touch-y, var(--icon-pos-public-draw-y, 1rem)) + var(--icon-pos-public-draw-flat-y, 0rem))
  );
}
body.is-touch-device #public-draw-button .icon-action-button-icon-wrap {
  width: var(--icon-size-public-draw-touch, var(--icon-size-public-draw, 3.5rem));
  height: var(--icon-size-public-draw-touch, var(--icon-size-public-draw, 3.5rem));
}
#board-zoom-button {
  position: fixed;
  bottom: 8.8rem;
  right: 1.2rem;
  z-index: 1000;
  transform: translate(var(--icon-pos-board-zoom-x, 0rem), var(--icon-pos-board-zoom-y, -0.71rem));
}
#hand-shuffle-button {
  position: fixed;
  bottom: 12.6rem;
  right: 1.2rem;
  z-index: 1000;
  transform: translate(var(--icon-pos-hand-shuffle-x, -7.5rem), var(--icon-pos-hand-shuffle-y, 7.79rem));
}
/* PC/タブレットでUI位置を分けたい場合の対応（#end-turn-button側と同じフォールバック
   チェーン方式）。 */
body.is-touch-device #draw-button {
  transform: translate(
    var(--icon-pos-draw-touch-x, var(--icon-pos-draw-x, -8rem)),
    var(--icon-pos-draw-touch-y, var(--icon-pos-draw-y, -4.71rem))
  );
}
body.is-touch-device #board-zoom-button {
  transform: translate(
    var(--icon-pos-board-zoom-touch-x, var(--icon-pos-board-zoom-x, 0rem)),
    var(--icon-pos-board-zoom-touch-y, var(--icon-pos-board-zoom-y, -0.71rem))
  );
}
body.is-touch-device #hand-shuffle-button {
  transform: translate(
    var(--icon-pos-hand-shuffle-touch-x, var(--icon-pos-hand-shuffle-x, -7.5rem)),
    var(--icon-pos-hand-shuffle-touch-y, var(--icon-pos-hand-shuffle-y, 7.79rem))
  );
}
/* ユーザー要望「タブレット2D位置調整にアイコン位置（手札シャッフル、ターン終了など
   すべて）も追加して」。既存のタッチ用位置に、2D表示専用の上乗せオフセットを足す
   （既定0＝タッチ用の位置から変わらない）。 */
body.diagnostic-flatten-3d.is-touch-device #draw-button {
  transform: translate(
    calc(var(--icon-pos-draw-touch-x, var(--icon-pos-draw-x, -8rem)) + var(--icon-pos-draw-flat-x, 0rem)),
    calc(var(--icon-pos-draw-touch-y, var(--icon-pos-draw-y, -4.71rem)) + var(--icon-pos-draw-flat-y, 0rem))
  );
}
body.diagnostic-flatten-3d.is-touch-device #board-zoom-button {
  transform: translate(
    calc(var(--icon-pos-board-zoom-touch-x, var(--icon-pos-board-zoom-x, 0rem)) + var(--icon-pos-board-zoom-flat-x, 0rem)),
    calc(var(--icon-pos-board-zoom-touch-y, var(--icon-pos-board-zoom-y, -0.71rem)) + var(--icon-pos-board-zoom-flat-y, 0rem))
  );
}
body.diagnostic-flatten-3d.is-touch-device #hand-shuffle-button {
  transform: translate(
    calc(var(--icon-pos-hand-shuffle-touch-x, var(--icon-pos-hand-shuffle-x, -7.5rem)) + var(--icon-pos-hand-shuffle-flat-x, 0rem)),
    calc(var(--icon-pos-hand-shuffle-touch-y, var(--icon-pos-hand-shuffle-y, 7.79rem)) + var(--icon-pos-hand-shuffle-flat-y, 0rem))
  );
}
/* 手札が0〜1枚（シャッフルしても見た目が変わらない）の間は押せなくする。 */
#hand-shuffle-button:disabled .icon-action-button-icon-wrap {
  background: rgba(71, 85, 105, 0.5);
  border-color: rgba(100, 116, 139, 0.5);
}

/* 「プレイヤー用ボタン」（手札シャッフル・盤面拡大・1枚ドロー）をドラッグして並び替える時の
   ゴースト。掴んだボタンのクローンをそのままカーソルに追従させる（player-buttons.js参照）。
   ドラッグ元のボタン自体は掴んでいる間少し薄くして、今どれを動かしているか分かるようにする。 */
.player-button-ghost {
  position: fixed !important;
  z-index: 9999;
  pointer-events: none;
  opacity: 0.85;
}
.is-drag-source {
  opacity: 0.35;
}

/* 駒スキン/カード裏面/プレイマット/背景画像選択モーダル。 */
#piece-skin-modal,
#card-back-skin-modal,
#playmat-picker-modal,
#background-picker-modal,
#pet-picker-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  /* ユーザー要望「着せ替えモーダルが縦に細長いので大きく見やすく」。横幅を広げ、下のグリッドを
     多列化して縦を短く・見やすくする。 */
  width: min(34rem, 92vw);
  /* 駒スキンが8バリエーション(2列4行)に増えたことで、縦に長い画面でないと収まりきらず、
     画面からはみ出て閉じるボタンにも手が届かなくなっていた（ユーザー報告）。
     max-height+overflow-yで、収まらない場合はモーダル自身がスクロールするようにする。 */
  max-height: 85vh;
  overflow-y: auto;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.5rem;
  padding: 1rem;
  z-index: 10002;
  font-family: sans-serif;
  color: #e2e8f0;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
.piece-skin-modal-title {
  font-weight: bold;
  margin-bottom: 0.8rem;
  padding-right: 1.6rem;
}
.piece-skin-modal-grid {
  display: grid;
  /* 固定2列だと縦に細長くなるため、幅に応じて列数が増える可変グリッドにする（ユーザー要望）。 */
  grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr));
  gap: 0.6rem;
}
.piece-skin-swatch {
  padding: 0.4rem;
  background: rgba(255, 255, 255, 0.04);
  border: 2px solid rgba(148, 163, 184, 0.3);
  border-radius: 0.4rem;
  cursor: pointer;
}
.piece-skin-swatch.is-selected {
  border-color: #7dd3fc;
}
.piece-skin-swatch img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 0.25rem;
  display: block;
}
/* ユーザー要望「駒スキン・カード裏面・プレイマット・背景を購入できるようにする」への
   対応。未所持の有料項目はモノクロ気味に沈めて「選べない」ことを示し、右下に価格の
   バッジを重ねる（クリックするとピッカー側がショップを開く）。 */
.piece-skin-swatch {
  position: relative;
}
.piece-skin-swatch.is-locked img {
  filter: grayscale(0.8) brightness(0.6);
}
.piece-skin-swatch-lock {
  position: absolute;
  bottom: 0.2rem;
  right: 0.2rem;
  background: rgba(0, 0, 0, 0.75);
  color: #fde68a;
  font-size: 0.65rem;
  font-weight: bold;
  padding: 0.1rem 0.3rem;
  border-radius: 0.6rem;
  pointer-events: none;
}

/* 自分（A）のターン中、自分の駒だけをその駒自身の色でゆっくり柔らかく発光させる
   （ロックエリア/名前ラベルの手番演出とは別物。--piece-turn-glow-colorはrenderBoardTokens内で
   駒ごとにJSから設定する）。 */
@keyframes piece-own-turn-glow {
  0%, 100% { box-shadow: 0 0 0.25rem 0.05rem var(--piece-turn-glow-color, transparent); }
  50% { box-shadow: 0 0 1rem 0.4rem var(--piece-turn-glow-color, transparent); }
}
.piece.is-my-turn-glow .piece-face {
  animation: piece-own-turn-glow 2.6s ease-in-out infinite;
}
/* ハマりどころ: 手番プレイヤーの駒にカーソルを乗せると、ホバーの水色グロー
   (.hover-active .piece-face)が全く見えなくなっていた。box-shadowは複数ルールの値を
   自動合成しないため、同じ詳細度(0,2,0)の2つのルールが同じ要素に競合した時は、CSS内で
   後に書かれた方（このケースでは.is-my-turn-glowのアニメーション）が丸ごと勝ってしまい、
   ホバー側の静的なbox-shadowが完全に無視されていた。両方のクラスが同時に付く場合専用の、
   より詳細度の高いルール（.hover-active.is-my-turn-glow、詳細度0,3,0）を用意し、
   1つのbox-shadow値の中に両方の光を層として含めることで両立させる。 */
@keyframes piece-own-turn-glow-hover {
  0%, 100% { box-shadow: 0 0 0.4rem 0.12rem rgba(56, 189, 248, 0.9), 0 0 0.25rem 0.05rem var(--piece-turn-glow-color, transparent); }
  50% { box-shadow: 0 0 0.4rem 0.12rem rgba(56, 189, 248, 0.9), 0 0 1rem 0.4rem var(--piece-turn-glow-color, transparent); }
}
.piece.hover-active.is-my-turn-glow .piece-face {
  animation: piece-own-turn-glow-hover 2.6s ease-in-out infinite;
}

/* 基本設定「常時光る演出」トグル(options-menu.js)がオフの間、bodyに付く。ゲーム中ずっと
   動き続けるCSSアニメーション（手番の駒/ロックエリア/名前ラベル/アバターのパルス、
   ロック中カードの周回演出等）だけを止める。一時的な到達/ロック演出（main.jsの
   spawnArrivalBurst/spawnLockStamp、別のトグルで制御）はDOM要素自体が作られなくなる
   方式のためここでは対象外。 */
body.reduce-glow .lock-area.is-turn-player,
body.reduce-glow .label.is-turn-player,
body.reduce-glow .player-avatar.is-turn-player,
body.reduce-glow .piece.is-my-turn-glow .piece-face,
body.reduce-glow .piece.hover-active.is-my-turn-glow .piece-face,
body.reduce-glow .board-card.is-usable-while-locked.effect-shine,
body.reduce-glow .board-card.is-usable-while-locked.effect-shine::after,
body.reduce-glow .board-card.is-usable-while-locked.effect-orbit::before,
body.reduce-glow .board-3d-glow.is-usable-while-locked.effect-shine,
body.reduce-glow .board-3d-glow.is-usable-while-locked.effect-shine::after,
body.reduce-glow .board-3d-glow.is-usable-while-locked.effect-orbit::before {
  animation: none !important;
}
/* ユーザー報告「砂時計ロープのアニメーションが環境によっては重くなる」への対応。
   ロープ本体(.turn-timer-rope-strand)はturn-timer.jsのtick()が200msごとに幅を書き換える
   ため、大きくぼかしたbox-shadow（アニメーションの有無に関わらず）を毎回再描画すること
   自体が負荷の主因になっている。既存の「常時光る演出を無効にする」設定を流用し、
   box-shadow自体の除去・点滅アニメーションの停止・drop-shadow/text-shadowの除去まで
   まとめて行うことで、幅の書き換えだけの軽い描画に留める「簡素バージョン」にする。 */
body.reduce-glow .turn-timer-rope-strand {
  animation: none !important;
  box-shadow: none !important;
}
body.reduce-glow .turn-timer-rope-spark {
  animation: none !important;
}
body.reduce-glow .turn-timer-rope-hourglass {
  filter: none !important;
}
body.reduce-glow .turn-timer-rope-name {
  text-shadow: none !important;
}
/* 続き251（タブレット点滅の残り）: ターン終了ボタン・タイマー警告の「常時パルス」も止める。
   #250で手番グロー（.is-turn-player等）は止めたが、ユーザー報告「flat/iso で自分の手番中、
   移動するまではチラつかない＝移動した後から始まる」で、移動完了時に付く
   #end-turn-button.is-emphasized（青の「そろそろ終了してよい」促しパルス）が残っていたと判明。
   弱いGPUでは、この小さなボタン1つの box-shadow の無限パルスでもコンポジタが回り続け、
   重なった2.5D側面ごと隣接要素（ロックバー・カード）を再合成→ちらつく。タイムアウト警告
   （turn-timer-warning-glow / turn-timer-warning-blink / 基本時間の is-critical 点滅）も同種の
   常時パルスなので合わせて止める。静的な強調 box-shadow は残して「そろそろ終了してよい／警告」の
   視覚的手がかりは維持する（アニメーションだけ止める）。 */
body.reduce-glow #end-turn-button.is-emphasized {
  animation: none !important;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4), 0 0 0 0.2rem rgba(56, 189, 248, 0.7) !important;
}
body.reduce-glow #end-turn-button.turn-timer-warning-glow,
body.reduce-glow #priority-transfer-buttons.turn-timer-warning-glow {
  animation: none !important;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4), 0 0 0 0.2rem rgba(239, 68, 68, 0.85) !important;
}
body.reduce-glow .turn-timer-warning,
body.reduce-glow .turn-timer-base-clock.is-critical .icon-action-button-icon-wrap {
  animation: none !important;
}

/* 自分専用の手札枚数ステータス。他のパネルと違う左下に置き、常時表示にする
   （state.turnPlayerの有無に関わらず、自分の手札は常に自分だけ見えていて良い情報のため）。 */
/* 「駒消し」「カード消し」（誤操作防止、それぞれ独立したトグル）ボタン。タッチ主体の
   端末でのみ表示される（interaction-mode.js参照）。#self-hand-status（左下ステータス）の
   すぐ上に縦に2つ並べる。以前は1つのボタンが4状態を巡回する仕組みだったが、分かりやすさの
   ため独立ボタンに分割した（同時ONはinteraction-mode.js側で禁止している）。 */
#piece-hide-button {
  position: fixed;
  bottom: 5.6rem;
  left: 1.2rem;
  z-index: 1000;
  transform: translate(var(--icon-pos-piece-hide-x, 0rem), var(--icon-pos-piece-hide-y, 0rem));
}
#card-hide-button {
  position: fixed;
  bottom: 9.2rem;
  left: 1.2rem;
  z-index: 1000;
  transform: translate(var(--icon-pos-card-hide-x, 0rem), var(--icon-pos-card-hide-y, 0rem));
}
/* 駒消し/カード消しはタッチ主体の端末でのみ表示されるボタンだが、他のプレイヤー用ボタン
   （手札シャッフル等）と同じ「タブレット専用オーバーライド」の仕組みが無かったため、
   ユーザー要望に合わせて同じフォールバックチェーン方式を追加した。 */
body.is-touch-device #piece-hide-button {
  transform: translate(
    var(--icon-pos-piece-hide-touch-x, var(--icon-pos-piece-hide-x, 0rem)),
    var(--icon-pos-piece-hide-touch-y, var(--icon-pos-piece-hide-y, 0rem))
  );
}
body.is-touch-device #card-hide-button {
  transform: translate(
    var(--icon-pos-card-hide-touch-x, var(--icon-pos-card-hide-x, 0rem)),
    var(--icon-pos-card-hide-touch-y, var(--icon-pos-card-hide-y, 0rem))
  );
}
body.diagnostic-flatten-3d.is-touch-device #piece-hide-button {
  transform: translate(
    calc(var(--icon-pos-piece-hide-touch-x, var(--icon-pos-piece-hide-x, 0rem)) + var(--icon-pos-piece-hide-flat-x, 0rem)),
    calc(var(--icon-pos-piece-hide-touch-y, var(--icon-pos-piece-hide-y, 0rem)) + var(--icon-pos-piece-hide-flat-y, 0rem))
  );
}
body.diagnostic-flatten-3d.is-touch-device #card-hide-button {
  transform: translate(
    calc(var(--icon-pos-card-hide-touch-x, var(--icon-pos-card-hide-x, 0rem)) + var(--icon-pos-card-hide-flat-x, 0rem)),
    calc(var(--icon-pos-card-hide-touch-y, var(--icon-pos-card-hide-y, 0rem)) + var(--icon-pos-card-hide-flat-y, 0rem))
  );
}
body.is-touch-device #piece-hide-button .icon-action-button-icon-wrap {
  width: var(--icon-size-piece-hide-touch, var(--icon-size-piece-hide, 2.6rem));
  height: var(--icon-size-piece-hide-touch, var(--icon-size-piece-hide, 2.6rem));
}
body.is-touch-device #card-hide-button .icon-action-button-icon-wrap {
  width: var(--icon-size-card-hide-touch, var(--icon-size-card-hide, 2.6rem));
  height: var(--icon-size-card-hide-touch, var(--icon-size-card-hide, 2.6rem));
}
/* スマホ専用: 駒消し/カード消しアイコンの一括位置・サイズ調整（管理者モード、ユーザー要望
   2026-08-08）。left/bottomにだけ効かせるので、上のタッチ用transformオフセットとは干渉せず、
   2つのボタンを同じ量だけまとめて動かせる（縦の並び間隔は保たれる）。+Yで上へ・+Xで右へ。
   これらの行はis-touch-deviceのルールより後に来るので、スマホでの同詳細度の上書きが効く。 */
body.is-phone-device #piece-hide-button,
body.is-phone-device #card-hide-button {
  left: calc(1.2rem + var(--eraser-icons-pos-phone-x, 0rem));
}
body.is-phone-device #piece-hide-button {
  bottom: calc(5.6rem + var(--eraser-icons-pos-phone-y, 0rem));
}
body.is-phone-device #card-hide-button {
  bottom: calc(9.2rem + var(--eraser-icons-pos-phone-y, 0rem));
}
/* スマホ専用: 盤面拡大ボタンの位置・大きさ（管理者モード、ユーザー要望2026-09-05・#278
   「駒消しボタン群の左側に持っていきたい」）。上の駒消し／カード消しと同じ作法で right/bottom
   にだけ効かせるので、タッチ用の transform オフセットとは干渉しない。＋Xで右へ・＋Yで上へ
   （右端基準なので、右へ動かす＝right を減らす）。 */
body.is-phone-device #board-zoom-button {
  right: calc(1.2rem - var(--board-zoom-btn-pos-phone-x, 0rem));
  bottom: calc(8.8rem + var(--board-zoom-btn-pos-phone-y, 0rem));
}
body.is-phone-device #board-zoom-button .icon-action-button-icon-wrap {
  width: var(--board-zoom-btn-size-phone, 3.5rem);
  height: var(--board-zoom-btn-size-phone, 3.5rem);
}
body.is-phone-device #piece-hide-button .icon-action-button-icon-wrap,
body.is-phone-device #card-hide-button .icon-action-button-icon-wrap {
  width: var(--eraser-icons-size-phone, 2.6rem);
  height: var(--eraser-icons-size-phone, 2.6rem);
}
@keyframes icon-mode-active-glow {
  0%, 100% { box-shadow: 0 4px 10px rgba(0, 0, 0, 0.4), 0 0 0 0.15rem rgba(250, 204, 21, 0.9); }
  50% { box-shadow: 0 4px 10px rgba(0, 0, 0, 0.4), 0 0 0 0.35rem rgba(250, 204, 21, 0.3); }
}
#piece-hide-button.is-glowing .icon-action-button-icon-wrap,
#card-hide-button.is-glowing .icon-action-button-icon-wrap {
  background: rgba(250, 204, 21, 0.2);
  border-color: rgba(250, 204, 21, 0.85);
  animation: icon-mode-active-glow 1.4s ease-in-out infinite;
}

/* 「誤操作防止」がONの間、対象（駒/カード）をグレー表示にして操作できなくする。
   ハマりどころ: .pieceはtransform-style:preserve-3dの立方体のため、.piece自身や
   その祖先にfilterを付けると3D立方体の壁が高さ0に潰れる既知のバグがある（駒の壁面・
   カード山の側面で過去に踏んだのと同じ）。そのためfilter(grayscale)は3D子孫を持たない
   flatな葉要素(.piece-face、5面それぞれ)にだけ適用し、.piece自身にはpointer-events/
   cursorだけを設定する。既存の陰影用filter(brightness)を持つ面は、上書きで消さないよう
   同じbrightness値と組み合わせておく。 */
body.pieces-interaction-hidden .piece {
  pointer-events: none;
  cursor: default;
}
body.pieces-interaction-hidden .piece-face {
  opacity: 0.4;
}
body.pieces-interaction-hidden .piece-top,
body.pieces-interaction-hidden .piece-wall-front {
  filter: grayscale(1);
}
body.pieces-interaction-hidden .piece-wall-back {
  filter: brightness(0.35) grayscale(1);
}
body.pieces-interaction-hidden .piece-wall-left {
  filter: brightness(0.55) grayscale(1);
}
body.pieces-interaction-hidden .piece-wall-right {
  filter: brightness(0.8) grayscale(1);
}
/* .board-cardは3D子孫を持たない平面のカードのため、直接filterを付けても安全。 */
body.cards-interaction-hidden .board-card {
  opacity: 0.4;
  filter: grayscale(1);
  pointer-events: none;
  cursor: default;
}

/* 管理者モードの「自分専用ステータスエリア」グループ、または自分専用ステータス再配置
   モード（self-status-rearrange.js、ドラッグで位置・ホイールでサイズ）が調整するCSS変数
   （--self-status-icon-gap＝アイコン間隔、--self-status-icon-{piece,cardback,
   playmat,online}-size＝各アイコン個別のサイズ、-pos-x/y＝各アイコン個別の位置微調整、
   --self-status-pos-x/y＝パネル全体の位置、--self-status-info-pos-x/y＝名前・手札枚数の
   位置、--self-status-large-avatar-*＝背面に大きく表示する自分のアバター）を参照する。
   外枠（背景・枠線）はユーザー要望によりビジュアルとしては表示しない
   （#self-hand-status自体は透明な位置決め用のフレックスコンテナのまま）。 */
#self-hand-status {
  position: fixed;
  bottom: 1.2rem;
  left: 1.2rem;
  z-index: 1000;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  font-family: sans-serif;
  color: #e2e8f0;
  transform: translate(var(--self-status-pos-x, 0rem), var(--self-status-pos-y, 0rem));
}
/* ユーザー要望「タブレット専用にステータスエリア群の一括拡大を新設」。アバター・
   4アイコン・名前欄を個別にではなく、パネル全体をまとめて拡大縮小できるようにする。
   パネル自体は画面左下に固定配置(bottom/left)されているため、拡大の基準点を左下に
   合わせ、拡大してもパネルの左下の位置がズレないようにした。PC版はこのCSS変数を
   参照しないため影響しない。 */
body.is-touch-device #self-hand-status {
  /* タブレット専用の一括位置(--self-status-pos-touch-*)＋一括拡大。未設定ならPC位置(--self-status-pos-*)
     へフォールバック（ユーザー要望2026-08-08で位置も調整可能に）。 */
  transform: translate(
      var(--self-status-pos-touch-x, var(--self-status-pos-x, 0rem)),
      var(--self-status-pos-touch-y, var(--self-status-pos-y, 0rem))
    )
    scale(var(--self-status-scale-touch, 1));
  transform-origin: bottom left;
}
/* スマホ専用の一括位置・拡大（ユーザー要望2026-08-08）。is-phone-deviceはis-touch-deviceでもあるため、
   同詳細度のこのルールを後に置いて上書きする。未設定はタブレット値→PC値の順でフォールバック。 */
body.is-phone-device #self-hand-status {
  transform: translate(
      var(--self-status-pos-phone-x, var(--self-status-pos-touch-x, var(--self-status-pos-x, 0rem))),
      var(--self-status-pos-phone-y, var(--self-status-pos-touch-y, var(--self-status-pos-y, 0rem)))
    )
    scale(var(--self-status-scale-phone, var(--self-status-scale-touch, 1)));
  transform-origin: bottom left;
}
/* 背面に大きく表示する自分専用アバター。ステータスエリアの他の要素（アイコン・名前欄）を
   「ラップする」ように大きく表示する演出（ユーザー要望）。以前あった小さい丸型の
   アバターアイコンはこれに統合し撤去した。DOM順でアイコングリッド/名前欄より前に
   置くことで、常にそれらの背面に描画される（preserve-3dが絡まないシンプルな2D要素同士の
   ため、DOM順だけで意図通りのスタッキングになる）。クリックするとアバター選択ピッカーが
   開く。ステータスエリアでは常に右向き（"right"）のバリエーションを表示する
   （main.jsのgetAvatarVariant参照、ユーザー指定）。 */
.self-status-large-avatar {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 1;
  font-size: calc(var(--self-status-large-avatar-size, 12rem) * 0.75);
  line-height: 1;
  cursor: pointer;
  transform: translate(var(--self-status-large-avatar-pos-x, -0.86rem), var(--self-status-large-avatar-pos-y, -5.12rem));
}
.self-status-large-avatar .avatar-image {
  width: var(--self-status-large-avatar-size, 12rem);
  height: var(--self-status-large-avatar-size, 12rem);
  /* Tailwind Preflightのimg{max-width:100%}がwidthをコンテナ幅で頭打ちにするのを解除
     （高さだけ伸びて下へズレるのを防ぐ。ステータスエリアのアバターは明示サイズで管理する）。 */
  max-width: none;
  max-height: none;
}
/* ユーザー要望（最終）「アバターは丸に戻す。ただし丸の“枠線”は見せない＝ドロップシャドウ等は
   不要」。丸く切り抜く(border-radius:50% + object-fit:cover)のは共通.avatar-imageのままでよく、
   “丸枠”の正体は本体(.self-status-large-avatar)に付けていたdrop-shadowが円形の輪郭をなぞって
   いたこと。drop-shadowは既に外してあるので、円形クロップだけ戻せば枠線なしの丸になる。
   （以前の経緯コメント）原因は共通の.avatar-imageに
   かかっている border-radius:50% + object-fit:cover で、国王アバター（炎など不定形の
   イラスト）が円形に切り抜かれ、その円形の輪郭に沿ってdrop-shadowが落ちることで
   「丸い枠」に見えていた。ステータスエリアの巨大アバターだけは円形クロップをやめ、
   イラスト全体を素の形のまま表示する（drop-shadow自体も上のルールから外した）。 */
.self-status-large-avatar .avatar-image {
  border-radius: 50%;
  object-fit: cover;
  /* ユーザー要望「丸く縁どられているのがばれてしまう灰色の背景を消したい（でも背面
     ゴーストは残す）」。アバター画像は背景（灰色の地）が焼き込まれているため、円形クロップの
     ハードな縁が“灰色の円盤の枠”として見えてしまう。縁を放射状マスクでフェードさせて、
     盤面に溶け込ませる（丸の枠線＝灰色の縁を目立たなくする）。中央のキャラクターは残る。 */
  -webkit-mask-image: radial-gradient(circle, #000 60%, rgba(0, 0, 0, 0.35) 74%, transparent 86%);
  mask-image: radial-gradient(circle, #000 60%, rgba(0, 0, 0, 0.35) 74%, transparent 86%);
}
/* ユーザー要望「不透明アバターの背面に、少しずらして半透明のアバターも表示して
   かっこよくしたい」。同じアバター画像をもう1枚、本体の背面に薄く・少しずらして重ねる。
   位置は本体(.self-status-large-avatar)と同じ基準に、ずらし量(--self-status-large-avatar-ghost-*)
   を足す。クリックは本体側で受けるのでこちらは透過させる。 */
.self-status-large-avatar-ghost {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 0;
  line-height: 1;
  pointer-events: none;
  opacity: var(--self-status-large-avatar-ghost-opacity, 0.4);
  transform: translate(
    calc(var(--self-status-large-avatar-pos-x, -0.86rem) + var(--self-status-large-avatar-ghost-offset-x, 1.4rem)),
    calc(var(--self-status-large-avatar-pos-y, -5.12rem) + var(--self-status-large-avatar-ghost-offset-y, -1.1rem))
  );
}
.self-status-large-avatar-ghost .avatar-image {
  /* 背面ゴーストは本体サイズ × 倍率（--...-ghost-scale）で独立にサイズ調整できる（管理者モード）。 */
  width: calc(var(--self-status-large-avatar-size, 12rem) * var(--self-status-large-avatar-ghost-scale, 1));
  height: calc(var(--self-status-large-avatar-size, 12rem) * var(--self-status-large-avatar-ghost-scale, 1));
  /* Tailwind Preflightのimg{max-width:100%}を解除。これが無いと倍率を上げた時に幅だけ
     コンテナ幅で頭打ちになり、高さだけ伸びて画像が下へズレていく（ユーザー報告のバグ）。 */
  max-width: none;
  max-height: none;
  border-radius: 50%;
  object-fit: cover;
  /* 本体と同じく縁をフェードさせ、灰色の円盤の枠が出ないようにする（上記参照）。 */
  -webkit-mask-image: radial-gradient(circle, #000 60%, rgba(0, 0, 0, 0.35) 74%, transparent 86%);
  mask-image: radial-gradient(circle, #000 60%, rgba(0, 0, 0, 0.35) 74%, transparent 86%);
}
/* スマホでは左下の自分アバターのサイズ変数を専用値に差し替える（ユーザー要望「もう少し
   小さくしたい」）。この変数はアバター本体(.avatar-image)・絵文字のfont-size・ランクリング
   のすべての算出元なので、ここを差し替えるだけで全部まとめて縮む。管理者モードの
   「左下アバター サイズ（スマホ）」スライダーで --self-status-large-avatar-size-phone を調整可能。 */
body.is-phone-device {
  --self-status-large-avatar-size: var(--self-status-large-avatar-size-phone, 12rem);
  --self-status-large-avatar-pos-x: var(--self-status-large-avatar-pos-x-phone, -0.7rem);
  --self-status-large-avatar-pos-y: var(--self-status-large-avatar-pos-y-phone, -11.5rem);
  /* 背面の半透明アバターは、本体サイズ×倍率で決まる。スマホでは本体を小さくしている分、
     同じ倍率だと大きすぎ／小さすぎになりがちなので、倍率とずらし量・透明度をスマホ専用の
     変数に差し替える（ユーザー要望2026-08-31）。 */
  --self-status-large-avatar-ghost-scale: var(--self-status-large-avatar-ghost-scale-phone, 1.6);
  --self-status-large-avatar-ghost-offset-x: var(--self-status-large-avatar-ghost-offset-x-phone, -1.6rem);
  --self-status-large-avatar-ghost-offset-y: var(--self-status-large-avatar-ghost-offset-y-phone, -8.1rem);
  --self-status-large-avatar-ghost-opacity: var(--self-status-large-avatar-ghost-opacity-phone, 0.35);
}
/* ユーザー要望「戦績システムと連携しているプレイヤーはステータスエリアにランクを
   表示させたい」。姉妹プロジェクト（戦績管理システム）のgetTierInfo/renderAvatarRingHtml
   と同じ見た目（対戦数に応じた色付きリング、15戦以上は虹色の回転リング）を、
   stats-profile.jsが計算したtierに従ってJS側（updateSelfStatusRankRing、main.js）で
   このリング要素へ反映する。アバター本体より一回り大きくして縁取りのように見せる
   （avatar-imageより背面に置く、z-index参照）。連携していないプレイヤーには何も
   表示しない（is-visibleが付かない）。 */
/* ハマりどころ: 当初.self-status-large-avatarの子要素だったが、avatar-render.jsの
   applyAvatarContent()が初回に`el.textContent = ""`で子要素を丸ごと消してから
   <img>を作るため、updateSelfHandStatus()が最初に呼ばれた瞬間にこのリングごと
   消えてしまっていた（実機検証で発覚）。.self-status-large-avatarの「兄弟」に
   変更し、applyAvatarContent()の対象を一切変更しないようにした。兄弟になった分、
   .self-status-large-avatar自身の位置オフセット（--self-status-large-avatar-pos-x/y）
   をこちらにも同じくtranslateへ足し込み、見た目の位置がアバター本体とズレないように
   している。DOM順でアバターより先に追加しているため、z-indexを個別に触らなくても
   自然にアバターの背面に描画される。 */
/* ユーザー報告「ランクリングが見当たらない」への対応。実際に戦績連携済みの
   アカウントでないと本来表示されないため管理者が普段は目視確認できない、という
   問題とは別に、位置・太さ自体もadmin.js（🏅 ランクリングの位置・太さ）から
   微調整できるようCSS変数化した。--rank-ring-thicknessはリングの太さ兼、
   アバター本体の縁からリングがどれだけ外側にはみ出すかを兼ねる（従来は0.5rem
   固定だった）。--rank-ring-offset-x/yは見た目の最終的な位置合わせ用の
   追加オフセット（従来は無かった、既定0＝今までと同じ位置）。 */
.self-status-rank-ring {
  position: absolute;
  top: 0;
  left: 0;
  width: calc(var(--self-status-large-avatar-size, 12rem) + var(--rank-ring-thickness, 0.5rem) * 2);
  height: calc(var(--self-status-large-avatar-size, 12rem) + var(--rank-ring-thickness, 0.5rem) * 2);
  transform: translate(
    calc(var(--self-status-large-avatar-pos-x, -0.86rem) - var(--rank-ring-thickness, 0.5rem) + var(--rank-ring-offset-x, 0rem)),
    calc(var(--self-status-large-avatar-pos-y, -5.12rem) - var(--rank-ring-thickness, 0.5rem) + var(--rank-ring-offset-y, 0rem))
  );
  border-radius: 50%;
  pointer-events: none;
  display: none;
  box-sizing: border-box;
}
.self-status-rank-ring.is-visible {
  display: block;
}
/* ユーザー報告「アバターの丸枠が解決しない。ランクリング表示時にドロップシャドウして
   ないか？」。犯人はこのis-glowのbox-shadowだった。.self-status-rank-ringはborder-radius:50%
   ＝正円なので、box-shadowはアバターの縁をなぞる“暗い丸枠”として描かれてしまう（ブラック
   マスター＝rgba(0,0,0,0.7)だと特にはっきりした暗いリングになる）。これは周回オーブ導入前の
   「静的な色付きリング＋グロー」時代の名残で、今はティア色は周回する発光ドット側が表現して
   いるため、この静的なリングのグローは不要（かつ、縁を透明にフェードさせるマスクの効果を
   丸枠で打ち消してしまう）。box-shadowを外して丸枠を消す。マスター系ティアの区別は、
   発光ドットの色（黒#0a0a0a / 白#ffffff、各ドット自体のbox-shadowグロー付き）で引き続き付く。
   is-glowクラス自体はJS側で付け外しされるが、ここで見た目の指定を持たないので実害はない。 */
.self-status-rank-ring.is-glow {
  box-shadow: none;
}
/* ユーザー要望「ランクリングを、ロックエリアのファースト/エターナルカードの周回演出
   （style.cssのeffect-orbit）みたいに、枠をその色の発光体がくるくる回る感じにしたい。
   残像も欲しい」への対応。effect-orbitは矩形（カードの四隅）専用の4点キーフレームで
   正円には使えないため、位置計算自体はJS（rank-ring-orbit.js、requestAnimationFrameで
   三角関数を使い正円の軌道を計算）が行う。ここでは発光体1つぶんの見た目だけを定義し、
   色は要素ごとに指定せず、親の--rank-ring-color（is-solid/is-glowと同じ、main.js側で
   tierごとに設定済み）をCSS変数の継承に任せる。 */
.rank-ring-orbit-dot {
  position: absolute;
  border-radius: 50%;
  background: var(--rank-ring-color, #facc15);
  box-shadow: 0 0 0.4rem 0.12rem var(--rank-ring-color, #facc15);
  pointer-events: none;
}
/* レインボーティアには単色の--rank-ring-colorが無い（本来7色を巡回するティアのため）ので、
   基準色にhue-rotateを連続適用して色自体も巡回させることで「虹色」らしさを保つ。 */
.self-status-rank-ring.is-rainbow .rank-ring-orbit-dot {
  background: #ef4444;
  box-shadow: 0 0 0.4rem 0.12rem #ef4444;
  animation: rank-ring-orbit-hue-cycle 3s linear infinite;
}
@keyframes rank-ring-orbit-hue-cycle {
  from {
    filter: hue-rotate(0deg);
  }
  to {
    filter: hue-rotate(360deg);
  }
}
/* 「アニメーションを減らす」設定がONの間は、rank-ring-orbit.js側で回転自体を止めて
   発光体を1つだけ静止表示するが、レインボーティアの色巡回（CSSアニメーション）は
   別物のためここで個別に止める（ロックエリアの周回演出を止める既存ルールと同じ扱い）。 */
body.reduce-glow .self-status-rank-ring.is-rainbow .rank-ring-orbit-dot {
  animation: none !important;
  filter: none !important;
}
.self-status-large-avatar:hover .phase-guide-tooltip {
  opacity: 1;
  visibility: visible;
}
.self-status-large-avatar .phase-guide-tooltip {
  left: 0;
  transform: none;
}
/* 駒スキン・カード裏面・プレイマット・オンライン状態の4アイコンを2x2でまとめる。
   サイズは各アイコン個別のCSS変数（--self-status-icon-*-size）で管理しているため、
   アイコンごとに異なる大きさにもできる（オンライン状態アイコンだけキャプション文字が
   下に付く分だけ、その行の高さがもう片方の行よりわずかに大きくなるが、grid-auto-rowsが
   行ごとに自動調整するため崩れない）。 */
.self-status-icon-grid {
  /* ユーザー要望「アイコン群5つの並びが崩れた、整列できる？」への対応。以前は
     grid-template-columns: repeat(2)＋各アイコンの個別transformで位置を手調整していたが、
     ペット追加で6個目になり grid のセル割りがズレて全部崩れてしまった。個別transformは
     やめ、flexで自動整列するようにした（アイコンの数が増減しても崩れない）。グループ全体の
     位置だけ1つのオフセット変数で微調整できるようにする（admin/rearrangeで動かせる）。 */
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--self-status-icon-gap, 0.4rem);
  /* アイコン群（駒スキン・カード裏・プレイマット・背景・ペット・オンライン状態）を
     まとめて位置(group-x/y)＋サイズ(group-scale)調整できる（管理者モード）。 */
  transform: translate(var(--self-status-icons-group-x, 0rem), var(--self-status-icons-group-y, 0rem))
    scale(var(--self-status-icons-group-scale, 1));
  z-index: 2;
  /* ハマりどころ（ユーザー報告「名前クリックの反応範囲が限られている」）: このグリッド
     自体はz-index:2で.self-status-info（名前・手札枚数、z-index:1）より手前にあるため、
     実際に見えている4つの丸いアイコンの隙間（grid gap分の余白）にも関わらず、コンテナ
     自体の透明な矩形全体がクリックを奪っていた。特に位置調整（自分専用ステータスエリア
     再配置モード）で名前欄とアイコン群がある程度重なる配置にすると、アイコンの絵柄が
     無い隙間の下に隠れている名前テキストがクリックできなくなっていた。コンテナ自体は
     クリックを透過させ(pointer-events:none)、実際のアイコンボタン(直接の子要素)だけに
     pointer-events:autoで戻すことで、見た目通り「アイコンの絵柄がある場所だけ」が
     クリックを受け取るようにした。 */
  pointer-events: none;
}
.self-status-icon-grid > * {
  pointer-events: auto;
}
/* スマホでは着せ替えアイコン群（駒スキン・カード裏・ペット・プレイマット・背景）を出さない
   （ユーザー要望2026-09-01。画面が狭く、盤面・手札の邪魔になるため）。着せ替えはマイページ
   から変更できるので機能自体は失われない。管理者モードの「📱 スマホ専用：着せ替えアイコン群の
   表示」をONにすると body に .phone-dressup-icons-visible が付いて元通り表示される。 */
/* 【#199・重要】ここは display:none にしてはいけない。ステータスエリアはフレックスなので、
   アイコン群の箱ごと消えると入れ物が縮み、名前・称号・手札枚数の欄(.self-status-info)に
   かかっている大きな左オフセット(--self-status-info-pos-x: -9.8rem、アイコン群がある前提で
   調整した値)が画面外まで押し出してしまう（実測: 情報欄の左端が x=93 → x=-38 で見切れる）。
   visibility なら箱は残るのでレイアウトは一切変わらず、アイコンだけが消える。
   （iOS のちらつき対策としても display:none の切り替えより visibility の方が無難。） */
body.is-phone-device:not(.phone-dressup-icons-visible) .self-status-icon-grid,
body.is-phone-device:not(.phone-dressup-icons-visible) .self-status-icon-grid > * {
  visibility: hidden;
  pointer-events: none;
}
/* 駒スキン・カード裏面・プレイマットの3つは、buildIconButtonContent()を使うアイコンボタン
   （手札シャッフル等）と違う独自の見た目のため、ホバー簡易説明(.phase-guide-tooltip)の
   表示トリガーもここで個別に用意する（中身の見た目・表示ルール自体は共通クラスを流用）。 */
.self-status-piece-thumb:hover .phase-guide-tooltip,
.self-status-card-back-thumb:hover .phase-guide-tooltip,
.self-status-playmat-thumb:hover .phase-guide-tooltip {
  opacity: 1;
  visibility: visible;
}
/* この4アイコン（オンライン状態を含む）は画面左下、左端に近い位置にあるため、
   .phase-guide-tooltipの既定（中央寄せ）のままだと画面左端で見切れる。左端をアイコンの
   左端に合わせ、常に右方向にだけ伸びるようにする（#piece-hide-button/#card-hide-buttonと
   同じ対策）。 */
.self-status-icon-grid .phase-guide-tooltip {
  left: 0;
  transform: none;
}
/* 駒スキンのサムネイル。ドラッグ中のゴースト(.drag-ghost-piece-outer/-inner)と同じ
   「perspective+盤面と同じ傾きを持つ入れ子」で、3D空間の外でも本物の.pieceを
   立方体のまま小さく表示する。セットアップ前（自分の駒の色が未確定）でも、選んだ
   バリエーション番号自体は色に依存しない好みのため、常に表示する（main.jsが色未確定の
   間は仮の色で描画する）。 */
.self-status-piece-thumb {
  position: relative;
  width: var(--self-status-icon-piece-size, 1.5rem);
  height: var(--self-status-icon-piece-size, 1.5rem);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(148, 163, 184, 0.3);
  border-radius: 50%;
  cursor: pointer;
  padding: 0;
  perspective: 400px;
  flex-shrink: 0;
  display: none;
  align-items: center;
  justify-content: center;
  transform: translate(var(--self-status-icon-piece-pos-x, 0rem), var(--self-status-icon-piece-pos-y, 0rem));
}
.self-status-piece-thumb:hover {
  background: rgba(255, 255, 255, 0.14);
}
/* overflow:hiddenは以前ボタン本体(.self-status-piece-thumb)に付けていたが、それだと
   ホバーツールチップ(.phase-guide-tooltip、ボタンの外・上に絶対配置される)まで一緒に
   クリップされて表示されなくなってしまっていた（ユーザー報告で発覚）。立方体の見た目を
   円形にクリップする役割だけをこの内側の入れ子に移し、ボタン本体は素通しにすることで
   両立させた。 */
.self-status-piece-thumb-inner {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  overflow: hidden;
  border-radius: 50%;
}
/* カード裏面セットのサムネイル。駒スキンの丸いボタンと区別が付くよう角丸の正方形にした
   （中身は実際にカードと同じ正方形の画像なので、丸くクロップすると絵柄が分かりにくい
   ため）。ゲーム開始前でも常に選べる（駒スキンと違い自分の色に依存しないため）。 */
.self-status-card-back-thumb {
  position: relative;
  width: var(--self-status-icon-cardback-size, 1.5rem);
  height: var(--self-status-icon-cardback-size, 1.5rem);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(148, 163, 184, 0.3);
  border-radius: 0.4rem;
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
  transform: translate(var(--self-status-icon-cardback-pos-x, 0rem), var(--self-status-icon-cardback-pos-y, 0rem));
}
.self-status-card-back-thumb:hover {
  background: rgba(255, 255, 255, 0.14);
}
/* overflow:hiddenを親ボタンではなくimg自身に丸め込む（border-radiusは要素自身の
   コンテンツも丸くクリップするため、親を素通しにしてもツールチップの表示を妨げない）。 */
.self-status-card-back-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  border-radius: 0.4rem;
}
/* プレイマットのサムネイル。カード裏面と同じ角丸正方形（プレイマットも正方形の画像）。 */
.self-status-playmat-thumb {
  position: relative;
  width: var(--self-status-icon-playmat-size, 1.5rem);
  height: var(--self-status-icon-playmat-size, 1.5rem);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(148, 163, 184, 0.3);
  border-radius: 0.4rem;
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
  /* ユーザー要望「4アイコンをきれいに整列させたい」。従来はプレイマット/背景が
     駒スキン/カード裏面と別々のX位置に浮いていて2x2として揃っていなかった。
     駒スキンと同じX（列）に揃え、縦にきれいな2x2グリッドになるようにした。 */
  transform: translate(var(--self-status-icon-playmat-pos-x, 0rem), var(--self-status-icon-playmat-pos-y, 0rem));
}
.self-status-playmat-thumb:hover {
  background: rgba(255, 255, 255, 0.14);
}
/* 背景画像のサムネイル（background.js参照）。プレイマットアイコンと同じ大きさ・見た目の
   まま、独立したCSS変数で位置だけ別々に調整できるようにする（ユーザー要望「プレイマット
   変更アイコンと同じサイズでその隣に配置」）。プレイマット用のルール(.self-status-playmat-
   thumb)は幅/高さ/位置も含めて丸ごと継承しているため、ここでは位置と幅/高さだけ、2つの
   クラスが両方付いた時だけ効くより詳細度の高いセレクタで上書きする。 */
.self-status-playmat-thumb.self-status-background-thumb {
  width: var(--self-status-icon-background-size, 1.5rem);
  height: var(--self-status-icon-background-size, 1.5rem);
  /* カード裏面と同じX（列）に揃え、縦にきれいな2x2グリッドになるようにした
     （上の.self-status-playmat-thumbのコメント参照）。 */
  transform: translate(var(--self-status-icon-background-pos-x, 0rem), var(--self-status-icon-background-pos-y, 0rem));
}
.self-status-playmat-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  border-radius: 0.4rem;
}
/* オンライン状態アイコン（icon-action-button.js共通部品）は他のボタン（手札シャッフル等）
   と共有クラスのため、アイコンのサイズ変数だけこの中でスコープして上書きする
   （.icon-action-button-icon-wrap自体には触れない）。位置調整はボタン本体(#self-status-online)
   へのtranslateで行う（キャプション文字ごと一緒に動く）。 */
#self-status-online {
  transform: translate(var(--self-status-icon-online-pos-x, 0rem), var(--self-status-icon-online-pos-y, 0rem));
}
.self-status-icon-grid #self-status-online .icon-action-button-icon-wrap {
  width: var(--self-status-icon-online-size, 2.6rem);
  height: var(--self-status-icon-online-size, 2.6rem);
}
/* ユーザー要望「タブレット専用位置調整にオンラインアイコンを追加」。他のタブレット専用
   オーバーライドと同じ「未設定ならPC値にフォールバック」の仕組み。 */
body.is-touch-device #self-status-online {
  transform: translate(
    var(--self-status-icon-online-pos-touch-x, var(--self-status-icon-online-pos-x, 0.27rem)),
    var(--self-status-icon-online-pos-touch-y, var(--self-status-icon-online-pos-y, -4.14rem))
  );
}
body.diagnostic-flatten-3d.is-touch-device #self-status-online {
  transform: translate(
    calc(var(--self-status-icon-online-pos-touch-x, var(--self-status-icon-online-pos-x, 0.27rem)) + var(--self-status-icon-online-flat-x, 0rem)),
    calc(var(--self-status-icon-online-pos-touch-y, var(--self-status-icon-online-pos-y, -4.14rem)) + var(--self-status-icon-online-flat-y, 0rem))
  );
}
body.is-touch-device .self-status-icon-grid #self-status-online .icon-action-button-icon-wrap {
  width: var(--self-status-icon-online-size-touch, var(--self-status-icon-online-size, 2.6rem));
  height: var(--self-status-icon-online-size-touch, var(--self-status-icon-online-size, 2.6rem));
}
.self-status-info {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  /* ハマりどころ（ユーザー報告「駒スキン/裏面/プレイマットのアイコンが反応しづらい」）:
     #self-hand-statusはdisplay:flexで、この要素はアイコングリッドより後にappendChildされる
     ため、両者ともz-index:auto（デフォルト）だとflexアイテムの並び順がそのままスタッキング
     順になり、名前・手札枚数のテキスト自体は短くても、要素の「箱」自体は幅130px前後まで
     広がっており、駒スキン・カード裏面アイコンの上に透明な当たり判定として覆いかぶさって
     クリックを奪っていた（見た目のテキストが被っていなくても、箱の透明な部分もクリックを
     奪う）。z-index:1を明示し、アイコングリップ側(z-index:2)に確実に負けるようにした。 */
  z-index: 1;
  transform: translate(var(--self-status-info-pos-x, -6.2rem), var(--self-status-info-pos-y, 2.2rem));
}
/* ユーザー要望「白いアバターだとプレイヤー名、手札枚数の視認性が悪い」への対応。
   アバター画像の色に関わらず読めるよう、テキストの背面に半透明の黒い塗りつぶし（角丸の
   小さな帯）を敷く。inline-blockにして帯の幅をテキスト自体の幅に合わせている
   （ブロック全体を覆う帯にすると不格好なため）。 */
.self-status-name,
.self-status-hand-count {
  display: inline-block;
  background: rgba(10, 14, 20, 0.55);
  border-radius: 0.3rem;
  padding: 0.05rem 0.4rem;
  backdrop-filter: blur(2px);
  width: fit-content;
}
.self-status-name {
  font-size: 0.85rem;
  font-weight: bold;
  cursor: pointer;
  border-bottom: 1px dashed transparent;
}
.self-status-name:hover {
  border-bottom-color: rgba(226, 232, 240, 0.5);
}
.self-status-name-input {
  font-size: 0.85rem;
  font-weight: bold;
  font-family: sans-serif;
  background: rgba(255, 255, 255, 0.08);
  color: #e2e8f0;
  border: 1px solid rgba(125, 211, 252, 0.6);
  border-radius: 0.2rem;
  padding: 0.05rem 0.3rem;
  width: 9rem;
}
.self-status-hand-count {
  font-size: 0.75rem;
  opacity: 0.8;
}

/* アバター選択モーダル（駒スキン選択モーダルと同じ見た目のパターン）。 */
#avatar-picker-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(20rem, 90vw);
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.5rem;
  padding: 1rem;
  z-index: 10002;
  font-family: sans-serif;
  color: #e2e8f0;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
.avatar-picker-modal-title {
  font-weight: bold;
  margin-bottom: 0.8rem;
  padding-right: 1.6rem;
}
.avatar-picker-modal-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.5rem;
}
.avatar-picker-swatch {
  font-size: 1.6rem;
  padding: 0.4rem;
  background: rgba(255, 255, 255, 0.04);
  border: 2px solid rgba(148, 163, 184, 0.3);
  border-radius: 0.4rem;
  cursor: pointer;
}
.avatar-picker-swatch.is-selected {
  border-color: #7dd3fc;
}
/* 国王アバターは有料。未所持は駒スキン等と同じくモノクロ気味に沈め、右下に価格バッジを
   重ねる（クリックするとピッカー側がショップを開く）。 */
.avatar-picker-swatch.is-locked {
  position: relative;
}
.avatar-picker-swatch.is-locked .avatar-image {
  filter: grayscale(0.8) brightness(0.6);
}
.avatar-picker-swatch-lock {
  position: absolute;
  bottom: 0.2rem;
  right: 0.2rem;
  background: rgba(0, 0, 0, 0.75);
  color: #fde68a;
  font-size: 0.6rem;
  font-weight: bold;
  padding: 0.1rem 0.3rem;
  border-radius: 0.6rem;
  pointer-events: none;
}

/* ユーザー要望「アバター画像を自分でアップロードできるようにしたい」への対応
   （avatar-upload.js参照）。 */
.avatar-upload-section {
  margin-top: 0.8rem;
  padding-top: 0.8rem;
  border-top: 1px solid rgba(148, 163, 184, 0.25);
}
.avatar-upload-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.avatar-upload-btn {
  flex: 1;
  padding: 0.5rem 0.7rem;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.3rem;
  color: #e2e8f0;
  cursor: pointer;
  font-size: 0.85rem;
  font-family: sans-serif;
}
.avatar-upload-btn:hover {
  background: rgba(255, 255, 255, 0.12);
}
.avatar-upload-btn:disabled {
  opacity: 0.5;
  cursor: default;
}
.avatar-upload-file-input {
  display: none;
}

/* Googleで初めてログインした時の「アバターとニックネームの確認」モーダル
   （ユーザー要望）。avatar-picker-modalと同じ見た目のグリッドを流用しつつ、大きめの
   プレビューと名前入力欄を追加した専用モーダル。 */
#first-login-profile-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(22rem, 90vw);
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.6rem;
  padding: 1.2rem;
  /* #opening-screen（z-index:50000）より確実に高くする（main.jsのopenFirstLoginProfileModal
     内のcreateBackdropコメント参照）。 */
  z-index: 50100;
  font-family: sans-serif;
  color: #e2e8f0;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
  text-align: center;
}
.first-login-profile-title {
  font-weight: bold;
  font-size: 1.1rem;
  margin-bottom: 0.6rem;
}
.first-login-profile-body {
  font-size: 0.82rem;
  line-height: 1.6;
  opacity: 0.9;
  margin-bottom: 0.9rem;
}
.first-login-profile-avatar-preview {
  width: 5rem;
  height: 5rem;
  margin: 0 auto 0.8rem;
  border-radius: 50%;
  overflow: hidden;
  border: 2px solid #7dd3fc;
  font-size: 2.4rem;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.04);
}
.first-login-profile-avatar-preview .avatar-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.first-login-profile-avatar-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.45rem;
  margin-bottom: 1rem;
}
.first-login-profile-name-label {
  text-align: left;
  font-size: 0.75rem;
  opacity: 0.75;
  margin-bottom: 0.3rem;
}
.first-login-profile-name-input {
  width: 100%;
  box-sizing: border-box;
  padding: 0.5rem 0.7rem;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.3rem;
  color: #e2e8f0;
  font-size: 0.9rem;
  font-family: sans-serif;
  margin-bottom: 1rem;
}
.first-login-profile-ok {
  width: 100%;
  padding: 0.6rem;
  background: linear-gradient(160deg, #22c55e, #15803d);
  color: #fff;
  border: none;
  border-radius: 0.4rem;
  cursor: pointer;
  font-size: 0.9rem;
  font-weight: bold;
}
.first-login-profile-ok:hover {
  background: linear-gradient(160deg, #4ade80, #16a34a);
}
.avatar-upload-status {
  min-height: 1.1em;
  font-size: 0.75rem;
  color: #94a3b8;
  margin-top: 0.4rem;
  text-align: center;
}

/* 画像アバター（実物のイラスト・Googleプロフィール画像共通）の共通スタイル
   （main.jsのapplyAvatarContent参照）。各コンテナの既存サイズ（.player-avatarはfont-size、
   .avatar-picker-swatch等は固定サイズ）に収まるよう、置かれた場所ごとに寸法だけ
   個別指定する。 */
.avatar-image {
  border-radius: 50%;
  object-fit: cover;
  display: block;
}
/* ユーザー要望「アバターがそのまま貼ってあると少し味気ない、輪郭にドロップシャドウ的な
   ものを」で一度追加したが、「やはり不要」とのことで撤回し、管理者モードのオンオフ
   （デフォルトOFF）に変更した。ONの間だけ、画像本体にはっきりした暗色のリング
   （box-shadow）を重ねる。 */
body.avatar-outline-visible .avatar-image {
  box-shadow: 0 0 0 0.12rem rgba(0, 0, 0, 0.55);
}

/* タブレット点滅の原因切り分け用（管理者モード「【診断用】3D効果を一時的に無効化」）。
   ユーザーがZ値・傾き角度・透視投影・will-changeの横展開の4種類の実験を試しても
   直らず、Firefox/Chrome/Safariいずれでも同様に起きるとの報告を受けた最終確認手段。
   perspectiveを外し、全要素のtransform-styleをflatに強制することで、preserve-3d
   による疑似3D合成そのものを完全に無効化する。見た目は真上から見たような平らな
   崩れた表示になるが、それは想定内（原因切り分け専用で、見た目を保つ機能ではない）。
   これで点滅が消えれば3D合成が原因、消えなければ描画とは別の原因を疑う必要がある。 */
body.diagnostic-flatten-3d .scene {
  perspective: none;
}
body.diagnostic-flatten-3d * {
  transform-style: flat !important;
}
/* ちらつきの真因対策（続き247）。手札(.hand-area)・ラベル(.label)・背景(.table-background-bg)・
   プレイマット(.playmat-bg)・アバター(.player-avatar)・手札公開エリア(.hand-reveal-area)・
   ロックエリアバー(.lock-area-bar)・駒(.piece)に付けていた will-change: transform は、
   すべて「3Dモード(preserve-3d)の深度バッファ精度によるz-fighting」対策として足したもの
   （body.is-touch-device スコープ、上の935〜965行や3536行）。ところがflat/iso表示は
   perspective:none＋全要素 transform-style:flat で深度バッファ自体が無い＝z-fightingは
   原理的に起き得ないため、これらの will-change は無意味。それどころか、要素の数だけ
   合成レイヤーを常時確保し、弱いGPU（古いiPhone等）のタイル/レイヤー上限を圧迫して、
   ロックエリアバーの色が飛ぶ/一瞬透明になる等のちらつき（＝レイヤーのタイルが描けない
   典型的な症状）の原因になっていた（実機の実測で、#245の駒デプロモーション・#246の盤面
   純2D化でも残り、自分のロックバーまでちらつくと判明）。flat/iso時はこれらの will-change を
   まとめて解除して合成レイヤー数を大幅に減らす。3Dモード（本編）は body.diagnostic-flatten-3d が
   付かないので上の z-fighting 対策はそのまま温存される。 */
body.diagnostic-flatten-3d .hand-area,
body.diagnostic-flatten-3d .label,
body.diagnostic-flatten-3d .table-background-bg,
body.diagnostic-flatten-3d .playmat-bg,
body.diagnostic-flatten-3d .player-avatar,
body.diagnostic-flatten-3d .hand-reveal-area,
body.diagnostic-flatten-3d .lock-area-bar,
body.diagnostic-flatten-3d .piece {
  will-change: auto !important;
}
/* ユーザー報告（続き63・続き64で再報告、PCでも発生）「2D表示の時、山札や捨て場の
   カード、駒などが見えない」の根本原因と対策。実機スクリーンショットで確認したところ、
   駒(.piece)・山(.stack)の「床+壁」の3D箱構築（壁は.piece-wall-*/.stack-front等、
   rotateX/rotateZ等で90度回転させた面）は、perspectiveが無い・transform-styleが
   flat強制の環境下では「見えない」のではなく、回転が正しく機能せず縮退した奇妙な
   小さい塊として上面の下に紛れ込み、結果として山札・駒がまともな形に見えなくなって
   いた（実際に見えなくなっていたのは壁の方で、上面(.piece-top/.stack-top)自体は
   描画されていたが、崩れた壁の残骸と重なって認識しづらい見た目になっていた）。
   2D表示中は「立体に見せる」こと自体を諦め、崩れた壁面をまるごと非表示にして、
   上面だけを平らなアイコンとして綺麗に見せる方が実態に合っている。 */
body.diagnostic-flatten-3d .piece-wall-back,
body.diagnostic-flatten-3d .piece-wall-front,
body.diagnostic-flatten-3d .piece-wall-left,
body.diagnostic-flatten-3d .piece-wall-right,
body.diagnostic-flatten-3d .stack-front,
body.diagnostic-flatten-3d .stack-back,
body.diagnostic-flatten-3d .stack-left,
body.diagnostic-flatten-3d .stack-right {
  display: none !important;
}
/* 続き67: 上記の壁hidden対策だけでは山札・捨て場・駒がユーザー実機で「見えない」
   （空白の四角として描画される）問題が解決しなかった。ブラウザの実描画をピクセル単位で
   検証したところ、原因は壁ではなく上面(.piece-top/.stack-top)自体にあった：この2つは
   本来の3D表示のために`transform: translateZ(...)`（駒の高さ/山の厚み分持ち上げる）を
   持っているが、`transform-style: flat !important`で強制的に平坦化された環境下では、
   この残存translateZが要素を「白紙の空箱」として描画させてしまう（background-imageが
   一切ペイントされない）Chromium系の描画不具合を引き起こしていた。transformそのものを
   無効化して初めて、カード裏面・駒の画像が正しく表示されることを確認した。 */
body.diagnostic-flatten-3d .piece-top,
body.diagnostic-flatten-3d .stack-top {
  transform: none !important;
}
/* 続き68: 上記のtransform無効化だけではまだ不十分だった（ユーザー報告「２Dにしたとたん
   山札などはプレイマットの裏に行ってしまっている」）。elementsFromPoint()で実際の
   ペイント順序を検証したところ、`.playmat-bg`（明示的にz-index:1）が`.zone-deck`等の
   山札コンテナ（z-index指定なし＝auto）より常に手前に描画されており、山札が完全に
   プレイマット画像の下に隠れていたことが判明した。通常の3D表示では駒(.piece)と同じ
   「preserve-3dによる実際の奥行き合成」がこのz-indexの差を覆い隠していたため露見して
   いなかったが（.pieceは.board、z-index:2の中にあるため元々問題無い）、2D平坦化環境
   ではz-indexの通常のルールがそのまま効いてしまう。山札系のゾーンだけ.board相当の
   z-index:2を与えて解決する。 */
body.diagnostic-flatten-3d .zone-deck,
body.diagnostic-flatten-3d .zone-eternal,
body.diagnostic-flatten-3d .zone-first,
body.diagnostic-flatten-3d .zone-discard {
  z-index: 2;
}
/* 続き69: 上の「駒は.board、z-index:2の中にあるため元々問題無い」という判断は
   プレイマットとの比較に限った話で不十分だった。ユーザー報告「盤に49枚並べた直後の
   盤面で、駒が見えておらず、駒を移動させたりすると見えるようになる」——セットアップ
   直後は全49マスにカード(.board-card)が置かれた状態になるが、同じ.cell内でDOM順は
   駒(.piece)が先・カードが後のため、z-index指定の無い者同士では後発のカードが手前に
   描画され、駒が完全にカードの下へ隠れてしまっていた（通常の3D表示では駒の実際の
   高さ(translateZ)によるpreserve-3dの奥行き合成がこのDOM順を覆い隠していたが、続き67で
   上面のtransformを無効化した2D平坦化環境ではその奥行き合成が働かず、単純なDOM順に
   フォールバックしてしまう）。駒を動かすと見えるようになるのは、移動先のマスに同じ
   競合が無い、または再描画でDOM順の効果がたまたま変わるため。駒自体にカードより
   確実に手前へ来るz-indexを与えて解決する。 */
body.diagnostic-flatten-3d .piece {
  z-index: 3;
}
/* 壁を消した分、上面だけでも駒だとひと目でわかるよう縁取りを足す（山(.stack-top)には
   既存のborderがあるため対象外）。 */
body.diagnostic-flatten-3d .piece-top {
  border-radius: 0.2rem;
  border: 2px solid rgba(255, 255, 255, 0.6);
  box-shadow: 0 0.15rem 0.3rem rgba(0, 0, 0, 0.6);
}

/* ===== 2.5D実験（続き242。ユーザー要望2026-08-22「駒/山が2D化するのは寂しい」）=====
   本編には一切影響しない（body.iso25dは ?iso=1 で開いた時だけ付く。tablet-2d-mode.js）。
   iso25dは必ずフラット表示(diagnostic-flatten-3d)と併用される＝perspective/preserve-3dを
   使わない2D合成のまま（＝ちらつきの根本対応を維持したまま）、駒を「平らなシール」では
   なく“高さのある立体トークン”に見せる。上面(.piece-top)は正方形のまま、手前
   (.piece-wall-front)と右(.piece-wall-right)の2面だけを2Dのskew(45°)でキャビネット図法の
   側面として描く（奥back・左leftは見えないので隠したまま）。3D回転(rotateX等)は一切使わない
   ので合成負荷は増えない。skewの向き・奥行き(--iso-piece-depth)・陰影は後で調整可能。
   ※まず「駒だけ」の試作（続き242）。良ければ山(.stack)・盤面の傾きへ広げる。 */
body.iso25d {
  --iso-piece-depth: calc(var(--piece-size) * 0.32);
}
/* ちらつきの真因対策（続き245）: 駒(.piece)はベースで will-change:transform ＋
   backface-visibility:hidden により“常に合成レイヤー”になっている。素フラットでは駒は
   上面だけ（枠内に収まる）なので無害だが、isoでは側面(skew)が駒の枠の外＝下・右へはみ出し、
   この合成レイヤーの矩形が隣の盤面カードや前方のロックエリアバーに重なる。iOSはこの
   「重なり合成(overlap compositing)」で重なった側の要素を再合成し続け、ロックバーの色が
   消える/カードが欠ける等のちらつきになる（ユーザー報告「ゲートから手前に限定」＝駒がいる
   前方に一致）。isoだけ強制レイヤー化を解除し、駒を通常の盤面レイヤーに描かせる（skewは
   2D変形なので単体ではレイヤーを作らない＝2.5Dの見た目は維持、隣を巻き込まなくなる）。
   3Dモード（本編）の .piece は body.iso25d が付かないので一切影響しない。 */
body.iso25d .piece {
  will-change: auto !important;
  backface-visibility: visible !important;
  /* 続き252（ちらつきの根本＝はみ出し重なりを無くす）: 従来は上面をマス中央に置き、側面が
     下・右へ約0.67rem（=--iso-piece-depth）はみ出して隣のマス/ロックバーに重なっていた。
     #159のユーザーA/Bテスト（素2D=ちらつかない／iso=ちらつく＝差分は側面のみ）で側面が
     確定的な犯人と判明しており、側面はアニメで動いていない（#248で無地・不透明・skewのみ）
     ため、残る原因は「はみ出した側面が隣接要素と物理的に重なり、弱いGPUが再合成のたびに
     その重なり領域を描き落とす」こと。上面を depth/2 ぶん左上へずらして立方体の中心を
     マス中央に合わせ、側面がマスの外へ出ないようにする＝隣と重ならない＝ちらつく対象が
     無くなる（駒がゲート＝端にいる時のロックバーへのはみ出しも同時に解消）。マスは
     3.4rem・立方体は約2.77rem(2.1+0.67)なので、中央寄せしても各辺に約0.31remの余白が残り
     はみ出さない。hitboxは.pieceの子なので一緒に動き、クリック位置は見た目と一致する。 */
  margin-top: calc(var(--piece-size) / -2 - var(--iso-piece-depth) / 2) !important;
  margin-left: calc(var(--piece-size) / -2 - var(--iso-piece-depth) / 2) !important;
  /* 駒自身のドロップシャドウ(0 0.3rem 0.4rem, blur)も下・右へはみ出して隣に重なり、かつ
     blur塗りが重い。isoでは立体感は側面の陰影で出しているので、このはみ出す外側シャドウは消す。 */
  box-shadow: none !important;
}
/* 上面: フラットが付けた白いシール枠は消し、立体の側面に形を語らせる。
   ※z-indexは付けない（側面は上面の“外側”＝下・右にしか出ず上面本体に重ならないので不要。
   z-indexは新しい合成レイヤー/スタッキング文脈を作り、iOSの弱いGPUで周囲の要素＝ロックバー・
   盤面カードのチカチカを誘発したため。続き244）。 */
body.iso25d .piece-top {
  border: none !important;
  box-shadow: none !important;
}
/* 奥・左の面は見えないので確実に隠す（フラットの display:none を継ぐが、念のため明示）。
   これらは3D回転(rotateX)前提のtransformを持つため、iso(2D)で見せると縮退した破片になる。 */
body.iso25d .piece-wall-back,
body.iso25d .piece-wall-left {
  display: none !important;
}
/* 手前の側面（上面の下辺から、下へ45°でせり出す）。
   ※続き248: 側面を「最軽量の塗り」にする——スキン背景画像も inset box-shadow も filter も使わず、
   単色の不透明 background-color だけにする。古いiPhoneのGPUでは、はみ出して隣のマス・ロックバーに
   重なる側面の「画像デコード＆拡縮＋大きな影のブラー」の塗りが重く、盤面の頻繁な再描画のたびに
   隣の要素がちらつく一因になっていた（#244〜#247で駒/盤面/周辺の合成レイヤーを一掃してもなお
   残ったため、次の容疑＝側面そのものの塗りコストと重なり）。単色・不透明なら画像も影も無く塗りが
   最小で、かつ不透明なので隣の要素とのブレンド（透過合成）も起きない。立体の陰影は「上面=明・
   右側面=中・手前側面=暗」の3段の無地グレーで表現する。 */
body.iso25d .piece-wall-front {
  display: block !important;
  top: 100%;
  left: 0;
  bottom: auto;
  /* 続き255: skewX(45deg)（変形）→ clip-path（変形なしで同じ平行四辺形に切り抜く）に変更。
     iOS Safari は transform（skew含む）を持つ要素を別レイヤーへ昇格させ、その別レイヤー同士の
     3D的な重なりを合成し損ねてちらつく（＝ちらつきが特定要素に限定される理由）。clip-path は
     paint時のクリップで別レイヤーを作らないため、側面が盤面/マスと同じ1枚のレイヤーに留まり、
     見た目は同じクッキリした立方体のままちらつかなくなる（狙い）。形を skew と同じにするため、
     ボックスを depth ぶん広げ（width = piece-size + depth）、平行四辺形に切り抜く。 */
  width: calc(var(--piece-size) + var(--iso-piece-depth));
  height: var(--iso-piece-depth);
  transform: none !important;
  transform-origin: initial;
  clip-path: polygon(0 0, calc(100% - var(--iso-piece-depth)) 0, 100% 100%, var(--iso-piece-depth) 100%);
  background-image: none !important;
  background-color: #2a2a30; /* 手前の側面: 暗い無地・不透明 */
  filter: none !important;
  box-shadow: none !important;
}
/* 右の側面（上面の右辺から、右へ45°でせり出す）。手前より明るめの無地。 */
body.iso25d .piece-wall-right {
  display: block !important;
  top: 0;
  left: 100%;
  right: auto;
  /* 続き255: skewY(45deg) → clip-path（同上）。ボックスを depth ぶん縦に広げ
     （height = piece-size + depth）、平行四辺形に切り抜く。 */
  width: var(--iso-piece-depth);
  height: calc(var(--piece-size) + var(--iso-piece-depth));
  transform: none !important;
  transform-origin: initial;
  clip-path: polygon(0 0, 100% var(--iso-piece-depth), 100% 100%, 0 calc(100% - var(--iso-piece-depth)));
  background-image: none !important;
  background-color: #46464e; /* 右の側面: 中間の無地・不透明 */
  filter: none !important;
  box-shadow: none !important;
}

/* 続き255: #253（盤面カード・ロックバーを translate プロパティで「浮かせる」実験）は撤回した。
   ユーザーの端末が iPhone 15 Pro Max（最上位の高性能機）と判明し、原因は「性能不足でシーンが重い」では
   なく「iOS Safari が別レイヤー化した要素の3D的な重なりを合成し損ねる」バグ（＝ちらつきが特定要素に
   限定される理由）だと切り分けられたため。translate プロパティはむしろ対象を別レイヤーへ昇格させて
   ちらつきを増やす恐れがあるので削除。真因対策は「側面を別レイヤーにしない」（下の piece-wall を
   skew→clip-path 化）。 */

/* 続き254（実験・軽量描画）: #161で ?iso=0&flat=1（素の2D、側面なし）でも「時折ちらつく」＋
   suspectedCrash が判明し、ちらつきの原因が2.5Dの側面ではなく「重いシーンをこの古いGPU/メモリで
   レンダリングすること自体」だと切り分けられた。最大の容疑＝大きく引き伸ばした背景画像
   （.table-background-bg、--table-background-scale 4.75×4.85 の巨大テクスチャ）を、?lite=1
   （body.lite-render, tablet-2d-mode.js）の時だけ隠す。実機で ?flat=1 と ?flat=1&lite=1 を見比べて、
   背景画像がちらつき/クラッシュの主因かを判定する。プレイマット(.playmat-bg)は盤面の見た目に必要な
   ので今回は残す（切り分けの変数を1つに絞る）。本編には影響しない（body.lite-render は ?lite=1 の時だけ付く）。 */
body.lite-render .table-background-bg {
  display: none !important;
}

.zone-bottom .player-avatar .avatar-image {
  width: var(--avatar-a-size);
  height: var(--avatar-a-size);
}
.zone-left .player-avatar .avatar-image {
  width: var(--avatar-b-size);
  height: var(--avatar-b-size);
}
.zone-top .player-avatar .avatar-image {
  width: var(--avatar-c-size);
  height: var(--avatar-c-size);
}
.zone-right .player-avatar .avatar-image {
  width: var(--avatar-d-size);
  height: var(--avatar-d-size);
}
.avatar-picker-swatch .avatar-image {
  width: 1.6rem;
  height: 1.6rem;
}
.start-player-avatar .avatar-image {
  width: var(--start-player-avatar-size, 12rem);
  height: var(--start-player-avatar-size, 12rem);
  margin: 0 auto; /* avatar-imageはdisplay:blockのため、親のtext-align:centerでは中央寄せされない */
}

/* 駒がカードの上に乗った（到達した）ことを知らせる一時的なモーダル。画面右上（右上に既にある
   山札一覧/セットアップ/オプションのボタン列より下）に固定表示する。中央に表示すると
   盤面中央の駒がモーダルに隠れてしまい「駒がそのカードに乗っている姿」を確認できないため、
   あえて盤面から外れた位置に固定した。裏向きのカードだった場合は先に自動でオープンしてから
   表示される（main.js/card-arrival.js参照）。数秒で自動的に消えるが、内側の「📌 消えないように
   する」ボタンでその場に留めることもできる（✕でいつでも閉じられる）。サイズ・表示時間は
   管理者モードで調整できる（--card-arrival-modal-size・--card-arrival-modal-duration）。 */
:root {
  --card-arrival-modal-size: 25rem;
  --card-arrival-modal-duration: 5; /* 秒 */
  --card-arrival-modal-top: 3rem;
  --card-arrival-modal-right: 1rem;
}
.card-arrival-modal {
  position: fixed;
  top: var(--card-arrival-modal-top);
  right: var(--card-arrival-modal-right);
  z-index: 10600;
  width: var(--card-arrival-modal-size);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem;
  background: rgba(15, 23, 32, 0.5);
  border-radius: 0.8rem;
  transform-origin: top right;
  transform: scale(0.85);
  opacity: 0;
  pointer-events: auto;
  transition: opacity 0.3s ease, transform 0.3s ease;
}
.card-arrival-modal.show {
  opacity: 1;
  transform: scale(1);
}
.card-arrival-modal-label {
  font-family: sans-serif;
  font-weight: bold;
  font-size: 1.2rem;
  letter-spacing: 0.2em;
  color: #fbbf24;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.9);
}
.card-arrival-modal img,
.card-arrival-modal .card-box {
  width: 100%;
  display: block;
  border-radius: 0.6rem;
  border: 2px solid rgba(255, 255, 255, 0.8);
  box-shadow: 0 1.2rem 3rem rgba(0, 0, 0, 0.7);
}
.card-arrival-modal-pin {
  padding: 0.35rem 0.9rem;
  background: rgba(15, 23, 32, 0.85);
  color: #e2e8f0;
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.4rem;
  cursor: pointer;
  font-family: sans-serif;
  font-size: 0.75rem;
}
.card-arrival-modal-pin:hover {
  background: rgba(56, 189, 248, 0.2);
}
/* 「キープ」ボタン（ユーザー要望2026-08-08）。押すとターンを跨いでも残る。 */
.card-arrival-modal-keep {
  padding: 0.35rem 0.9rem;
  background: rgba(15, 23, 32, 0.85);
  color: #fde68a;
  border: 1px solid rgba(251, 191, 36, 0.5);
  border-radius: 0.4rem;
  cursor: pointer;
  font-family: sans-serif;
  font-size: 0.75rem;
}
.card-arrival-modal-keep:hover {
  background: rgba(251, 191, 36, 0.2);
}
/* キープ中は、新たに右上へ出る到達モーダルと重ならないよう左上へ寄せ、複数キープは斜めに
   少しずつずらして重ね置きする（--kept-indexはcard-arrival.jsが採番）。 */
.card-arrival-modal.is-kept {
  right: auto;
  left: calc(var(--card-arrival-modal-right, 1rem) + var(--kept-index, 0) * 1.4rem);
  top: calc(var(--card-arrival-modal-top, 1rem) + var(--kept-index, 0) * 1.4rem);
  transform-origin: top left;
}
/* 到達した駒の持ち主にだけ表示される「このカードを手札に加える」ボタン。モーダル右下に
   配置する（ユーザー指定）。 */
.card-arrival-modal-add-to-hand {
  align-self: flex-end;
  padding: 0.4rem 0.9rem;
  background: rgba(21, 128, 61, 0.7);
  color: #dcfce7;
  border: 1px solid rgba(74, 222, 128, 0.7);
  border-radius: 0.4rem;
  cursor: pointer;
  font-family: sans-serif;
  font-size: 0.75rem;
  font-weight: bold;
}
.card-arrival-modal-add-to-hand:hover {
  background: rgba(21, 128, 61, 0.95);
}

/* 駒が裏向きのカードに乗った時、自動でオープンする代わりに出す「オープンする/しない」の
   選択アイコン。駒のすぐ上に浮かべる（main.jsのpromptCardOpen参照、左右中央基準で
   left/topをJS側から直接指定するのでtransformで自分の幅の半分・自分の高さぶんだけ
   ずらして駒の真上に来るようにする）。 */
.card-open-prompt {
  position: fixed;
  transform: translate(-50%, calc(-100% - 0.5rem));
  /* 不具合#34「接触するボタンの左端しか反応しない」。このプロンプトはbody直下のz-index
     10400だが、フローティングUI全部を載せる #option-area がz-index 50100のbody兄弟のため、
     その中の操作可能な子（特に盤面上部にある相手プレイヤーのステータス/アバターパネル）が
     プロンプトより前面に来て、相手の近くで出る接触ボタンの右側のクリックを奪っていた。
     #option-areaより前面へ上げ、ボタン全域が確実にクリックできるようにする。
     追記(不具合#82関連): 不具合報告ボタンをbody直下z:100055へ最前面化したため、上部で
     接触ボタンと重なると右側のクリックを奪われうる。接触プロンプトをそれより前面へ上げる。 */
  z-index: 100058;
  display: flex;
  gap: 0.4rem;
  background: rgba(15, 23, 32, 0.92);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.5rem;
  padding: 0.4rem;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
}
.card-open-prompt button {
  padding: 0.35rem 0.6rem;
  border-radius: 0.4rem;
  border: none;
  cursor: pointer;
  font-family: sans-serif;
  font-size: 0.75rem;
  font-weight: bold;
  white-space: nowrap;
  color: #fff;
}
.card-open-prompt-yes {
  background: linear-gradient(160deg, #22c55e, #15803d);
}
.card-open-prompt-yes:hover {
  background: linear-gradient(160deg, #4ade80, #16a34a);
}
.card-open-prompt-no {
  background: linear-gradient(160deg, #64748b, #334155);
}
.card-open-prompt-no:hover {
  background: linear-gradient(160deg, #94a3b8, #475569);
}
/* ユーザー報告2026-08-17「スマホで接触するときに駒の上に出る『接触する』ボタンが小さすぎ」。
   .card-open-prompt は「🤝 接触する」と「オープンする/しない」の両方で共用（駒の上に出る
   浮遊ボタン）。盤面はステージtransformで縮小されるため、スマホではタップ領域を大きくする。 */
body.is-phone-device .card-open-prompt {
  gap: 0.7rem;
  padding: 0.7rem;
}
body.is-phone-device .card-open-prompt button {
  font-size: 1.6rem;
  padding: 0.7rem 1.2rem;
  border-radius: 0.6rem;
}

/* カードが手札に加わった時の「何をゲットしたか」トースト。#end-turn-button・#draw-button・
   #board-zoom-buttonの上に重ねて表示されるよう、その分の高さを空けてbottomを取っている。
   クリックすると即座に消せる（右上の✕ボタンでも同様）。サイズ・表示時間は管理者モードで
   調整できる（--hand-pickup-toast-scale・--hand-pickup-toast-duration、hand-announcer.js参照）。 */
:root {
  --hand-pickup-toast-scale: 1.3;
  --hand-pickup-toast-duration: 5; /* 秒 */
}
.hand-pickup-toast {
  position: fixed;
  bottom: 16rem; /* #hand-shuffle-button（bottom: 12.6rem）の上に出るよう積み増した */
  right: 1.2rem;
  z-index: 10500;
  max-width: calc(20rem * var(--hand-pickup-toast-scale));
  background: rgba(15, 23, 32, 0.97);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.5rem;
  padding: calc(0.6rem * var(--hand-pickup-toast-scale)) calc(1.6rem * var(--hand-pickup-toast-scale)) calc(0.6rem * var(--hand-pickup-toast-scale)) calc(0.8rem * var(--hand-pickup-toast-scale));
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.5);
  font-family: sans-serif;
  color: #e2e8f0;
  cursor: pointer;
  opacity: 0;
  transform: translateY(0.6rem);
  transition: opacity 0.3s ease, transform 0.3s ease;
}
.hand-pickup-toast.show {
  opacity: 1;
  transform: translateY(0);
}
.hand-pickup-toast-close {
  /* トースト用に一回り小さく、既定の.modal-close-xより少し内側に */
  top: 0.4rem;
  right: 0.4rem;
}
.hand-pickup-toast-title {
  font-size: calc(0.72rem * var(--hand-pickup-toast-scale));
  opacity: 0.8;
  margin-bottom: 0.3rem;
}
.hand-pickup-toast-cards {
  display: flex;
  flex-wrap: wrap;
  gap: calc(0.5rem * var(--hand-pickup-toast-scale));
}
.hand-pickup-toast-card {
  width: calc(4.2rem * var(--hand-pickup-toast-scale));
  text-align: center;
}
.hand-pickup-toast-card img,
.hand-pickup-toast-img {
  position: relative;
  width: calc(4.2rem * var(--hand-pickup-toast-scale));
  height: calc(4.2rem * var(--hand-pickup-toast-scale));
  object-fit: cover;
  overflow: hidden;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border-radius: 0.3rem;
  display: block;
}
.hand-pickup-toast-name {
  font-size: calc(0.65rem * var(--hand-pickup-toast-scale));
  line-height: 1.3;
  margin-top: 0.2rem;
}
.hand-pickup-toast-hidden-note {
  font-size: calc(0.68rem * var(--hand-pickup-toast-scale));
  opacity: 0.7;
  margin-top: 0.4rem;
}

/* オンライン対戦の相手ゲート侵攻ボーナス通知（src/gate-invasion-modal.js）。ローカル版の
   showBonusStepModal（gate-invasion.js）と似た見た目の、画面中央・1件ずつ自動送りの
   モーダル。サイズ・1ステップの表示時間は管理者モードで調整できる
   （--gate-invasion-modal-size・--gate-invasion-modal-step-duration）。 */
:root {
  --gate-invasion-modal-size: 28rem;
  --gate-invasion-modal-step-duration: 3.5; /* 秒 */
}
.gate-invasion-modal-cards {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.6rem;
}
.gate-invasion-modal-card {
  width: 4.6rem;
  text-align: center;
}
.gate-invasion-modal-card img,
.gate-invasion-modal-card-img {
  position: relative;
  width: 4.6rem;
  height: 4.6rem;
  object-fit: cover;
  overflow: hidden;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border-radius: 0.3rem;
  display: block;
}
.gate-invasion-modal-card-name {
  font-size: 0.68rem;
  line-height: 1.3;
  margin-top: 0.2rem;
}
.gate-invasion-modal-hidden-note {
  font-size: 0.72rem;
  opacity: 0.7;
  margin-bottom: 0.6rem;
}

/* 最後のロック承認バナー（src/final-lock-approval.js）。ユーザー報告「画面上部でなく
   画面中央に点滅強調など目立つように表示してください」への対応で、画面上部から
   画面中央（縦横とも50%）へ変更し、pendingFinalLockが立っている間は境界線の発光を
   点滅させて見逃しにくくした。displayではなくopacity/pointer-eventsで隠す方式は
   従来通り（フェードのトランジションを効かせるため）。 */
#final-lock-approval-banner {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 10450;
  background: linear-gradient(160deg, #422006, #1c1004);
  border: 2px solid #facc15;
  border-radius: 0.7rem;
  padding: 0.8rem 1.4rem;
  text-align: center;
  font-family: sans-serif;
  color: #fef3c7;
  box-shadow: 0 0 1.5rem rgba(250, 204, 21, 0.5), 0 10px 30px rgba(0, 0, 0, 0.6);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}
#final-lock-approval-banner.is-visible {
  opacity: 1;
  pointer-events: auto;
  animation: final-lock-approval-blink 1.1s ease-in-out infinite;
}
@keyframes final-lock-approval-blink {
  0%, 100% {
    box-shadow: 0 0 1.5rem rgba(250, 204, 21, 0.5), 0 10px 30px rgba(0, 0, 0, 0.6);
    border-color: #facc15;
  }
  50% {
    box-shadow: 0 0 3rem rgba(250, 204, 21, 0.95), 0 10px 30px rgba(0, 0, 0, 0.6);
    border-color: #fff7cc;
  }
}
/* 「アニメーションを減らす」設定がONの間は、他の常時光る演出と同じく点滅を止める。 */
body.reduce-glow #final-lock-approval-banner.is-visible {
  animation: none;
}
.final-lock-approval-title {
  font-weight: bold;
  font-size: 1rem;
  margin-bottom: 0.2rem;
}
.final-lock-approval-status {
  font-size: 0.8rem;
  opacity: 0.9;
  margin-bottom: 0.5rem;
}
.final-lock-approval-buttons {
  display: flex;
  gap: 0.6rem;
  justify-content: center;
}
.final-lock-approval-approve,
.final-lock-approval-reject {
  font-size: 0.85rem;
  border-radius: 0.4rem;
  padding: 0.35rem 0.9rem;
  cursor: pointer;
  border: 1px solid rgba(255, 255, 255, 0.3);
  font-weight: bold;
}
.final-lock-approval-approve {
  background: rgba(34, 172, 56, 0.85);
  color: #f0fdf4;
}
.final-lock-approval-approve:hover {
  background: rgba(34, 172, 56, 1);
}
.final-lock-approval-reject {
  background: rgba(153, 27, 27, 0.7);
  color: #fef2f2;
}
.final-lock-approval-reject:hover {
  background: rgba(153, 27, 27, 0.95);
}

/* 【2026-10-12】ユーザー報告「最後のロックの承認で、承認＝緑／ゴメンナサイ＝赤だったので
   押し間違えた」。赤は「やめる・キャンセル」に見えるのに、ここでは**ゴメンナサイの方が
   能動的な選択**なので読み違える。**色を入れ替えない**（入れ替えると今度は逆向きに誤読される）。
   ゴメンナサイ側に**実際のカードそのもの**を出し、承認側からは色を抜く（ユーザー了承済み）。
   🔴 `.final-lock-approval-approve` / `-reject` は**他のバナーでも流用されている**
   （タイマーのオン/オフ・自動処理のオン/オフ・接触の承認）ので、**必ずこのバナーの中だけ**に
   効くよう `#final-lock-approval-banner` で囲う。囲わないと、関係ないバナーの「いいえ」まで
   色が変わる。 */
#final-lock-approval-banner .final-lock-approval-approve {
  background: rgba(148, 163, 184, 0.22);
  /* 🔴 文字色を決め打ちしない（教訓15）。このバナーは明るいテーマだとアイボリー地に変わるので、
     白っぽい色のままだと**明るいテーマで文字が消える**（実際にそうなった＝撮って気づいた）。
     ⚠ `color` を書かないだけでは足りない——共通の `.final-lock-approval-approve` が
     `#f0fdf4`（ほぼ白）を当てており、それは緑地の上でだけ成り立つ色だった。明示的に
     `inherit` にして、バナー自身が両テーマで持っている文字色を継ぐ。 */
  color: inherit;
  border-color: currentColor;
  opacity: 0.85;
}
#final-lock-approval-banner .final-lock-approval-approve:hover {
  background: rgba(148, 163, 184, 0.36);
}
#final-lock-approval-banner .final-lock-approval-gomennasai {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: rgba(88, 28, 135, 0.55);
  color: #f5e8ff;
  border-color: var(--color-purple, #a855f7);
  padding: 0.3rem 0.8rem 0.3rem 0.35rem;
}
#final-lock-approval-banner .final-lock-approval-gomennasai:hover {
  background: rgba(88, 28, 135, 0.82);
}
/* このゲームのカードは正方形（物理のカードがそうなので）。 */
.final-lock-approval-gomennasai-card {
  width: 2.4rem;
  height: 2.4rem;
  flex: 0 0 auto;
  border-radius: 0.2rem;
  object-fit: cover;
  display: block;
  box-shadow: 0 0 0.4rem rgba(168, 85, 247, 0.75);
}

/* 「タイマーをオン、オフ」ボタン（src/timer-toggle.js、続き64）。
   ユーザー要望（続き68）「常時表示ではなく、基本時間アイコンを押すとひょこっと出てくる
   仕様にしたい」への対応で、#phase-guide-bar内に常設する横並びボタンから、基本時間
   アイコン（.turn-timer-base-clock）の真上に浮かぶポップオーバーに変更した。
   #phase-guide-bar自体がposition:fixedのため、この子要素をposition:absoluteにすれば
   そのまま基準にできる（新たにposition:relativeを足す必要は無い）。 */
#timer-toggle-button {
  position: absolute;
  bottom: calc(100% + 0.6rem);
  right: 0;
  /* 「自分/相手のターンです」表示(#phase-automation-skip-button, z-index:1001)より前に出す
     （z-index未指定だと背面に回ってクリックできない＝ユーザー報告）。 */
  z-index: 1002;
  font-family: sans-serif;
  font-size: 0.7rem;
  font-weight: bold;
  padding: 0.3rem 0.6rem;
  border-radius: 0.4rem;
  border: 1px solid rgba(148, 163, 184, 0.4);
  background: rgba(15, 23, 32, 0.7);
  color: #e2e8f0;
  cursor: pointer;
  white-space: nowrap;
  box-shadow: 0 0.2rem 0.6rem rgba(0, 0, 0, 0.5);
}
#timer-toggle-button:hover:not(:disabled) {
  background: rgba(56, 189, 248, 0.25);
  border-color: rgba(56, 189, 248, 0.6);
}
#timer-toggle-button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* タイマーオン/オフの承認バナー（src/timer-toggle.js、続き64）。最後のロック承認
   バナーと全く同じ見た目・仕組み（final-lock-approval-*クラスを流用）だが、
   万一両方が同時に出てもさすがに完全に重ならないよう、縦位置だけ少しずらす。 */
#timer-toggle-approval-banner {
  position: fixed;
  top: 30%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 10440;
  background: linear-gradient(160deg, #422006, #1c1004);
  border: 2px solid #38bdf8;
  border-radius: 0.7rem;
  padding: 0.8rem 1.4rem;
  text-align: center;
  font-family: sans-serif;
  color: #fef3c7;
  box-shadow: 0 0 1.5rem rgba(56, 189, 248, 0.5), 0 10px 30px rgba(0, 0, 0, 0.6);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}
#timer-toggle-approval-banner.is-visible {
  opacity: 1;
  pointer-events: auto;
}

/* 自動処理モードのオン/オフ承認バナー（src/main.js、続き66）。final-lock-approval-*/
   timer-toggle-approval-bannerと同じ見た目・仕組みで、縦位置だけさらにずらす。 */
#auto-processing-toggle-approval-banner {
  position: fixed;
  top: 70%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 10440;
  background: linear-gradient(160deg, #1e293b, #0f172a);
  border: 2px solid #a78bfa;
  border-radius: 0.7rem;
  padding: 0.8rem 1.4rem;
  text-align: center;
  font-family: sans-serif;
  color: #fef3c7;
  box-shadow: 0 0 1.5rem rgba(167, 139, 250, 0.5), 0 10px 30px rgba(0, 0, 0, 0.6);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}
#auto-processing-toggle-approval-banner.is-visible {
  opacity: 1;
  pointer-events: auto;
}

/* 接触の承認/拒否モーダル（src/contact-approval.js）。final-lock-approval-bannerと同じ
   「pendingを見て自動表示」の仕組みだが、ユーザーが明示的に「モーダル」を指定したため
   バナーではなく背景を暗くする中央モーダルにしてある。承認/拒否いずれかのボタンで
   解決するまで消えない（閉じるボタンは無い）。 */
#contact-approval-backdrop {
  position: fixed;
  inset: 0;
  z-index: 10610;
  background: rgba(0, 0, 0, 0.6);
}
#contact-approval-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(26rem, 90vw);
  /* #134: 儀式ピック/ゲート侵攻の奪取演出(#sleight-ritual-modal, z:10621)より上に置く。
     以前は10611で、ゲート侵攻の奪取演出（sleight-ritual-card, pointer-events:auto）が
     この確認モーダルのボタンを覆ってしまい、ゴメンナサイ等の確認が押せなかった。 */
  z-index: 10631;
  background: linear-gradient(160deg, #422006, #1c1004);
  border: 2px solid #facc15;
  border-radius: 0.7rem;
  padding: 1rem 1.4rem;
  text-align: center;
  font-family: sans-serif;
  color: #fef3c7;
  box-shadow: 0 0 1.5rem rgba(250, 204, 21, 0.5), 0 20px 50px rgba(0, 0, 0, 0.6);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}
#contact-approval-modal.is-visible {
  opacity: 1;
  pointer-events: auto;
}
.contact-approval-title {
  font-weight: bold;
  font-size: 1.05rem;
  margin-bottom: 0.5rem;
}
.contact-approval-body {
  font-size: 0.85rem;
  line-height: 1.6;
  opacity: 0.95;
  margin-bottom: 0.8rem;
}
.contact-approval-buttons {
  display: flex;
  gap: 0.6rem;
  justify-content: center;
}
.contact-approval-approve,
.contact-approval-reject {
  font-size: 0.85rem;
  border-radius: 0.4rem;
  padding: 0.4rem 1rem;
  cursor: pointer;
  border: 1px solid rgba(255, 255, 255, 0.3);
  font-weight: bold;
}
.contact-approval-approve {
  background: rgba(34, 172, 56, 0.85);
  color: #f0fdf4;
}
.contact-approval-approve:hover {
  background: rgba(34, 172, 56, 1);
}
.contact-approval-reject {
  background: rgba(153, 27, 27, 0.7);
  color: #fef2f2;
}
.contact-approval-reject:hover {
  background: rgba(153, 27, 27, 0.95);
}

/* タッチ端末専用の操作確認モーダル（main.jsのconfirmTouchAction、続き62）。
   #contact-approval-modalと同じ「中央固定・.contact-approval-*クラスを使い回す」
   構成だが、こちらは表示のたびにappendChild/removeで出し入れするだけの単純な作りの
   ため、opacityのフェード制御（is-visibleトグル）は不要——最初から見える状態でよい。
   続き79で発覚: このCSSルール自体が無く（IDが#anytime-hand-effect-confirm-modalの
   ままで、続き77で予約制を廃止した際に対応するJSごと削除されていた）、
   confirmTouchAction()が作る#touch-action-confirm-modal自体には位置指定が一切
   無いまま放置されていた。中身（.contact-approval-title/-buttons）は見た目を持つが
   コンテナ自身がposition:fixedで中央固定されていないため、暗転した背景
   （createBackdrop、z-index:10610）だけが画面全体を覆い、肝心のYes/Noボタンは
   どこか別の場所（bodyの先頭付近）に埋もれて見えなくなっていた——ユーザー報告
   「画面が暗めになって手札以外のタップが効かなくなった、モーダルが出てるんだろうけど
   見えなくなっちゃってる」の原因。使われなくなっていたこのCSSルールをIDだけ
   差し替えて再利用する形で復旧した。 */
/* #generic-confirm-modal（続き89、main.jsのconfirmGenericYesNo）はconfirmTouchAction
   と同じ見た目でよい任意選択用の汎用Yes/Noモーダルのため、同じルールを共有する。 */
#touch-action-confirm-modal,
#generic-confirm-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(26rem, 90vw);
  /* #134: 儀式ピック/奪取演出(#sleight-ritual-modal, z:10621)より上（contact-approvalと同値）。
     ゴメンナサイ等の確認ボタンが奪取演出のカードに覆われて押せない不具合を防ぐ。 */
  z-index: 10631;
  background: linear-gradient(160deg, #422006, #1c1004);
  border: 2px solid #facc15;
  border-radius: 0.7rem;
  padding: 1rem 1.4rem;
  text-align: center;
  font-family: sans-serif;
  color: #fef3c7;
  box-shadow: 0 0 1.5rem rgba(250, 204, 21, 0.5), 0 20px 50px rgba(0, 0, 0, 0.6);
}
/* 確認モーダルに添えるカード画像（confirmTouchActionにcardIdを渡した時。ユーザー要望2026-08-13
   「このカードを選びますか等の確認モーダルにカード画像も添えたい」）。タイトルとボタンの間。 */
#touch-action-confirm-modal .touch-action-confirm-card {
  display: block;
  position: relative;
  /* <img>→<div>（テキスト合成/画像）へ置換。divは内在サイズが無いため正方形サイズを明示。 */
  width: min(60%, 40vh);
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  margin: 0.7rem auto 0.2rem;
  border-radius: 0.4rem;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.55);
}

/* ゲーム開始時の操作説明モーダル（game-intros.js、ユーザー要望2026-08-12）。 */
.game-intro-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(28rem, 92vw);
  z-index: 10601;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(56, 189, 248, 0.4);
  border-radius: 0.7rem;
  padding: 1.3rem 1.4rem 1.1rem;
  color: #e2e8f0;
  font-family: sans-serif;
  box-shadow: 0 20px 55px rgba(0, 0, 0, 0.65);
}
.game-intro-title {
  font-size: 1.15rem;
  font-weight: bold;
  margin-bottom: 0.7rem;
}
.game-intro-body {
  font-size: 0.9rem;
  line-height: 1.7;
  color: #cbd5e1;
  margin-bottom: 1.2rem;
}
.game-intro-actions {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.5rem;
}
.game-intro-ok {
  padding: 0.55rem 1rem;
  background: #0284c7;
  border: none;
  border-radius: 0.4rem;
  color: #fff;
  font-size: 0.9rem;
  font-weight: bold;
  cursor: pointer;
}
.game-intro-ok:hover {
  filter: brightness(1.1);
}
.game-intro-alloff {
  padding: 0.35rem;
  background: none;
  border: none;
  color: rgba(148, 163, 184, 0.85);
  font-size: 0.78rem;
  cursor: pointer;
  text-decoration: underline;
}
.game-intro-alloff:hover {
  color: #cbd5e1;
}

/* 用語リンク（glossary-linkify.js）。ルール説明・チュートリアル文中の基本用語をクリック可能に。 */
.glossary-term {
  color: #7dd3fc;
  text-decoration: underline dotted;
  text-underline-offset: 2px;
  cursor: pointer;
}
.glossary-term:hover {
  color: #bae6fd;
  text-decoration-style: solid;
}
.glossary-popup {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(24rem, 92vw);
  max-height: 80vh;
  overflow-y: auto;
  z-index: 10701;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(56, 189, 248, 0.5);
  border-radius: 0.7rem;
  padding: 1.1rem 1.2rem 1rem;
  color: #e2e8f0;
  font-family: sans-serif;
  box-shadow: 0 20px 55px rgba(0, 0, 0, 0.65);
}
.glossary-popup-title {
  font-size: 1.05rem;
  font-weight: bold;
  margin-bottom: 0.6rem;
  color: #bae6fd;
}
.glossary-popup-paragraph {
  font-size: 0.9rem;
  line-height: 1.7;
  color: #cbd5e1;
  margin: 0 0 0.6rem;
}
.glossary-popup-close {
  display: block;
  margin: 0.4rem 0 0 auto;
  padding: 0.4rem 1.1rem;
  background: rgba(56, 189, 248, 0.18);
  border: 1px solid rgba(56, 189, 248, 0.6);
  border-radius: 0.4rem;
  color: #e2e8f0;
  cursor: pointer;
  font-size: 0.85rem;
}
.glossary-popup-close:hover {
  filter: brightness(1.12);
}

/* 「今後このモーダルを表示しない」（confirmTouchAction、ロック前/手札使用前の確認）。
   Yes/Noの下に控えめな小さいテキストリンクとして置く。押すと以後この確認を出さない
   設定にして、その回の操作はそのまま実行する（再表示はオプションの基本設定から）。 */
#touch-action-confirm-dontshow {
  display: block;
  margin: 0.7rem auto 0;
  background: none;
  border: none;
  color: rgba(254, 243, 199, 0.65);
  font-family: sans-serif;
  font-size: 0.72rem;
  text-decoration: underline;
  cursor: pointer;
}
#touch-action-confirm-dontshow:hover {
  color: #fef3c7;
}

/* ゲストログイン前の注意確認（続き83、opening-screen.jsのconfirmGuestLogin）。
   #touch-action-confirm-modalと同じ「中央固定・.contact-approval-*クラスを
   使い回す」構成。注意喚起の意味合いを強めるため、他の確認モーダル（金色系）とは
   別の赤系の縁取りにする。
   ユーザー報告（続き103）「ゲストでログインを押しても何も起きない」の原因判明:
   このモーダルは#opening-screen（z-index:50000）がまだ表示されている間に出る
   （ログイン完了前の確認のため）唯一の確認モーダルだが、z-indexが他の対局中専用の
   確認モーダル（#touch-action-confirm-modal/#generic-confirm-modal、続き89、
   z-index:10611のまま）と同じ値を使い回していたため、#opening-screenの裏に
   完全に隠れて見えなくなっていた。main.jsのopenFirstLoginProfileModal
   （同じく#opening-screenより手前に出す必要がある、z-index:50100）と同じ考え方で
   #opening-screenより確実に高いz-indexに変更した。 */
#guest-login-confirm-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(26rem, 90vw);
  z-index: 50110;
  background: linear-gradient(160deg, #3f1414, #1c0808);
  border: 2px solid #f87171;
  border-radius: 0.7rem;
  padding: 1rem 1.4rem;
  text-align: center;
  font-family: sans-serif;
  color: #fef2f2;
  box-shadow: 0 0 1.5rem rgba(248, 113, 113, 0.5), 0 20px 50px rgba(0, 0, 0, 0.6);
}

/* 手品師の技専用（ユーザー要望「儀式的に相手の手札が裏向きの状態のまま画面中央に
   拡大表示されてその中から選ぶ方式にしたい」、main.jsのrequestOpponentHandRitualPick）。
   紫系にして他の確認モーダル（金色系）と見分けやすくする。 */
#sleight-ritual-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  max-width: 90vw;
  z-index: 10621;
  background: linear-gradient(160deg, #1e1b4b, #0f0a24);
  border: 2px solid #a78bfa;
  border-radius: 0.8rem;
  padding: 1.2rem 1.6rem;
  text-align: center;
  font-family: sans-serif;
  color: #ede9fe;
  box-shadow: 0 0 1.5rem rgba(167, 139, 250, 0.5), 0 20px 50px rgba(0, 0, 0, 0.6);
}
.sleight-ritual-title {
  font-weight: bold;
  font-size: 0.95rem;
  margin-bottom: 0.9rem;
}
.sleight-ritual-cards {
  display: flex;
  gap: 0.6rem;
  justify-content: center;
  flex-wrap: wrap;
}
.sleight-ritual-card {
  /* ユーザー報告「手札を選ぶときのカードが正方形になっていない」への対応。 */
  position: relative; /* カード面テキストのマウント(.card-face-mount inset:0)の包含ブロック */
  width: 6rem;
  height: 6rem;
  overflow: hidden;
  background-size: cover;
  background-position: center;
  border-radius: 0.3rem;
  cursor: pointer;
  transition: transform 0.15s ease;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
}
.sleight-ritual-card:hover {
  transform: translateY(-0.4rem) scale(1.05);
}
/* ゲート侵攻の複数枚奪取（requestOpponentHandRitualMultiPick）: タップで選択中のカードを
   金色で強調。確定ボタンは必要枚数に達するまで無効（ユーザー要望2026-08-13）。 */
.sleight-ritual-card.is-selected {
  transform: translateY(-0.6rem) scale(1.1);
  box-shadow: 0 0 1.2rem rgba(250, 204, 21, 0.95), 0 6px 14px rgba(0, 0, 0, 0.6);
  outline: 3px solid #facc15;
  z-index: 1;
}
#sleight-ritual-multi-confirm {
  margin-top: 1rem;
  align-self: center;
}
#sleight-ritual-multi-confirm:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
/* ユーザー要望「スリカエで1枚引っこ抜く前にシャッフル演出が欲しい」。ピック開始前の
   一定時間だけ、裏向きの手札を中央へ寄せて戻す動きを繰り返し、シャッフルしている様子を
   見せる（この間はクリック不可＝pointer-events:none）。各カードの--shuffle-x/--shuffle-rotと
   animation-delayはmain.jsのrequestOpponentHandRitualPickが1枚ごとに設定する。 */
@keyframes sleight-shuffle {
  0% { transform: translateX(0) rotate(0deg); }
  45% { transform: translateX(var(--shuffle-x, 0)) rotate(var(--shuffle-rot, 0deg)); }
  55% { transform: translateX(var(--shuffle-x, 0)) rotate(var(--shuffle-rot, 0deg)); }
  100% { transform: translateX(0) rotate(0deg); }
}
#sleight-ritual-modal.is-shuffling .sleight-ritual-card {
  pointer-events: none;
  animation: sleight-shuffle 0.55s ease-in-out infinite;
}
/* ユーザー要望「奪われる側にも表向きで表示されて相手のマウスがどこにホバー
   されているかわかるようにしてほしい」。相手側の画面から届いたホバー位置に
   対応するカードを光らせる（main.jsのopenRitualPickWatch/onRitualPickHoverEvents）。 */
.sleight-ritual-card.is-hovered {
  transform: translateY(-0.4rem) scale(1.05);
  box-shadow: 0 0 1rem rgba(250, 204, 21, 0.9), 0 4px 10px rgba(0, 0, 0, 0.5);
  outline: 3px solid #facc15;
}
/* ユーザー要望「奪われるカードが決まったら、そのカードを拡大し『このカードが
   奪われました』的な感じでわかるようにしてほしい」（main.jsのrevealRitualPickWatchResult）。
   選ばれた1枚だけ赤系に発光・拡大させ、選ばれなかった残りは薄暗くして目立たなくする。 */
.sleight-ritual-card.is-stolen-reveal {
  transform: translateY(-0.6rem) scale(1.25);
  box-shadow: 0 0 1.4rem rgba(248, 113, 113, 0.95), 0 6px 14px rgba(0, 0, 0, 0.6);
  outline: 3px solid #f87171;
  z-index: 1;
}
.sleight-ritual-card.is-not-picked {
  opacity: 0.35;
  filter: grayscale(60%);
  transform: scale(0.92);
}
/* ゲート侵攻の奪取（#9）はサーバーが無作為に決めた札を左→右にめくって公開するだけで、
   スリカエの本物のピックのように個別の札を選べるわけではない。個別カードのホバーで浮く演出は
   「この札を選んでいる」誤解を招くので消す（タップでめくる操作自体は有効なままにする）。
   めくり終えた札(is-stolen-reveal)の拡大表示はそのまま残す。 */
#sleight-ritual-modal.is-steal-reveal .sleight-ritual-card:not(.is-stolen-reveal):hover {
  transform: none;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
}
/* 合同建設の「山札から/手札から」選択（main.jsのrequestPlaceSourceChoiceForEffect）。 */
.place-source-choice-buttons {
  display: flex;
  gap: 0.8rem;
  justify-content: center;
}
.place-source-choice-buttons button {
  padding: 0.6rem 1rem;
  background: rgba(167, 139, 250, 0.2);
  color: #ede9fe;
  border: 1px solid rgba(167, 139, 250, 0.6);
  border-radius: 0.4rem;
  cursor: pointer;
  font-family: sans-serif;
  font-size: 0.85rem;
}
.place-source-choice-buttons button:hover {
  background: rgba(167, 139, 250, 0.4);
}

/* ターン終了時の中央告知（src/turn-announce.js）。ゲーム操作を妨げないよう
   pointer-events:noneにし、一瞬だけ画面中央に大きく次のプレイヤー名を表示してから
   自動的に消える（クリック不要のトースト、victory-modalとは違い操作を待たない）。 */
#turn-announce-toast {
  position: fixed;
  top: 40%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.7);
  z-index: 9000;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s ease, transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
#turn-announce-toast.show {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
/* 【ユーザー要望2026-09-05・②】ターンの始まりに、その人の席の方向から光が走ってくる。
   モーダルを増やすと待ち時間が伸びるので、今のモーダルに重ねるだけの一発演出にした
   （1.1秒で消える・クリックは一切拾わない）。色はその人の駒の色。 */
.turn-announce-sweep {
  position: fixed;
  inset: 0;
  z-index: 8999;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(
    var(--turn-announce-sweep-angle, 0deg),
    transparent 0%,
    color-mix(in srgb, var(--turn-announce-color, #facc15) 55%, transparent) 18%,
    transparent 42%
  );
  background-size: 100% 300%;
  animation: turn-announce-sweep 1.1s cubic-bezier(0.25, 0.8, 0.3, 1) forwards;
}
/* 席の辺＝光がやって来る向き。手前(bottom)の席なら画面の下から上へ。 */
.turn-announce-sweep[data-from="bottom"] { --turn-announce-sweep-angle: 0deg; }
.turn-announce-sweep[data-from="top"] { --turn-announce-sweep-angle: 180deg; }
.turn-announce-sweep[data-from="left"] { --turn-announce-sweep-angle: 90deg; }
.turn-announce-sweep[data-from="right"] { --turn-announce-sweep-angle: 270deg; }
@keyframes turn-announce-sweep {
  0% {
    opacity: 0;
    background-position: 50% 100%;
  }
  25% {
    opacity: 0.85;
  }
  100% {
    opacity: 0;
    background-position: 50% 0%;
  }
}
body.reduce-glow .turn-announce-sweep {
  animation: none;
  opacity: 0;
}

.turn-announce-label {
  font-family: sans-serif;
  font-weight: bold;
  font-size: 2.2rem;
  color: #fef3c7;
  background: linear-gradient(160deg, rgba(66, 32, 6, 0.92), rgba(28, 16, 4, 0.92));
  border: 3px solid #facc15;
  border-radius: 1rem;
  padding: 1rem 2.2rem;
  text-shadow: 0 0 0.6rem rgba(250, 204, 21, 0.8);
  /* 【②】枠と光は、その番のプレイヤーの駒の色（未指定なら従来の金色）。 */
  border-color: var(--turn-announce-color, #facc15);
  text-shadow: 0 0 0.6rem color-mix(in srgb, var(--turn-announce-color, #facc15) 80%, transparent);
  box-shadow:
    0 0 2.5rem color-mix(in srgb, var(--turn-announce-color, #facc15) 55%, transparent),
    0 20px 50px rgba(0, 0, 0, 0.6);
  white-space: nowrap;
}
/* 【②】自分の番の時だけ、名前が一拍だけ大きく脈打つ（相手の番は静かなまま）。 */
.turn-announce-label.is-self {
  animation: turn-announce-self-beat 0.9s cubic-bezier(0.34, 1.56, 0.64, 1) 0.25s 1 both;
}
@keyframes turn-announce-self-beat {
  0% { transform: scale(1); }
  35% { transform: scale(1.14); }
  100% { transform: scale(1); }
}
body.reduce-glow .turn-announce-label.is-self {
  animation: none;
}

/* フェイズ自動進行（src/phase-automation.js）。ユーザー要望「効果自動処理がオンの時は
   フェイズも自動で流れるように。フェイズごとに画面中央に『ロックフェイズ』とかを
   英語とカタカナルビでかっこよく表示」。turn-announce-toastと同じ「一瞬だけ表示して
   自動的に消える」トーストパターン。 */
.phase-announce-toast {
  position: fixed;
  top: 32%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.7);
  z-index: 9001;
  pointer-events: none;
  opacity: 0;
  text-align: center;
  transition: opacity 0.4s ease, transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.phase-announce-toast.show {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
.phase-announce-title {
  font-family: sans-serif;
  font-weight: bold;
  font-size: 2.6rem;
  letter-spacing: 0.15em;
  color: #e0f2fe;
  text-shadow: 0 0 0.8rem rgba(56, 189, 248, 0.8), 0 4px 12px rgba(0, 0, 0, 0.7);
}
.phase-announce-ruby {
  display: block;
  font-size: 0.95rem;
  letter-spacing: 0.3em;
  color: #7dd3fc;
  margin-top: 0.1rem;
}
.phase-announce-desc {
  margin-top: 0.4rem;
  font-family: sans-serif;
  font-size: 1rem;
  color: #fef3c7;
  background: linear-gradient(160deg, rgba(66, 32, 6, 0.92), rgba(28, 16, 4, 0.92));
  border: 2px solid #facc15;
  border-radius: 0.6rem;
  padding: 0.4rem 1.2rem;
  display: inline-block;
  box-shadow: 0 0 1.2rem rgba(250, 204, 21, 0.4), 0 10px 25px rgba(0, 0, 0, 0.5);
}

/* 常設のスキップボタン。ユーザー報告「他のアイコンと被っている」への対応で、固定座標
   ではなく#phase-guide-bar（flexコンテナ）内でロックボタンの直後（＝右隣）にDOM上の
   兄弟として挿入する方式に変更した（phase-automation.js側）。ここでは通常のflexアイテム
   として振る舞わせるだけでよく、position:fixedは不要になった。ムーブフェイズは
   「移動」か「接触」を必ず行う必要があるルールのため対象外（JS側でdisplay制御）。
   ユーザー要望「難しければ管理者モードに位置調整を追加してほしい」への対応で、
   微調整用のオフセットCSS変数（--phase-skip-button-offset-x/-y、admin.js）も
   pulseアニメーションのtransformに含めてある。 */
#phase-automation-skip-button {
  /* ユーザー要望（続き76、当初は「案内板の右へ」だったが後に「案内板の上へ」に
     変更）。#phase-guide-bar（position:fixed）の子のまま、position:absoluteで
     フローから外し、バーの真上に浮かせる——src/timer-toggle.jsの#timer-toggle-button
     （バーの右上に浮かぶポップオーバー）と全く同じ技法。左右が被らないよう、
     あちらはright:0、こちらはleft:0にしてある。 */
  position: absolute;
  bottom: calc(100% + 0.6rem);
  left: 0;
  z-index: 1001;
  display: none;
  padding: 0.5rem 1.1rem;
  background: linear-gradient(160deg, #facc15, #f59e0b);
  border: none;
  border-radius: 0.5rem;
  color: #1c1006;
  font-weight: bold;
  font-family: sans-serif;
  font-size: 0.9rem;
  cursor: pointer;
  box-shadow: 0 0 1rem rgba(250, 204, 21, 0.6), 0 6px 14px rgba(0, 0, 0, 0.5);
  animation: phase-automation-skip-pulse 1.6s ease-in-out infinite;
}
@keyframes phase-automation-skip-pulse {
  0%, 100% { transform: translate(var(--phase-skip-button-offset-x, 0rem), var(--phase-skip-button-offset-y, 0rem)) scale(1); }
  50% { transform: translate(var(--phase-skip-button-offset-x, 0rem), var(--phase-skip-button-offset-y, 0rem)) scale(1.06); }
}
#phase-automation-skip-button:hover {
  filter: brightness(1.1);
}
/* マイデッキ戦（マイデッキ.txt）: ロックフェイズの「マイデッキから1枚引く」ボタン。
   スキップボタン(left:0, 案内板の真上)と縦に段積みして重ならないよう、さらに一段上に置く。
   スキップ(黄)と区別できるよう青系にする。 */
#phase-automation-mydeck-button {
  position: absolute;
  bottom: calc(100% + 3.2rem);
  left: 0;
  z-index: 1001;
  display: none;
  padding: 0.5rem 1.1rem;
  background: linear-gradient(160deg, #38bdf8, #0ea5e9);
  border: none;
  border-radius: 0.5rem;
  color: #04121f;
  font-weight: bold;
  font-family: sans-serif;
  font-size: 0.9rem;
  cursor: pointer;
  white-space: nowrap;
  box-shadow: 0 0 1rem rgba(56, 189, 248, 0.55), 0 6px 14px rgba(0, 0, 0, 0.5);
}
#phase-automation-mydeck-button:hover:not(:disabled) {
  filter: brightness(1.1);
}
#phase-automation-mydeck-button:disabled {
  opacity: 0.6;
  cursor: default;
}
/* ユーザー要望（続き81）「スキップボタンについては個別でサイズ位置調整も追加して」。
   このボタンは常時pulseアニメーション中で、通常のtransformプロパティは実行中の
   キーフレームに丸ごと上書きされてしまう（transformの値はカスケードで合成されず、
   実行中のアニメーションのtransformが常に勝つ）ため、スマホ専用の位置・サイズは
   静的なtransformとしてではなく、専用のキーフレーム名に差し替える形で実現する
   （animation-nameだけを上書きし、duration等は元のanimationショートハンドの
   ままにする）。transform-originはボタン自身の固定辺（bottom/left）に合わせる。 */
@keyframes phase-automation-skip-pulse-phone {
  0%, 100% {
    transform: translate(
      calc(var(--phase-skip-button-offset-x, 0rem) + var(--phase-skip-button-pos-phone-x, 0rem)),
      calc(var(--phase-skip-button-offset-y, 0rem) + var(--phase-skip-button-pos-phone-y, 0rem))
    ) scale(var(--phase-skip-button-scale-phone, 1));
  }
  50% {
    transform: translate(
      calc(var(--phase-skip-button-offset-x, 0rem) + var(--phase-skip-button-pos-phone-x, 0rem)),
      calc(var(--phase-skip-button-offset-y, 0rem) + var(--phase-skip-button-pos-phone-y, 0rem))
    ) scale(calc(var(--phase-skip-button-scale-phone, 1) * 1.06));
  }
}
body.is-phone-device #phase-automation-skip-button {
  animation-name: phase-automation-skip-pulse-phone;
  transform-origin: bottom left;
}
/* ユーザー要望（続き76）「スキップボタンを表示させていない時は『自分のターンです』
   『相手のターンです』とそこに表示させておきたい」。スキップボタンと同じ枠
   （#phase-guide-bar末尾）を共有し、どちらか一方だけ表示する（phase-automation.js
   のupdateSkipButtonVisibility参照）。 */
#phase-automation-turn-status {
  /* スキップボタンと同じ「バーの真上に浮かぶ」位置（上のコメント参照）。
     ハマりどころ（ユーザー報告「スマホでターン表示が画面右端で見切れる」）: 案内板バーは
     画面右下(right:2.3rem)にあるため、left:0基準＋nowrapで右へ伸ばすと長文
     （「相手のターンです（ムーブフェイズ）（優先権は相手にあります）」等）が画面右端を
     越えて切れていた。右端基準(right:0)にして左方向へ伸ばし、画面内に収める。 */
  position: absolute;
  bottom: calc(100% + 0.6rem);
  right: 0;
  display: none;
  padding: 0.4rem 0.9rem;
  background: rgba(15, 23, 32, 0.75);
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.5rem;
  color: #e2e8f0;
  font-family: sans-serif;
  font-size: 0.8rem;
  font-weight: bold;
  white-space: nowrap;
  text-align: right;
}
/* ユーザー要望（続き81）「相手のターン自分のターン表示についても個別でサイズ位置
   調整も追加して」。こちらはアニメーション無しの静止要素のため、スキップボタンと
   違い素直にtransformを重ねるだけでよい。 */
body.is-phone-device #phase-automation-turn-status {
  transform-origin: bottom right;
  transform: translate(var(--phase-turn-status-pos-phone-x, 0rem), var(--phase-turn-status-pos-phone-y, 0rem))
    scale(var(--phase-turn-status-scale-phone, 1));
  /* スマホでは画面幅を越える長文（フェイズ名・優先権の注記付き）は折り返して見切れを防ぐ。 */
  white-space: normal;
  /* 【#414・2026-10-16】ユーザー報告「右下の『エイドスのターンです』の**改行が気になります**」。
     原因は `max-width` ではなく**入れ物の幅**だった——この表示は案内板バーの中に
     `position: absolute; right: 0` で置かれており、**幅を auto にしていると入れ物（バー）の
     幅までしか広がれない**（実測: 上限は352pxと書いてあるのに、折り返した時の箱は
     実画面168px＝CSS約279pxで頭打ち）。名前が入って1文字ぶん長くなるだけで折り返す。
     `width: max-content` にすると**入れ物をはみ出して左へ伸びられる**ので1行に収まる
     （右端基準なので、伸びる先は画面の内側）。長すぎる時だけ max-width で折り返す。
     ⚠ `vw` は使わない（ステージ倍率が掛かるので実画面では意図の半分以下になる＝続き575 の★）。 */
  width: max-content;
  max-width: 44rem;
}
body.reduce-glow #phase-automation-skip-button {
  animation: none;
}

/* フェイズ案内板（phase-guide.js）で、今のフェイズのボタンだけ光らせる
   （phase-guide.js冒頭コメントで「将来の土台」として予告されていた仕組み）。 */
#phase-guide-bar button.is-current-phase {
  outline: 2px solid rgba(56, 189, 248, 0.9);
  outline-offset: 2px;
  animation: phase-guide-current-glow 1.8s ease-in-out infinite;
}
@keyframes phase-guide-current-glow {
  0%, 100% { box-shadow: 0 0 0.4rem 0.05rem rgba(56, 189, 248, 0.6); }
  50% { box-shadow: 0 0 1rem 0.3rem rgba(56, 189, 248, 0.9); }
}
body.reduce-glow #phase-guide-bar button.is-current-phase {
  animation: none;
}

/* 自動フェイズ送りトグル（フェイズ案内板の先頭）。ON＝緑枠、OFF＝暗い赤枠で状態が
   ひと目で分かるようにし、丸アイコンの右下に ON/OFF バッジを重ねる。 */
.phase-auto-skip-toggle .phase-auto-skip-toggle-glyph {
  font-size: 1.2rem;
  line-height: 1;
}
.phase-auto-skip-toggle .phase-auto-skip-toggle-state {
  position: absolute;
  bottom: -0.15rem;
  right: -0.15rem;
  font-size: 0.5rem;
  font-weight: bold;
  padding: 0.02rem 0.22rem;
  border-radius: 0.5rem;
  line-height: 1.3;
  pointer-events: none;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}
.phase-auto-skip-toggle.is-on .icon-action-button-icon-wrap {
  border-color: rgba(74, 222, 128, 0.9);
}
.phase-auto-skip-toggle.is-on .phase-auto-skip-toggle-state {
  background: #15803d;
  color: #dcfce7;
}
.phase-auto-skip-toggle.is-off .icon-action-button-icon-wrap {
  border-color: rgba(248, 113, 113, 0.8);
}
.phase-auto-skip-toggle.is-off .phase-auto-skip-toggle-glyph {
  opacity: 0.65;
}
.phase-auto-skip-toggle.is-off .phase-auto-skip-toggle-state {
  background: #7f1d1d;
  color: #fecaca;
}

/* ムーブフェイズの移動可能／接触可能ハイライト。既存のcard-effect-target-cell
   （黄色、クリックして選ぶ候補）とは別物であることが分かるよう、移動可能は緑、
   接触可能はオレンジにする。 */
.cell.phase-move-highlight {
  outline: 3px solid rgba(74, 222, 128, 0.85);
  outline-offset: -3px;
  animation: phase-move-highlight-pulse 1.4s ease-in-out infinite;
}
.cell.phase-contact-highlight {
  outline: 3px solid rgba(251, 146, 60, 0.9);
  outline-offset: -3px;
  animation: phase-contact-highlight-pulse 1.4s ease-in-out infinite;
}
/* ユーザー報告「接触できる相手の駒がなぜかペッちゃんこです」の原因・修正: opacity（0.7↔1）で
   パルスさせていたが、opacityが1未満の要素はブラウザが独立した合成レイヤーへ強制的に
   分離する仕様のため、その中にいる3D立体駒（buildCubePieceのpreserve-3dキューブ技法）が
   平面化して描画されてしまっていた。既存のtutorial-move-range-pulse（.card-effect-
   target-cell等で使用、駒を巻き込む心配のない実績あるパターン）と同じくoutline-colorの
   明暗だけをパルスさせる方式に変更する。 */
@keyframes phase-move-highlight-pulse {
  0%, 100% { outline-color: rgba(74, 222, 128, 0.9); }
  50% { outline-color: rgba(74, 222, 128, 0.35); }
}
@keyframes phase-contact-highlight-pulse {
  0%, 100% { outline-color: rgba(251, 146, 60, 0.9); }
  50% { outline-color: rgba(251, 146, 60, 0.35); }
}
body.reduce-glow .cell.phase-move-highlight,
body.reduce-glow .cell.phase-contact-highlight {
  animation: none;
}
/* ユーザー要望「移動先ハイライト時、範囲外のトーンを落としてください。ジャンプ台の時と
   同じように」。card-effect-picking-cellsと同じ「候補以外を暗くする」演出だが、ここでは
   filter/opacityを使わない。理由（ユーザー報告、2件）:
   ① filterを.cellへ直接かけると.board-card（カード画像）はなぜか暗くならず、マス自体しか
      暗くならなかった。
   ② .cellはpreserve-3dで自分の駒(.piece、buildCubePieceの立方体技法)を子に持つことがあり、
      filter/opacityはCSS仕様上その要素を独立した合成レイヤーへ強制分離してしまうため、
      中の3D立体駒が平面化（ペっちゃんこ）して描画される（前回のパルス演出と同じ既知の罠）。
   代わりに、::after擬似要素で半透明の黒を「一番最後に重ねて描く」オーバーレイ方式にする。
   擬似要素は他のfilter/opacity系プロパティを一切使わないため合成レイヤー分離が起きず、
   かつ通常のDOM子要素より必ず後に描画される（＝マスの背景にもカード画像にも重なって
   両方きちんと暗く見える）。box-shadow: insetも試したが、不透明なカード画像の「下」に
   描かれてしまい見た目に反映されなかったため採用しなかった。 */
body.phase-move-picking .cell:not(.phase-move-highlight):not(.phase-contact-highlight)::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: rgba(0, 0, 0, 0.55);
  pointer-events: none;
}

/* ユーザー要望「ロックフェイズではロックできるカードのみ手札内でハイライトし、それ以外は
   トーンオフしてほしい」。.hand-cardは（.cellと違い）preserve-3dの3D立体駒を子に持つ
   ことがないため、filterを直接かけても平面化の罠は無い（既存のcard-effect-picking-hand
   と同じ、実績のある方式をそのまま流用する）。 */
.hand-card.phase-lock-highlight {
  outline: 3px solid rgba(74, 222, 128, 0.9);
  outline-offset: 2px;
  animation: phase-move-highlight-pulse 1.4s ease-in-out infinite;
}
body.reduce-glow .hand-card.phase-lock-highlight {
  animation: none;
}
/* ユーザー報告（続き85）「紫のキューブの追色コストを選ぶとき手札のゴメンナサイが
   トーンオフされてしまっている」の原因: このロックフェイズ用の暗転ルールが
   card-effect-target-cell（手札効果の追色コスト等、別のピッカーの候補ハイライト）を
   考慮しておらず、ロックフェイズ中に手札効果の候補選択が重なると、ロック候補では
   ないというだけで手札効果側の正当な候補まで暗くしてしまっていた。 */
body.phase-lock-picking .hand-card:not(.phase-lock-highlight):not(.card-effect-target-cell) {
  filter: brightness(0.45) saturate(0.6);
  transition: filter 0.2s ease;
}

/* フェイズ自動スキップの通知モーダル（src/phase-automation.js）。confirm-modalと似た
   見た目だが、クリック不要で数秒後に自動的に消える（1ターンに複数回起こり得るため）。 */
.phase-skip-modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 10002;
  background: rgba(0, 0, 0, 0.35);
}
.phase-skip-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 10003;
  background: rgba(15, 23, 32, 0.98);
  border: 2px solid rgba(148, 163, 184, 0.5);
  border-radius: 0.6rem;
  padding: 1rem 1.6rem;
  color: #e2e8f0;
  font-family: sans-serif;
  font-size: 0.9rem;
  text-align: center;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
  cursor: pointer;
}

/* 手札効果「このカードを使用します」告知モーダル（src/hand-effect-ui.js）。
   ユーザー要望「到達拡大モーダルの位置に表示・同じく消えないように」への対応で、
   card-arrival-modal（到達）と全く同じ位置・大きさ・消え方（--card-arrival-modal-*
   のCSS変数を共有）にしてある。到達と使用が同時に起きた場合は後から表示された
   方が手前に重なる（DOM appendの順序通り）——別々のDOM/タイマーで管理しているため
   互いを消し合うことは無い。 */
.hand-effect-use-modal {
  position: fixed;
  top: var(--card-arrival-modal-top);
  right: var(--card-arrival-modal-right);
  z-index: 10600;
  width: var(--card-arrival-modal-size);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  padding: 0.6rem;
  background: rgba(15, 23, 32, 0.5);
  border-radius: 0.8rem;
  transform-origin: top right;
  transform: scale(0.85);
  opacity: 0;
  pointer-events: auto;
  transition: opacity 0.3s ease, transform 0.3s ease;
  cursor: pointer;
}
.hand-effect-use-modal.show {
  opacity: 1;
  transform: scale(1);
}
.hand-effect-use-modal-label {
  font-family: sans-serif;
  font-weight: bold;
  font-size: 1.1rem;
  letter-spacing: 0.2em;
  color: #38bdf8;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.9);
}
.hand-effect-use-modal img,
.hand-effect-use-modal .card-box {
  width: 100%;
  display: block;
  border-radius: 0.6rem;
  border: 2px solid rgba(255, 255, 255, 0.8);
  box-shadow: 0 1.2rem 3rem rgba(0, 0, 0, 0.7);
}
.hand-effect-use-modal-name {
  font-family: sans-serif;
  font-size: 0.7rem;
  color: #e2e8f0;
  text-align: center;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9);
}

/* ユーザー要望「スリカエなどで渡されたカードは何が渡されたのか大きくモーダルで
   表示してわかるようにしてほしい」（src/hand-effect-ui.jsのshowCardReceivedModal）。
   儀式的ピック系モーダル（紫系）や使用告知（水色・左上）と見分けやすいよう緑系・
   画面中央・背景ディム付きにする。 */
.card-received-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.85);
  z-index: 10641;
  width: 13rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  padding: 1.1rem 1.3rem;
  background: linear-gradient(160deg, #052e1f, #021208);
  border: 2px solid #34d399;
  border-radius: 0.9rem;
  opacity: 0;
  pointer-events: auto;
  cursor: pointer;
  transition: opacity 0.25s ease, transform 0.25s ease;
  box-shadow: 0 0 1.8rem rgba(52, 211, 153, 0.5), 0 20px 50px rgba(0, 0, 0, 0.6);
}
.card-received-modal.show {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
.card-received-modal-label {
  font-family: sans-serif;
  font-weight: bold;
  font-size: 1.2rem;
  letter-spacing: 0.2em;
  color: #6ee7b7;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.9);
}
.card-received-modal img,
.card-received-modal .card-box {
  width: 100%;
  display: block;
  border-radius: 0.6rem;
  border: 2px solid rgba(255, 255, 255, 0.8);
  box-shadow: 0 1.2rem 3rem rgba(0, 0, 0, 0.7);
}
.card-received-modal-name {
  font-family: sans-serif;
  font-size: 0.95rem;
  font-weight: bold;
  color: #ecfdf5;
  text-align: center;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9);
}
.card-received-modal-subtitle {
  font-family: sans-serif;
  font-size: 0.75rem;
  color: #a7f3d0;
  text-align: center;
}
/* 複数枚をまとめて見せる中央モーダル（showMultipleCardsReceivedModal、ゲート侵攻の複数枚奪取。
   ユーザー要望2026-08-13）。カードを横並び（枚数が多ければ折り返す）。 */
.card-received-modal-multi {
  width: auto;
  max-width: min(46rem, 94vw);
  cursor: pointer;
}
.card-received-modal-cards {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.7rem;
}
.card-received-modal-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  width: 8rem;
  max-width: 40vw;
}
.card-received-modal-card .card-box {
  width: 100%;
  border-radius: 0.4rem;
  border: 2px solid rgba(255, 255, 255, 0.7);
}

/* プレゼントの到達効果等で「誰がドロー対象か」をアバターで画面中央に周知するモーダル
   （main.jsのannounceDrawTargetsForEffect）。ユーザー要望2026-08-07。 */
.draw-targets-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.85);
  z-index: 10641;
  max-width: min(30rem, 92vw);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.8rem;
  padding: 1.2rem 1.5rem;
  background: linear-gradient(160deg, #0b2e4a, #041524);
  border: 2px solid #38bdf8;
  border-radius: 0.9rem;
  opacity: 0;
  pointer-events: auto;
  cursor: pointer;
  transition: opacity 0.25s ease, transform 0.25s ease;
  box-shadow: 0 0 1.8rem rgba(56, 189, 248, 0.5), 0 20px 50px rgba(0, 0, 0, 0.6);
}
.draw-targets-modal.show {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
.draw-targets-modal-title {
  font-family: sans-serif;
  font-weight: bold;
  font-size: 1rem;
  color: #bae6fd;
  text-align: center;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.9);
}
.draw-targets-modal-avatars {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1rem;
}
.draw-targets-modal-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
}
.draw-targets-modal-avatar {
  width: 4.5rem;
  height: 4.5rem;
  border-radius: 50%;
  overflow: hidden;
  border: 2px solid rgba(255, 255, 255, 0.8);
  box-shadow: 0 0.6rem 1.5rem rgba(0, 0, 0, 0.6);
  background: rgba(0, 0, 0, 0.3);
}
.draw-targets-modal-avatar img,
.draw-targets-modal-avatar svg {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.draw-targets-modal-name {
  font-family: sans-serif;
  font-size: 0.8rem;
  font-weight: bold;
  color: #e0f2fe;
  text-align: center;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9);
}
/* ライトテーマ（body.theme-light）: プレゼント等の「ドロー対象周知」モーダルがダークのままだった
   ので、他のライトモーダルと同じアイボリー地＋ゴールド枠＋濃い文字へ（ユーザー報告2026-08-08）。 */
body.theme-light .draw-targets-modal {
  background: linear-gradient(160deg, #fffdf7, #f6efdd);
  border-color: rgba(176, 141, 63, 0.7);
  box-shadow: 0 0 1.4rem rgba(176, 141, 63, 0.3), 0 20px 50px rgba(0, 0, 0, 0.25);
}
body.theme-light .draw-targets-modal-title {
  color: #6b4e0a;
  text-shadow: none;
}
body.theme-light .draw-targets-modal-name {
  color: #3a3326;
  text-shadow: none;
}

/* 効果の発動理由を説明するモーダル（src/hand-effect-ui.jsのshowEffectReasonModal、
   例: カウンターロックの「あなたは１番少なくロックしているので１枚ドローします」・
   不発の案内）。ユーザー要望「すぐ消える一時的なモーダルは画面中央に表示させて
   ください」への対応で画面中央に出す（以前は到達モーダル（右上）・使用モーダル
   （左上）との重なりを避けるため画面下中央にしていたが、このモーダル自体は毎回
   awaitして単独で表示されるタイミングでしか出さないため、中央に出しても他の
   モーダルと同時に重なることは無い）。 */
.effect-reason-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: 10600;
  width: min(20rem, 80vw);
  padding: 0.7rem 1rem;
  background: rgba(15, 23, 32, 0.92);
  border: 1px solid rgba(56, 189, 248, 0.6);
  border-radius: 0.6rem;
  text-align: center;
  transform: translate(-50%, -50%) scale(0.94);
  opacity: 0;
  pointer-events: auto;
  cursor: pointer;
  transition: opacity 0.3s ease, transform 0.3s ease;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.6);
}
.effect-reason-modal.show {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
.effect-reason-modal-name {
  font-family: sans-serif;
  font-weight: bold;
  font-size: 0.75rem;
  color: #7dd3fc;
  margin-bottom: 0.3rem;
}
.effect-reason-modal-text {
  font-family: sans-serif;
  font-size: 0.8rem;
  color: #e2e8f0;
  line-height: 1.5;
}

/* 手札効果の選択肢モーダル（なないろの欠片等、src/hand-effect-ui.js）。使用できない
   選択肢はグレー表示・クリック不可にする（ユーザー要望）。 */
.hand-effect-option-picker-backdrop {
  position: fixed;
  inset: 0;
  z-index: 10004;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity 0.2s ease;
}
.hand-effect-option-picker-backdrop.show {
  opacity: 1;
}
.hand-effect-option-picker {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.9);
  z-index: 10005;
  width: min(20rem, 90vw);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  background: rgba(15, 23, 32, 0.98);
  border: 2px solid rgba(148, 163, 184, 0.5);
  border-radius: 0.6rem;
  padding: 1.2rem;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
  opacity: 0;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.hand-effect-option-picker.show {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
.hand-effect-option-picker-title {
  font-family: sans-serif;
  font-size: 0.9rem;
  color: #e2e8f0;
  text-align: center;
}
.hand-effect-option-picker-img {
  position: relative;
  width: 6rem;
  border-radius: 0.5rem;
  border: 2px solid rgba(255, 255, 255, 0.6);
  /* <img>→<div>（テキスト合成/画像）へ置換したため、正方形・クリップ・画像敷き設定を明示。 */
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
.hand-effect-option-picker-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  width: 100%;
}
.hand-effect-option-picker-btn {
  padding: 0.6rem 0.8rem;
  background: rgba(56, 189, 248, 0.2);
  color: #e2e8f0;
  border: 1px solid rgba(56, 189, 248, 0.6);
  border-radius: 0.4rem;
  cursor: pointer;
  font-family: sans-serif;
  font-size: 0.85rem;
}
.hand-effect-option-picker-btn:hover {
  background: rgba(56, 189, 248, 0.4);
}
.hand-effect-option-picker-btn.is-unusable {
  background: rgba(100, 116, 139, 0.15);
  color: rgba(226, 232, 240, 0.4);
  border-color: rgba(100, 116, 139, 0.35);
  cursor: not-allowed;
}
/* ユーザー要望「選択モーダルが盤面を隠していて見にくい。『盤面を見る』ボタンを
   モーダル内に追加、押すと盤面を確認することができる。適当にクリックすると
   選択モーダルに戻る」への対応（hand-effect-ui.jsのshowHandEffectOptionPicker）。
   ユーザー要望「『盤面を見る』ボタンを押したとき、モーダルは消すのではなく画面の
   左側に移すようにしてほしい」への対応で、非表示にする代わりに画面左端へ寄せる
   （盤面の中央〜右側が見えるようにしつつ、モーダル自体は見えたまま操作もできる）。 */
.hand-effect-option-picker-backdrop.is-peeking {
  background: transparent;
}
.hand-effect-option-picker.is-peeking {
  left: 1.5rem;
  transform: translate(0, -50%) scale(1);
}
/* ユーザー報告「『盤面を見る』が薄くて見にくい」。半透明グレーだと、特に既定のライト
   （アイボリー）モーダル上でほぼ見えなかった。濃いスレートの単色ボタンにして両テーマで
   はっきり読めるようにする。 */
.hand-effect-option-picker-peek-btn {
  width: 100%;
  padding: 0.5rem 0.8rem;
  background: rgba(71, 85, 105, 0.9);
  color: #f1f5f9;
  border: 1px solid rgba(30, 41, 59, 0.9);
  border-radius: 0.4rem;
  cursor: pointer;
  font-family: sans-serif;
  font-size: 0.85rem;
  font-weight: 600;
}
/* クリックしても「何も起きない」と誤解されないための軽いお知らせ（main.js showQuickNote）。 */
.quick-note-toast {
  position: fixed;
  left: 50%;
  bottom: 6rem;
  transform: translate(-50%, 1rem);
  z-index: 10050;
  padding: 0.7rem 1.2rem;
  border-radius: 0.5rem;
  background: rgba(15, 23, 32, 0.92);
  color: #f1f5f9;
  border: 1px solid rgba(251, 191, 36, 0.5);
  font-family: sans-serif;
  font-size: 0.9rem;
  box-shadow: 0 0.6rem 1.6rem rgba(0, 0, 0, 0.45);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease, transform 0.25s ease;
}
.quick-note-toast.show {
  opacity: 1;
  transform: translate(-50%, 0);
}
/* 不具合報告モーダル（bug-report.js）。 */
#bug-report-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(34rem, 94vw);
  z-index: 10041;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(251, 191, 36, 0.5);
  border-radius: 0.6rem;
  padding: 1.3rem;
  font-family: sans-serif;
  color: #e2e8f0;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
.bug-report-title {
  font-weight: bold;
  font-size: 1.1rem;
  color: #fbbf24;
  margin-bottom: 0.6rem;
  padding-right: 1.6rem;
}
.bug-report-desc {
  font-size: 0.82rem;
  line-height: 1.6;
  color: #cbd5e1;
  margin-bottom: 0.8rem;
}
/* 不具合報告の画像添付（bug-report.js、ユーザー要望2026-08-29）。
   ボタン／プレビュー／ドラッグ中のハイライト。 */
.bug-report-attach {
  margin-top: 0.5rem;
  padding: 0.5rem;
  border: 1px dashed rgba(148, 163, 184, 0.45);
  border-radius: 0.4rem;
}
.bug-report-attach.is-dragover {
  border-color: rgba(250, 204, 21, 0.9);
  background: rgba(250, 204, 21, 0.08);
}
.bug-report-attach-btn {
  padding: 0.35rem 0.7rem;
  border-radius: 0.35rem;
  border: 1px solid rgba(148, 163, 184, 0.5);
  background: rgba(2, 6, 12, 0.4);
  color: #e2e8f0;
  font-size: 0.82rem;
  cursor: pointer;
}
.bug-report-attach-btn:hover { background: rgba(30, 41, 59, 0.6); }
/* 「今の画面の状態も送る」のチェック行（続き572）。文字色は決め打ちせず周りから継ぐ
   ——明暗2テーマがあり、決め打ちすると片方で文字が見えなくなる（教訓15）。 */
.bug-report-screen {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin-top: 0.5rem;
  font-size: 0.8rem;
  cursor: pointer;
}
.bug-report-screen input {
  flex: none;
  width: 1rem;
  height: 1rem;
  cursor: pointer;
}
.bug-report-attach-hint {
  margin-top: 0.35rem;
  font-size: 0.7rem;
  color: #94a3b8;
  line-height: 1.5;
}
.bug-report-attach-preview { display: flex; align-items: flex-start; gap: 0.5rem; }
.bug-report-attach-preview img {
  max-width: 12rem;
  max-height: 8rem;
  border-radius: 0.3rem;
  border: 1px solid rgba(148, 163, 184, 0.4);
}
.bug-report-attach-remove {
  padding: 0.25rem 0.5rem;
  border-radius: 0.3rem;
  border: 1px solid rgba(248, 113, 113, 0.5);
  background: transparent;
  color: #fca5a5;
  font-size: 0.72rem;
  cursor: pointer;
}
body.theme-light .bug-report-attach-btn { background: #fff; color: #1f2937; }
body.theme-light .bug-report-attach-hint { color: #4b5563; }

.bug-report-textarea {
  width: 100%;
  box-sizing: border-box;
  resize: vertical;
  padding: 0.6rem 0.7rem;
  border-radius: 0.4rem;
  border: 1px solid rgba(148, 163, 184, 0.5);
  background: rgba(2, 6, 12, 0.5);
  color: #f1f5f9;
  font-family: sans-serif;
  font-size: 0.9rem;
  line-height: 1.5;
}
.bug-report-status {
  min-height: 1.2rem;
  font-size: 0.82rem;
  color: #86efac;
  margin: 0.5rem 0 0.2rem;
}
.bug-report-status.is-error {
  color: #fca5a5;
}
.bug-report-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.6rem;
  margin-top: 0.6rem;
}
.bug-report-cancel,
.bug-report-submit {
  padding: 0.5rem 1.3rem;
  border: none;
  border-radius: 0.35rem;
  cursor: pointer;
  font-family: sans-serif;
  font-size: 0.9rem;
  font-weight: 600;
}
.bug-report-cancel {
  background: rgba(148, 163, 184, 0.25);
  color: #e2e8f0;
}
.bug-report-submit {
  background: #f59e0b;
  color: #201400;
}
.bug-report-submit:disabled,
.bug-report-cancel:disabled {
  opacity: 0.6;
  cursor: default;
}
/* ライトテーマ（既定）でも読めるように。 */
body.theme-light #bug-report-modal {
  background: #fdfbf5;
  border-color: rgba(176, 141, 63, 0.6);
  color: #3a3326;
}
body.theme-light .bug-report-desc {
  color: #6b5c3c;
}
body.theme-light .bug-report-title {
  color: #a67c1a;
}
body.theme-light .bug-report-textarea {
  background: #fff;
  border-color: rgba(176, 141, 63, 0.5);
  color: #3a3326;
}
/* ライトテーマではキャンセルボタンの文字（既定は白 #e2e8f0）が、薄いグレー地＋アイボリーの
   モーダル背景に埋もれて読めなかった（ユーザー報告「送信ボタンの横のボタンが読めない」）。
   ゴールド系の薄地＋濃いめの文字＋枠線で、他のライトモーダルのボタンと揃えて読めるようにする。 */
body.theme-light .bug-report-cancel {
  background: rgba(176, 141, 63, 0.14);
  color: #6b5c3c;
  border: 1px solid rgba(176, 141, 63, 0.45);
}
/* 【続き572・2026-10-11】**スマホでは報告モーダル全体を拡大する。**
   このアプリは 1600x900 のステージを画面に収まる倍率へ縮めて表示するので、iPhoneの
   横持ち（932x430）では 1rem が実画面で 7〜8px にしかならない。実測で**送信ボタンの文字が
   6.9px・添付の案内が 5.4px・ボタンの高さが19px**＝読めず押しにくい状態だった
   （続き566 の CAMPFIRE リンク 6.3px、#389 の「画面最上部の17pxの帯」と同じ形）。
   不具合を報告する側の画面が読めないと報告自体が来ないので、オープニングと同じ考え方で
   文字と当たり判定を 2 倍前後に拡大する。**PCは一切変えない**（この規則はスマホ判定時だけ）。
   入力欄は行数（rows=6）のままだと拡大で縦に伸びすぎるので、高さを決めて抑える。 */
body.is-phone-device #bug-report-modal {
  width: min(60rem, 94vw);
  padding: 2rem 2.2rem;
  border-radius: 1rem;
  /* 画像を添付するとプレビュー（最大8rem）が増えて画面の高さを超えかけるので上限を決める。
     ステージは900px高（=56.25rem）なので52remでほぼ画面いっぱい。はみ出す分は中で巻く。 */
  max-height: 52rem;
  overflow-y: auto;
}
body.is-phone-device .bug-report-title {
  font-size: 2.2rem;
  margin-bottom: 1rem;
  padding-right: 3rem;
}
body.is-phone-device .bug-report-desc {
  font-size: 1.35rem;
  margin-bottom: 1rem;
}
body.is-phone-device .bug-report-textarea {
  font-size: 1.6rem;
  height: 9rem;
  padding: 0.9rem 1rem;
  border-radius: 0.7rem;
}
body.is-phone-device .bug-report-attach { padding: 0.9rem; border-radius: 0.7rem; }
body.is-phone-device .bug-report-attach-btn {
  font-size: 1.5rem;
  padding: 0.6rem 1.2rem;
  border-radius: 0.6rem;
}
body.is-phone-device .bug-report-attach-hint { font-size: 1.2rem; margin-top: 0.6rem; }
body.is-phone-device .bug-report-attach-remove { font-size: 1.3rem; padding: 0.5rem 0.9rem; }
body.is-phone-device .bug-report-screen { font-size: 1.5rem; gap: 0.8rem; margin-top: 0.9rem; padding: 0.35rem 0; }
body.is-phone-device .bug-report-screen input { width: 2.4rem; height: 2.4rem; }
body.is-phone-device .bug-report-status { font-size: 1.35rem; min-height: 2rem; }
body.is-phone-device .bug-report-cancel,
body.is-phone-device .bug-report-submit {
  font-size: 1.6rem;
  padding: 0.8rem 2rem;
  border-radius: 0.6rem;
}
.hand-effect-option-picker-peek-btn:hover {
  background: rgba(51, 65, 85, 1);
}
.hand-effect-option-picker-peek-hint {
  position: fixed;
  top: 1.2rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10006;
  padding: 0.5rem 1rem;
  background: rgba(15, 23, 32, 0.85);
  color: #e2e8f0;
  border: 1px solid rgba(148, 163, 184, 0.5);
  border-radius: 2rem;
  font-family: sans-serif;
  font-size: 0.8rem;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}
.hand-effect-option-picker-peek-hint.show {
  opacity: 1;
}

/* ザ・ギャンブル/試練の儀式の「色を宣言する」モーダル。 */
.declare-colors-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 10631;
  width: min(20rem, 90vw);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.8rem;
  background: rgba(15, 23, 32, 0.98);
  border: 2px solid rgba(148, 163, 184, 0.5);
  border-radius: 0.6rem;
  padding: 1.2rem;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
.declare-colors-modal-title {
  font-family: sans-serif;
  font-size: 0.9rem;
  color: #e2e8f0;
  text-align: center;
}
/* 7色を七角形に並べる（緑=上・赤=左下から時計回り）。位置はJS側で slot に%指定
   （main.jsのdeclareColorsForEffect）。位置(slot)とホバー拡大(swatch)を別要素に分けて
   transformの衝突を避ける。 */
.declare-colors-modal-grid {
  position: relative;
  width: 15rem;
  height: 15rem;
  margin: 0.1rem auto 0;
}
.declare-colors-modal-slot {
  position: absolute;
  transform: translate(-50%, -50%);
}
.declare-colors-modal-swatch {
  position: relative;
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background: var(--swatch-color);
  border: 3px solid rgba(255, 255, 255, 0.3);
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease;
}
.declare-colors-modal-swatch:hover {
  transform: scale(1.12);
}
/* 【撤去（ユーザー報告2026-09-01 #193）】以前は「選択が1つでもあれば未選択を沈める」に
   していたが、3色宣言のカードで1色選んだ時点で残り6色が薄くなり「もう選べないのか」と
   誤解させてしまった。選択済みは下の白＋金の二重リングと✓で十分に目立つので、
   未選択を沈める処理はやめる。 */
/* 選択された色を、どの色の上でもはっきり分かるようにする（白＋金の二重リング・
   強めの拡大・持続する発光・中央のチェックマーク）。 */
.declare-colors-modal-swatch.is-selected {
  border-color: #fff;
  transform: scale(1.22);
  box-shadow:
    0 0 0 3px #fff,
    0 0 0 6px #facc15,
    0 0 16px 4px rgba(250, 204, 21, 0.85);
  z-index: 1;
}
.declare-colors-modal-swatch.is-selected::after {
  content: "✓";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  font-weight: 900;
  color: #fff;
  text-shadow: 0 0 3px rgba(0, 0, 0, 0.9), 0 1px 2px rgba(0, 0, 0, 0.8);
  pointer-events: none;
}
.declare-colors-modal-confirm {
  padding: 0.6rem 1.2rem;
  background: rgba(56, 189, 248, 0.2);
  color: #e2e8f0;
  border: 1px solid rgba(56, 189, 248, 0.6);
  border-radius: 0.4rem;
  cursor: pointer;
  font-family: sans-serif;
  font-size: 0.85rem;
}
.declare-colors-modal-confirm:hover:not(:disabled) {
  background: rgba(56, 189, 248, 0.4);
}
/* ライト配色（theme-light=全体 / theme-light-ingame=盤面）時は明るいパネルに（ユーザー報告）。 */
body.theme-light .declare-colors-modal,
body.theme-light-ingame .declare-colors-modal {
  background: rgba(255, 253, 245, 0.98);
  border-color: rgba(146, 102, 10, 0.45);
  box-shadow: 0 20px 50px rgba(80, 60, 20, 0.35);
}
body.theme-light .declare-colors-modal-title,
body.theme-light-ingame .declare-colors-modal-title {
  color: #1e293b;
}
body.theme-light .declare-colors-modal-swatch,
body.theme-light-ingame .declare-colors-modal-swatch {
  border-color: rgba(30, 41, 59, 0.28);
}
body.theme-light .declare-colors-modal-confirm,
body.theme-light-ingame .declare-colors-modal-confirm {
  background: rgba(14, 116, 144, 0.15);
  color: #0e5568;
  border-color: rgba(14, 116, 144, 0.5);
}
body.theme-light .declare-colors-modal-confirm:hover:not(:disabled),
body.theme-light-ingame .declare-colors-modal-confirm:hover:not(:disabled) {
  background: rgba(14, 116, 144, 0.28);
}
/* 色宣言の常駐表示（src/main.jsのshowDeclaredColorsIndicator、続き65）。画面中央に
   出た直後、少し経つと画面左側へすっと移行して常駐する（結果が判明するまで自動では
   消えない）。丸いアイコンは.declare-colors-modal-swatchと同じ--swatch-color変数を使う。 */
:root {
  --declared-colors-corner-left: 1rem;
  --declared-colors-corner-scale: 0.85;
}
.declared-colors-indicator {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 10615;
  background: linear-gradient(160deg, #1e293b, #0f172a);
  border: 2px solid rgba(255, 255, 255, 0.3);
  border-radius: 0.7rem;
  /* ユーザー要望「色周知モーダルをもっと大きく」。中央に出る瞬間を大きく見せる
     （隅へ寄った後の大きさは --declared-colors-corner-scale で従来どおり調整可能）。 */
  padding: 1.1rem 1.8rem;
  text-align: center;
  font-family: sans-serif;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
  transition: top 0.6s ease, left 0.6s ease, transform 0.6s ease, padding 0.6s ease;
}
/* ユーザー要望（続き68）「モーダルを左下に移動するのではなくそのまま左に移動して
   ほしい」への対応。以前は下端(bottom:1rem)＋左端(left:1rem)の「左下」だったが、
   topは基本形（top:50%）のまま変えず、transformのY成分だけ-50%を保つことで、
   垂直方向の位置は動かさず水平方向にだけ寄せる「まっすぐ左」の移動にした。
   位置(--declared-colors-corner-left)・大きさ(--declared-colors-corner-scale)は
   管理者モードから調整できる（admin.jsのGROUPS参照、範囲は大きめに確保）。 */
.declared-colors-indicator.is-corner {
  left: var(--declared-colors-corner-left, 1rem);
  transform: translate(0, -50%) scale(var(--declared-colors-corner-scale, 0.85));
  transform-origin: center left;
}
.declared-colors-indicator-title {
  font-size: 1rem;
  font-weight: 700;
  color: #e2e8f0;
  margin-bottom: 0.5rem;
  white-space: nowrap;
}
.declared-colors-indicator-swatches {
  display: flex;
  gap: 0.45rem;
  justify-content: center;
}
.declared-colors-indicator-swatch {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  background: var(--swatch-color);
  border: 2px solid rgba(255, 255, 255, 0.4);
}
/* ユーザー要望「作業を促すモーダルに『盤面を見る』ボタンをつけてほしい」への対応
   （main.jsのdeclareColorsForEffect、hand-effect-option-picker-peek-*と共通のクラスを流用）。
   ユーザー要望「『盤面を見る』ボタンを押したとき、モーダルは消すのではなく画面の
   左側に移すようにしてほしい」への対応で非表示にせず画面左端へ寄せる。 */
.declare-colors-modal.is-peeking {
  left: 1.5rem;
  transform: translate(0, -50%);
}
/* ユーザー報告（続き92）「パーティ・選べる罠の選択モーダルで盤面を見るを押した時
   画面左端で見切れる」と全く同じCSS詳細度の逆転が、同じ「盤面を見る」機構を使う
   このモーダル（ザ・ギャンブル・試練の儀式の色宣言）にも起きていたため、
   同じ考え方で修正する（.hand-effect-option-picker.is-peeking参照）。
   transform-originも同じ理由（既定の中心基準だとscale()で左半分が画面外へ
   はみ出す）で左端に固定する。 */
body.is-phone-device .declare-colors-modal.is-peeking {
  transform-origin: 0% 50%;
  transform: translate(0, -50%) scale(var(--center-modal-scale-phone, 1));
}
.declare-colors-modal-confirm:disabled {
  background: rgba(100, 116, 139, 0.15);
  color: rgba(226, 232, 240, 0.4);
  border-color: rgba(100, 116, 139, 0.35);
  cursor: not-allowed;
}

/* ユーザー要望「全員のマウスカーソルの位置が全員に見える化したい。アバターとその
   プレイヤーの色、名前が載っているとわかりやすい」（main.jsのensureRemoteCursorEl/
   onCursorPositionEvents参照）。document.bodyの直下に置くことで、既存のステージの
   scale/offset transform（applyViewportStage）をそのまま自動的に受け継ぐ
   （left/topはstageClientToLocalで変換したステージのローカル座標＝1600x900基準）。 */
.remote-cursor {
  position: fixed;
  z-index: 10700;
  display: flex;
  align-items: center;
  gap: 0.3rem;
  pointer-events: none;
  transform: translate(0.6rem, 0.6rem);
  transition: left 0.08s linear, top 0.08s linear;
}
.remote-cursor-avatar {
  width: 1.8rem;
  height: 1.8rem;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
  border-radius: 50%;
  border: 2px solid var(--cursor-color, #94a3b8);
  overflow: hidden;
  background: #1e293b;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
}
.remote-cursor-avatar .avatar-image {
  width: 100%;
  height: 100%;
}
.remote-cursor-name {
  font-family: sans-serif;
  font-size: 0.7rem;
  font-weight: bold;
  color: #fff;
  background: rgba(15, 23, 32, 0.85);
  border: 1px solid var(--cursor-color, #94a3b8);
  padding: 0.1rem 0.5rem;
  border-radius: 0.3rem;
  white-space: nowrap;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
}

/* ユーザー要望「配置後ここに配置したよがわかるように配置場所をしっかりハイライトして
   ください。マスの枠だけでなくカードの面もね」。マスの枠はoutline（3D立体駒を巻き込む
   filter/opacity系の罠を避けるため、既存のphase-move-highlight-pulseと同じ手法を流用）、
   カードの面はbox-shadow（.board-card自体は3D立体では無いためfilter系でも問題無いが、
   card-effect-target-cellの既存の光り方と揃える）。 */
@keyframes card-effect-just-placed-pulse {
  0%, 100% { outline-color: rgba(250, 204, 21, 0.9); }
  50% { outline-color: rgba(250, 204, 21, 0.35); }
}
.cell.card-effect-just-placed {
  outline: 3px solid rgba(250, 204, 21, 0.9);
  outline-offset: -3px;
  animation: card-effect-just-placed-pulse 1.4s ease-in-out infinite;
}
body.reduce-glow .cell.card-effect-just-placed {
  animation: none;
}
.cell.card-effect-just-placed .board-card {
  box-shadow: 0 0 1.2rem 0.4rem rgba(250, 204, 21, 0.85);
}

/* 7色ロック達成時の勝利モーダル。金色のグロー＋トロフィーで祝う演出。 */
@keyframes victory-modal-glow {
  0%, 100% { box-shadow: 0 0 2rem 0.4rem rgba(250, 204, 21, 0.5), 0 20px 60px rgba(0, 0, 0, 0.7); }
  50% { box-shadow: 0 0 4rem 1rem rgba(250, 204, 21, 0.9), 0 20px 60px rgba(0, 0, 0, 0.7); }
}
@keyframes victory-modal-pop-in {
  0% { transform: translate(-50%, -50%) scale(0.7); opacity: 0; }
  100% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
}
/* ============================================================================
   勝利演出「七色、集結」（src/victory-celebration.js）。
   WAIT → COLORS → GATHER → PULSE → FLASH → VICTORY →（リザルトへ）
   物語の線引き: 七色を集めたことで世界に色が戻ったとは断定しない。だから盤面に色を戻す演出は
   入れず、何が起きたのかは白い光で覆い隠す。
   盤面は3D階層なので盤面自身に filter は掛けない（3Dが平坦化して壊れる。CLAUDE.md教訓2）。
   暗くするのは上に重ねたオーバーレイ側で行う。
   数値は全て --vic-* のCSS変数（管理者モードのシミュレーターが同じ変数を書き換える）。
   ========================================================================= */
:root {
  --vic-speed: 0.85;       /* 全体の速さ（大きいほど速い）。既定=派手版 */
  --vic-color-step: 1;     /* 1色ごとの間隔 */
  --vic-gather-speed: 1;   /* キューブへ集まる速さ */
  --vic-stream: 1.8;       /* 光の帯・霧の量。既定=派手版 */
  --vic-pulse-count: 3;    /* 脈動の回数 */
  --vic-pulse-power: 1.6;  /* 脈動の強さ。既定=派手版 */
  --vic-shake: 1.6;        /* 画面振動の強さ。既定=派手版 */
  --vic-flash-speed: 1;    /* 白飛びが広がる速さ */
  --vic-white: 0.88;       /* 白オーバーレイの濃さ（0.5〜0.98） */
  --vic-residue: 2;        /* 白い画面の中の七色残光の量。既定=派手版 */
  --vic-rings: 1.2;        /* 【#385】脈動ごとの七色の衝撃波（0で切れる） */
  --vic-rays: 1;           /* 【#385】VICTORYの背後の七色の光芒（0で切れる） */
  /* 脈動している疑似キューブの「白い芯」の強さ。0にすると白い丸が完全に消えて、
     勝者の色のにじみだけになる（ユーザー要望2026-08-31「脈動中の白丸いらないかも」）。 */
  --vic-cube-white: 0;
  --vic-avatar-size: 16;   /* 勝者アバターの大きさ（vmin） */
  --vic-fan: 1;            /* ロックした7枚を中央に扇状に並べるか（0/1） */
  --vic-hold: 1;           /* 勝利表示を見せる長さ */
}

/* 演出中は盤面に触らせない（勝敗・通信処理は演出とは切り離して既に確定済み）。 */
body.victory-celebration-active #game-table,
body.victory-celebration-active #option-area {
  pointer-events: none;
}

#victory-celebration {
  position: fixed;
  inset: 0;
  z-index: 10450; /* 勝利モーダル(10500)より下・盤面より上 */
  pointer-events: none;
  overflow: hidden;
}
/* スキップ用に、演出中だけタップを拾う（押すと残りが一気に詰まる）。 */
#victory-celebration.is-hushed { pointer-events: auto; }

/* WAIT: 世界の音と光が引く。勝者のロックエリアの周りだけ残す。 */
#victory-celebration .vic-veil {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 420ms cubic-bezier(0.3, 0, 0.2, 1);
  background: radial-gradient(ellipse at 50% 50%, rgba(3, 4, 8, 0.35) 0%, rgba(3, 4, 8, 0.82) 100%);
}
@supports (backdrop-filter: saturate(1)) or (-webkit-backdrop-filter: saturate(1)) {
  #victory-celebration .vic-veil {
    -webkit-backdrop-filter: saturate(0.35) brightness(0.42);
    backdrop-filter: saturate(0.35) brightness(0.42);
  }
}
#victory-celebration.is-hushed .vic-veil { opacity: 1; }
/* 【重さ対策・2026-09-06】白い光が画面を覆った後（.is-flash）は、この幕は白の裏に隠れて
   ほとんど見えないのに、**画面全体の backdrop-filter を毎フレーム掛け続ける**。勝利演出は
   そのまま勝利モーダル→通貨→順位→個人結果→対戦終了パネルの背景として数分間残るので、
   その間ずっと画面全体のフィルタが走り、実機（iPhone）で1フレーム 740→1500ms まで落ちていた
   （報告 #318 のログ）。暗い地のグラデーション自体は残すので見た目はほぼ変わらない。 */
#victory-celebration.is-flash .vic-veil {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  /* filter を外すと盤面が白の向こうに薄く透けてしまう（白は 88% で、残り 12% に盤面が出る）。
     見た目を今までどおり「一面の白」に保つため、幕の地の色そのものをほぼ不透明にして盤面を隠す。
     ただの塗りなので、毎フレーム画面全体を作り直す backdrop-filter とは負荷が桁違いに軽い。 */
  background: rgba(3, 4, 8, 0.96);
}

/* GATHER: 光の帯・霧を描くキャンバス（位置に依らない大量描画はここ） */
#victory-celebration .vic-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* COLORS: 勝者のロックスロットが順に発光 → 最後に同時に強く光る。
   【重要】盤面側の .lock-slot を光らせると、幕(.vic-veil)の backdrop-filter で彩度と明るさが
   落ちるため「暗転の裏側」になって色が出なかった（ユーザー報告2026-08-30）。そこで、幕より
   手前のこのレイヤーに、スロットと同じ位置・同じ大きさの光を置き直している（JS側の
   spawnSlotFlares が矩形を実測して配置する）。色は演出用の VIVID（--vic-slot-rgb）。 */
#victory-celebration .vic-slots {
  position: absolute;
  inset: 0;
}
/* 【重要】盤面のスロットは画面上では台形に映る（傾き・縮み・せん断が混ざる）。
   矩形をそのまま重ねると光が「立って」見え、自前の perspective で寝かせても盤面とは消失点が
   違うので位置がずれる（ユーザー報告2026-08-31）。そこで JS 側（measureSlotQuad）が実際の
   写り方を測って matrix() を作り、この箱にそのまま当てる。板の拡大縮小は、その写り方の
   「中で」行うので、いつでもスロットの上に乗ったままになる。 */
#victory-celebration .vic-slot {
  position: absolute;
  left: 0;
  top: 0;
  transform-origin: 0 0;
  pointer-events: none;
}
#victory-celebration .vic-slot-plate {
  width: 100%;
  height: 100%;
  border-radius: 12%;
  opacity: 0;
  transform-origin: 50% 50%;
  transform: scale(0.82);
  /* 「スロットそのものが灯った」ように見せる＝中は淡く、ふちだけはっきり。
     にじみ(box-shadow)を大きくしすぎると隣とつながって、ただの虹色の帯に見えてしまう。 */
  background: radial-gradient(closest-side,
    rgba(var(--vic-slot-rgb, 255, 255, 255), 0.55) 0%,
    rgba(var(--vic-slot-rgb, 255, 255, 255), 0.28) 62%,
    rgba(var(--vic-slot-rgb, 255, 255, 255), 0) 100%);
  will-change: transform, opacity;
}
#victory-celebration .vic-slot-plate.is-lit {
  animation: vic-slot-lit 900ms cubic-bezier(0.2, 0.8, 0.3, 1) forwards;
}
@keyframes vic-slot-lit {
  0% { opacity: 0; transform: scale(0.82); }
  20% {
    opacity: 1;
    transform: scale(1.16);
    box-shadow: inset 0 0 0 0.14rem rgba(var(--vic-slot-rgb), 1),
      0 0 1.1rem 0.12rem rgba(var(--vic-slot-rgb), 0.9),
      0 0 2.2rem 0.4rem rgba(var(--vic-slot-rgb), 0.45);
  }
  100% {
    opacity: 0.95;
    transform: scale(1);
    box-shadow: inset 0 0 0 0.1rem rgba(var(--vic-slot-rgb), 0.9),
      0 0 0.6rem 0.02rem rgba(var(--vic-slot-rgb), 0.7),
      0 0 1.2rem 0.14rem rgba(var(--vic-slot-rgb), 0.3);
  }
}
#victory-celebration .vic-slot-plate.is-all {
  animation: vic-slot-all 560ms cubic-bezier(0.2, 0.8, 0.3, 1) forwards;
}
@keyframes vic-slot-all {
  0% { opacity: 0.95; transform: scale(1); }
  30% {
    opacity: 1;
    transform: scale(1.3);
    box-shadow: inset 0 0 0 0.16rem rgba(var(--vic-slot-rgb), 1),
      0 0 1.6rem 0.3rem rgba(var(--vic-slot-rgb), 0.95),
      0 0 3rem 0.7rem rgba(var(--vic-slot-rgb), 0.5);
  }
  100% {
    opacity: 1;
    transform: scale(1.06);
    box-shadow: inset 0 0 0 0.12rem rgba(var(--vic-slot-rgb), 0.95),
      0 0 0.8rem 0.06rem rgba(var(--vic-slot-rgb), 0.75),
      0 0 1.7rem 0.2rem rgba(var(--vic-slot-rgb), 0.35);
  }
}
/* カードが浮き上がったら、色はカードへ移ったものとしてロックエリアの光は消す。
   （.is-all より後に書くこと。同じ詳細度なので後勝ちでアニメーションを差し替える。） */
#victory-celebration .vic-slot-plate.is-drained {
  animation: vic-slot-drain 620ms ease-out forwards;
}
@keyframes vic-slot-drain {
  0% { opacity: 1; transform: scale(1.06); }
  100% { opacity: 0; transform: scale(0.94); box-shadow: none; }
}

/* （任意）ロックした7枚が中央に扇状に並び、最後はキューブへ吸われるように消える */
#victory-celebration .vic-card {
  position: absolute;
  transform-origin: 50% 50%;
  transform: translate(0, 0) rotate(0deg) scale(1);
  opacity: 1;
  transition: transform 900ms cubic-bezier(0.22, 0.9, 0.24, 1), opacity 320ms ease-in;
  will-change: transform;
}
#victory-celebration .vic-card .card-box {
  width: 100%;
  height: 100%;
  border-radius: 0.5rem;
  box-shadow: 0 0 0 0.18rem var(--vic-card-color), 0 0 1.2rem 0.25rem var(--vic-card-color),
    0 0.4rem 1.4rem rgba(0, 0, 0, 0.5);
}
#victory-celebration .vic-card.is-flying {
  transform: translate(var(--vic-card-tx), var(--vic-card-ty)) rotate(var(--vic-card-rot)) scale(var(--vic-card-scale));
}
/* キューブへ飛び込んで消える（その場で縮むのではなく、位置ごと駒へ吸い込ませる）。 */
#victory-celebration .vic-card.is-absorbing {
  transition: transform 520ms cubic-bezier(0.55, 0, 0.75, 0.3), opacity 520ms ease-in 120ms;
  transform: translate(var(--vic-card-tx), var(--vic-card-ty))
    rotate(calc(var(--vic-card-rot) + var(--vic-card-spin, 0deg))) scale(0.08);
  opacity: 0;
}

/* GATHER〜PULSE: 勝者のキューブ。実物の駒はそのままに、少し半透明の「疑似的な駒」を幕より
   手前に重ねて、そちらを脈動させる（ユーザー提案2026-08-30。実物は幕の裏で暗くなるうえ、
   以前は白い丸が広がるだけに見えていた）。中身はドラッグゴーストと同じく本物の .piece の
   複製で、perspective + 盤面と同じ傾きの入れ子に入れて立方体のまま見せる。
   駒は preserve-3d なので filter は使わず box-shadow だけで光らせる（教訓2）。 */
#victory-celebration .vic-cubes {
  position: absolute;
  inset: 0;
}
#victory-celebration .vic-cube {
  position: absolute;
  transform: translate(-50%, -50%);
  perspective: var(--camera-perspective);
  opacity: 0;
  transition: opacity 460ms ease-out;
  will-change: transform, opacity;
}
#victory-celebration .vic-cube-inner {
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
}
/* 実物と重なっても「もう1つ乗っている」と分かる程度の半透明にする。 */
#victory-celebration .vic-cube.is-charging { opacity: 0.66; }
/* 発光は「白い芯＋勝者の色の広いにじみ」。白を強く広く出すと、脈動が画面の中央で
   ただの白い丸に見えてしまう（ユーザー報告2026-08-31）。白は薄く狭く、色は広くぼかす。 */
/* 【重要】にじみを大きく取ると、駒のまわりに丸い光の溜まり＝スポットライトができてしまう
   （ユーザー報告2026-08-31「スポットライトは不要で駒自体が光ってればそれでいい」）。
   ぼかしは駒の輪郭からわずかに出る程度に留める。 */
#victory-celebration .vic-cube .piece-face {
  box-shadow:
    0 0 0.35rem 0 rgba(255, 255, 255, calc(0.3 * var(--vic-cube-white, 1))),
    0 0 0.7rem 0.02rem rgba(var(--vic-cube-rgb, 255, 255, 255), 0.5);
}
#victory-celebration .vic-cube.is-pulse .piece-face {
  animation: vic-cube-face-beat var(--vic-pulse-ms, 380ms) cubic-bezier(0.2, 0.9, 0.3, 1);
}
@keyframes vic-cube-face-beat {
  0% {
    box-shadow: 0 0 0.35rem 0 rgba(255, 255, 255, calc(0.3 * var(--vic-cube-white, 1))),
      0 0 0.7rem 0.02rem rgba(var(--vic-cube-rgb, 255, 255, 255), 0.5);
  }
  35% {
    box-shadow:
      0 0 calc(0.5rem * var(--vic-pulse-power, 1)) 0 rgba(255, 255, 255, calc(0.42 * var(--vic-cube-white, 1))),
      0 0 calc(1.3rem * var(--vic-pulse-power, 1)) calc(0.12rem * var(--vic-pulse-power, 1)) rgba(var(--vic-cube-rgb, 255, 255, 255), 0.85);
  }
  100% {
    box-shadow: 0 0 0.35rem 0 rgba(255, 255, 255, calc(0.3 * var(--vic-cube-white, 1))),
      0 0 0.7rem 0.02rem rgba(var(--vic-cube-rgb, 255, 255, 255), 0.5);
  }
}
/* 脈動のたびに、同じキューブが一回り大きく広がって消える残像を1つ置く。 */
#victory-celebration .vic-cube.is-echo {
  opacity: 0.45;
  animation: vic-cube-echo 880ms cubic-bezier(0.2, 0.7, 0.3, 1) forwards;
}
@keyframes vic-cube-echo {
  0% { opacity: 0.45; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(calc(1.9 + 1.1 * var(--vic-echo-power, 1))); }
}
/* 脈動に合わせた、ごく小さな画面振動（盤面のtransformには触らず、ステージ全体を揺らす）。 */
#victory-celebration.is-pulsing .vic-cubes {
  animation: vic-shake var(--vic-pulse-ms, 380ms) ease-out;
}
@keyframes vic-shake {
  0%, 100% { transform: translate(0, 0); }
  20% { transform: translate(calc(var(--vic-shake-amp, 2px) * -1), var(--vic-shake-amp, 2px)); }
  45% { transform: translate(var(--vic-shake-amp, 2px), calc(var(--vic-shake-amp, 2px) * -0.6)); }
  70% { transform: translate(calc(var(--vic-shake-amp, 2px) * -0.5), 0); }
}
/* （脈動のたびの「白い丸」は、上の疑似キューブの残像 .vic-cube.is-echo に置き換えた。 */

/* FLASH: キューブ中心から白が全画面へ。真っ白にはしない＝盤面の輪郭がうっすら残る。 */
#victory-celebration .vic-flash {
  position: absolute;
  inset: 0;
  opacity: 0;
  background:
    radial-gradient(circle at var(--vic-flash-x, 50%) var(--vic-flash-y, 50%),
      rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0.96) 42%, rgba(255, 255, 255, 0.9) 100%);
}
#victory-celebration.is-flash .vic-flash {
  animation: vic-flash-expand var(--vic-flash-ms, 560ms) cubic-bezier(0.3, 0, 0.15, 1) forwards;
}
@keyframes vic-flash-expand {
  0% { opacity: 0; clip-path: circle(0% at var(--vic-flash-x, 50%) var(--vic-flash-y, 50%)); }
  35% { opacity: 1; }
  100% { opacity: var(--vic-white-alpha, 0.88); clip-path: circle(150% at var(--vic-flash-x, 50%) var(--vic-flash-y, 50%)); }
}
/* 白い光の中を漂う、ごく淡い七色の残光（世界へ放出されたようには見せない）。 */
#victory-celebration.is-flash .vic-flash::after {
  content: "";
  position: absolute;
  inset: -20%;
  opacity: calc(0.1 * var(--vic-residue, 1));
  /* 【重さ対策・2026-09-06】以前はここに filter: blur(18px) を掛けていた。全画面より一回り
     大きい板（inset:-20%）を毎フレームぼかし直す＝実機で一番重かった処理で、しかも勝利演出は
     リザルトの背景として数分間残るため、その間ずっと効き続けていた（実測: これを外すだけで
     1フレーム 333ms→250ms＝演出前の水準に戻る）。ぼかしの代わりに、グラデーションを中心から
     そのまま薄れる形にして同じやわらかさを出す（不透明度0.1・白地の上なので見分けは付かない）。 */
  background:
    radial-gradient(circle at 22% 32%, #ff405c 0%, transparent 46%),
    radial-gradient(circle at 68% 24%, #ff8a32 0%, transparent 44%),
    radial-gradient(circle at 82% 58%, #ffd84a 0%, transparent 46%),
    radial-gradient(circle at 58% 78%, #42e58a 0%, transparent 44%),
    radial-gradient(circle at 30% 72%, #49a8ff 0%, transparent 46%),
    radial-gradient(circle at 12% 54%, #ff74c8 0%, transparent 44%),
    radial-gradient(circle at 46% 46%, #a875ff 0%, transparent 48%);
  animation: vic-residue-drift 9s ease-in-out infinite alternate;
}
@keyframes vic-residue-drift {
  0% { transform: translate(0, 0) scale(1); }
  100% { transform: translate(2.5%, -2%) scale(1.06); }
}

/* VICTORY: 白い光の中に浮かび上がる勝利表示。文言は意味を断定しないものだけ。 */
#victory-celebration .vic-text {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  opacity: 0;
  transform: translateY(1.2rem);
  transition: opacity 520ms ease-out, transform 520ms cubic-bezier(0.16, 1, 0.3, 1);
  color: #2f2a22;
  text-align: center;
  text-shadow: 0 0.1rem 0.6rem rgba(255, 255, 255, 0.9);
}
#victory-celebration.is-victory .vic-text { opacity: 1; transform: translateY(0); }
#victory-celebration .vic-avatar {
  line-height: 1;
  filter: drop-shadow(0 0.3rem 1.2rem rgba(0, 0, 0, 0.22));
}
#victory-celebration .vic-avatar img {
  width: 1em;
  height: 1em;
  border-radius: 50%;
  object-fit: cover;
}
#victory-celebration .vic-lead {
  font-size: 1.5rem;
  letter-spacing: 0.5em;
  text-indent: 0.5em;
  font-weight: 600;
  opacity: 0.85;
}
#victory-celebration .vic-title {
  font-size: 3.6rem;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-indent: 0.18em;
  line-height: 1.05;
}
#victory-celebration .vic-name {
  font-size: 1.25rem;
  font-weight: 700;
  opacity: 0.9;
}
/* リザルトへ渡した後、白をゆっくり引く（急な切り替えにしない）。 */
/* 【重さ対策・2026-09-06】演出が終わってリザルトの背景になった後（.is-settled）は、
   白の中の七色残光を止める。全画面より一回り大きい板に七色のグラデーションを7枚重ねて
   常時ゆっくり動かし続けるもので、実測でフレーム間隔が 250ms→300ms（＝この1枚だけで
   2割増し）になっていた。演出中（FLASH〜VICTORY の数秒）はそのまま見せる。 */
#victory-celebration.is-settled .vic-flash::after { display: none; }
#victory-celebration.is-dismissing { transition: opacity 650ms ease-out; opacity: 0; }

@media (max-width: 760px) {
  #victory-celebration .vic-lead { font-size: 1.05rem; }
  #victory-celebration .vic-title { font-size: 2.4rem; }
  #victory-celebration .vic-name { font-size: 1rem; }
}

#victory-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(26rem, 90vw);
  padding: 2.2rem 1.5rem;
  border-radius: 1rem;
  background: linear-gradient(160deg, #422006, #1c1004);
  border: 3px solid #facc15;
  text-align: center;
  z-index: 10501;
  font-family: sans-serif;
  color: #fff8e1;
  animation: victory-modal-glow 2s ease-in-out infinite, victory-modal-pop-in 0.4s ease-out;
}
/* ユーザー要望「勝利モーダルにはアバターも大きく表示させてください」。 */
.victory-modal-avatar {
  width: 8rem;
  height: 8rem;
  margin: 0 auto 0.8rem;
  border-radius: 50%;
  overflow: hidden;
  border: 3px solid #facc15;
  box-shadow: 0 0 1rem 0.2rem rgba(250, 204, 21, 0.6);
  background: #1c1004;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 4rem;
  line-height: 1;
}
.victory-modal-avatar .avatar-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.victory-modal-trophy {
  font-size: 4rem;
  line-height: 1;
  margin-bottom: 0.8rem;
  filter: drop-shadow(0 0 0.6rem rgba(250, 204, 21, 0.8));
}
.victory-modal-title {
  font-size: 1.5rem;
  font-weight: bold;
  color: #facc15;
  margin-bottom: 0.5rem;
  letter-spacing: 0.03em;
}
.victory-modal-subtitle {
  font-size: 0.9rem;
  opacity: 0.85;
}
.victory-modal-close {
  background: rgba(255, 255, 255, 0.12);
}

/* ユーザー要望「勝利時ランクアップした場合に何かモーダル出したい」。post-game-panel.js
   が承認後の見込みティアを楽観的に計算して呼ぶ（rank-up-modal.js参照）。 */
@keyframes rank-up-modal-pop-in {
  0% { transform: translate(-50%, -50%) scale(0.7); opacity: 0; }
  100% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
}
#rank-up-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(24rem, 90vw);
  padding: 1.8rem 1.5rem;
  border-radius: 1rem;
  background: linear-gradient(160deg, #1e1b4b, #0f0a2e);
  border: 2px solid #a855f7;
  box-shadow: 0 0 2.5rem 0.4rem rgba(168, 85, 247, 0.5), 0 20px 60px rgba(0, 0, 0, 0.7);
  text-align: center;
  z-index: 10701;
  font-family: sans-serif;
  color: #f5f3ff;
  animation: rank-up-modal-pop-in 0.4s ease-out;
}
.rank-up-modal-title {
  font-size: 1.3rem;
  font-weight: bold;
  margin-bottom: 1.2rem;
  letter-spacing: 0.03em;
}
.rank-up-modal-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  margin-bottom: 1rem;
}
.rank-up-modal-arrow {
  font-size: 1.5rem;
  color: #c4b5fd;
}
.rank-up-modal-badge {
  width: 4rem;
  height: 4rem;
  border-radius: 50%;
  border: 0.4rem solid var(--rank-up-badge-color, #facc15);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0.7;
}
.rank-up-modal-badge.is-large {
  width: 6rem;
  height: 6rem;
  opacity: 1;
  box-shadow: 0 0 1rem 0.2rem var(--rank-up-badge-color, #facc15);
}
.rank-up-modal-badge.is-rainbow {
  border: none;
  background: conic-gradient(#ef4444, #f97316, #eab308, #22c55e, #3b82f6, #6366f1, #a855f7, #ef4444);
}
.rank-up-modal-subtitle {
  font-size: 0.85rem;
  color: #c4b5fd;
  line-height: 1.5;
}
.rank-up-modal-close {
  background: rgba(255, 255, 255, 0.12);
}

/* ユーザー要望（続き87）「勝利時にお金を獲得した演出モーダルが欲しい」。
   victory.jsのcheckForVictory()から、勝利モーダルが閉じた後に出す。 */
@keyframes currency-award-modal-pop-in {
  0% { transform: translate(-50%, -50%) scale(0.7); opacity: 0; }
  100% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
}
@keyframes currency-award-modal-coin-bounce {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50% { transform: translateY(-0.4rem) rotate(-8deg); }
}
#currency-award-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(20rem, 88vw);
  padding: 1.8rem 1.5rem;
  border-radius: 1rem;
  background: linear-gradient(160deg, #14532d, #052e16);
  border: 2px solid #4ade80;
  box-shadow: 0 0 2.5rem 0.4rem rgba(74, 222, 128, 0.5), 0 20px 60px rgba(0, 0, 0, 0.7);
  text-align: center;
  z-index: 10520;
  font-family: sans-serif;
  color: #f0fdf4;
  animation: currency-award-modal-pop-in 0.4s ease-out;
}
.currency-award-modal-coin {
  font-size: 3.4rem;
  line-height: 1;
  margin-bottom: 0.6rem;
  animation: currency-award-modal-coin-bounce 1s ease-in-out infinite;
}
.currency-award-modal-title {
  font-size: 1.1rem;
  font-weight: bold;
  margin-bottom: 0.4rem;
  letter-spacing: 0.03em;
  color: #86efac;
}
.currency-award-modal-amount {
  font-size: 2.2rem;
  font-weight: bold;
  color: #facc15;
}

/* ユーザー要望（続き87）「勝利後、自分の順位を表示させたい。最下位から順位が
   上がっていき、自分の順位のところで自分が強調されている感じがいい」。
   通貨獲得モーダルが閉じた後に出す（rank-reveal-modal.js参照）。 */
@keyframes rank-reveal-modal-pop-in {
  0% { transform: translate(-50%, -50%) scale(0.7); opacity: 0; }
  100% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
}
@keyframes rank-reveal-modal-settle-glow {
  0% { text-shadow: none; }
  100% { text-shadow: 0 0 0.8rem rgba(250, 204, 21, 0.9); }
}
#rank-reveal-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(22rem, 88vw);
  padding: 1.8rem 1.5rem;
  border-radius: 1rem;
  background: linear-gradient(160deg, #0c4a6e, #082f49);
  border: 2px solid #38bdf8;
  box-shadow: 0 0 2.5rem 0.4rem rgba(56, 189, 248, 0.5), 0 20px 60px rgba(0, 0, 0, 0.7);
  text-align: center;
  z-index: 10530;
  font-family: sans-serif;
  color: #f0f9ff;
  animation: rank-reveal-modal-pop-in 0.4s ease-out;
}
.rank-reveal-modal-title {
  font-size: 1.1rem;
  font-weight: bold;
  margin-bottom: 0.8rem;
  letter-spacing: 0.03em;
  color: #7dd3fc;
}
/* 対戦終了後のランキング演出（ユーザー要望「アバターと名前が連なる一覧を最下位から登っていく」）。
   一覧をスクロール可能にし、ハイライトが下から自分の行まで上がっていく。 */
#rank-reveal-modal.is-climb {
  width: min(30rem, 92vw);
}
.rank-reveal-climb-list {
  max-height: 55vh;
  overflow-y: auto;
  text-align: left;
  margin: 0.4rem 0 0.6rem;
  scroll-behavior: smooth;
}
.rank-reveal-climb-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.35rem 0.5rem;
  border-bottom: 1px solid rgba(148, 163, 184, 0.14);
  border-radius: 0.4rem;
  font-size: 0.9rem;
  transition: background 0.15s ease, box-shadow 0.15s ease;
}
.rank-reveal-climb-row .rr-rank {
  color: #7dd3fc;
  font-weight: bold;
  min-width: 2.2rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.rank-reveal-climb-row .rr-avatar {
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 50%;
  object-fit: cover;
  flex: 0 0 auto;
}
.rank-reveal-climb-row .rr-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rank-reveal-climb-row .rr-val {
  color: #bae6fd;
  font-variant-numeric: tabular-nums;
}
/* 登っていく途中でフォーカスされている行（一時的な明滅）。 */
.rank-reveal-climb-row.is-climb-focus {
  background: rgba(56, 189, 248, 0.22);
  box-shadow: 0 0 0 1px rgba(56, 189, 248, 0.6) inset;
}
/* 自分の行（到達前は控えめにマーク）。 */
.rank-reveal-climb-row.is-self .rr-name {
  font-weight: bold;
  color: #fde68a;
}
/* 到達＝自分の順位。強く光らせて脈動させる。 */
.rank-reveal-climb-row.is-arrived {
  background: rgba(250, 204, 21, 0.22);
  box-shadow: 0 0 0 1px rgba(250, 204, 21, 0.85) inset, 0 0 1rem rgba(250, 204, 21, 0.4);
  animation: rank-reveal-arrived-pulse 1.4s ease-in-out infinite;
}
@keyframes rank-reveal-arrived-pulse {
  0%,
  100% {
    box-shadow: 0 0 0 1px rgba(250, 204, 21, 0.85) inset, 0 0 0.6rem rgba(250, 204, 21, 0.3);
  }
  50% {
    box-shadow: 0 0 0 1px rgba(250, 204, 21, 1) inset, 0 0 1.4rem rgba(250, 204, 21, 0.6);
  }
}
@media (prefers-reduced-motion: reduce) {
  .rank-reveal-climb-list {
    scroll-behavior: auto;
  }
  .rank-reveal-climb-row.is-arrived {
    animation: none;
  }
}
/* 未ランクイン時：勝率ランキング一覧＋理由説明＋ログイン導線（showNotRankedModal）。 */
#rank-reveal-modal.is-not-ranked {
  text-align: left;
  width: min(24rem, 90vw);
}
.rank-reveal-leaderboard {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  margin: 0.2rem 0 0.8rem;
  max-height: 38vh;
  overflow-y: auto;
}
.rank-reveal-leaderboard-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.25rem 0.5rem;
  border-radius: 0.4rem;
  background: rgba(255, 255, 255, 0.05);
  font-size: 0.85rem;
}
.rank-reveal-leaderboard-row .rr-rank { color: #7dd3fc; font-weight: bold; min-width: 2.6rem; }
.rank-reveal-leaderboard-row .rr-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rank-reveal-leaderboard-row .rr-val { color: #bae6fd; font-variant-numeric: tabular-nums; }
.rank-reveal-leaderboard-empty { font-size: 0.85rem; color: #bae6fd; text-align: center; padding: 0.8rem 0; }
.rank-reveal-not-ranked-note {
  font-size: 0.85rem;
  line-height: 1.6;
  color: #e0f2fe;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(56, 189, 248, 0.35);
  border-radius: 0.5rem;
  padding: 0.6rem 0.7rem;
}
.rank-reveal-cta-btn {
  display: block;
  width: 100%;
  margin-top: 0.7rem;
  padding: 0.6rem 1rem;
  border: none;
  border-radius: 0.6rem;
  cursor: pointer;
  font-family: sans-serif;
  font-size: 0.95rem;
  font-weight: bold;
  background: linear-gradient(160deg, #38bdf8, #0ea5e9);
  color: #082f49;
}
.rank-reveal-cta-btn:hover { filter: brightness(1.08); }
.rank-reveal-modal-number {
  font-size: 2.6rem;
  font-weight: bold;
  color: #f0f9ff;
  font-variant-numeric: tabular-nums;
}
.rank-reveal-modal-number.is-settled {
  color: #facc15;
  animation: rank-reveal-modal-settle-glow 0.6s ease-out forwards;
}
.rank-reveal-modal-delta {
  margin-top: 0.6rem;
  font-size: 1rem;
  font-weight: bold;
  min-height: 1.4rem;
  color: #cbd5e1;
}
.rank-reveal-modal-delta.is-up {
  color: #4ade80;
}
.rank-reveal-modal-delta.is-down {
  color: #f87171;
}

/* ユーザー要望（続き95）「対戦終了時の個人結果を実装」。match-personal-result-modal.js、
   システム順位モーダル(rank-reveal-modal)の後・戦績パネルの前に出す。 */
@keyframes match-personal-result-modal-pop-in {
  0% { transform: translate(-50%, -50%) scale(0.7); opacity: 0; }
  100% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
}
#match-personal-result-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(24rem, 90vw);
  max-height: 85vh;
  overflow-y: auto;
  padding: 1.6rem 1.4rem;
  border-radius: 1rem;
  background: linear-gradient(160deg, #4c1d95, #1e1b4b);
  border: 2px solid #a78bfa;
  box-shadow: 0 0 2.5rem 0.4rem rgba(167, 139, 250, 0.5), 0 20px 60px rgba(0, 0, 0, 0.7);
  text-align: center;
  z-index: 10531;
  font-family: sans-serif;
  color: #f5f3ff;
  animation: match-personal-result-modal-pop-in 0.4s ease-out;
  box-sizing: border-box;
}
.match-personal-result-title {
  font-size: 1.15rem;
  font-weight: bold;
  margin-bottom: 0.9rem;
  letter-spacing: 0.03em;
  color: #c4b5fd;
}
.match-personal-result-standings {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-bottom: 1rem;
}
.match-personal-result-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 0.6rem;
  border-radius: 0.5rem;
  background: rgba(255, 255, 255, 0.06);
  text-align: left;
}
.match-personal-result-row.is-self {
  background: rgba(167, 139, 250, 0.28);
  border: 1px solid #c4b5fd;
}
.match-personal-result-rank {
  font-weight: bold;
  font-variant-numeric: tabular-nums;
  min-width: 2.2rem;
  color: #ddd6fe;
}
.match-personal-result-row.is-winner .match-personal-result-rank {
  color: #facc15;
}
.match-personal-result-avatar {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  overflow: hidden;
  flex-shrink: 0;
  background: rgba(255, 255, 255, 0.15);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.1rem;
}
.match-personal-result-avatar .avatar-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.match-personal-result-name {
  flex: 1;
  font-size: 0.9rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.match-personal-result-locked {
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  color: #ddd6fe;
}
.match-personal-result-self {
  padding-top: 0.8rem;
  border-top: 1px solid rgba(255, 255, 255, 0.2);
}
.match-personal-result-self-title {
  font-size: 0.85rem;
  color: #c4b5fd;
  margin-bottom: 0.3rem;
}
.match-personal-result-self-stats {
  font-size: 1.05rem;
  font-weight: bold;
}
.match-personal-result-self-stats-sub {
  font-size: 0.85rem;
  color: #ddd6fe;
  margin-top: 0.3rem;
}
/* MVPカード（全体・自分、対戦終了時の個人結果モーダル）。 */
.match-personal-result-mvp {
  padding-top: 0.8rem;
  margin-top: 0.8rem;
  border-top: 1px solid rgba(255, 255, 255, 0.2);
}
.match-personal-result-mvp-row {
  display: flex;
  justify-content: center;
  gap: 1.4rem;
  margin-top: 0.4rem;
}
.match-personal-result-mvp-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
}
.match-personal-result-mvp-label {
  font-size: 0.8rem;
  color: #fbbf24;
  font-weight: bold;
}
.match-personal-result-mvp-card {
  position: relative;
  width: 6rem;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border-radius: 0.4rem;
  box-shadow: 0 0.3rem 0.9rem rgba(0, 0, 0, 0.5);
}
.match-personal-result-mvp-name {
  font-size: 0.8rem;
  text-align: center;
  max-width: 8rem;
  line-height: 1.4;
}
/* ターンごとのロック枚数の折れ線グラフ（対戦終了時の個人結果モーダル）。 */
.match-personal-result-chart {
  padding-top: 0.8rem;
  margin-top: 0.8rem;
  border-top: 1px solid rgba(255, 255, 255, 0.2);
}
.match-lock-chart-wrap {
  margin-top: 0.4rem;
}
.match-lock-chart {
  width: 100%;
  height: auto;
  display: block;
}
.match-lock-chart-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.3rem 0.9rem;
  margin-top: 0.4rem;
  font-size: 0.8rem;
}
.match-lock-chart-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
}
.match-lock-chart-legend-dot {
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  display: inline-block;
}

/* 効果音ごとの音量（0〜100）。src/sound.jsがオプションメニューの「マスター音量」と
   掛け算して最終的な再生音量にする。管理者モードの「効果音の音量（個別）」グループから
   調整できる。 */
:root {
  --sound-volume-hand-shuffle: 80;
  --sound-volume-deck-shuffle: 80;
  --sound-volume-card-flip: 80;
  --sound-volume-card-place: 80;
  --sound-volume-card-draw: 80;
  --sound-volume-arrival-effect: 80;
  --sound-volume-lock: 80;
}

/* プレイヤーDのロックエリア(.lock-right)は7色スロットの並び順を正すため祖先自体に
   180度回転を掛けている。柱状バースト・ロックスタンプはどの辺でも常に画面の「上方向」に
   伸びる向きで作られているため、そのままだとD側だけ上下逆さまに表示されてしまう。
   main.jsのappendEffectHost()が、.lock-rightの子孫にだけこの入れ子（180度回転）を
   挟んで祖先の回転を打ち消す（中身は中心基準の配置・アニメーションのため、入れ子を
   挟んでも見た目の位置はズレない）。 */
.effect-side-flip {
  position: absolute;
  inset: 0;
  pointer-events: none;
  transform: rotate(180deg);
}

/* 駒がカードに到達した瞬間、そのマス/ロックスロット自体がカードの色で発光する演出
   （main.jsのtriggerCardArrival参照。.cell/.lock-slotは元からposition:relativeなので、
   このposition:absoluteな子要素はそのマスを基準に配置される）。カードの外枠(.arrival-effect-frame)
   が縁取りとして光り、そこから複数の柱(.arrival-effect-flame、太さ・タイミングをずらして
   3本重ねることで1本より厚みのある「オーラ」らしい揺らめきを出す)が立ち上り、根本に光の輪
   (.arrival-effect-ring)が広がる、という3層構成。アニメーション終了後はJS側がsetTimeoutで
   要素ごと消す（使い捨てのワンショット演出）。 */
.arrival-effect-burst {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 30;
}
/* カードの外枠自体が発光源になっているように見せる縁取り（「カードの枠から立ち上る」印象を
   柱の演出より先に作る）。 */
.arrival-effect-frame {
  position: absolute;
  inset: -8%;
  border: 0.2rem solid var(--arrival-effect-color);
  border-radius: 0.3rem;
  box-shadow:
    0 0 0.5rem 0.15rem var(--arrival-effect-color),
    0 0 1.4rem 0.5rem var(--arrival-effect-color),
    inset 0 0 0.9rem 0.3rem var(--arrival-effect-color);
  opacity: 0;
  animation: arrival-frame-glow 1.3s ease-out forwards;
}
/* 柱本体。base(80%幅)・mid(55%幅)・core(32%幅、遅れて立ち上る芯)の3本を重ね、
   mix-blend-mode:screenで色を飽和させすぎずに濃く見せる。 */
.arrival-effect-flame {
  position: absolute;
  left: 50%;
  bottom: 45%;
  width: 80%;
  height: 320%;
  transform-origin: bottom center;
  background: linear-gradient(
    to top,
    var(--arrival-effect-color) 0%,
    color-mix(in srgb, var(--arrival-effect-color) 80%, transparent) 32%,
    color-mix(in srgb, var(--arrival-effect-color) 40%, transparent) 62%,
    transparent 100%
  );
  border-radius: 50% 50% 0 0 / 16% 16% 0 0;
  filter: blur(0.15rem);
  mix-blend-mode: screen;
  opacity: 0;
  animation: arrival-pillar-rise 1.3s cubic-bezier(0.15, 0.7, 0.25, 1) forwards;
}
.arrival-effect-flame-mid {
  width: 55%;
  animation-delay: 0.08s;
  animation-duration: 1.15s;
}
.arrival-effect-flame-core {
  width: 32%;
  filter: blur(0.05rem);
  animation-delay: 0.15s;
  animation-duration: 1s;
}
.arrival-effect-ring {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 55%;
  height: 55%;
  transform: translate(-50%, -50%) scale(0);
  border-radius: 50%;
  background: radial-gradient(
    circle,
    var(--arrival-effect-color) 0%,
    color-mix(in srgb, var(--arrival-effect-color) 45%, transparent) 45%,
    transparent 75%
  );
  opacity: 0;
  animation: arrival-burst-ring 0.9s ease-out forwards;
}

/* 虹（なないろの欠片）カードの到達演出だけ、柱・光の輪を単色ではなく虹色にする。
   border-color/box-shadow/color-mix()はグラデーションを受け付けないため、
   --arrival-effect-colorをそのまま使う通常ルールでは虹を表現できない。
   spawnArrivalBurst()がcolor==="rainbow"の時だけこのクラスを付け、柱・光の輪の
   background/枠線だけをここで個別に上書きする（transform/opacityのアニメーションは
   通常ルールと共通のまま）。 */
.arrival-effect-burst.is-rainbow .arrival-effect-frame {
  border-color: transparent;
  border-image: linear-gradient(90deg, #c70025, #ee781f, #fabe00, #22ac38, #1bb8ce, #f19ec2, #915da3) 1;
  box-shadow:
    0 0 0.5rem 0.15rem rgba(255, 255, 255, 0.55),
    0 0 1.4rem 0.5rem rgba(255, 255, 255, 0.35);
}
.arrival-effect-burst.is-rainbow .arrival-effect-flame {
  background: linear-gradient(
    to top,
    #c70025 0%,
    #ee781f 14%,
    #fabe00 28%,
    #22ac38 42%,
    #1bb8ce 57%,
    #f19ec2 71%,
    #915da3 85%,
    transparent 100%
  );
}
.arrival-effect-burst.is-rainbow .arrival-effect-ring {
  background: conic-gradient(#c70025, #ee781f, #fabe00, #22ac38, #1bb8ce, #f19ec2, #915da3, #c70025);
}
@keyframes arrival-frame-glow {
  0% {
    opacity: 0;
  }
  18% {
    opacity: 1;
  }
  65% {
    opacity: 0.7;
  }
  100% {
    opacity: 0;
  }
}
/* rise(縦に伸びる)・widen(横に太くなる)・flicker(左右にわずかに揺れる)を1つの
   transformにまとめて焼き込む。別々の@keyframesを同じtransformプロパティに重ねがけ
   することはできない（後勝ちで上書きされる）ため、この形にしている。 */
@keyframes arrival-pillar-rise {
  0% {
    transform: translateX(-50%) scaleY(0) scaleX(0.5);
    opacity: 0;
  }
  12% {
    opacity: 1;
  }
  30% {
    transform: translateX(-53%) scaleY(0.5) scaleX(0.8);
  }
  45% {
    transform: translateX(-47%) scaleY(0.75) scaleX(1);
  }
  60% {
    transform: translateX(-51%) scaleY(0.95) scaleX(1.2);
    opacity: 1;
  }
  80% {
    transform: translateX(-49%) scaleY(1.05) scaleX(1.4);
    opacity: 0.8;
  }
  100% {
    transform: translateX(-50%) scaleY(1.1) scaleX(1.55);
    opacity: 0;
  }
}
@keyframes arrival-burst-ring {
  0% {
    transform: translate(-50%, -50%) scale(0);
    opacity: 0.95;
  }
  60% {
    transform: translate(-50%, -50%) scale(2.6);
    opacity: 0.6;
  }
  100% {
    transform: translate(-50%, -50%) scale(3.6);
    opacity: 0;
  }
}

/* エターナルカード獲得演出（ユーザー要望「ゲート侵攻によりエターナルカードを手に入れる
   ときの演出を取り入れたい」、main.jsのplayEternalAcquisitionAnim参照）。画面中央固定の
   カード1枚を、裏向き待機(is-suspense、虹色の縁取りが脈打つ)→3Dフリップ(is-flipped)→
   表向きで色付きに脈打つ(is-revealed)、と切り替えていく。flyGhostの.setup-fly-cardと
   違い、こちらは「止まって見せる」区間を持つため専用のクラスにしてある。 */
/* ザ・ギャンブル成功／試練の儀式「〇回成功！」のお祝い演出（案A: 紙吹雪＋大きな見出し）。
   ユーザー要望2026-08-07。tone-success=紙吹雪あり、tone-fail=控えめ。 */
.result-celebration {
  position: fixed;
  inset: 0;
  z-index: 10650;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.25s ease;
}
.result-celebration.show {
  opacity: 1;
}
.result-celebration-card {
  text-align: center;
  transform: scale(0.6);
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
  padding: 1.2rem 2rem;
}
.result-celebration.show .result-celebration-card {
  transform: scale(1);
}
.result-celebration-headline {
  font-family: "Arial Black", sans-serif;
  font-weight: 900;
  font-size: clamp(2.4rem, 9vw, 5rem);
  letter-spacing: 0.02em;
  color: #fde68a;
  text-shadow: 0 2px 0 #b45309, 0 0 1.4rem rgba(251, 191, 36, 0.9), 0 6px 18px rgba(0, 0, 0, 0.6);
  animation: celebration-pop 0.6s ease-out;
}
.result-celebration.tone-fail .result-celebration-headline {
  color: #cbd5e1;
  text-shadow: 0 2px 0 #334155, 0 6px 18px rgba(0, 0, 0, 0.6);
  font-size: clamp(1.8rem, 6vw, 3.2rem);
}
.result-celebration-sub {
  margin-top: 0.8rem;
  font-family: sans-serif;
  font-size: clamp(0.9rem, 2.4vw, 1.15rem);
  font-weight: bold;
  color: #f1f5f9;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.9);
}
@keyframes celebration-pop {
  0% { transform: scale(0.5); }
  60% { transform: scale(1.12); }
  100% { transform: scale(1); }
}
.result-celebration-confetti {
  position: absolute;
  inset: 0;
  overflow: hidden;
}
.result-celebration-confetti i {
  position: absolute;
  top: -8%;
  width: 0.6rem;
  height: 1rem;
  border-radius: 1px;
  opacity: 0.95;
  animation-name: confetti-fall;
  animation-timing-function: ease-in;
  animation-iteration-count: 1;
}
@keyframes confetti-fall {
  0% { transform: translateY(-10vh) translateX(0) rotate(0deg); opacity: 1; }
  100% { transform: translateY(112vh) translateX(var(--drift, 0)) rotate(var(--rot, 360deg)); opacity: 0.9; }
}
body.reduce-glow .result-celebration-confetti i,
body.reduce-glow .result-celebration-headline {
  animation: none;
}

/* マスチェンジの入れ替え演出（ユーザー要望2026-08-08: 発光＋不安定な電撃で結ばれて入れ替わる）。 */
:root {
  --swap-anim-charge-duration: 0.8; /* 発光＋電撃の“溜め”（秒） */
  --swap-anim-flight-duration: 0.6; /* 入れ替え飛翔（秒） */
}
.piece-swap-glow {
  filter: drop-shadow(0 0 0.35rem #fb923c) drop-shadow(0 0 0.8rem #fdba74);
  animation: piece-swap-glow-pulse 0.5s ease-in-out infinite alternate;
}
@keyframes piece-swap-glow-pulse {
  from {
    filter: drop-shadow(0 0 0.25rem #f97316) drop-shadow(0 0 0.5rem #fdba74);
  }
  to {
    filter: drop-shadow(0 0 0.6rem #fb923c) drop-shadow(0 0 1.2rem #fed7aa);
  }
}
/* ユーザー要望2026-08-08: 鋭い実線の電撃ではなく「ぼやけた湯気のようなオーラの電撃」に。
   芯(bolt)も白ではなく暖色にしてぼかし、外側にglow・auraの2層のにじみを重ねて霞ませる。 */
.swap-arc-bolt {
  fill: none;
  stroke: #fed7aa; /* 芯も白ではなく淡い橙にして柔らかく */
  stroke-width: 6;
  stroke-linejoin: round;
  stroke-linecap: round;
  opacity: 0.85;
  filter: blur(2.5px) drop-shadow(0 0 6px #fb923c) drop-shadow(0 0 12px #f97316);
}
.swap-arc-bolt-glow {
  stroke: #f97316;
  stroke-width: 22;
  opacity: 0.3;
  filter: blur(9px);
}
/* いちばん外側の、広くぼかした“湯気のようなオーラ” */
.swap-arc-bolt-aura {
  stroke: #fdba74;
  stroke-width: 40;
  opacity: 0.18;
  filter: blur(18px);
}
body.reduce-glow .piece-swap-glow {
  animation: none;
}

.eternal-reveal-card {
  position: fixed;
  z-index: 9999;
  pointer-events: none;
}
/* スマホの一部ブラウザでrotateY＋backface-visibility＋preserve-3dのフリップが効かず
   「裏向きのまま」になる報告への対応で、3Dを使わず横スケール(scaleX)＋絵の差し替えで
   反転する方式に変更（JS側 playEternalAcquisitionAnim 参照）。transform-originを中央に
   して、横幅が中央から0へつぶれて戻るようにする。 */
.eternal-reveal-card-inner {
  position: relative;
  width: 100%;
  height: 100%;
  transform-origin: center center;
}
.eternal-reveal-card-face {
  position: absolute;
  inset: 0;
  /* カードは正方形で reveal も正方形なので cover でも切れないが、万一枠と絵の比率が
     ずれても上下左右が絶対に切れないよう contain にする（ユーザー報告: フリップ時に上下が
     見切れる、への保険）。 */
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  border-radius: 0.5rem;
  box-shadow: 0 1rem 2.4rem rgba(0, 0, 0, 0.6);
}
.eternal-reveal-card.is-suspense::before {
  content: "";
  position: absolute;
  inset: -0.5rem;
  border-radius: 0.8rem;
  background: conic-gradient(#c70025, #ee781f, #fabe00, #22ac38, #1bb8ce, #f19ec2, #915da3, #c70025);
  filter: blur(0.7rem);
  z-index: -1;
  animation: eternal-rainbow-shimmer 1.1s linear infinite;
}
@keyframes eternal-rainbow-shimmer {
  0% {
    filter: blur(0.7rem) hue-rotate(0deg);
  }
  100% {
    filter: blur(0.7rem) hue-rotate(360deg);
  }
}
.eternal-reveal-card.is-bursting::after {
  content: "";
  position: absolute;
  inset: -1.5rem;
  border-radius: 50%;
  background: radial-gradient(circle, var(--eternal-reveal-color, #fff) 0%, transparent 70%);
  opacity: 0.9;
  animation: eternal-burst-pulse 0.5s ease-out;
}
@keyframes eternal-burst-pulse {
  0% {
    transform: scale(0.4);
    opacity: 1;
  }
  100% {
    transform: scale(1.6);
    opacity: 0;
  }
}
/* ハマりどころ（ユーザー報告「エターナル獲得でフリップ後に一瞬裏向き＝2回フリップに見える」）:
   glowをpreserve-3dの.eternal-reveal-card-innerにfilterで掛けていたが、filterはpreserve-3dを
   フラット化する（このプロジェクトの駒立方体“ぺっちゃんこ”と同じCSSの罠）。その結果フリップ直後に
   backface-visibilityの効きが崩れ、裏面が一瞬見えて「もう一度フリップした」ように見えていた。
   3Dに触れないよう、フラットな表面(.is-front)へbox-shadowでglowを掛ける。 */
.eternal-reveal-card.is-revealed .eternal-reveal-card-face.is-front {
  animation: eternal-color-pulse 1.1s ease-in-out infinite;
}
@keyframes eternal-color-pulse {
  0%,
  100% {
    box-shadow: 0 1rem 2.4rem rgba(0, 0, 0, 0.6), 0 0 1rem var(--eternal-reveal-color, #fff);
  }
  50% {
    box-shadow: 0 1rem 2.4rem rgba(0, 0, 0, 0.6), 0 0 2.4rem var(--eternal-reveal-color, #fff);
  }
}

/* クイックスタート: 画面右上（山札一覧/セットアップ/オプションの下）に常設する、
   「無色あり/なし」の切り替えと「2人/3人/4人」のボタン。押すだけでセットアップ
   ウィザードの０〜３を一気に実行する（quick-start.js参照）。 */
#quick-start-bar {
  position: fixed;
  top: 9rem;
  right: 1rem;
  z-index: 900;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  align-items: flex-end;
}
.quick-start-toggle,
.quick-start-counts {
  display: flex;
  gap: 0.2rem;
}
.quick-start-toggle button,
.quick-start-counts button {
  padding: 0.3rem 0.55rem;
  background: rgba(15, 23, 32, 0.85);
  color: #e2e8f0;
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.4rem;
  cursor: pointer;
  font-family: sans-serif;
  font-size: 0.7rem;
  white-space: nowrap;
}
.quick-start-toggle button:hover,
.quick-start-counts button:hover {
  background: rgba(56, 189, 248, 0.2);
}
.quick-start-toggle button.is-selected {
  background: rgba(56, 189, 248, 0.35);
  border-color: #7dd3fc;
  font-weight: bold;
}
.quick-start-counts button {
  background: rgba(8, 145, 178, 0.25);
}
.quick-start-counts button:hover {
  background: rgba(8, 145, 178, 0.45);
}

/* オンライン対戦（第一弾・最小構成）中は、まだサーバー側にポートしていないアクション
   （セットアップウィザードの個別ステップ・クイックスタート）へ繋がるボタンを丸ごと隠す。
   main.jsのrender()がisOnlineMode()に応じてbody.is-online-modeを付け外しするだけで
   済むよう、CSS側でまとめて隠す。手札シャッフルはSHUFFLE_HANDアクションをso7-apply-action
   Edge Function側にもポートしたため、ここでは隠さない（state.js/shuffleHand参照）。 */
body.is-online-mode #game-setup-toggle-button,
body.is-online-mode #quick-start-bar {
  display: none;
}
/* CPU戦（1人用）の間は、右上の「🎲 セットアップ」ウィザードのボタン／パネル／人数バーを
   隠す（ユーザー要望2026-08-07: CPU対戦時は不要）。パネルのボタンはopen/closeでinlineの
   displayを付け外しするため、!importantでCSSを優先させる。cpu-battle.jsがbodyに
   cpu-battle-modeクラスを付ける。 */
body.cpu-battle-mode #game-setup-toggle-button,
body.cpu-battle-mode #game-setup-panel,
body.cpu-battle-mode #quick-start-bar {
  display: none !important;
}

/* セットアップアニメーション（src/setup-animation.js）: ファーストカード配布・盤面への
   カード配置を、山/ファーストカードの位置から各マス/ロックスロットへ飛んでいく演出で見せる。 */

/* 配られる最中はまだ場の実物要素を隠しておき、ゴーストが届いた瞬間に表示に切り替える。
   .board-cardと.pieceの両方に使うため、共通のopacity/transformプロパティだけを扱う。 */
.is-setup-pending {
  opacity: 0;
}
.piece.is-setup-pending {
  transform: scale(0.2);
}
/* 通常時のtransition。is-setup-pendingが外れた瞬間にこのtransitionで滑らかに現れる
   （駒はcubic-bezierのオーバーシュートで「ぶわーん」というポップ感を出す）。
   ハマりどころ（ユーザー報告、タブレット）: セットアップ配布時、is-setup-pendingが外れて
   opacity:0→1へtransitionする瞬間、深いpreserve-3d階層の中のこのカードが一瞬プレイマットの
   下に回り込んで見える（アニメーション終了後は正しく最前面に戻る）ことがあった。一旦
   will-change: opacityを試したが、同じ発想で.pieceに適用した際に実機PCで駒自体が
   描画されなくなる重大な副作用が判明したため、こちらも安全のため撤去した（.board-cardは
   preserve-3dの子要素を持たない平面要素で.pieceほどのリスクは無いはずだが、確認する
   手段が無い以上は同じ性質の対策を残さない判断とした）。この表示崩れ自体への対策は
   持ち越しとなる。 */
.board-card {
  transition: opacity 0.2s ease-out;
}
.piece {
  transition:
    opacity 0.3s ease-out,
    transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* ファーストカードの山・山札からマス/ロックスロットへ飛んでいく途中のゴースト。
   ドラッグ中のゴースト(.drag-ghost)と同じくdocument.body直下、3D空間の外に浮かべる。 */
/* 駒の移動の飛翔ゴースト。カードと同じ仕組み（.setup-fly-card）を使うが、駒のスキン画像は
   正方形なので、カードのように上下が切れる心配はない。 */
.setup-fly-piece {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 9998;
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  pointer-events: none;
  filter: drop-shadow(0 0.2rem 0.3rem rgba(0, 0, 0, 0.45));
}

/* 【ユーザー要望2026-09-05】駒の移動を「立体のまま跳ねて動く」ようにする（.setup-fly-piece の
   平たい1枚絵に代わるもの）。役割を3枚に分けているのが要点:
     .piece-hop-ghost  … 横の移動だけ（transition で A地点→B地点）
     .piece-hop-lift   … 上下の跳ねと潰れ（squash & stretch）だけ（CSSアニメ）
     .piece-hop-inner  … 盤面と同じ傾き（rotateX）。中に本物の .piece（立方体）が入る
   横と縦を別の要素に分けると、それぞれ別のイージングをかけられるので素直な放物線になる。 */
.piece-hop-ghost {
  position: fixed;
  left: 0;
  top: 0;
  perspective: var(--camera-perspective);
  pointer-events: none;
  z-index: 9998;
  will-change: transform;
}
.piece-hop-lift {
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transform-origin: 50% 100%; /* 足元を軸に潰れる（浮き上がって見えないように） */
  /* 【ユーザー報告2026-09-05】アニメは .is-animating を付けてから始める。0%が squash
     （scale(1.12, 0.85)）なので、付けたまま置くと**大きさを測る前に横へ1.12倍された値**を
     測ってしまう（main.js の playPieceHopGhost が実物と大きさを揃えるために測っている）。 */
  animation-timing-function: linear; /* 緩急はキーフレームの配置側で作る */
  animation-fill-mode: both;
}
.piece-hop-lift.is-animating {
  animation-name: piece-hop-lift;
}
.piece-hop-inner {
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
}
/* 【ユーザー要望2026-09-05】「移動方向に前方を持ち上げて、よっこいしょって感じ」。
   進行方向を +X に合わせる(.piece-hop-yaw) → その辺を持ち上げる(.piece-hop-pitch) →
   向きを戻す(.piece-hop-yaw) の3枚重ね。立方体そのものは回さない（面ごとの陰影が固定なので、
   回すと暗い面が正面に来て光の向きが破綻するため）。 */
.piece-hop-yaw,
.piece-hop-pitch {
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
}
.piece-hop-pitch.is-leaning {
  animation-name: piece-hop-lean;
  animation-timing-function: ease-in-out;
  animation-fill-mode: both;
}
@keyframes piece-hop-lean {
  0% {
    transform: rotateY(0deg);
  }
  22% {
    transform: rotateY(calc(var(--piece-hop-lean, 16deg) * -1));
  }
  62% {
    transform: rotateY(calc(var(--piece-hop-lean, 16deg) * -0.7));
  }
  90% {
    transform: rotateY(calc(var(--piece-hop-lean, 16deg) * 0.35));
  }
  100% {
    transform: rotateY(0deg);
  }
}
/* ワープ（紫のキューブ ディメンションの移動）。跳ばずに、その場で縦に伸びて消え、
   移動先で戻る。位置の瞬間移動はJS側（消えている一瞬で行う）。 */
.piece-hop-lift.is-warp.is-animating {
  animation-name: piece-warp-lift;
  animation-timing-function: ease-in-out;
}
@keyframes piece-warp-lift {
  0% {
    transform: scale3d(1, 1, 1);
    opacity: 1;
  }
  30% {
    transform: scale3d(0.55, 1.7, 0.55);
    opacity: 0.55;
  }
  46%, 54% {
    transform: scale3d(0.06, 2.3, 0.06);
    opacity: 0;
  }
  70% {
    transform: scale3d(0.55, 1.7, 0.55);
    opacity: 0.55;
  }
  88% {
    transform: scale3d(1.12, 0.86, 1.12);
    opacity: 1;
  }
  100% {
    transform: scale3d(1, 1, 1);
    opacity: 1;
  }
}
/* 通り道に残る光の軌跡（連続移動で道になる）。左端(0 50%)を起点に回転させて置く。 */
.piece-move-trail {
  position: fixed;
  left: 0;
  top: 0;
  pointer-events: none;
  z-index: 9996;
  border-radius: 999px;
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0) 0%,
    var(--trail-color, #ffffff) 22%,
    var(--trail-color, #ffffff) 78%,
    rgba(255, 255, 255, 0) 100%
  );
  filter: blur(1.5px);
  animation-name: piece-move-trail;
  animation-timing-function: ease-out;
  animation-fill-mode: both;
}
@keyframes piece-move-trail {
  0% {
    opacity: 0;
  }
  18% {
    opacity: var(--trail-peak, 0.4);
  }
  100% {
    opacity: 0;
  }
}
@keyframes piece-hop-lift {
  0% {
    transform: translateY(0) scale(1.12, 0.85);
  }
  14% {
    transform: translateY(calc(var(--piece-hop-height, 2rem) * -0.55)) scale(0.94, 1.1);
  }
  46% {
    transform: translateY(calc(var(--piece-hop-height, 2rem) * -1)) scale(1, 1);
  }
  80% {
    transform: translateY(calc(var(--piece-hop-height, 2rem) * -0.45)) scale(0.97, 1.05);
  }
  93% {
    transform: translateY(0) scale(1.16, 0.82);
  }
  100% {
    transform: translateY(0) scale(1, 1);
  }
}
/* 盤面に落ちる影。浮いている間だけ薄くぼやける（高さが伝わる）。移動そのものは
   transition（JS側）で駒と一緒に動かすので、ここでは transform を触らない
   ——同じプロパティをアニメとトランジションの両方で動かすと取り合いになるため。 */
.piece-hop-shadow {
  position: fixed;
  left: 0;
  top: 0;
  border-radius: 50%;
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0) 72%);
  pointer-events: none;
  z-index: 9997;
}
.piece-hop-shadow.is-flying {
  animation-name: piece-hop-shadow;
  animation-timing-function: linear;
  animation-fill-mode: both;
}
@keyframes piece-hop-shadow {
  0% {
    opacity: 0.6;
    filter: blur(0.5px);
  }
  46% {
    opacity: 0.22;
    filter: blur(2.5px);
  }
  100% {
    opacity: 0.6;
    filter: blur(0.5px);
  }
}
/* 着地の衝撃。マス（3D変形の中）の子なので、輪は盤面に寝た状態で広がる。 */
.piece-land-ring {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 70%;
  height: 70%;
  border: 0.12rem solid var(--land-ring-color, #fff);
  border-radius: 50%;
  pointer-events: none;
  animation: piece-land-ring 0.42s cubic-bezier(0.2, 0.8, 0.3, 1) forwards;
}
@keyframes piece-land-ring {
  0% {
    transform: translate(-50%, -50%) scale(0.25);
    opacity: 0.9;
  }
  100% {
    transform: translate(-50%, -50%) scale(1.6);
    opacity: 0;
  }
}
.setup-fly-card {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 9998;
  border-radius: 0.3rem;
  /* カード画像は正方形。飛翔ゴーストのサイズは飛び元の要素（ロックスロット/マス等、
     必ずしも正方形でない）に合わせるため、cover だと縦長の時にカード上下が切れていた
     （ユーザー報告「手札に入る時などのアニメでカードの上下が切れる」）。contain にして
     カード全体が必ず収まるようにする。 */
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  pointer-events: none;
}

/* ③演出（ユーザー要望2026-08-18）: カード配置/回収の「着地の風/ホコリ」。document.body直下・
   ステージのtransform下に置くゴースト(.setup-fly-card)と同じ層。位置は外側(.card-landing-puff)の
   inline transformで、拡大＋フェードは内側(.card-landing-puff-inner)のアニメで——同じtransformを
   2箇所から操作すると後勝ちで壊れる（既存の教訓）ため役割を分ける。 */
.card-landing-puff {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 9997; /* 飛翔ゴースト(.setup-fly-card=9998)のすぐ下 */
  pointer-events: none;
}
.card-landing-puff-inner {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    rgba(245, 240, 225, 0) 40%,
    rgba(245, 240, 225, 0.5) 52%,
    rgba(245, 240, 225, 0.12) 64%,
    rgba(245, 240, 225, 0) 74%
  );
  animation: card-landing-puff-kf 480ms ease-out forwards;
  will-change: transform, opacity;
}
@keyframes card-landing-puff-kf {
  0% {
    transform: scale(0.35);
    opacity: 0.9;
  }
  60% {
    opacity: 0.6;
  }
  100% {
    transform: scale(1.3);
    opacity: 0;
  }
}

/* 続き216（ユーザー要望2026-08-18）: 手札の使用が決定した時、画面中央にカードを拡大表示し、
   その色のオーラ(--burn-color)を纏いつつ、下から燃えて「燃えカス(embers)」になって崩れ消える演出。
   document.body直下・pointer-events:noneで下の盤面操作は妨げない。演出のあとに右の使用モーダル。 */
.hand-effect-burn-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 10660;
  --burn-color: #f6c945;
}
.hand-effect-burn-aura {
  position: absolute;
  width: 34rem;
  height: 34rem;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    var(--burn-color) 0%,
    color-mix(in srgb, var(--burn-color) 45%, transparent) 30%,
    transparent 66%
  );
  filter: blur(2.6rem);
  mix-blend-mode: screen;
  opacity: 0;
  animation: hand-effect-burn-aura-kf calc(var(--hand-burn-duration, 2.6) * 1s) ease-out forwards;
}
.hand-effect-burn-stage {
  position: relative;
  width: var(--hand-burn-card-size, 17rem);
  height: var(--hand-burn-card-size, 17rem);
}
.hand-effect-burn-card {
  position: absolute;
  inset: 0;
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  border-radius: 0.6rem;
  transform-origin: center;
  animation: hand-effect-burn-card-kf calc(var(--hand-burn-duration, 2.6) * 1s) cubic-bezier(0.33, 0, 0.2, 1) forwards;
  will-change: transform, opacity, filter;
}
/* 虹（なないろ）は単色オーラではなく虹色のオーラにする（drop-shadowは単色制約のため金の光のまま）。 */
.hand-effect-burn-overlay:has(.hand-effect-burn-card.is-rainbow) .hand-effect-burn-aura {
  background: conic-gradient(
    from 0deg,
    #ef4444,
    #f97316,
    #eab308,
    #22c55e,
    #3b82f6,
    #ec4899,
    #a855f7,
    #ef4444
  );
}
@keyframes hand-effect-burn-aura-kf {
  0% {
    opacity: 0;
    transform: scale(0.7);
  }
  12% {
    opacity: 0.8;
    transform: scale(1);
  }
  55% {
    opacity: 0.95;
    transform: scale(1.05);
  }
  100% {
    opacity: 0;
    transform: scale(1.32);
  }
}
/* 続き217（ユーザー報告「手札使用演出が早すぎて何が起きたかわからない」）: カードを“読める”よう
   全面表示＋glowで長めにホールドしてから（〜55%）、下から上へ燃え上がって崩れ消える。マス境界は
   0〜55%はカードの下（全面可視）、55〜100%で上へ掃引して燃え尽きる。 */
@keyframes hand-effect-burn-card-kf {
  0% {
    transform: scale(0.55) translateY(1.4rem);
    opacity: 0;
    filter: brightness(1) saturate(1) drop-shadow(0 0 0 transparent);
    -webkit-mask-image: linear-gradient(to top, transparent -20%, #000 -10%);
    mask-image: linear-gradient(to top, transparent -20%, #000 -10%);
  }
  6% {
    transform: scale(1.03) translateY(0);
    opacity: 1;
  }
  16% {
    transform: scale(1);
    opacity: 1;
    filter: brightness(1.06) saturate(1.05) drop-shadow(0 0 0.6rem var(--burn-color));
  }
  55% {
    /* ここまで全面が見えている（読める）。マス境界はカードの下（-15%付近）で維持。 */
    transform: scale(1.02);
    opacity: 1;
    filter: brightness(1.12) saturate(1.25) drop-shadow(0 0 1rem var(--burn-color));
    -webkit-mask-image: linear-gradient(to top, transparent -20%, #000 -10%);
    mask-image: linear-gradient(to top, transparent -20%, #000 -10%);
  }
  66% {
    /* 着火 */
    transform: scale(1.05);
    filter: brightness(1.28) saturate(1.55) drop-shadow(0 0 1.5rem var(--burn-color));
  }
  85% {
    /* 焦げていく */
    filter: brightness(0.55) saturate(1.6) drop-shadow(0 0 1rem var(--burn-color));
  }
  100% {
    transform: scale(1.18) translateY(-1.3rem);
    opacity: 1;
    filter: brightness(0.28) saturate(1) drop-shadow(0 0 0.55rem var(--burn-color));
    /* マスの透明域が下から上へ完全に抜け切る＝燃え尽きて消える。 */
    -webkit-mask-image: linear-gradient(to top, transparent 112%, #000 122%);
    mask-image: linear-gradient(to top, transparent 112%, #000 122%);
  }
}
.hand-effect-burn-embers {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.hand-effect-burn-embers i {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 0.42rem;
  height: 0.42rem;
  border-radius: 50%;
  background: radial-gradient(circle, #fff 0%, var(--burn-color) 45%, transparent 76%);
  box-shadow: 0 0 0.5rem var(--burn-color);
  opacity: 0;
  transform: translate(-50%, -50%) translate(var(--ex), var(--ey)) scale(var(--ers));
  animation-name: hand-effect-burn-ember-kf;
  animation-timing-function: ease-out;
  animation-fill-mode: forwards;
}
/* 続き218: 燃えカスは中央のカード位置(ex,ey)から右上(etx,ety＝使用モーダルの方)へ流れて集まる。 */
@keyframes hand-effect-burn-ember-kf {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) translate(var(--ex), var(--ey)) scale(var(--ers));
  }
  16% {
    opacity: 1;
  }
  80% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) translate(var(--etx), var(--ety)) scale(calc(var(--ers) * 0.5));
  }
}

/* 手札シャッフル演出（main.jsのanimateHandShuffle）: 裏向きのまま1束に集め、その場で数枚が
   出たり入ったりしてから、元の手札の位置へ戻る間だけ現れるゴースト。.setup-fly-cardと同じ
   「document.body直下、3D空間の外」の考え方。位置(transform: translate)はJS側が制御し、
   「出し入れ」の動き(translate/rotateプロパティ、transform関数とは別物)はCSSアニメーション側が
   担当する——同じtransformを2箇所から操作すると後勝ちで上書きされてしまう
   （box-shadowの合成不可バグと同種の教訓）ため、位置と出し入れモーションを独立した
   CSSプロパティに分けた。 */
.hand-shuffle-ghost {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 9998;
  border-radius: 0.3rem;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.5);
  pointer-events: none;
}
.hand-shuffle-ghost.is-popping {
  animation: hand-shuffle-pop 0.5s ease-in-out;
  animation-delay: var(--pop-delay, 0s);
}
@keyframes hand-shuffle-pop {
  0%,
  100% {
    translate: 0 0;
    rotate: 0deg;
  }
  45% {
    translate: 0 -1.6rem;
    rotate: -6deg;
  }
}

/* アニメーション中に画面全体を覆う透明な受け皿。クリックで残りを即座に完了させる
   （セットアップは何度もやり直すことが多いテストプレイ用途を考慮）。 */
#setup-animation-skip-overlay {
  position: fixed;
  inset: 0;
  z-index: 9997;
  cursor: pointer;
}
.setup-animation-skip-hint {
  position: fixed;
  bottom: 1.5rem;
  left: 50%;
  transform: translateX(-50%);
  background: rgba(15, 23, 32, 0.85);
  color: rgba(226, 232, 240, 0.8);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.4rem;
  padding: 0.4rem 0.9rem;
  font-family: sans-serif;
  font-size: 0.75rem;
  pointer-events: none;
}

/* オープニング画面（起動直後の「ローカル」「オンライン」2択メニュー、opening-screen.js
   参照）。ゲーム本体より確実に手前に出す必要があるため、他のどのモーダル/パネルより
   高いz-indexにしてある。画像・メニューともゆっくりフェードインさせる演出。 */
#opening-screen {
  position: fixed;
  inset: 0;
  z-index: 50000;
  /* ユーザー要望「STARTを押した後、暗転せずに白からタイトル画像へそのまま行きたい」。
     以前は暗い背景色だったため、.opening-start-gate（白背景）がフェードアウトし
     きる前にタイトル画像がまだ薄い間、この暗い下地が透けて一瞬暗転して見えていた。
     白にすることで「白→（うっすら）タイトル画像→タイトル画像」と自然に繋がる。 */
  background: #ffffff;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  touch-action: none; /* オープニング画面表示中のピンチでもブラウザ標準ズームが出ないように */
  /* ユーザー報告「盤面画面に移動した時、何かクリックしてからじゃないとマウスホイール
     ズームなどいろいろ動作しません」の原因調査で発見: ログインカードを✕で閉じても
     （hideCard()）このオーバーレイ自体（画面全体をinset:0で覆う、全体の中で最も高い
     z-index）はdisplay:noneにならず、右下の小さい「ログイン」ボタン等を表示したまま
     居座り続ける設計になっている。その状態でこの要素自体がpointer-events:autoの
     ままだと、見た目には盤面が透けて見えていても実際のクリック・ホイールは全てこの
     透明な全画面レイヤーに吸われてしまい、盤面の.sceneには一切届いていなかった。
     コンテナ自体はクリックを素通しにし（none）、実際に押せる必要がある子要素
     （ログインカード一式・ストーリーテロップ・STARTボタン、テストモードボタンは
     既に個別にautoを持つ）にだけ明示的にautoを戻す。 */
  pointer-events: none;
}
#opening-screen.is-closing {
  transition: opacity 0.6s ease;
  opacity: 0;
  pointer-events: none;
}
.opening-screen-bg {
  position: absolute;
  inset: 0;
  /* 背景画像はJS側(opening-screen.js)でinline styleとして設定する。CSSのurl()は
     このスタイルシート自身の場所(src/)基準で解決されるため、プロジェクトルート直下の
     assets/を指すにはJS側（ドキュメント基準で解決される）で設定する必要がある
     （他の実物画像アセットと同じ、このプロジェクト共通の設定方法）。 */
  background-size: cover;
  background-position: center;
  opacity: 0;
}
/* ユーザー要望「最初は真っ白な画面にSTARTボタン。押したらタイトル画像がフェードイン」。
   以前はページ読み込み直後に無条件でフェードインしていたが、.opening-start-gateが
   最初に全体を覆っているため、STARTボタンが押されて#opening-screenに
   stage-titleクラスが付いた時だけこのフェードインを再生するようにした。 */
#opening-screen.stage-title .opening-screen-bg {
  animation: opening-screen-fade-in 1.6s ease forwards;
}
.opening-screen-dim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 0.15) 55%, rgba(0, 0, 0, 0.7) 100%);
}
/* タイトル画面のペット散歩（title-pet.js）。画面下側を横切る飾りレイヤー。クリックは透過。 */
.title-pet-layer {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 9%;
  height: 6rem;
  z-index: 1;
  pointer-events: none;
  overflow: visible;
}
.title-pet {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 6rem;
  width: auto;
  max-width: none;
  will-change: transform;
  filter: drop-shadow(0 0.35rem 0.5rem rgba(0, 0, 0, 0.5));
  user-select: none;
}
/* ユーザー要望「ログインモーダルは画面中央左側に表示してほしい」。以前は画面下部中央
   だったが、縦中央・横は左寄りに変更した。 */
.opening-screen-content {
  position: absolute;
  top: 50%;
  left: 12%;
  transform: translateY(-50%);
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  opacity: 0;
  /* #opening-screen自体はpointer-events:noneにしたため、ここ（ログインボタン一式）は
     明示的に戻す。 */
  pointer-events: auto;
}
/* ストーリーテロップが終わる（または飛ばされる）と#opening-screenに
   stage-contentクラスが付き、ここで初めてログインボタンが浮かび上がる。 */
#opening-screen.stage-content .opening-screen-content {
  animation: opening-screen-content-rise-in 1.1s ease forwards;
  animation-delay: 0.2s;
}
.opening-screen-menu {
  display: flex;
  gap: 1.5rem;
}
.opening-screen-menu-btn {
  font-family: sans-serif;
  font-size: 1.05rem;
  letter-spacing: 0.05em;
  padding: 0.9rem 2.4rem;
  border-radius: 2rem;
  background: rgba(15, 23, 32, 0.75);
  border: 1px solid rgba(226, 232, 240, 0.5);
  color: #f1f5f9;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}
.opening-screen-menu-btn:hover {
  background: rgba(56, 189, 248, 0.25);
  border-color: rgba(56, 189, 248, 0.85);
  transform: translateY(-0.15rem);
}
/* ローカル1人用「CPU戦」の入口ボタン（cpu-battle.js）。ログインボタンの下に少し離して置き、
   遊びの導線だと分かるようシアン寄りの控えめなアクセントにする。 */
.opening-screen-cpu-btn {
  margin-top: 0.9rem;
  font-size: 0.95rem;
  padding: 0.7rem 1.9rem;
  background: rgba(8, 145, 178, 0.4);
  border-color: rgba(56, 189, 248, 0.7);
}
.opening-screen-cpu-btn:hover {
  background: rgba(8, 145, 178, 0.6);
}
/* 【2026-09-11】試遊の入口（?trial・opening-screen.js / trial-entry.js）。
   ログインのボタンの代わりに出す。タイトルの絵の上に乗るので、文字には影を付けて読めるようにする。 */
.opening-trial-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  /* 22rem だと右端がタイトルロゴの「SHADES」の文字にかかり、説明文が読みにくかった（実測）。 */
  width: 18rem;
  max-width: 100%;
}
/* 「開発中のアプリです」の囲み。ユーザー要望「最初にでっかく、しっかり出そう」で、パネルの先頭で
   一番目立つもの（不透明な地＝タイトルの絵に重なっても読める）にした。 */
.opening-trial-notice {
  width: 100%;
  box-sizing: border-box;
  background: rgba(154, 52, 18, 0.94);
  border: 2px solid #fbbf24;
  border-radius: 0.8rem;
  padding: 0.55rem 0.8rem 0.6rem;
  margin-bottom: 0.4rem;
  text-align: center;
  box-shadow: 0 0.3rem 1rem rgba(0, 0, 0, 0.35);
}
.opening-trial-notice-title {
  font-family: sans-serif;
  font-size: 1.5rem;
  font-weight: 800;
  line-height: 1.3;
  color: #ffffff;
}
.opening-trial-notice-note {
  font-family: sans-serif;
  font-size: 0.78rem;
  line-height: 1.5;
  color: #ffedd5;
  margin-top: 0.25rem;
}
.opening-trial-lead {
  font-family: sans-serif;
  font-size: 1rem;
  color: #f8fafc;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.8);
  margin: 0.2rem 0 0.3rem;
  text-align: center;
}
.opening-trial-btn {
  width: 100%;
  margin-top: 0.5rem;
  padding-left: 1rem;
  padding-right: 1rem;
}
.opening-trial-btn.is-primary {
  background: rgba(8, 145, 178, 0.6);
  border-color: rgba(56, 189, 248, 0.9);
}
.opening-trial-desc {
  font-family: sans-serif;
  font-size: 0.75rem;
  line-height: 1.5;
  color: rgba(241, 245, 249, 0.9);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9);
  text-align: center;
}
/* スマホ: このアプリは1600x900の画面を縮めて描くので、1remが実画面で7px前後にしかならない
   （続き439の実測）。そのままでは説明文が4〜5pxで読めないため、スマホでは説明文を省き、
   ボタンと見出しを指で押せる大きさにする（ボタンは不透明なので、ロゴに重なっても読める）。 */
body.is-phone-device .opening-trial-panel {
  /* 幅を決めておかないと、囲みの説明文が1行に伸びようとしてパネルごと横に広がる。 */
  width: 42rem;
  gap: 0.7rem;
}
body.is-phone-device .opening-trial-notice {
  border-width: 0.25rem;
  border-radius: 1.4rem;
  padding: 0.7rem 1.6rem 0.8rem;
  margin-bottom: 0;
}
body.is-phone-device .opening-trial-notice-title {
  font-size: 3rem;
}
body.is-phone-device .opening-trial-notice-note {
  font-size: 1.7rem;
}
body.is-phone-device .opening-trial-lead {
  font-size: 2.1rem;
}
body.is-phone-device .opening-trial-btn {
  font-size: 2.5rem;
  padding: 1.1rem 2.2rem;
  margin-top: 0.4rem;
}
body.is-phone-device .opening-trial-desc {
  display: none;
}
/* 「ログインして、すべての機能を使う」は、以前は下線付きの小さな文字だった。ユーザー要望で
   他の2つと同じボタン（.opening-trial-btn）にしたので、専用の見た目はもう持たない。 */
/* ユーザー要望「1画面で複数人が遊べるモードは、実際にそういう遊び方をさせる予定が
   無いのでテストモード画面とし、右下に小さく置きたい」。ログイン導線（.opening-screen-menu-btn）
   とは違い、あえて目立たせない見た目にする。#opening-screenがposition:fixed要素の
   基準（main.jsのステージ方式）になっているため、これも同じくposition:fixedで
   画面右下に固定できる。 */
.opening-test-mode-btn {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 2;
  font-family: sans-serif;
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  padding: 0.3rem 0.7rem;
  border-radius: 0.3rem;
  background: rgba(15, 23, 32, 0.35);
  border: 1px solid rgba(148, 163, 184, 0.3);
  color: rgba(226, 232, 240, 0.55);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: background 0.2s ease, color 0.2s ease, opacity 0.6s ease;
}
/* ログインボタン(.opening-screen-content)が浮かび上がるのと同じタイミングで
   一緒に現れるようにする（タイトル演出・ストーリーテロップの最中は隠しておく）。 */
#opening-screen.stage-content .opening-test-mode-btn {
  opacity: 1;
  pointer-events: auto;
  transition-delay: 0.3s;
}
.opening-test-mode-btn:hover {
  background: rgba(15, 23, 32, 0.6);
  color: rgba(226, 232, 240, 0.9);
}
/* 管理者パネルを開くボタン（タイトル画面、テストモードの少し上）。ユーザー要望で、
   テストモードともども管理者(body.is-admin-user)にだけ表示する。 */
/* 【2026-09-07・ユーザー報告】右下の管理者向けボタンは4つ縦に並ぶが、**間隔がボタンの高さより
   狭く**、実測で「管理者パネル×テストモード」が2px、「スモークテスト×ダッシュボード」が18px
   重なっていた。ここは position:fixed だが**変形した祖先（1600x900のステージ）の中にある**ため
   fixed が absolute のように振る舞い、1rem が実画面では約12.9px にしかならない。一方ボタンの
   高さは実測23px（＝約1.78rem。border 1px×2 は rem に連動しないぶん相対的に厚くなる）。
   つまり **rem で 1.7〜1.8 の間隔を空けても足りない**。実測に合わせて 2.1rem 間隔にしてある。
   **数字を変える時は必ず実際に測って重なりが0であることを確かめること**（計算だけで決めない）。 */
.opening-admin-btn {
  position: fixed;
  right: 1rem;
  bottom: 3.1rem;
  z-index: 2;
  font-family: sans-serif;
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  padding: 0.3rem 0.7rem;
  border-radius: 0.3rem;
  background: rgba(15, 23, 32, 0.4);
  border: 1px solid rgba(250, 204, 21, 0.45);
  color: rgba(253, 224, 71, 0.85);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: background 0.2s ease, color 0.2s ease, opacity 0.6s ease;
}
#opening-screen.stage-content .opening-admin-btn {
  opacity: 1;
  pointer-events: auto;
  transition-delay: 0.3s;
}
.opening-admin-btn:hover {
  background: rgba(15, 23, 32, 0.65);
  color: #fde68a;
}
/* 管理者ダッシュボード（別ページ）へのリンク（タイトル画面、管理者パネルのさらに上）。
   ユーザー要望2026-09-02。テストモード(1rem)・管理者パネル(3.1rem)の上に 2.1rem 間隔で置く。
   ※以前ここに「ボタンの高さ28px＜間隔28.8pxなので1pxも重ならない」と書いていたが、**実測は
   高さ23px・間隔22px で逆に重なっていた**（1rem が16pxではない。上の .opening-admin-btn の
   コメント参照）。表示は管理者(body.is-admin-user)のときだけ。 */
.opening-dashboard-btn {
  position: fixed;
  right: 1rem;
  bottom: 5.2rem;
  z-index: 2;
  font-family: sans-serif;
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  padding: 0.3rem 0.7rem;
  border-radius: 0.3rem;
  background: rgba(15, 23, 32, 0.4);
  border: 1px solid rgba(56, 189, 248, 0.45);
  color: rgba(125, 211, 252, 0.85);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s ease;
}
#opening-screen.stage-content .opening-dashboard-btn {
  opacity: 1;
  pointer-events: auto;
  transition-delay: 0.3s;
}
.opening-dashboard-btn:hover {
  background: rgba(15, 23, 32, 0.65);
  color: #bae6fd;
}
/* テストモード・管理者パネルのボタンは管理者(body.is-admin-user)にだけ表示する（ユーザー要望）。 */
body:not(.is-admin-user) .opening-test-mode-btn,
body:not(.is-admin-user) .opening-admin-btn,
body:not(.is-admin-user) .opening-dashboard-btn {
  display: none;
}
/* HUERISE画面右下の控えめなバージョン表示（opening-screen.js）。白いスタート画面(#fff、
   .opening-start-gate z-index:10)の上に出すためz-index:11。白背景でも、その後のタイトル
   画像（暗め）でも読めるよう、暗い文字＋白い薄縁取り(text-shadow)にする。テストモード
   ボタン(bottom:1rem)の下にこっそり置く。 */
.opening-version-badge {
  position: fixed;
  right: 0.7rem;
  bottom: 0.55rem;
  z-index: 11;
  font-family: sans-serif;
  /* ユーザー要望2026-08-25「白背景枠はダサい。サイズは今のままで、もう少しさりげなく」。
     続き249で読みやすさ用に付けていた白い下地・枠・影を廃止し、文字自体に薄い影だけ付けて
     どんな背景でも読めるが控えめに見えるようにする（サイズ 1.15rem／スマホ 1.5rem は維持）。 */
  font-size: 1.15rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.6);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.75), 0 0 2px rgba(0, 0, 0, 0.55);
  pointer-events: none;
  user-select: text;
}
/* スマホはさらに大きく（ステージ縮小で小さくなる分を補う）。 */
body.is-phone-device .opening-version-badge {
  font-size: 1.5rem;
}
/* 言語トグル（日本語 / English）。START画面から見えるよう、開始ゲート(z-index:10)・
   バージョン表示(z-index:11)より上のz-index:12で固定する。
   ユーザー要望: 右上のオプションアイコン(#options-menu-button, top:0.3rem/right:1rem)と
   重なっていたので**左上**へ移動。両方の言語を並べ、今の言語(.is-current)を大きく明るく、
   切り替え先を小さく淡くして「今どちらのモードか」が一目で分かるようにする。 */
.opening-lang-toggle {
  position: fixed;
  top: 0.6rem;
  left: 0.7rem;
  z-index: 12;
  display: flex;
  align-items: baseline;
  gap: 0.15rem;
  background: rgba(15, 23, 42, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 999px;
  padding: 0.22rem 0.5rem;
  backdrop-filter: blur(2px);
  /* どこを押しても次の言語へ切り替わる（2026-09-11 ユーザー要望）。 */
  cursor: pointer;
  /* 【2026-09-11 実測で判明】#opening-screen は pointer-events:none（下の .opening-screen-content と
     同じ事情）で、このトグルはそれを受け継いだまま戻していなかった＝**押しても指が素通りして、
     一度も切り替わっていなかった**（その点の最前面は開始ゲート／#scene だった）。 */
  pointer-events: auto;
}
.opening-lang-toggle:hover {
  border-color: rgba(255, 255, 255, 0.6);
}
.opening-lang-toggle-seg {
  font-family: sans-serif;
  font-weight: 600;
  letter-spacing: 0.02em;
  background: none;
  border: none;
  border-radius: 999px;
  cursor: pointer;
  /* 既定＝切り替え先（小さく淡く） */
  font-size: 0.72rem;
  color: rgba(226, 232, 240, 0.55);
  padding: 0.18rem 0.5rem;
  /* font-size はトランジションさせない（文字サイズが動くと隣の文字が押されて読みにくい）。 */
  transition: color 0.15s, background 0.15s;
}
/* 枠のどこにカーソルを乗せても、切り替え先の文字が明るくなる（押すとそちらになる、の合図）。 */
.opening-lang-toggle:hover .opening-lang-toggle-seg:not(.is-current) {
  color: #f8fafc;
  background: rgba(255, 255, 255, 0.12);
}
/* 現在の言語＝大きく明るく（押すと次の言語へ切り替わるので、カーソルは指のまま） */
.opening-lang-toggle-seg.is-current {
  font-size: 1.02rem;
  color: #f8fafc;
  background: rgba(255, 255, 255, 0.18);
}
/* 【続き575】実測で選んでいない側が7.3px・選んでいる側が10.3px。どちらも押すものなので、
   指で押せる大きさにする（「English」が読めないと切り替えられない）。 */
body.is-phone-device .opening-lang-toggle-seg {
  font-size: 1.5rem;
  padding: 0.45rem 1rem;
}
body.is-phone-device .opening-lang-toggle-seg.is-current {
  font-size: 1.7rem;
}
@keyframes opening-screen-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes opening-screen-rise-in {
  from { opacity: 0; transform: translateY(0.9rem); }
  to { opacity: 1; transform: translateY(0); }
}
/* .opening-screen-contentは縦中央寄せのtranslateY(-50%)を基本transformに持つため、
   共有のopening-screen-rise-inをそのまま使うと（アニメーション側のtransformが
   丸ごと上書きしてしまい）中央寄せが解除されてしまう。中央寄せの分を織り込んだ
   専用のkeyframeにする。 */
@keyframes opening-screen-content-rise-in {
  from { opacity: 0; transform: translateY(calc(-50% + 0.9rem)); }
  to { opacity: 1; transform: translateY(-50%); }
}

/* ユーザー要望「最初真っ白な画面に7色のオーラが漂い、STARTボタンがフェードイン。押すと
   BGM再生開始」。#opening-screen内の最前面レイヤーとして、bg/dim/contentを完全に覆い
   隠す（真っ白なので下のタイトル画像も見えない）。STARTを押すとJS側(opening-screen.js)
   がis-closingを付けてフェードアウトさせ、その後display:noneにする。 */
.opening-start-gate {
  position: absolute;
  inset: 0;
  z-index: 10;
  background: #ffffff;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  /* #opening-screen自体はpointer-events:noneにしたため、ここ（STARTボタン）は
     明示的に戻す。 */
  pointer-events: auto;
}
.opening-start-gate.is-closing {
  transition: opacity 0.6s ease;
  opacity: 0;
  pointer-events: none;
}
/* 7色の人魂風のオーラ。ぼかした円形グラデーションを、色ごとに異なる経路・速さで
   ふわふわ漂わせる（@keyframesを色ごとに分けて、動きが揃って見えないようにしている）。
   ユーザー要望「もっと活発に移動、ぼやけすぎなのでもっとはっきり」を受けて一度
   blur()を外したが、その後「輪郭はボケていた方がいい、ただの丸ではなく軌跡が
   欲しい（同じ道を戻らず動く感じ）」と方針が変わった。CSSの@keyframesは
   必ず一定周期で同じ経路をなぞり直してしまうため、位置の計算自体をJS側
   （opening-screen.jsのstartAuraTrails、requestAnimationFrameでのワンダリング
   アルゴリズム）に移し、CSSは「ぼかした円をwrap内の%位置に置く」ことだけを
   担当するようにした。1つの人魂につき複数の.opening-aura-dot（本体+残像）を
   position:left/top(%指定、JSが毎フレーム更新)で並べ、本体に近いほど不透明・
   大きく、古い残像ほど透明・小さくすることで彗星の尾のように見せる。 */
.opening-aura-wrap {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.opening-aura-dot {
  position: absolute;
  width: 12rem;
  height: 12rem;
  border-radius: 50%;
  filter: blur(1.4rem);
  background: radial-gradient(circle, var(--aura-color) 0%, transparent 70%);
  pointer-events: none;
  will-change: left, top, opacity, transform;
}
/* ユーザー報告「2D表示・アニメーション削減を全部オンにしても古いタブレットで画面が
   壊れる」への対応（opening-screen.jsのstartAuraTrailsSimplified参照）。2D表示中は
   blur()もアニメーションも一切使わない、色ごとに1個だけの静止した単色円に差し替える。 */
.opening-aura-dot-static {
  position: absolute;
  border-radius: 50%;
  background: var(--aura-color);
  opacity: 0.5;
  pointer-events: none;
}
/* ユーザー要望「人魂の設定をしようとする時は疑似的にオープニングの人魂の画面に
   なるようにしてほしい」。管理者パネル(z-index:1000)より奥・実際のゲーム画面より
   手前に、オープニングのSTART画面と同じ白背景+人魂だけを一時的に表示する
   （opening-screen.jsのpreviewOpeningAuras参照、admin.jsのスライダー操作から
   注入パターンで呼ばれる）。クリックすると即座に閉じられる。 */
#opening-aura-preview {
  position: fixed;
  inset: 0;
  z-index: 900;
  background: #ffffff;
  cursor: pointer;
  overflow: hidden;
}
/* ユーザー提供の画像（assets/opening-start-btn.png、HUERISEブランドのSTARTボタン
   グラフィック）に差し替えたため、丸ボタンの背景色・枠線・文字装飾は不要になった。
   ボタン自体は透明な当たり判定の器にするだけで、見た目は中のimg1枚に任せる。 */
.opening-start-btn {
  position: relative;
  z-index: 1;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  opacity: 0;
  /* ユーザー要望「STARTボタンのフェードインをもっとゆっくりじれったく」。
     delay・durationともに大幅に伸ばした。 */
  animation: opening-screen-rise-in 4s ease forwards;
  animation-delay: 2.5s;
  transition: transform 0.2s ease;
}
/* 【#277】ホバーできる端末（マウス）だけに限定する。タッチ端末では「1回目のタップが
   hover 扱いになり click が来ない」ことがあり、STARTボタンが何度も押さないと反応しない
   原因になる（ユーザー報告2026-09-05）。 */
@media (hover: hover) and (pointer: fine) {
  .opening-start-btn:hover {
    transform: translateY(-0.15rem) scale(1.03);
  }
}
.opening-start-btn img {
  display: block;
  width: 20rem;
  max-width: 60vw;
  height: auto;
  pointer-events: none;
}
/* ユーザー要望「優しく、すごくゆっくり、完全に消えない（透過率50%〜0%＝不透明度
   50%〜100%）を繰り返す点滅」。最初のフェードインが終わった後(opening-screen.jsの
   animationendで付与)、無限に繰り返す。 */
@keyframes opening-start-btn-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}
.opening-start-btn.is-blinking {
  animation: opening-start-btn-blink 6s ease-in-out infinite;
}

/* ユーザー要望「タイトル画像フェードイン中にストーリーテロップを流す。クリックで
   飛ばせる」。bg/dimより手前、ログインボタン(content)より奥に表示する
   （z-indexは.opening-screen-contentの1より大きく、.opening-start-gateの10より
   小さい値にする）。 */
.opening-story-crawl {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: none;
  cursor: pointer;
  overflow: hidden;
  /* #opening-screen自体はpointer-events:noneにしたため、ここ（クリックでスキップ）は
     明示的に戻す。 */
  pointer-events: auto;
}
/* ハマりどころ（ユーザー報告「まだ下から出てこない」、34remでも直らなかった）:
   前回はtranslateYの開始位置を「中央からの相対オフセット」にしていたが、
   このテロップは39行分の長文でトータルの高さがかなりあり（line-height2で
   概算80rem前後）、flexで縦中央寄せされた状態だと先頭行が中央よりかなり上に
   来てしまうため、そこから34rem下げた程度では画面内にまだ入ってしまっていた
   （文章が長いほど必要なオフセットも増える、という文章の長さ依存の問題だった）。
   文章の長さに関係なく確実に「画面外の下」から始まるよう、flexでの中央寄せをやめて
   position:absolute; top:100%（＝このオーバーレイの一番下の外側）を先頭行の基準位置
   にし、そこからtranslateYだけで押し上げる方式に変更した。 */
.opening-story-crawl-text {
  position: absolute;
  top: 100%;
  left: 50%;
  width: min(34rem, 80vw);
  text-align: center;
  color: #f8fafc;
  font-family: sans-serif;
  line-height: 2;
  text-shadow: 0 0 0.4rem rgba(0, 0, 0, 0.9), 0 0.1rem 0.3rem rgba(0, 0, 0, 0.8);
  /* ハマりどころ: 当初はJS側(opening-screen.js)がdisplay:flexにした直後にJSで
     animationDurationを上書きする方式だったが、display変更と同じ同期処理内でも
     ブラウザがまずスタイルシート側の既定（animationショートハンドにdurationを
     書いていなかったため実質0秒）でアニメーションを開始・即完了させてしまい、
     その後のJSでのduration上書きが間に合わずanimationendが一瞬で発火する不具合が
     あった（move-blink-durationと同じ「calc(var(...))をスタイルシート側に直接
     書く」方式に変更して解決）。 */
  animation-name: opening-story-crawl-scroll;
  animation-timing-function: linear;
  animation-fill-mode: forwards;
  animation-duration: calc(var(--opening-story-crawl-duration, 40) * 1s);
}
.opening-story-crawl-text p {
  margin: 0 0 0.3rem 0;
}
.opening-story-crawl-text p.is-blank {
  margin-bottom: 1.1rem;
}
@keyframes opening-story-crawl-scroll {
  /* left:50%だけでは横方向が中央寄せにならない（translateX(-50%)とセットで初めて
     中央寄せになる）ため、アニメーション側のtransformにも常にtranslateX(-50%)を
     含めておく必要がある（animationのtransformは要素の基本transformを丸ごと
     上書きするため）。top:100%で既に画面外(下)から始まっているため、あとは文章
     全体の高さ＋画面の高さ分だけ上へ動かせば確実に上まで抜けられる。39行・
     line-height2での実測高さ（約85rem）にステージの高さ(56.25rem)を足した140remを、
     余裕を見て160remにした。文章を増減した場合はこの値も調整が必要（JSで実測して
     自動計算する方式ではない）。 */
  from { transform: translateX(-50%) translateY(0); opacity: 0; }
  5% { opacity: 1; }
  95% { opacity: 1; }
  to { transform: translateX(-50%) translateY(-160rem); opacity: 0; }
}
.opening-story-crawl-skip-hint {
  position: absolute;
  bottom: 6%;
  left: 50%;
  transform: translateX(-50%);
  color: rgba(248, 250, 252, 0.6);
  font-family: sans-serif;
  font-size: 0.85rem;
  letter-spacing: 0.05em;
}

/* オープニング画面のログインカード（opening-screen.js参照）。ゲストログインを主ボタンに
   据え、マジックリンク・Googleログインは右下の「その他のログイン方法」に格納してある。 */
.opening-login-card {
  position: relative;
  width: min(24rem, 90vw);
  max-height: 80vh;
  overflow-y: auto;
  box-sizing: border-box;
  background: rgba(15, 23, 32, 0.82);
  border: 1px solid rgba(226, 232, 240, 0.35);
  border-radius: 1rem;
  padding: 1.5rem 1.6rem 1.1rem;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.7rem;
  color: #f1f5f9;
  font-family: sans-serif;
}
.opening-login-title {
  font-size: 0.9rem;
  text-align: center;
  color: #cbd5e1;
  margin-bottom: 0.2rem;
}
.opening-login-primary-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
}
.opening-login-primary-btn {
  font-family: sans-serif;
  font-size: 1.05rem;
  letter-spacing: 0.05em;
  padding: 0.85rem 2.2rem;
  border-radius: 2rem;
  background: rgba(56, 189, 248, 0.18);
  border: 1px solid rgba(56, 189, 248, 0.7);
  color: #f1f5f9;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}
.opening-login-primary-btn:hover:not(:disabled) {
  background: rgba(56, 189, 248, 0.32);
  transform: translateY(-0.1rem);
}
.opening-login-primary-btn:disabled { opacity: 0.6; cursor: default; }
.opening-login-info-btn {
  flex-shrink: 0;
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(226, 232, 240, 0.45);
  color: #f1f5f9;
  font-size: 0.85rem;
  font-style: italic;
  font-family: serif;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.opening-login-info-btn:hover { background: rgba(255, 255, 255, 0.2); }
.opening-login-status {
  min-height: 1.1em;
  font-size: 0.78rem;
  color: #94a3b8;
  text-align: center;
}
.opening-login-local-link,
.opening-login-signout-link {
  background: none;
  border: none;
  color: #94a3b8;
  font-size: 0.8rem;
  text-decoration: underline;
  cursor: pointer;
  padding: 0.2rem;
  align-self: center;
}
.opening-login-local-link:hover,
.opening-login-signout-link:hover { color: #e2e8f0; }
/* ゲストログインボタン専用（続き85）。ユーザー要望「Googleでログインの下に、
   もっと小さく」。.opening-login-local-link等と同じ下線付きテキストリンク調。 */
.opening-login-tiny-btn {
  background: none;
  border: none;
  color: #94a3b8;
  font-size: 0.75rem;
  text-decoration: underline;
  cursor: pointer;
  padding: 0.2rem;
}
.opening-login-tiny-btn:hover:not(:disabled) { color: #e2e8f0; }
.opening-login-tiny-btn:disabled { opacity: 0.6; cursor: default; }
.opening-login-more-row {
  align-self: flex-end;
  font-size: 0.78rem;
  color: #94a3b8;
  cursor: pointer;
  user-select: none;
  margin-top: 0.3rem;
}
.opening-login-more-row:hover { color: #e2e8f0; }
.opening-login-more-section {
  display: none;
  flex-direction: column;
  gap: 0.5rem;
  border-top: 1px solid rgba(148, 163, 184, 0.25);
  padding-top: 0.7rem;
  margin-top: 0.2rem;
}
.opening-login-secondary-btn {
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding: 0.5rem;
  border-radius: 0.4rem;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(148, 163, 184, 0.4);
  color: #f1f5f9;
  cursor: pointer;
  font-size: 0.85rem;
}
.opening-login-secondary-btn:hover:not(:disabled) { background: rgba(255, 255, 255, 0.12); }
.opening-login-secondary-btn:disabled { opacity: 0.6; cursor: default; }
.opening-login-divider {
  text-align: center;
  font-size: 0.7rem;
  color: #94a3b8;
}
.opening-login-email-input {
  width: 100%;
  box-sizing: border-box;
  padding: 0.4rem;
  border-radius: 0.3rem;
  border: 1px solid rgba(148, 163, 184, 0.4);
  background: rgba(255, 255, 255, 0.05);
  color: inherit;
}
#opening-login-info-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 51100;
  background: rgba(15, 23, 32, 0.97);
  border: 1px solid rgba(226, 232, 240, 0.3);
  border-radius: 0.6rem;
  padding: 1.2rem 1.4rem;
  width: min(26rem, 90vw);
  color: #f1f5f9;
  font-family: sans-serif;
}

/* ============================================================
 * 管理者モードの値の「唯一の正」となる:rootブロック（ファイル末尾）
 * ------------------------------------------------------------
 * ハマりどころ（重大）: これまで管理者モードで調整した値をadmin.jsの
 * `default:`フィールドだけ更新して「反映済み」としてきたが、実際に画面に
 * 描画される値はadmin.jsのdefaultを一切参照しない。特にタブレット専用の
 * 変数の多くは「var(--foo-touch, var(--foo, 固定値))」という3段階の
 * フォールバック構造になっており、--foo-touch自体がどこにも:root宣言
 * されていない限り、常に中間のPC用--foo（さらにその固定フォールバック）に
 * 落ちてしまい、admin.jsのdefaultは事実上「スライダーの初期表示用の見た目
 * だけの値」でしかなかった（ユーザー報告「大幅に変えたのに再起動したら
 * 反映されていない」の直接の原因）。
 * このブロックはadmin.jsのCONTROLS全項目（ユーザーから明示的に「無視して
 * ください」と指定された手札Z値5項目・手札A傾き角度は除く）を、実際に
 * 効果を持つ直接の:root宣言としてファイル最後に書き出したもの。CSSの
 * カスケード規則上、同じ詳細度の:root宣言は「後に書かれた方が勝つ」ため、
 * ファイル末尾に置くことで、これより前にある個別の:root宣言や
 * var()の途中フォールバックより必ず優先される。
 * 今後、管理者モードの値を同期する際は、admin.jsのdefaultを更新するのと
 * 必ずセットで、このブロックも同じ値に更新すること（このブロックだけが
 * 実際の見た目を決める「本当の」デフォルト値）。
 * このブロックはregen_override_block.js相当のスクリプトで丸ごと再生成する
 * 前提（手動で個別に編集しない）。admin.jsのCONTROLSが変わったら、この
 * ブロック全体をadmin.jsの現在値から再生成し直すこと。
 * ============================================================ */
:root {
  --table-tilt: 42deg;
  --camera-perspective: 1090px;
  --camera-perspective-origin-y: 8.4rem;
  --camera-offset-y: -1.4rem;
  --camera-zoom: 1.05;
  --card-preview-size: 32rem;
  --home-rank-pos-x: 43.8rem;
  --home-rank-pos-y: 13.1rem;
  --home-rank-scale: 1;
  --home-rank-name-pos-x: 0rem;
  --home-rank-name-pos-y: -1.8rem;
  --mypage-rank-name-pos-x: 0rem;
  --mypage-rank-name-pos-y: -2.8rem;
  --shop-thumb-x: 0rem;
  --shop-thumb-y: 0rem;
  --shop-thumb-scale: 0.6;
  --shop-bg-x: 0rem;
  --shop-bg-y: -3.5rem;
  --shop-bg-scale: 1.35;
  --shop-bg-rotate: 0deg;
  --shop-price-x: 0rem;
  --shop-price-y: 0rem;
  --shop-price-scale: 1;
  --shop-badge-x: 0rem;
  --shop-badge-y: 0rem;
  --shop-badge-scale: 1;
  --shop-card-bg-alpha: 0.04;
  --shop-thumb-shadow-alpha: 0.4;
  --shop-thumb-shadow-blur: 0.5rem;
  --shop-thumb-shadow-y: 0.35rem;
  --hand-pickup-toast-scale: 1.3;
  --hand-pickup-toast-duration: 5;
  --card-arrival-modal-size: 25rem;
  --card-arrival-modal-duration: 5;
  --card-arrival-modal-top: 3rem;
  --card-arrival-modal-right: 1rem;
  --card-landing-glide-ms: 300;
  --card-landing-hold-ms: 500;
  --card-landing-drop-ms: 150;
  --card-landing-lift-scale: 1.08;
  /* 続き220: 手札使用のCanvas霧散演出（card-dissolve.js）の調整値。speedは小さいほど長い。 */
  --dissolve-speed: 0.85;
  --dissolve-mist: 1;
  --dissolve-residue: 1.6;
  --dissolve-card-size: 340;
  --hand-burn-duration: 2.6;
  --hand-burn-card-size: 17rem;
  --hand-burn-modal-delay: 2;
  --move-blink-duration: 7;
  --move-blink-arrow-size: 1.6rem;
  --move-blink-arrow-stroke: 0.2rem;
  --move-blink-avatar-rotate: 0deg;
  --start-player-avatar-size: 12rem;
  --gate-invasion-modal-size: 28rem;
  --gate-invasion-modal-step-duration: 3.5;
  /* 駒が1マス移動するのにかける時間（秒）。一瞬で次のマスに現れると「移動した」感じが
     しないため、他人の移動を見せる時と同じ飛翔演出を自分/CPUの移動にも使う。 */
  --piece-move-duration: 0.45;
  --contact-anim-pre-delay: 1;
  --contact-anim-runup-duration: 0.5;
  --contact-anim-tackle-duration: 0.5;
  --contact-anim-flight-duration: 0.5;
  --eternal-anim-glow-duration: 1;
  --eternal-anim-flight-duration: 1.5;
  --eternal-anim-suspense-duration: 1.5;
  --eternal-anim-flip-duration: 1;
  --eternal-anim-hold-duration: 2;
  --eternal-anim-return-duration: 1;
  --spotlight-inner-radius: 15%;
  --spotlight-outer-radius: 100%;
  --spotlight-opacity: 0.95;
  --spotlight-width: 55%;
  --spotlight-height: 50%;
  --pet-dist: 0.9;
  --pet-lift: 0.05;
  --pet-size: 0.85;
  --pet-follow: 0.16;
  --pet-wander: 0.35;
  --pet-liveliness: 1;
  --pet-orbit-radius: 1.1;
  --pet-orbit-squash: 0.72;
  --pet-orbit-dur: 3.4;
  --pet-orbit-offset-x: 0;
  --pet-orbit-offset-y: 0;
  --sound-volume-button-press: 80%;
  --sound-volume-hand-shuffle: 80%;
  --sound-volume-deck-shuffle: 80%;
  --sound-volume-card-flip: 80%;
  --sound-volume-card-place: 80%;
  --sound-volume-piece-place: 80%;
  --sound-volume-card-draw: 80%;
  --sound-volume-arrival-effect: 80%;
  --sound-volume-lock: 80%;
  --sound-volume-turn-switch: 80%;
  --sound-volume-swap: 80%;
  --sound-volume-jump: 80%;
  --sound-volume-victory-bgm: 40%;
  --sound-volume-opening-bgm: 40%;
  --sound-volume-game-bgm: 40%;
  --sound-volume-waiting-bgm: 40%;
  --opening-story-crawl-duration: 40;
  --opening-aura-size: 2rem;
  --opening-aura-trail-length: 25;
  --opening-aura-speed: 1;
  --board-zoom-margin: 1;
  --board-zoom-offset-x: 0rem;
  --board-zoom-offset-y: 3.2rem;
  --board-zoom-reference-height: 1000px;
  --board-zoom-2-margin: 2;
  --board-zoom-2-offset-x: 0rem;
  --board-zoom-2-offset-y: -2.5rem;
  --board-zoom-2-reference-height: 1000px;
  --home-tile-icon-size: 12rem;
  --home-title-offset-y: 0rem;
  --home-grid-pos-x: 0rem;
  --home-grid-pos-y: 6.4rem;
  --home-grid-scale: 1;
  --phase-guide-bottom: 0rem;
  --phase-guide-right: 2.3rem;
  --phase-guide-pos-x: 0rem;
  --phase-guide-pos-y: 0rem;
  --phase-guide-scale: 1;
  --phase-guide-pos-touch-x: 0rem;
  --phase-guide-pos-touch-y: 0rem;
  --phase-guide-scale-touch: 1;
  --turn-round-counter-top: 3.1rem;
  --turn-round-counter-right: 1.6rem;
  --turn-round-counter-font-size: 0.65rem;
  --phase-skip-button-offset-x: 0rem;
  --phase-skip-button-offset-y: 0rem;
  --home-badge-offset-x: 2rem;
  --home-badge-offset-y: 1.6rem;
  --home-new-badge-offset-x: 2rem;
  --home-new-badge-offset-y: 1.6rem;
  --bug-report-icon-size: 1.9rem;
  /* 全画面ボタンを不具合報告(旧19.5)と2D/3D(16.9)の間へ入れるため、不具合報告を19.5→22.1へ左送り。 */
  --bug-report-icon-right: 22.1rem;
  --bug-report-icon-top: 0.6rem;
  --action-log-icon-size: 1.95rem;
  --action-log-icon-right: 3.9rem;
  --action-log-icon-top: 0.6rem;
  --profile-maindeck-right: 2.5rem;
  --profile-maindeck-bottom: 0rem;
  --profile-maindeck-width: 10.5rem;
  --profile-maindeck-scale: 2.25;
  --profile-maindeck-badge-x: 0rem;
  --profile-maindeck-badge-y: 2.1rem;
  --profile-maindeck-badge-scale: 0.7;
  --profile-maindeck-name-x: 0rem;
  --profile-maindeck-name-y: -1.9rem;
  --profile-maindeck-name-scale: 1;
  --profile-maindeck-edit-x: 0.3rem;
  --profile-maindeck-edit-y: -1.7rem;
  --profile-maindeck-edit-scale: 0.8;
  --smoke-test-btn-right: 1rem;
  --smoke-test-btn-bottom: 7.3rem;
  --smoke-test-btn-font-size: 0.85rem;
  --turn-timer-rope-pos-x: 0rem;
  --turn-timer-rope-pos-y: 0rem;
  --turn-timer-rope-width: 46rem;
  --priority-transfer-pos-x: -1.6rem;
  --priority-transfer-pos-y: 1.5rem;
  --piece-hitbox-scale: 1;
  --playmat-scale: 1.42;
  --playmat-pos-x: 0%;
  --playmat-pos-y: 0%;
  --table-background-scale-x: 4.75;
  --table-background-scale-y: 4.85;
  --table-background-pos-x: 0%;
  --table-background-pos-y: 108.5%;
  --self-status-icon-piece-size: 1.5rem;
  --self-status-icon-cardback-size: 1.5rem;
  --self-status-icon-playmat-size: 1.5rem;
  --self-status-icon-background-size: 1.5rem;
  --self-status-icon-online-size: 2.6rem;
  --self-status-icon-gap: 0.4rem;
  --self-status-large-avatar-size: 16rem;
  --self-status-large-avatar-pos-x: -0.7rem;
  --self-status-large-avatar-pos-y: -11.5rem;
  --self-status-large-avatar-ghost-scale: 1.6;
  --self-status-large-avatar-ghost-offset-x: -1.6rem;
  --self-status-large-avatar-ghost-offset-y: -8.1rem;
  --self-status-large-avatar-ghost-opacity: 0.35;
  --self-status-pos-x: 0rem;
  --self-status-pos-y: 0rem;
  --self-status-info-pos-x: -9.8rem;
  --self-status-info-pos-y: -1.5rem;
  --self-status-icon-piece-pos-x: 0rem;
  --self-status-icon-piece-pos-y: 0rem;
  --self-status-icon-cardback-pos-x: 0rem;
  --self-status-icon-cardback-pos-y: 0rem;
  --self-status-icon-playmat-pos-x: 0rem;
  --self-status-icon-playmat-pos-y: 0rem;
  --self-status-icon-background-pos-x: 0rem;
  --self-status-icon-background-pos-y: 0rem;
  --self-status-icon-online-pos-x: 1.42rem;
  --self-status-icon-online-pos-y: 1.01rem;
  --self-status-icons-group-x: 3.6rem;
  --self-status-icons-group-y: 1.1rem;
  --self-status-icons-group-scale: 1.45;
  --lock-top-pos-x: 0rem;
  --lock-top-pos-y: -1.8rem;
  --lock-bottom-pos-x: 0rem;
  --lock-bottom-pos-y: 1.8rem;
  --lock-left-pos-x: -1.9rem;
  --lock-left-pos-y: 0rem;
  --lock-right-pos-x: 1.8rem;
  --lock-right-pos-y: 0rem;
  --lock-slot-border-width: 0.01rem;
  --lock-slot-glow-scale: 0.25;
  --lock-bar-scale: 1.13;
  --lock-bar-top-pos-x: 0rem;
  --lock-bar-top-pos-y: 0rem;
  --lock-bar-bottom-pos-x: 0rem;
  --lock-bar-bottom-pos-y: 0rem;
  --lock-bar-left-pos-x: 0rem;
  --lock-bar-left-pos-y: 0rem;
  --lock-bar-right-pos-x: 0rem;
  --lock-bar-right-pos-y: 0rem;
  --label-a-pos-x: -14.7rem;
  --label-a-pos-y: -1.1rem;
  --label-b-pos-x: -4.5rem;
  --label-b-pos-y: -5.1rem;
  --label-c-pos-x: 7.3rem;
  --label-c-pos-y: -7rem;
  --label-d-pos-x: 3.8rem;
  --label-d-pos-y: -5.2rem;
  --label-bcd-font-size: 1.6rem;
  --avatar-a-size: 5.7rem;
  --avatar-b-size: 8rem;
  --avatar-c-size: 8rem;
  --avatar-d-size: 8rem;
  --avatar-a-pos-x: 0rem;
  --avatar-a-pos-y: 1.9rem;
  --avatar-b-pos-x: -3.5rem;
  --avatar-b-pos-y: 0.1rem;
  --avatar-c-pos-x: 0.1rem;
  --avatar-c-pos-y: -6.8rem;
  --avatar-d-pos-x: 3.5rem;
  --avatar-d-pos-y: -0.1rem;
  --hand-a-pos-x: 0rem;
  --hand-a-pos-y: 4.3rem;
  --hand-b-pos-x: -0.3rem;
  --hand-b-pos-y: 0rem;
  --hand-c-pos-x: 0rem;
  --hand-c-pos-y: -3rem;
  --hand-d-pos-x: 0rem;
  --hand-d-pos-y: 0rem;
  --mini-lock-bottom: -0.2rem;
  --mini-lock-x-offset: 0rem;
  --mini-lock-top: 2.2rem;
  --mini-lock-top-x-offset: 0rem;
  --mini-lock-bottom-fixed-hand: 0rem;
  --fixed-hand-bottom: 0.5rem;
  --fixed-hand-x-offset: 0rem;
  --fixed-hand-scale: 1;
  --hand-a-size: 17rem;
  --hand-b-size: 7rem;
  --hand-c-size: 7rem;
  --hand-d-size: 7rem;
  --hand-a-thickness: 10rem;
  --hand-b-thickness: 5rem;
  --hand-c-thickness: 4rem;
  --hand-d-thickness: 5rem;
  --hand-a-peek-lift: 3rem;
  --hand-card-self-size: 9rem;
  --hand-card-opponent-size: 2.6rem;
  --deck-scale: 1;
  --deck-pos-x: 4.1rem;
  --deck-pos-y: 1.5rem;
  --discard-scale: 1;
  --discard-pos-x: -4.1rem;
  --discard-pos-y: -3.7rem;
  --eternal-scale: 1;
  --eternal-pos-x: 4.1rem;
  --eternal-pos-y: -3.7rem;
  --first-scale: 1;
  --first-pos-x: -4.1rem;
  --first-pos-y: 1.5rem;
  --declared-colors-corner-left: 1rem;
  --declared-colors-corner-scale: 0.85;
  --icon-pos-hand-shuffle-x: -7.5rem;
  --icon-pos-hand-shuffle-y: 7.79rem;
  --icon-pos-board-zoom-x: 0rem;
  --icon-pos-board-zoom-y: -0.71rem;
  --icon-pos-draw-x: -8rem;
  --icon-pos-draw-y: -4.71rem;
  --icon-pos-end-turn-x: 0rem;
  --icon-pos-end-turn-y: -3.4rem;
  --icon-pos-options-x: 0rem;
  --icon-pos-options-y: 0rem;
  --icon-pos-help-x: 0rem;
  --icon-pos-help-y: 0rem;
  --currency-display-pos-x: 0rem;
  --currency-display-pos-y: 0rem;
  --icon-pos-piece-hide-x: 0rem;
  --icon-pos-piece-hide-y: 0rem;
  --icon-pos-card-hide-x: 0rem;
  --icon-pos-card-hide-y: 0rem;
  --icon-pos-public-draw-x: 0.3rem;
  --icon-pos-public-draw-y: 1rem;
  --icon-size-hand-shuffle: 3.5rem;
  --icon-size-board-zoom: 3.5rem;
  --icon-size-draw: 3.5rem;
  --icon-size-end-turn: 3.5rem;
  --icon-size-options: 2.6rem;
  --icon-size-help: 2.6rem;
  --icon-size-piece-hide: 2.6rem;
  --icon-size-card-hide: 2.6rem;
  --icon-size-public-draw: 3.5rem;
  --icon-pos-hand-shuffle-touch-x: -8.1rem;
  --icon-pos-hand-shuffle-touch-y: 11.38rem;
  --icon-size-hand-shuffle-touch: 6rem;
  --icon-pos-board-zoom-touch-x: 0.9rem;
  --icon-pos-board-zoom-touch-y: -3.6rem;
  --icon-size-board-zoom-touch: 6rem;
  --icon-pos-draw-touch-x: -9rem;
  --icon-pos-draw-touch-y: -7.6rem;
  --icon-size-draw-touch: 6rem;
  --icon-pos-end-turn-touch-x: 0.7rem;
  --icon-pos-end-turn-touch-y: 0rem;
  --icon-size-end-turn-touch: 6rem;
  --icon-pos-options-touch-x: 1.7rem;
  --icon-pos-options-touch-y: -0.9rem;
  --icon-size-options-touch: 6rem;
  --icon-pos-piece-hide-touch-x: 0rem;
  --icon-pos-piece-hide-touch-y: -20rem;
  --icon-size-piece-hide-touch: 6rem;
  --icon-pos-card-hide-touch-x: 6.7rem;
  --icon-pos-card-hide-touch-y: -16.3rem;
  --icon-size-card-hide-touch: 6rem;
  --priority-transfer-pos-touch-x: -2.2rem;
  --priority-transfer-pos-touch-y: -5rem;
  --icon-pos-public-draw-touch-x: 0.3rem;
  --icon-pos-public-draw-touch-y: -1.2rem;
  --icon-size-public-draw-touch: 4.7rem;
  --phase-guide-bottom-touch: 0rem;
  --phase-guide-right-touch: 2.3rem;
  --self-status-icon-online-pos-touch-x: 0.27rem;
  --self-status-icon-online-pos-touch-y: -4.14rem;
  --self-status-icon-online-size-touch: 3.2rem;
  --hand-a-pos-touch-x: 0rem;
  --hand-a-pos-touch-y: 2.6rem;
  --option-area-pos-phone-x: 0rem;
  --option-area-pos-phone-y: 0rem;
  --option-area-scale-phone: 1.65;
  --icon-pos-options-phone-x: 0.7rem;
  --icon-pos-options-phone-y: -0.7rem;
  --icon-size-options-phone: 3.9rem;
  --center-modal-scale-phone: 2;
  --options-menu-scale-phone: 1;
  --admin-panel-scale-phone: 1.45;
  --self-status-large-avatar-size-phone: 10.2rem;
  /* スマホでの「半透明の巨大アバター（背面ゴースト）」だけを、PCとは別に調整するための値
     （ユーザー要望2026-08-31）。既定はPCと同じなので、触らなければ今までと同じ見た目。 */
  --self-status-large-avatar-ghost-scale-phone: 1.45;
  --self-status-large-avatar-ghost-offset-x-phone: -1.6rem;
  --self-status-large-avatar-ghost-offset-y-phone: -4.7rem;
  --self-status-large-avatar-ghost-opacity-phone: 0.35;
  /* スマホでの「本体（半透明でない方）」の位置。PCとは画面の余白の作りが違うので別に持つ
     （ユーザー要望2026-08-31）。既定はPCと同値なので、触らなければ今までと同じ位置。
     この2つは本体だけでなく、背面ゴーストとランクリングの基準位置にもなっている（3つとも
     この値を土台に自分のずらし量を足す作り）ので、動かすと3つまとめて同じだけ動く。 */
  --self-status-large-avatar-pos-x-phone: -0.7rem;
  --self-status-large-avatar-pos-y-phone: -7.5rem;
  --phase-guide-pos-phone-x: 0rem;
  --phase-guide-pos-phone-y: 0rem;
  --phase-guide-scale-phone: 2.1;
  --phase-skip-button-pos-phone-x: 3.2rem;
  --phase-skip-button-pos-phone-y: 0rem;
  --phase-skip-button-scale-phone: 0.9;
  --phase-turn-status-pos-phone-x: -4.3rem;
  --phase-turn-status-pos-phone-y: 0rem;
  --phase-turn-status-scale-phone: 0.6;
  --hand-a-pos-phone-x: -26.3rem;
  --hand-a-pos-phone-y: -10.5rem;
  --hand-a-size-phone: 15rem;
  --hand-a-rotate-z-phone: 70deg;
  --fixed-hand-bottom-phone: 11.3rem;
  --fixed-hand-x-offset-phone: -34.1rem;
  --fixed-hand-scale-phone: 1;
  --fixed-hand-rotate-phone: 47deg;
  --eraser-icons-pos-phone-x: 5.4rem;
  --eraser-icons-pos-phone-y: 19rem;
  --eraser-icons-size-phone: 5.3rem;
  /* スマホ専用: 盤面拡大ボタンの位置・大きさ（管理者モード、ユーザー要望2026-09-05・#278）。 */
  --board-zoom-btn-pos-phone-x: 0rem;
  --board-zoom-btn-pos-phone-y: 0rem;
  --board-zoom-btn-size-phone: 3.5rem;
  --mini-lock-top-phone: 4.3rem;
  --mini-lock-top-x-offset-phone: 0rem;
  --mini-lock-top-scale-phone: 1;
  --mini-lock-bottom-phone: 0rem;
  --mini-lock-x-offset-phone: 0rem;
  --mini-lock-scale-phone: 1;
  --hand-reveal-fixed-phone-x: 15.3rem;
  --hand-reveal-fixed-phone-y: -17.5rem;
  --hand-reveal-fixed-phone-scale: 1.4;
  --hand-reveal-fixed-phone-rotate: -64deg;
  --hand-a-translate-z-touch: 2.4rem;
  --hand-b-translate-z-touch: 0.5rem;
  --hand-c-translate-z-touch: 0.5rem;
  --hand-d-translate-z-touch: 0.5rem;
  --label-translate-z-touch: 0rem;
  --hand-a-rotate-x-touch: -40deg;
  --table-tilt-flat: -21deg;
  --table-scale-flat: 0.99;
  --table-flat-offset-x: -0.1rem;
  --table-flat-offset-y: 6.2rem;
  --hand-a-rotate-x-flat: 0deg;
  --hand-a-flat-offset-x: 0rem;
  --hand-a-flat-offset-y: 5.8rem;
  --hand-b-rotate-x-flat: 0deg;
  --hand-b-flat-offset-x: 0rem;
  --hand-b-flat-offset-y: 0rem;
  --hand-c-rotate-x-flat: 0deg;
  --hand-c-flat-offset-x: 0rem;
  --hand-c-flat-offset-y: 0rem;
  --hand-d-rotate-x-flat: 0deg;
  --hand-d-flat-offset-x: 0rem;
  --hand-d-flat-offset-y: 0rem;
  --icon-pos-hand-shuffle-flat-x: 0rem;
  --icon-pos-hand-shuffle-flat-y: -2.3rem;
  --icon-pos-board-zoom-flat-x: 0rem;
  --icon-pos-board-zoom-flat-y: 0rem;
  --icon-pos-draw-flat-x: 0.9rem;
  --icon-pos-draw-flat-y: 0rem;
  --icon-pos-end-turn-flat-x: 0rem;
  --icon-pos-end-turn-flat-y: -2.3rem;
  --icon-pos-piece-hide-flat-x: 0rem;
  --icon-pos-piece-hide-flat-y: 0rem;
  --icon-pos-card-hide-flat-x: 0rem;
  --icon-pos-card-hide-flat-y: 0rem;
  --icon-pos-public-draw-flat-x: 0rem;
  --icon-pos-public-draw-flat-y: 0rem;
  --priority-transfer-flat-x: 0rem;
  --priority-transfer-flat-y: 0rem;
  --phase-guide-bottom-flat: 0rem;
  --phase-guide-right-flat: 0.7rem;
  --self-status-icon-online-flat-x: 0rem;
  --self-status-icon-online-flat-y: 0rem;
  --camera-perspective-touch: 1090px;
  --camera-perspective-origin-y-touch: 8.4rem;
  --self-status-scale-touch: 1.5;
  --self-status-pos-touch-x: 0rem;
  --self-status-pos-touch-y: 0rem;
  --self-status-scale-phone: 1.75;
  --self-status-pos-phone-x: 0rem;
  --self-status-pos-phone-y: 3.7rem;
  --priority-transfer-scale-touch: 1;
  --hand-reveal-card-size: 2.9rem;
  --hand-reveal-bottom-pos-x: -16.2rem;
  --hand-reveal-bottom-pos-y: -0.4rem;
  --hand-reveal-top-pos-x: 13rem;
  --hand-reveal-top-pos-y: -2.2rem;
  --hand-reveal-left-pos-x: -1rem;
  --hand-reveal-left-pos-y: -13.4rem;
  --hand-reveal-right-pos-x: -2.4rem;
  --hand-reveal-right-pos-y: 13.5rem;
  --rank-ring-thickness: 0.75rem;
  --rank-ring-offset-x: 0rem;
  --rank-ring-offset-y: 0rem;
  --rank-ring-orbit-size: 0.4rem;
  --rank-ring-orbit-trail-length: 23;
  --rank-ring-orbit-speed: 7.6;
}

/* チュートリアル（tutorial.js）。実際の盤面のUI要素をハイライトしながら遊び方を
   説明するステップ形式のオーバーレイ。既存モーダル（最大z-index約10701）より
   確実に上、ただしオープニング画面(#opening-screen、50000)よりは下にする
   （チュートリアルはオープニング画面が終わった後にしか出さないため実害は無いが、
   念のため序列を保つ）。 */
#tutorial-overlay {
  position: fixed;
  inset: 0;
  z-index: 40000;
  display: none;
}
#tutorial-overlay.is-visible {
  display: block;
}
#tutorial-scrim {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
}
/* ハイライト対象の実際の位置・サイズにJS(tutorial.js)が毎回合わせる。box-shadowの
   スプレッドを画面全体を覆うほど大きくすることで、この要素自体は透明なまま
   「周囲だけ暗い穴」を作る、よくあるスポットライト演出の手法。 */
#tutorial-spotlight {
  position: fixed;
  border-radius: 0.4rem;
  box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.55);
  outline: 2px solid rgba(250, 204, 21, 0.9);
  outline-offset: 0.2rem;
  pointer-events: none;
  transition: left 0.25s ease, top 0.25s ease, width 0.25s ease, height 0.25s ease;
}
/* ユーザー要望「ムーブフェイズ、実際に駒を移動できる範囲をハイライトさせたりできる？」
   への対応。自分の駒に隣接する最大4マスへ、tutorial.jsが直接このクラスを付け外しする
   （#tutorial-spotlightと違い、実際の盤面要素そのものを軽く縁取るだけの簡易版）。 */
.cell.tutorial-move-range {
  outline: 3px solid rgba(250, 204, 21, 0.9);
  outline-offset: -3px;
  animation: tutorial-move-range-pulse 1.4s ease-in-out infinite;
}
@keyframes tutorial-move-range-pulse {
  0%, 100% { outline-color: rgba(250, 204, 21, 0.9); }
  50% { outline-color: rgba(250, 204, 21, 0.3); }
}
body.reduce-glow .cell.tutorial-move-range {
  animation: none;
}
/* カード効果の自動処理（card-effect-engine.js）が、対象マス・対象の手札カードを
   プレイヤーに選ばせる時のハイライト。tutorial-move-rangeと同じ黄色パルスの見た目だが、
   クリックで選べることを示すためcursor:pointerを明示している。 */
.cell.card-effect-target-cell,
.hand-card.card-effect-target-cell,
/* ロックスロットも候補になり得る（ゴメンナサイで奪う相手のロックを選ぶ等）。以前は
   .cell/.hand-cardにしか黄色ハイライトが付かず、ロックスロットを候補にすると盤面が
   暗転するだけで「どこを押せばいいか分からない」状態だった（ユーザー報告「ゴメンナサイを
   使うを押したら盤面がグレーになっただけで何も起きない」の主因）。 */
.lock-slot.card-effect-target-cell {
  outline: 3px solid rgba(250, 204, 21, 0.9);
  outline-offset: -3px;
  animation: tutorial-move-range-pulse 1.4s ease-in-out infinite;
  cursor: pointer;
}
body.reduce-glow .cell.card-effect-target-cell,
body.reduce-glow .hand-card.card-effect-target-cell,
body.reduce-glow .lock-slot.card-effect-target-cell {
  animation: none;
}
/* ユーザー要望「移動先候補が少しわかりずらい。ほかのマスのトーンを落として、カードの面も
   ハイライトされるようにしたい」への対応。候補選択中は他の全マス/手札カードを暗くし、
   候補の中の実際のカード画像自体にも光彩を足す（枠線だけだと目に入りにくいため）。
   ユーザー報告「マスチェンジの手札効果で入れ替える相手を選ぶ時、自分の駒がぺっちゃんこに
   なっている」への対応: `.cell`はpreserve-3dで自分の駒(.piece、buildCubePieceの立方体
   技法)を子に持つことがあり、filter/opacityを直接かけるとCSSの仕様上その要素が独立した
   合成レイヤーへ強制分離され、中の3D立体駒が平面化してしまう（phase-move-picking側で
   既に踏んだのと同じ罠——あちらは::afterオーバーレイ方式へ切り替えて解決済みだった。
   詳しい経緯は下のbody.phase-move-picking .cell::afterのコメント参照）。同じ理由で
   ここも::afterの半透明オーバーレイへ切り替える。 */
body.card-effect-picking-cells .cell:not(.card-effect-target-cell)::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: rgba(0, 0, 0, 0.55);
  pointer-events: none;
}
/* 上のbody.phase-lock-pickingルールと同じ理由で、逆方向（手札効果の候補選択中に
   ロックフェイズのハイライトが重なる場合）も互いのハイライトを暗転対象から除外する。 */
body.card-effect-picking-hand .hand-card:not(.card-effect-target-cell):not(.phase-lock-highlight) {
  filter: brightness(0.45) saturate(0.6);
  transition: filter 0.2s ease;
}
.cell.card-effect-target-cell .board-card {
  box-shadow: 0 0 1.2rem 0.3rem rgba(250, 204, 21, 0.85);
}
body.reduce-glow .cell.card-effect-target-cell .board-card {
  box-shadow: 0 0 0.4rem 0.1rem rgba(250, 204, 21, 0.6);
}
/* ユーザー要望「マスの選択などをさせる場合は案内を出してほしい」への対応。候補選択中、
   案内バナーを出す。pointer-events:noneで盤面・手札のクリックは一切妨げない
   （turn-announce-toastと同じ「操作を止めないトースト」パターン）。ユーザー要望
   「作業を促すモーダルは画面中央にお願いします」への対応で画面上部から中央へ移動した
   （pointer-events:noneのままなので、中央に表示されても盤面/手札のクリックは
   引き続き妨げない）。 */
#card-effect-picker-hint {
  position: fixed;
  /* 以前は top:50%（画面ど真ん中）で、サフラン等の「マスを選択」時に選択対象マスの上に
     案内バナーが被って見づらかった（不具合報告）。ヘッダー直下の上部に固定して、盤面中央の
     選択マスに被らないようにする。pointer-events:none なのでクリック自体は元々通す。 */
  top: 4.6rem;
  left: 50%;
  transform: translate(-50%, -0.6rem);
  z-index: 3200;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(160deg, rgba(66, 32, 6, 0.92), rgba(28, 16, 4, 0.92));
  border: 2px solid #facc15;
  border-radius: 0.6rem;
  padding: 0.5rem 1.2rem;
  color: #fef3c7;
  font-family: sans-serif;
  font-weight: bold;
  font-size: 0.9rem;
  white-space: nowrap;
  box-shadow: 0 0 1.5rem rgba(250, 204, 21, 0.4), 0 10px 25px rgba(0, 0, 0, 0.5);
  transition: opacity 0.25s ease, transform 0.25s ease;
}
#card-effect-picker-hint.show {
  opacity: 1;
  transform: translateX(-50%);
  /* ユーザー要望「目立つように枠を点滅させる等の工夫をしてほしい」。出現時の
     フェードインとは別に、表示中はずっと枠が点滅し続けて視線を誘導する。 */
  animation: card-effect-picker-hint-blink 1.1s ease-in-out infinite;
}
@keyframes card-effect-picker-hint-blink {
  0%, 100% { border-color: #facc15; box-shadow: 0 0 1.5rem rgba(250, 204, 21, 0.4), 0 10px 25px rgba(0, 0, 0, 0.5); }
  50% { border-color: #fff7cc; box-shadow: 0 0 2.6rem rgba(250, 204, 21, 0.9), 0 10px 25px rgba(0, 0, 0, 0.5); }
}
/* CPU自動スキップOFF時「画面をクリックしてCPUの手を進める」案内（main.js）。画面下部に
   出す。pointer-events:none で下のクリックを妨げない（クリックはdocumentのcaptureで拾う）。 */
#cpu-step-hint {
  position: fixed;
  bottom: 1.4rem;
  left: 50%;
  transform: translate(-50%, 0.6rem);
  z-index: 3200;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(160deg, rgba(6, 40, 52, 0.94), rgba(4, 18, 28, 0.94));
  border: 2px solid #38bdf8;
  border-radius: 0.6rem;
  padding: 0.5rem 1.2rem;
  color: #e0f2fe;
  font-family: sans-serif;
  font-weight: bold;
  font-size: 0.9rem;
  white-space: nowrap;
  box-shadow: 0 0 1.5rem rgba(56, 189, 248, 0.4), 0 10px 25px rgba(0, 0, 0, 0.5);
  transition: opacity 0.25s ease, transform 0.25s ease;
}
#cpu-step-hint.show {
  opacity: 1;
  transform: translate(-50%, 0);
  animation: cpu-step-hint-blink 1.1s ease-in-out infinite;
}
@keyframes cpu-step-hint-blink {
  0%, 100% { border-color: #38bdf8; box-shadow: 0 0 1.5rem rgba(56, 189, 248, 0.4), 0 10px 25px rgba(0, 0, 0, 0.5); }
  50% { border-color: #bae6fd; box-shadow: 0 0 2.6rem rgba(56, 189, 248, 0.9), 0 10px 25px rgba(0, 0, 0, 0.5); }
}
body.reduce-glow #cpu-step-hint.show {
  animation: none;
}
body.reduce-glow #card-effect-picker-hint.show {
  animation: none;
}
/* 「これ以上選ばない」スキップボタン（任意選択の効果。requestCellChoiceForEffectの
   options.allowSkip / showEffectSkipButton参照）。案内バナー(#card-effect-picker-hint)の
   すぐ下に、クリック可能(pointer-events:auto=button既定)なボタンとして出す。黄のキューブ
   サフラン等の「してもよい」効果を途中でやめられるようにするため。 */
#card-effect-skip-button {
  position: fixed;
  /* 案内バナー(#card-effect-picker-hint)を上部へ移したので、そのすぐ下へ合わせる。 */
  top: 7.4rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 3201;
  display: none;
  padding: 0.4rem 1.1rem;
  border: 1px solid rgba(250, 204, 21, 0.7);
  border-radius: 0.5rem;
  background: rgba(28, 16, 4, 0.95);
  color: #fde68a;
  font-family: sans-serif;
  font-size: 0.85rem;
  font-weight: bold;
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.5);
}
#card-effect-skip-button.show {
  display: block;
}
#card-effect-skip-button:hover {
  filter: brightness(1.12);
}
/* 【続き545】捨てる枚数が決まっている場面で、枚数が揃うまで確定を薄く見せる。
   実際に押せなくしているのは confirmOrder 側（このボタンは自前の当たり判定で拾うため）。 */
#card-effect-skip-button:disabled {
  opacity: 0.45;
  cursor: default;
  box-shadow: none;
}
#card-effect-skip-button:disabled:hover {
  filter: none;
}
/* 【2026-09-28】順番にこだわらない人のための「おまかせ」。確定ボタンのすぐ下に出す。
   横に並べると確定ボタンの文言の長さで中央がズレる（枚数によって文字数が変わる）ので、
   同じ中央寄せのまま真下に置く。色は控えめにして、主役（確定）と見分けが付くようにする。 */
#card-effect-auto-button {
  position: fixed;
  top: 10.3rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 3201;
  display: none;
  padding: 0.3rem 0.9rem;
  border: 1px solid rgba(148, 163, 184, 0.6);
  border-radius: 0.5rem;
  background: rgba(15, 23, 32, 0.95);
  color: #cbd5e1;
  font-family: sans-serif;
  font-size: 0.78rem;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
}
#card-effect-auto-button.show {
  display: block;
}
#card-effect-auto-button:hover {
  filter: brightness(1.2);
}
/* ユーザー要望「収穫と種まきで獲得したカードを手札の中で効果が終わるまで光らせて
   ほしい」。既存のhas-mystic-auraとは別に、緑系の「獲得しました」を表す光。 */
.hand-card.card-effect-just-acquired-glow {
  animation: card-effect-hand-acquired-glow 1.4s ease-in-out infinite;
}
@keyframes card-effect-hand-acquired-glow {
  0%, 100% { box-shadow: 0 0 0.6rem 0.15rem rgba(74, 222, 128, 0.7); }
  50% { box-shadow: 0 0 1.4rem 0.5rem rgba(74, 222, 128, 0.95); }
}
body.reduce-glow .hand-card.card-effect-just-acquired-glow {
  animation: none;
  box-shadow: 0 0 0.5rem 0.15rem rgba(74, 222, 128, 0.6);
}
/* ユーザー要望「スリカエを所持しているときは常に手札のスリカエに対し特殊なEFFECTで
   めだたせてください」。「いつでも使える」＝手札に持っている間ずっと使用可能という
   特殊な状態を伝える常設演出のため、上のcard-effect-just-acquired-glow（一時的な
   「獲得した」演出、緑）とは別の色（金）にして意味の違いを見た目でも区別する。 */
.hand-card.is-anytime-usable-glow {
  animation: card-effect-anytime-usable-glow 1.8s ease-in-out infinite;
}
@keyframes card-effect-anytime-usable-glow {
  0%, 100% { box-shadow: 0 0 0.6rem 0.15rem rgba(250, 204, 21, 0.7); }
  50% { box-shadow: 0 0 1.4rem 0.5rem rgba(250, 204, 21, 0.95); }
}
body.reduce-glow .hand-card.is-anytime-usable-glow {
  animation: none;
  box-shadow: 0 0 0.5rem 0.15rem rgba(250, 204, 21, 0.6);
}
/* ユーザー要望「ハンドフェイズに『善処の原則』等により使えない手札はトーンダウン
   させてください」（自動処理モード中限定、main.jsのbuildPlayerZone参照）。実際に
   ドラッグ操作は妨げない（pointer-events:autoのまま）——あくまで見た目の判別用。 */
.hand-card.hand-card-effect-unusable {
  filter: grayscale(75%) brightness(0.6);
  opacity: 0.55;
}
/* ユーザー要望「どこに置かれるのかを忘れないように置かれるマスをハイライトして
   ほしい」。クリックして選ぶ候補（黄色、.card-effect-target-cell）とは見た目を変え、
   「あなたが選んだ結果、ここに戻ってくる」という情報表示専用（クリック対象ではない）
   ことが分かるよう青系にする。 */
.cell.card-effect-placement-target {
  outline: 3px dashed rgba(56, 189, 248, 0.9);
  outline-offset: -3px;
  animation: card-effect-placement-target-pulse 1.6s ease-in-out infinite;
}
@keyframes card-effect-placement-target-pulse {
  0%, 100% { box-shadow: inset 0 0 0.6rem rgba(56, 189, 248, 0.3); }
  50% { box-shadow: inset 0 0 1.2rem rgba(56, 189, 248, 0.6); }
}
body.reduce-glow .cell.card-effect-placement-target {
  animation: none;
}
#tutorial-callout {
  position: fixed;
  width: min(22rem, 90vw);
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(250, 204, 21, 0.5);
  border-radius: 0.6rem;
  padding: 1rem;
  font-family: sans-serif;
  color: #e2e8f0;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
  transition: left 0.25s ease, top 0.25s ease;
}
.tutorial-callout-title {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-weight: bold;
  font-size: 1rem;
  margin-bottom: 0.5rem;
  color: #fde68a;
}
/* ユーザー提供の到達効果/手札効果アイコン（tutorial.jsのstep.icon）。 */
.tutorial-callout-title-icon {
  width: 1.5rem;
  height: 1.5rem;
  object-fit: contain;
  flex-shrink: 0;
}
.tutorial-callout-body p {
  margin: 0 0 0.6rem 0;
  font-size: 0.85rem;
  line-height: 1.6;
}
.tutorial-callout-body p:last-child {
  margin-bottom: 0;
}
/* ユーザー要望「チュートリアルの続き（到達効果・手札効果・ゲート侵攻の応用ルール）」
   への対応。手札効果の説明で実際のカード（画像＋名前＋補足）を見せる部分。
   ユーザー要望「カードはもっともっと大きく！ホバー拡大時くらいのサイズがいい」への
   対応で、盤面のホバー拡大プレビューと同じ--card-preview-size変数をそのまま流用する
   （管理者モードでそちらのサイズを変えると、この例も連動して大きさが変わる）。
   画像がかなり大きくなるため、横並びではなく縦積みレイアウトに変更した。 */
.tutorial-card-example {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.4rem;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(148, 163, 184, 0.25);
  border-radius: 0.4rem;
  padding: 0.6rem;
  margin: 0.4rem 0 0.6rem;
}
.tutorial-card-example-image {
  position: relative;
  width: min(var(--card-preview-size, 20rem), 100%);
  aspect-ratio: 1 / 1;
  object-fit: contain;
  overflow: hidden;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border-radius: 0.4rem;
}
.tutorial-card-example-name {
  font-weight: bold;
  font-size: 0.9rem;
  margin-top: 0.2rem;
}
.tutorial-card-example-note {
  font-size: 0.78rem;
  color: #cbd5e1;
  line-height: 1.5;
}
/* 手札効果ステップはカード画像がかなり大きくなるため、コールアウト自体も広げる
   （tutorial.jsのstep.wideフラグで切り替える）。 */
#tutorial-callout.is-wide {
  width: min(28rem, 92vw);
}

/* ユーザー要望「ダミーの手札を一時的に持たせる」への対応。本物の.hand-fanへ一時的に
   追加する見た目だけのカード（tutorial.jsのapplyDummyHand参照）。本物のhand-cardと
   同じ見た目にしつつ、操作対象にはしない。 */
.tutorial-dummy-hand-card {
  pointer-events: none;
}

/* ユーザー要望「駒を1マス前に、隣に相手の駒を、もう一方のマスのカードを無くして
   説明しやすくできますか？必要があれば仮想盤面としてもいいです」への対応。本物の
   盤面には一切触れない、説明パネル内だけの簡易な仮想盤面（tutorial.jsの
   buildMoveDemoDiagram）。十字型に5マスだけ配置する。 */
.tutorial-move-diagram {
  display: grid;
  grid-template-columns: repeat(3, 3.4rem);
  grid-template-rows: repeat(3, 3.4rem);
  grid-template-areas:
    ".    up    ."
    "left center right"
    ".    down  .";
  gap: 0.3rem;
  justify-content: center;
  margin: 0.5rem auto 0.6rem;
}
.tutorial-move-diagram-cell {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(148, 163, 184, 0.3);
  border-radius: 0.3rem;
}
.tutorial-move-diagram-cell.pos-up { grid-area: up; }
.tutorial-move-diagram-cell.pos-left { grid-area: left; }
.tutorial-move-diagram-cell.pos-center { grid-area: center; }
.tutorial-move-diagram-cell.pos-right { grid-area: right; }
.tutorial-move-diagram-cell.pos-down { grid-area: down; }
.tutorial-move-diagram-cell.is-target {
  outline: 3px solid rgba(250, 204, 21, 0.9);
  outline-offset: -3px;
  animation: tutorial-move-range-pulse 1.4s ease-in-out infinite;
}
.tutorial-move-diagram-card {
  width: 80%;
  height: 80%;
  background-size: cover;
  background-position: center;
  border-radius: 0.2rem;
}
.tutorial-move-diagram-piece {
  width: 1.4rem;
  height: 1.4rem;
  border-radius: 0.3rem;
  background: var(--diagram-piece-color, #94a3b8);
  box-shadow: 0 0 0.4rem rgba(0, 0, 0, 0.5);
}
.tutorial-move-diagram-label {
  position: absolute;
  bottom: 0.1rem;
  font-size: 0.6rem;
  color: #cbd5e1;
  width: 100%;
  text-align: center;
}
.tutorial-move-diagram-empty {
  width: 70%;
  height: 70%;
  border: 1px dashed rgba(148, 163, 184, 0.4);
  border-radius: 0.2rem;
}
.tutorial-callout-footer {
  font-size: 0.72rem;
  color: #94a3b8;
  font-style: italic;
}
.tutorial-callout-progress {
  font-size: 0.7rem;
  color: #94a3b8;
  margin-top: 0.4rem;
}
.tutorial-callout-buttons {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.8rem;
}
.tutorial-callout-skip {
  margin-right: auto;
  background: none;
  border: none;
  color: #94a3b8;
  font-size: 0.8rem;
  cursor: pointer;
  text-decoration: underline;
  padding: 0.3rem 0;
}
.tutorial-callout-back,
.tutorial-callout-next {
  padding: 0.4rem 0.9rem;
  border-radius: 0.3rem;
  font-size: 0.85rem;
  cursor: pointer;
}
.tutorial-callout-back {
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(148, 163, 184, 0.3);
  color: #e2e8f0;
}
.tutorial-callout-back:disabled {
  opacity: 0.4;
  cursor: default;
}
.tutorial-callout-next {
  background: #be185d;
  border: none;
  color: white;
  font-weight: bold;
}

/* ホーム画面（続き73、src/home-screen.js）。HUERISE→タイトル画面→ログイン後の
   ナビゲーションのハブとなる画面全体のページ（モーダルではない）。opening-screen.js
   の「オンラインで続ける」から遷移する。 */
#home-screen {
  position: fixed;
  inset: 0;
  /* ユーザー要望どおりフレンドリーマッチ/ショップを実際に開けるようにした際、
     ショップパネル(#shop-panel、z-index:2601)がこのホーム画面より低いz-indexだと
     裏に隠れてしまうことが実機確認で判明した。ショップ/マイページ等の既存パネルは
     いずれも2000〜2601付近のz-indexを使っているため、ホーム画面はその全てより
     確実に低い値にしておく（オンライン対戦パネル、z-index:10002は元々余裕がある）。 */
  z-index: 1500;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 2rem 1rem;
  /* ユーザー提供の背景画像（assets/home-bg.webp、元は画像素材/ホーム画面/タイトル寄せ.png）。
     ユーザー報告「ホームに入る時に一瞬“以前の黒い画面”が出て、そこへ今のホーム画面が覆い
     かぶさるように出る」の対策として、下地の色を（旧デザインの暗色ではなく）画像に近い
     クリーム色にした。画像の読み込みが間に合わない一瞬でも暗転せず、オープニングの黒からの
     切替も自然になる。画像自体はhome-screen.jsで事前読み込み(preload)する。 */
  background: url("../assets/home-bg.webp") center / cover no-repeat, #ece6da;
  font-family: sans-serif;
  color: #1e293b;
  overflow-y: auto;
  box-sizing: border-box;
}
/* ユーザー報告「明るい背景で文字が見えにくい」。濃色＋白いにじみ影で明るい背景でも読める
   ようにする。 */
#home-screen-title {
  font-size: 1.6rem;
  font-weight: bold;
  letter-spacing: 0.08em;
  color: #1e293b;
  text-shadow: 0 1px 8px rgba(255, 255, 255, 0.85), 0 1px 2px rgba(255, 255, 255, 0.95);
  /* タイトルの縦位置（ユーザー報告「上すぎる」2026-08-16）。管理者モードで調整（--home-title-offset-y）。 */
  margin-top: var(--home-title-offset-y, 0rem);
}
#home-screen-subtitle {
  font-size: 0.9rem;
  color: #334155;
  margin-bottom: 1.6rem;
  letter-spacing: 0.2em;
  text-shadow: 0 1px 5px rgba(255, 255, 255, 0.85);
}
/* ホームの現ランク表示（rank-badge.js、フェーズ4/6）。ホームは常にクリーム色のライト背景なので
   枠は付けず（ユーザー要望「枠はいらないかも」）、文字はタイトルと同じ濃色＋白いにじみ影で読ませる。
   位置・サイズは管理者モードで調整（--home-rank-pos-x/y/scale）。 */
/* ユーザー報告2026-08-16「ホーム画面が縦に大きくなってスクロールが必要」。ランク表示（大きな
   バッジで背が高い）を flex フローから外して absolute にし、グリッド（8メニュー）を押し下げない
   ようにする。位置は --home-rank-pos-x/y（左右・上下オフセット）で調整。#home-screen は
   position:fixed なので絶対配置の基準になる。 */
#home-screen-rank {
  position: absolute;
  /* 大きなランクバッジのヒーロー表示は背が高く、中央の8メニュー（グリッド）はほぼ全画面を使うため、
     縦にも横にも並べると短い画面では重なる/スクロールする。既定はコンパクトに左上へ置いて重ならない
     ようにし、位置・サイズは --home-rank-pos-x/y・--home-rank-scale で自由に調整できる（ユーザーが
     好きな場所・大きさへ）。 */
  top: 2%;
  left: 2%;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  transform: translate(var(--home-rank-pos-x, 43.8rem), var(--home-rank-pos-y, 13.1rem)) scale(var(--home-rank-scale, 1));
  transform-origin: top left;
  pointer-events: none; /* 表示専用。裏のグリッド等のクリックを妨げない。 */
}
.home-rank-label {
  font-size: 0.72rem;
  letter-spacing: 0.15em;
  color: #475569;
  text-shadow: 0 1px 4px rgba(255, 255, 255, 0.85);
}
#home-screen-rank .rank-badge-name { color: #1e293b; text-shadow: 0 1px 4px rgba(255, 255, 255, 0.9); }
#home-screen-rank .rank-badge-dot { background: rgba(30, 41, 59, 0.16); border-color: rgba(30, 41, 59, 0.28); }
.home-rank-name {
  font-size: 0.95rem;
  font-weight: 800;
  color: #1e293b;
  text-shadow: 0 1px 4px rgba(255, 255, 255, 0.9);
  /* 段位名テキスト（「ブロンズ」等）の位置を管理者モードで調整（ユーザー要望2026-08-17）。
     transformなのでレイアウトに影響せずショーケースとは独立に動かせる。 */
  transform: translate(var(--home-rank-name-pos-x, 0rem), var(--home-rank-name-pos-y, 0rem));
}

#home-screen-grid {
  display: grid;
  /* ユーザー要望「4個ずつ並べて（今は上5下3でバランスが悪い）」。8タイルを4×2にする。 */
  grid-template-columns: repeat(4, 1fr);
  gap: 1.6rem 1.6rem;
  /* ユーザー報告「アイコンを9.8rem以上にしても大きくならない」。原因は、グリッド幅が
     固定(46rem)で1マス≒11.5rem→中身(img)がその幅にmax-width:100%で頭打ちだったこと。
     グリッド幅をアイコンサイズに連動させ、マスがアイコンより十分広くなるようにする
     （5.5rem時=46remで従来と同じ、大きくすると比例して広がる）。 */
  width: min(100%, calc(4 * var(--home-tile-icon-size, 5.5rem) + 24rem));
  /* 8メニューの一括位置・サイズ調整（ユーザー要望2026-08-16）。管理者モードの
     --home-grid-pos-x/y/scale。transformなのでレイアウトに影響せず自由に動かせる。 */
  transform: translate(var(--home-grid-pos-x, 0rem), var(--home-grid-pos-y, 6.4rem)) scale(var(--home-grid-scale, 1));
}
/* スマホなど狭い画面では2列に（4列だと窮屈なため）。 */
@media (max-width: 34rem) {
  #home-screen-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}
/* ユーザー要望「アイコンごとの枠は不要」。従来の枠（背景グラデ・枠線・角丸）を撤去し、
   アイコン画像＋ラベルだけを表示する。ホバーは軽い浮き上がり＋明るさで示す。 */
.home-screen-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 0.4rem;
  padding: 0.5rem;
  background: none;
  border: none;
  border-radius: 0;
  color: #e2e8f0;
  font-family: sans-serif;
  cursor: pointer;
  transition: transform 0.15s ease, filter 0.15s ease;
}
.home-screen-tile:hover {
  transform: translateY(-0.2rem);
  filter: brightness(1.12);
}
.home-screen-tile.is-soon {
  color: rgba(226, 232, 240, 0.6);
  filter: grayscale(0.6) opacity(0.75);
}
.home-screen-tile.is-soon:hover {
  transform: none;
  filter: grayscale(0.6) opacity(0.75);
}
.home-screen-tile-icon {
  position: relative;
  font-size: 2rem;
  line-height: 1;
}
.home-screen-tile-icon-img {
  position: relative;
  z-index: 1;
  /* 管理者モードで一括調整できるサイズ（ユーザー要望）。admin.jsのGROUPS参照。 */
  width: var(--home-tile-icon-size, 5.5rem);
  height: var(--home-tile-icon-size, 5.5rem);
  object-fit: contain;
  display: block;
  /* 明るい背景でも細い線が見えるよう、暗い影で縁取りする（ユーザー報告の視認性対策）。 */
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.35)) drop-shadow(0 0 1px rgba(0, 0, 0, 0.4));
}
/* ホバー時に背面へ浮かび上がる幻想的なオーラ（ユーザー要望）。放射状の淡いグロー＋回転する
   虹色リングの2枚重ね。どちらもアイコンの後ろ(z-index:0 < img:1)、クリックは奪わない。 */
.home-screen-tile-icon::before,
.home-screen-tile-icon::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
  z-index: 0;
  transition: opacity 0.35s ease, transform 0.35s ease;
}
.home-screen-tile-icon::before {
  inset: -28%;
  background: radial-gradient(
    circle,
    rgba(147, 197, 253, 0.6),
    rgba(196, 181, 253, 0.42) 42%,
    rgba(253, 224, 71, 0.28) 66%,
    transparent 75%
  );
  filter: blur(9px);
  transform: scale(0.6);
}
.home-screen-tile-icon::after {
  inset: -22%;
  background: conic-gradient(
    from 0deg,
    rgba(248, 113, 113, 0.5),
    rgba(251, 191, 36, 0.5),
    rgba(74, 222, 128, 0.5),
    rgba(56, 189, 248, 0.5),
    rgba(167, 139, 250, 0.5),
    rgba(244, 114, 182, 0.5),
    rgba(248, 113, 113, 0.5)
  );
  filter: blur(8px);
  transform: scale(0.7);
}
.home-screen-tile:hover .home-screen-tile-icon::before {
  opacity: 1;
  transform: scale(1.05);
  animation: home-icon-aura-pulse 2.2s ease-in-out infinite;
}
.home-screen-tile:hover .home-screen-tile-icon::after {
  opacity: 0.5;
  transform: scale(0.95);
  animation: home-icon-aura-rotate 7s linear infinite;
}
.home-screen-tile.is-soon:hover .home-screen-tile-icon::before,
.home-screen-tile.is-soon:hover .home-screen-tile-icon::after {
  opacity: 0; /* 近日公開タイルはオーラを出さない */
}
@keyframes home-icon-aura-pulse {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.15);
  }
}
@keyframes home-icon-aura-rotate {
  to {
    transform: rotate(360deg);
  }
}
.home-screen-tile-label {
  font-size: 1rem;
  font-weight: bold;
  text-align: center;
  line-height: 1.35;
  /* アイコンが大きくなったぶん、ラベルは幅を抑えてアイコンの下にまとまるようにする。 */
  max-width: 12rem;
  margin-top: 0.1rem;
  /* 明るい背景で読めるよう濃色＋白いにじみ影（ユーザー報告の視認性対策）。 */
  color: #1e293b;
  text-shadow: 0 1px 2px rgba(255, 255, 255, 0.9), 0 0 4px rgba(255, 255, 255, 0.75);
}
/* ユーザー要望「近日公開／NEWバッジが小さく見づらい」→ 大きく・くっきりに。
   近日公開は淡い半透明ではなく、濃いアンバー地＋濃い文字でコントラストを上げる。 */
.home-screen-tile-badge {
  position: absolute;
  /* 位置は管理者モードで微調整できる（--home-badge-offset-x/y、admin.js）。既定値はユーザー調整値。 */
  top: calc(0.5rem + var(--home-badge-offset-y, 1.6rem));
  right: calc(0.5rem + var(--home-badge-offset-x, 2rem));
  font-size: 0.8rem;
  font-weight: 800;
  padding: 0.24rem 0.6rem;
  border-radius: 999px;
  background: rgba(250, 204, 21, 0.95);
  border: 1px solid rgba(146, 100, 12, 0.7);
  color: #3f2a00;
  white-space: nowrap;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
/* 未読お知らせの「NEW」バッジ（ユーザー要望）。明るい背景でも目立つ赤＋白フチ＋鼓動。 */
.home-screen-tile-new-badge {
  position: absolute;
  /* 位置は管理者モードで微調整できる（--home-new-badge-offset-x/y、admin.js）。既定値はユーザー調整値。 */
  top: calc(0.1rem + var(--home-new-badge-offset-y, 1.6rem));
  right: calc(0.9rem + var(--home-new-badge-offset-x, 2rem));
  font-size: 0.8rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  padding: 0.16rem 0.5rem;
  border-radius: 999px;
  background: #ef4444;
  color: #fff;
  white-space: nowrap;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.85), 0 2px 6px rgba(0, 0, 0, 0.4);
  animation: home-new-badge-pulse 1.5s ease-in-out infinite;
}
@keyframes home-new-badge-pulse {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.14);
  }
}
/* 続き162: ランク戦タイルの「今◯人が募集中」バッジ。NEWバッジと同じ配置の考え方で緑・下寄せ。 */
.home-screen-tile-waiting-badge {
  position: absolute;
  bottom: 0.5rem;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.74rem;
  font-weight: 800;
  letter-spacing: 0.02em;
  padding: 0.16rem 0.6rem;
  border-radius: 999px;
  background: #16a34a;
  color: #fff;
  white-space: nowrap;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.85), 0 2px 6px rgba(0, 0, 0, 0.4);
  animation: home-new-badge-pulse 1.5s ease-in-out infinite;
  pointer-events: none;
}

/* 続き162: ランク戦「対戦相手が現れたら通知」のバナー（ranked-notify.js）。画面上部中央に固定。 */
#ranked-notify-banner {
  position: fixed;
  top: 0.8rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 21000;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  max-width: min(92vw, 34rem);
  padding: 0.55rem 0.7rem 0.55rem 1rem;
  background: linear-gradient(160deg, #14532d, #052e16);
  border: 1px solid #22c55e;
  border-radius: 0.7rem;
  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.5);
  color: #eafff1;
  font-size: 0.92rem;
  animation: ranked-ready-pop 0.3s ease;
}
.ranked-notify-banner-text {
  flex: 1;
  line-height: 1.35;
}
.ranked-notify-banner-join {
  flex-shrink: 0;
  padding: 0.4rem 1rem;
  border: none;
  border-radius: 0.5rem;
  background: linear-gradient(160deg, #4ade80, #16a34a);
  color: #04240f;
  font-weight: 800;
  font-size: 0.9rem;
  cursor: pointer;
}
.ranked-notify-banner-join:hover {
  filter: brightness(1.08);
}
.ranked-notify-banner-close {
  flex-shrink: 0;
  padding: 0.2rem 0.4rem;
  border: none;
  background: transparent;
  color: #a7f3c8;
  font-size: 1.1rem;
  cursor: pointer;
  line-height: 1;
}
/* 図鑑／ルールブックの全画面版（codex-page.js）。ランキング/プロフィール全画面と同じ器。 */
#codex-page {
  position: fixed;
  inset: 0;
  z-index: 1500;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 2rem 1rem;
  background: radial-gradient(ellipse at top, #1e293b 0%, #0f172a 60%, #060a12 100%);
  font-family: sans-serif;
  color: #e2e8f0;
  overflow-y: auto;
  box-sizing: border-box;
}
#codex-page-back {
  align-self: flex-start;
  background: none;
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.4rem;
  color: #e2e8f0;
  padding: 0.35rem 0.8rem;
  cursor: pointer;
  font-size: 0.85rem;
}
#codex-page-back:hover {
  background: rgba(56, 189, 248, 0.15);
  border-color: rgba(56, 189, 248, 0.6);
}
#codex-page-title {
  font-size: 1.4rem;
  font-weight: bold;
  letter-spacing: 0.06em;
  margin: 0.4rem 0 1.2rem;
}
#codex-page-content {
  width: min(72rem, 100%);
}
.codex-section-heading {
  font-size: 1.05rem;
  font-weight: bold;
  color: #7dd3fc;
  border-bottom: 1px solid rgba(148, 163, 184, 0.3);
  padding-bottom: 0.3rem;
  margin: 1.4rem 0 0.8rem;
}
.codex-section-heading:first-child {
  margin-top: 0;
}
#home-screen-toast {
  position: fixed;
  left: 50%;
  bottom: 2.2rem;
  transform: translate(-50%, 0.6rem);
  z-index: 9100;
  background: rgba(15, 23, 32, 0.95);
  border: 1px solid rgba(250, 204, 21, 0.5);
  border-radius: 0.5rem;
  padding: 0.6rem 1rem;
  font-family: sans-serif;
  font-size: 0.8rem;
  color: #e2e8f0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease, transform 0.25s ease;
  white-space: nowrap;
}
#home-screen-toast.is-visible {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* 対戦モード選択モーダル（home-screen.js openMatchChoiceModal、ユーザー要望2026-08-08）。
   「CPUマッチ＆フレンドリーマッチ」を押すと出る、フレンドリー/CPUを選ぶ巨大モーダル。 */
#home-match-choice {
  position: fixed;
  inset: 0;
  z-index: 9200;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(4, 8, 14, 0.72);
  backdrop-filter: blur(3px);
  font-family: sans-serif;
  animation: home-match-choice-fade 0.2s ease-out;
}
@keyframes home-match-choice-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}
.home-match-choice-panel {
  width: min(46rem, 94vw);
  box-sizing: border-box;
  padding: 2rem 1.6rem 1.6rem;
  background: radial-gradient(1000px 300px at 50% -10%, rgba(56, 189, 248, 0.12), transparent 60%),
    linear-gradient(160deg, rgba(20, 28, 40, 0.98), rgba(8, 12, 20, 0.99));
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 1rem;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.6);
  color: #e2e8f0;
  text-align: center;
}
.home-match-choice-title {
  font-size: 1.5rem;
  font-weight: bold;
  letter-spacing: 0.05em;
  margin-bottom: 1.4rem;
}
.home-match-choice-options {
  display: flex;
  gap: 1.2rem;
  justify-content: center;
  flex-wrap: wrap;
}
.home-match-choice-option {
  flex: 1 1 16rem;
  min-width: 13rem;
  max-width: 20rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  padding: 1.6rem 1.2rem;
  cursor: pointer;
  color: #e6edf6;
  background: linear-gradient(160deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.02));
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.9rem;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
  transition: transform 0.15s ease-out, box-shadow 0.15s ease-out, border-color 0.15s ease-out;
}
.home-match-choice-option:hover {
  transform: translateY(-0.3rem);
  border-color: rgba(56, 189, 248, 0.7);
  box-shadow: 0 0.8rem 1.8rem rgba(0, 0, 0, 0.5), 0 0 1.2rem rgba(56, 189, 248, 0.25);
}
.home-match-choice-option-icon {
  font-size: 3.4rem;
  line-height: 1;
}
.home-match-choice-option-label {
  font-size: 1.15rem;
  font-weight: bold;
}
.home-match-choice-option-desc {
  font-size: 0.82rem;
  color: #9fb0c3;
  line-height: 1.5;
}
.home-match-choice-cancel {
  margin-top: 1.4rem;
  background: rgba(148, 163, 184, 0.12);
  border: 1px solid rgba(148, 163, 184, 0.6);
  border-radius: 0.4rem;
  color: #e2e8f0;
  font-family: sans-serif;
  font-size: 0.9rem;
  font-weight: bold;
  padding: 0.5rem 1.4rem;
  cursor: pointer;
}
.home-match-choice-cancel:hover {
  border-color: rgba(56, 189, 248, 0.8);
  background: rgba(56, 189, 248, 0.18);
}
/* CPU戦の強さ選択（対戦モード選択モーダル内、ユーザー要望2026-08-09）。 */
.home-match-choice-difficulty {
  margin-top: 1.4rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
}
.home-match-choice-difficulty-label {
  font-size: 0.9rem;
  font-weight: bold;
  color: #cbd5e1;
}
.home-match-choice-segment {
  display: inline-flex;
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.5rem;
  overflow: hidden;
}
.home-match-choice-segment-btn {
  padding: 0.4rem 0.95rem;
  background: rgba(255, 255, 255, 0.04);
  border: none;
  border-right: 1px solid rgba(148, 163, 184, 0.25);
  color: #cbd5e1;
  font-family: sans-serif;
  font-size: 0.9rem;
  font-weight: bold;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.home-match-choice-segment-btn:last-child {
  border-right: none;
}
.home-match-choice-segment-btn:hover {
  background: rgba(56, 189, 248, 0.15);
}
.home-match-choice-segment-btn.is-selected {
  background: linear-gradient(160deg, #38bdf8, #0ea5e9);
  color: #06202e;
}
.home-match-choice-difficulty-hint {
  font-size: 0.75rem;
  color: #8397ab;
  line-height: 1.5;
  max-width: 32rem;
}
/* CPU戦の「対戦ルール」4つ（続き506）。人数・強さのセグメントと違い、それぞれが独立した
   入/切なので、1つずつ枠を持たせて折り返せるようにする（スマホでは2行になる）。 */
.home-match-choice-rules {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.4rem;
}
.home-match-choice-rule-btn {
  padding: 0.4rem 0.85rem;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.5rem;
  color: #cbd5e1;
  font-family: sans-serif;
  font-size: 0.85rem;
  font-weight: bold;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.home-match-choice-rule-btn:hover {
  background: rgba(56, 189, 248, 0.15);
}
.home-match-choice-rule-btn.is-selected {
  background: linear-gradient(160deg, #38bdf8, #0ea5e9);
  border-color: rgba(56, 189, 248, 0.7);
  color: #06202e;
}
/* ライトテーマ */
body.theme-light #home-match-choice {
  background: rgba(60, 51, 38, 0.5);
}
body.theme-light .home-match-choice-panel {
  background: linear-gradient(160deg, #fffdf7, #f3ecdd);
  border-color: rgba(176, 141, 63, 0.4);
  color: #3a3326;
}
body.theme-light .home-match-choice-option {
  background: rgba(255, 253, 247, 0.9);
  border-color: rgba(176, 141, 63, 0.4);
  color: #3a3326;
}
body.theme-light .home-match-choice-option-desc {
  color: #6b5c3c;
}
body.theme-light .home-match-choice-option:hover {
  border-color: rgba(176, 141, 63, 0.9);
}
body.theme-light .home-match-choice-difficulty-label {
  color: #6b5c3c;
}
body.theme-light .home-match-choice-segment {
  border-color: rgba(176, 141, 63, 0.5);
}
body.theme-light .home-match-choice-segment-btn {
  background: rgba(255, 253, 247, 0.9);
  border-right-color: rgba(176, 141, 63, 0.35);
  color: #6b5c3c;
}
body.theme-light .home-match-choice-segment-btn:hover {
  background: rgba(176, 141, 63, 0.15);
}
body.theme-light .home-match-choice-segment-btn.is-selected {
  background: linear-gradient(160deg, #d9b45a, #b8933f);
  color: #3a2f15;
}
body.theme-light .home-match-choice-difficulty-hint {
  color: #8a7a55;
}
/* ライトテーマの「← 戻る」ボタン（ユーザー報告2026-08-09「薄くて見づらい」）。
   既定の淡いグレー文字だとアイボリー背景に沈むため、濃い文字＋金の枠＋淡い塗りにする。 */
body.theme-light .home-match-choice-cancel {
  background: rgba(176, 141, 63, 0.12);
  border-color: rgba(176, 141, 63, 0.7);
  color: #4a3f28;
}
body.theme-light .home-match-choice-cancel:hover {
  border-color: rgba(176, 141, 63, 1);
  background: rgba(176, 141, 63, 0.22);
}

/* プロフィール／マイページの画面全体版（続き74、src/profile-page.js）。中身
   （アバター・戦績等）はmy-page.jsのrenderMyPageBody()を呼んで作るため、器だけを
   ホーム画面と揃えた見た目にする。 */
#profile-page {
  position: fixed;
  inset: 0;
  /* 不具合#84: ショップ(#shop-panel z:2601)を開いたままオプションエリアのマイページを押すと、
     マイページ(旧z:1500)がショップの裏に開いて見えなかった。ショップ/管理者パネル(2600)より
     前面へ上げ、どこから開いても最前面に出す（閉じると元の画面＝ショップ/ホームへ戻る）。 */
  z-index: 2650;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 2rem 1rem;
  background: radial-gradient(ellipse at top, #1e293b 0%, #0f172a 60%, #060a12 100%);
  font-family: sans-serif;
  color: #e2e8f0;
  overflow-y: auto;
  box-sizing: border-box;
}
#profile-page-back {
  align-self: flex-start;
  background: none;
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.4rem;
  color: #e2e8f0;
  font-family: sans-serif;
  font-size: 0.8rem;
  padding: 0.4rem 0.8rem;
  cursor: pointer;
  margin-bottom: 1rem;
}
#profile-page-back:hover {
  border-color: rgba(56, 189, 248, 0.7);
  background: rgba(56, 189, 248, 0.12);
}
#profile-page-title {
  font-size: 1.2rem;
  font-weight: bold;
  margin-bottom: 1.4rem;
}
#profile-page-card {
  width: min(24rem, 92vw);
  background: rgba(15, 23, 32, 0.7);
  border: 1px solid rgba(148, 163, 184, 0.3);
  border-radius: 0.6rem;
  padding: 1.2rem;
}

/* =========================================================================
   ライトテーマ（body.theme-light）— 第1弾：マイページのみ。
   ユーザー方針「事故防止のため従来のダークUIは残し、管理者トグルでライトに切り替える。
   まずはマイページから」。ダーク既定には一切手を入れず、theme-light が付いている時だけ
   アイボリー／羊皮紙＋ゴールド（王家）系に上書きする。アプリの明るい水彩＋金彩の
   絵柄となじむ配色。インラインstyleで色指定された要素だけ !important を使う。
   ========================================================================= */
body.theme-light #profile-page {
  background: url("../assets/backgrounds/page-bg-light.webp") center / cover no-repeat, #f0e8d6;
  color: #3a3326;
}
body.theme-light #profile-page-title {
  color: #3a3326;
}
body.theme-light #profile-page-back {
  border-color: rgba(176, 141, 63, 0.5);
  color: #6b5c3c;
}
body.theme-light #profile-page-back:hover {
  border-color: rgba(176, 141, 63, 0.9);
  background: rgba(176, 141, 63, 0.12);
}
body.theme-light #profile-page-card {
  background: rgba(255, 253, 247, 0.92);
  border: 1px solid rgba(176, 141, 63, 0.4);
  box-shadow: 0 10px 30px rgba(120, 100, 60, 0.15);
}
/* 名前行・戦績行（インラインstyleの色を上書き）。行の値(.my-page-row-value)は
   色指定インラインが無く親から継承するため、上でcolorを暗色にすれば自動で暗くなる。 */
body.theme-light .my-page-row {
  border-bottom-color: rgba(176, 141, 63, 0.25) !important;
}
body.theme-light .my-page-row-label {
  color: #8a7d63 !important;
}
body.theme-light .my-page-name-input {
  background: #ffffff !important;
  border-color: rgba(176, 141, 63, 0.6) !important;
  color: #3a3326 !important;
}
/* 着せ替えボタン群（.my-page-cosmetic-* はスタイルシート側のクラスなので !important 不要）。 */
body.theme-light .my-page-cosmetics-title {
  color: #6b5c3c;
}
body.theme-light .my-page-cosmetic-btn {
  background: rgba(176, 141, 63, 0.12);
  border-color: rgba(176, 141, 63, 0.45);
  color: #3a3326;
}
body.theme-light .my-page-cosmetic-btn:hover {
  background: rgba(176, 141, 63, 0.22);
  border-color: rgba(176, 141, 63, 0.8);
}
/* アバター変更ボタン・名前の鉛筆・戦績ステータス文（いずれもインラインstyleの
   薄い文字色のため、白背景では見えなくなる。theme-lightで暗色へ上書き）。 */
body.theme-light .my-page-change-btn {
  background: rgba(176, 141, 63, 0.12) !important;
  border-color: rgba(176, 141, 63, 0.45) !important;
  color: #3a3326 !important;
}
body.theme-light .my-page-name-pencil {
  color: #8a7d63 !important;
}
body.theme-light .my-page-status {
  color: #6b5c3c !important;
}
/* モーダル版マイページ（#my-page-panel、インラインstyleのため !important）。 */
body.theme-light #my-page-panel {
  background: rgba(255, 253, 247, 0.98) !important;
  border-color: rgba(176, 141, 63, 0.4) !important;
  color: #3a3326 !important;
  box-shadow: 0 20px 50px rgba(120, 100, 60, 0.25) !important;
}

/* 共通の「×」閉じるボタン（.modal-close-x）は各ライトモーダルで薄い白のままだと見えづらい
   ので暗色に（マイページ/ショップ/ルールブック/オンライン等すべてのライトモーダル共通）。 */
body.theme-light .modal-close-x {
  color: #8a7d63;
}
body.theme-light .modal-close-x:hover {
  color: #3a3326;
}

/* ---- ライトテーマ：ランキング画面（#ranking-page）。全てCSSクラス/id指定なので
   !important不要。構造はマイページ全画面版と同じ（ゴールド系アクセント）。 ---- */
body.theme-light #ranking-page {
  background: url("../assets/backgrounds/page-bg-light.webp") center / cover no-repeat, #f0e8d6;
  color: #3a3326;
}
body.theme-light #ranking-page-title {
  color: #3a3326;
}
body.theme-light #ranking-page-back {
  border-color: rgba(176, 141, 63, 0.5);
  color: #6b5c3c;
}
body.theme-light #ranking-page-back:hover {
  border-color: rgba(176, 141, 63, 0.9);
  background: rgba(176, 141, 63, 0.12);
}
body.theme-light #ranking-page-info-btn {
  border-color: rgba(176, 141, 63, 0.6);
  color: #8a7d63;
}
body.theme-light #ranking-page-info-btn:hover {
  border-color: rgba(176, 141, 63, 0.9);
  color: #a67c1a;
}
body.theme-light #ranking-page-status,
body.theme-light .ranking-page-empty {
  color: #8a7d63;
}
body.theme-light .ranking-page-column-heading {
  color: #a67c1a;
  border-bottom-color: rgba(176, 141, 63, 0.35);
}
body.theme-light .ranking-page-row {
  border-bottom-color: rgba(176, 141, 63, 0.2);
}
body.theme-light .ranking-page-row-rank {
  color: #8a7d63;
}
body.theme-light .ranking-page-row.is-top1 .ranking-page-row-rank {
  color: #b8860b; /* 金は白背景だと薄いのでダークゴールドへ */
}
body.theme-light .ranking-page-row.is-top2 .ranking-page-row-rank {
  color: #6b7280; /* シルバーも白背景だと薄いので濃いグレーへ */
}
body.theme-light .ranking-page-row.is-self .ranking-page-row-name {
  color: #a67c1a; /* is-selfの淡い黄名は白背景で見えないため暗いゴールドへ */
}
body.theme-light #ranking-info-modal {
  background: rgba(255, 253, 247, 0.98);
  border-color: rgba(176, 141, 63, 0.4);
  color: #3a3326;
}

/* ---- ライトテーマ：オプションメニュー（右上ドロップダウン、#options-menu-panel）。
   中身はCSSクラス/idなので !important 不要。 ---- */
body.theme-light #options-menu-panel {
  background: rgba(255, 253, 247, 0.98);
  border-color: rgba(176, 141, 63, 0.4);
  color: #3a3326;
  box-shadow: 0 10px 24px rgba(120, 100, 60, 0.25);
}
body.theme-light .options-menu-item {
  color: #3a3326;
}
body.theme-light .options-menu-item:hover {
  background: rgba(176, 141, 63, 0.18);
}
body.theme-light .options-menu-section-title,
body.theme-light .options-menu-sub-label,
body.theme-light .options-menu-details-title::before {
  color: #a67c1a;
}
body.theme-light .options-menu-plain-group-header {
  color: #3a3326;
  border-bottom-color: rgba(176, 141, 63, 0.3);
}
body.theme-light .options-menu-detail-entry {
  color: #3a3326;
  background: rgba(176, 141, 63, 0.12);
  border-color: rgba(176, 141, 63, 0.4);
}
body.theme-light .options-menu-detail-entry:hover {
  background: rgba(176, 141, 63, 0.22);
  border-color: rgba(176, 141, 63, 0.7);
}
body.theme-light .options-menu-detail-entry-hint {
  color: #6b5c3c;
}
body.theme-light .options-menu-divider {
  border-top-color: rgba(176, 141, 63, 0.3);
}
body.theme-light .options-menu-details {
  border-color: rgba(176, 141, 63, 0.3);
}
body.theme-light .options-menu-reset-hint {
  color: #8a7d63;
}
body.theme-light .options-menu-stats-link-card.is-linked .options-menu-stats-link-title {
  color: #6b5c3c;
}
/* 折りたたみ見出し（音量・表示演出・自動処理タイマー・ショートカット）の薄い文字色や、
   各ボタン（戻す／アカウント初期化／ショートカットキー）が白背景で読みづらかったのを暗色化
   （ユーザー報告のライトモード視認性）。 */
body.theme-light .options-menu-details > summary {
  color: #3a3326;
}
body.theme-light .options-menu-details {
  border-color: rgba(176, 141, 63, 0.3);
}
body.theme-light .options-menu-divider {
  border-top-color: rgba(176, 141, 63, 0.3);
}
body.theme-light .options-menu-stats-link-title {
  color: #a67c1a;
}
body.theme-light .options-menu-section-reset-btn {
  color: #6b5c3c;
  background: rgba(176, 141, 63, 0.12);
  border-color: rgba(176, 141, 63, 0.4);
}
body.theme-light .options-menu-section-reset-btn:hover {
  background: rgba(176, 141, 63, 0.22);
  color: #3a3326;
}
body.theme-light .options-menu-reset-btn {
  color: #3a3326;
  background: rgba(176, 141, 63, 0.14);
}
body.theme-light .options-menu-reset-btn:hover {
  background: rgba(176, 141, 63, 0.24);
}
body.theme-light .options-menu-shortcut-key,
body.theme-light .options-menu-shortcut-clear {
  color: #3a3326;
  background: rgba(176, 141, 63, 0.14);
  border-color: rgba(176, 141, 63, 0.4);
}
body.theme-light .options-menu-shortcut-key:hover,
body.theme-light .options-menu-shortcut-clear:hover {
  background: rgba(176, 141, 63, 0.24);
}
body.theme-light .options-menu-item:hover,
body.theme-light .options-menu-shortcut-row.is-highlighted {
  background: rgba(176, 141, 63, 0.18);
}

/* ---- ライトテーマ：オンライン対戦パネル（部屋一覧 #online-panel と待合 .online-lobby-card）。
   中身はインライン指定が多いが、器を明るくすれば継承テキストは暗色になり読める。入力欄や
   ボタン（暗色コントロール）はアイボリー地の上でもそのままで自然なので触らない。薄い
   グレー/ゴールドの見出し類だけ暗色化する。 ---- */
body.theme-light #online-panel,
body.theme-light .online-lobby-card {
  background: rgba(255, 253, 247, 0.98);
  border-color: rgba(176, 141, 63, 0.4);
  color: #3a3326;
  box-shadow: 0 20px 50px rgba(120, 100, 60, 0.25);
}
body.theme-light .online-lobby-count {
  color: #6b5c3c;
}
body.theme-light .online-lobby-waiting {
  color: #a67c1a;
}

/* ---- ライトテーマ：図鑑の「山札一覧を開く」ボタン（インラインで白文字＝白地で見えない）。 ---- */
body.theme-light .codex-open-deck-btn {
  background: rgba(176, 141, 63, 0.12) !important;
  border-color: rgba(176, 141, 63, 0.5) !important;
  color: #3a3326 !important;
}

/* ---- ライトテーマ：お知らせ／更新情報（#changelog-modal）。 ---- */
body.theme-light #changelog-modal {
  background: url("../assets/backgrounds/page-bg-light.webp") center / cover no-repeat, #f0e8d6;
  border-color: rgba(176, 141, 63, 0.4);
  color: #3a3326;
  box-shadow: 0 20px 50px rgba(120, 100, 60, 0.25);
}
body.theme-light .changelog-modal-title {
  color: #a67c1a;
}
body.theme-light .changelog-entry {
  border-top-color: rgba(176, 141, 63, 0.2);
}
body.theme-light .changelog-date {
  color: #2563eb;
}

/* ---- ライトテーマ：チュートリアル（ヘルプから開始、#tutorial-callout）。スクリム(暗幕)は
   注目を集める演出なのでダークのまま。説明ボックスだけライトに。 ---- */
body.theme-light #tutorial-callout {
  background: rgba(255, 253, 247, 0.98);
  border-color: rgba(176, 141, 63, 0.6);
  color: #3a3326;
  box-shadow: 0 20px 50px rgba(120, 100, 60, 0.28);
}
body.theme-light .tutorial-callout-title {
  color: #a67c1a;
}
body.theme-light .tutorial-callout-footer,
body.theme-light .tutorial-callout-progress,
body.theme-light .tutorial-callout-skip {
  color: #8a7d63;
}
body.theme-light .tutorial-card-example {
  background: rgba(176, 141, 63, 0.08);
  border-color: rgba(176, 141, 63, 0.3);
}
body.theme-light .tutorial-card-example-note {
  color: #6b5c3c;
}
body.theme-light .tutorial-callout-back {
  background: rgba(176, 141, 63, 0.14);
  border-color: rgba(176, 141, 63, 0.4);
  color: #3a3326;
}

/* ---- ライトテーマ：物語チュートリアル（tutorial-battle-ui.js、#tutorial-battle-callout）。
   【2026-09-11・ユーザー報告「最初のチュートリアルはモーダルがダークになっている」】
   あちらは自前の <style> で暗い配色を入れており、ライト配色の指定が1つも無かった
   （実測: ライト配色でも文字色 rgb(226,232,240)＝暗い地用の白文字）。
   同じ id に body.theme-light を足して**詳細度で**上書きする（<style> の差し込み順に依存しない）。
   見た目は通常チュートリアル（#tutorial-callout）のライト配色に揃える。暗幕はダークのまま。 ---- */
body.theme-light #tutorial-battle-callout {
  background: rgba(255, 253, 247, 0.98);
  border-color: rgba(176, 141, 63, 0.6);
  color: #3a3326;
  box-shadow: 0 1rem 3rem rgba(120, 100, 60, 0.28);
}
body.theme-light #tutorial-battle-callout .tb-callout-title {
  color: #a67c1a;
}
body.theme-light #tutorial-battle-callout .tb-callout-body p.tb-note {
  color: #8a7d63;
}
body.theme-light #tutorial-battle-callout .tb-callout-body .tb-body-image,
body.theme-light #tutorial-battle-callout .tb-callout-body .tb-body-icontext img {
  background: rgba(176, 141, 63, 0.1);
}
body.theme-light #tutorial-battle-callout .tb-callout-back {
  background: rgba(176, 141, 63, 0.14);
  border-color: rgba(176, 141, 63, 0.4);
  color: #3a3326;
}
body.theme-light #tutorial-battle-callout .tb-callout-next {
  background: #be185d;
  color: #ffffff;
}
body.theme-light #tutorial-battle-tip {
  background: rgba(255, 253, 247, 0.97);
  border-color: rgba(176, 141, 63, 0.6);
  color: #6b4f12;
  box-shadow: 0 0.4rem 1.4rem rgba(120, 100, 60, 0.28);
}

/* ---- ライトテーマ：ショップ（#shop-panel、全画面）。オレンジ系のボタン/リボンは
   そのまま（白背景でも映える）、地・カード・タブ・文字だけライト化する。 ---- */
body.theme-light #shop-panel {
  background: url("../assets/backgrounds/page-bg-light.webp") center / cover no-repeat, #f0e8d6;
  color: #3a3326;
}
body.theme-light #shop-panel-header {
  border-bottom-color: rgba(176, 141, 63, 0.3);
}
body.theme-light #shop-panel-back {
  border-color: rgba(176, 141, 63, 0.5);
  color: #6b5c3c;
}
body.theme-light #shop-panel-back:hover {
  border-color: rgba(176, 141, 63, 0.9);
  background: rgba(176, 141, 63, 0.12);
}
body.theme-light #shop-panel-balance {
  color: #a67c1a;
}
body.theme-light #shop-panel-completion {
  color: #8a7d63;
}
body.theme-light #shop-panel-status {
  color: #15803d;
}
body.theme-light #shop-panel-status.is-error {
  color: #b91c1c;
}
body.theme-light .shop-tab {
  background: rgba(176, 141, 63, 0.1);
  color: #6b5c3c;
  border-color: rgba(176, 141, 63, 0.4);
}
body.theme-light .shop-tab:hover {
  background: rgba(176, 141, 63, 0.2);
}
/* .shop-tab.is-active（オレンジグラデ）はそのまま */
body.theme-light .shop-item-card {
  /* 白の塗りつぶしの透明度を管理者モードで調整可能に（--shop-card-bg-alpha、ユーザー要望2026-08-08。
     下げると後ろのページ背景が透けて見える）。 */
  background: rgba(255, 255, 255, var(--shop-card-bg-alpha, 0.72));
  border-color: rgba(176, 141, 63, 0.4);
  box-shadow: 0 0.4rem 1rem rgba(120, 100, 60, 0.15);
}
body.theme-light .shop-item-card:hover {
  border-color: rgba(176, 141, 63, 0.85);
  box-shadow: 0 0.7rem 1.6rem rgba(120, 100, 60, 0.28);
}

/* ---- ライトテーマ：ヘルプ／ルールブック（#help-panel と #help-item-modal）。
   シアン系の「チュートリアルを見る」ボタンはそのまま（白背景でも映える）。 ---- */
body.theme-light #help-panel,
body.theme-light #help-item-modal {
  background: rgba(255, 253, 247, 0.98);
  border-color: rgba(176, 141, 63, 0.4);
  color: #3a3326;
  box-shadow: 0 20px 50px rgba(120, 100, 60, 0.25);
}
body.theme-light .help-index-section > summary,
body.theme-light .help-item-modal-title {
  color: #a67c1a;
}
body.theme-light .help-index-subsection > summary {
  color: #2563eb;
}
body.theme-light .help-index-item {
  background: rgba(176, 141, 63, 0.1);
  color: #3a3326;
}
body.theme-light .help-index-item:hover {
  background: rgba(176, 141, 63, 0.2);
}

/* ---- ライトテーマ：図鑑（#codex-page、全画面）。中身はカード画像なので器だけライトに。 ---- */
body.theme-light #codex-page {
  background: url("../assets/backgrounds/page-bg-light.webp") center / cover no-repeat, #f0e8d6;
  color: #3a3326;
}
body.theme-light #codex-page-back {
  border-color: rgba(176, 141, 63, 0.5);
  color: #6b5c3c;
}
body.theme-light #codex-page-back:hover {
  border-color: rgba(176, 141, 63, 0.9);
  background: rgba(176, 141, 63, 0.12);
}
body.theme-light .codex-section-heading {
  color: #a67c1a;
  border-bottom-color: rgba(176, 141, 63, 0.35);
}

/* =====================================================================
   ライトテーマ（対戦画面）: body.theme-light-ingame（方針B、メニュー系とは独立トグル）。
   第1弾。盤面（プレイマット）自体は元々明るいので、ここでは"冷たいスレート系"の常設
   チェック（ターン表示・山札ツールチップ・重なりカード一覧）だけをライト化する。
   承認/割り込み等の"暖色ゴールド系"モーダルは意図的な装飾で白背景でも馴染むため、
   あえてダークのまま残す。全CSS指定なので !important 不要。合わなければトグルをオフに
   するだけで従来ダークへ即戻し。今後、実際に見て気になる面を順次追加していく。 */
body.theme-light-ingame #phase-automation-turn-status,
body.theme-light-ingame #pile-tooltip,
body.theme-light-ingame #stack-modal,
body.theme-light-ingame #lock-pick-modal {
  background: rgba(255, 253, 247, 0.98);
  color: #3a3326;
  border-color: rgba(176, 141, 63, 0.5);
}
/* ラウンド/ターン表示は薄い白文字で明るい盤面だと読みづらいので暗色へ。
   （右上の残金・オンラインのピルは黒系のデザインが前提で、白系にすると違和感が出るため
   ライト化しない——ユーザー指摘。ダークのまま残す。） */
body.theme-light-ingame #turn-round-counter {
  color: #6b5c3c;
}

/* 操作アイコン（1枚ドロー・盤面拡大・シャッフル・ターン終了等）は、白いSVGアイコンを
   filterで暗色化すると塗りつぶし系アイコンが真っ黒の塊になり不格好だった（ユーザー報告）。
   小さい丸バッジは明るい盤面の上でもダーク＋白アイコンの方がきれいなので、ライト化せず
   ダークのまま残す（フェイズ案内板のボタンも同様）。 */
/* スタートプレイヤー決定「発表」モーダルだけライトに（共通buildSimpleModalの中身はインライン
   指定のため !important。設定フォーム等の他のsimple modalは対象外＝start-player-announce限定）。 */
body.theme-light-ingame .start-player-announce,
body.theme-light .start-player-announce {
  background: rgba(255, 253, 247, 0.98) !important;
  border-color: rgba(176, 141, 63, 0.4) !important;
  color: #3a3326 !important;
  box-shadow: 0 20px 50px rgba(120, 100, 60, 0.25) !important;
}
body.theme-light-ingame .start-player-announce .start-player-name,
body.theme-light .start-player-announce .start-player-name {
  color: #2563eb !important; /* 薄い水色#7dd3fcは白背景で読みづらいので濃い青へ */
}

/* フェイズ案内板（#phase-guide-bar）のボタンも上記の操作アイコンと同じ理由でダークのまま
   残す（塗りつぶし系アイコンをfilter暗色化すると真っ黒になり不格好なため）。 */

/* カード効果関連モーダルの背景をライトに（ユーザー要望：到達拡大・手札使用拡大の背景も）。
   スレート系のみ対象。緑/紫など意図的に色付けされたモーダル（.card-received-modal等）は
   白背景でも成立するのでそのまま残す。 */
body.theme-light-ingame .card-arrival-modal,
body.theme-light-ingame .hand-effect-use-modal {
  background: rgba(255, 253, 247, 0.62);
}
body.theme-light-ingame .hand-effect-use-modal-name {
  color: #3a3326;
  text-shadow: none;
}
body.theme-light-ingame .effect-reason-modal {
  background: rgba(255, 253, 247, 0.97);
  border-color: rgba(176, 141, 63, 0.6);
}
body.theme-light-ingame .effect-reason-modal-name {
  color: #2563eb;
}
body.theme-light-ingame .effect-reason-modal-text {
  color: #3a3326;
}
body.theme-light-ingame .hand-effect-option-picker {
  background: rgba(255, 253, 247, 0.98);
  border-color: rgba(176, 141, 63, 0.5);
}
body.theme-light-ingame .hand-effect-option-picker-title {
  color: #3a3326;
}
body.theme-light-ingame .hand-effect-option-picker-btn {
  background: rgba(176, 141, 63, 0.15);
  color: #3a3326;
  border-color: rgba(176, 141, 63, 0.5);
}
body.theme-light-ingame .hand-effect-option-picker-btn:hover {
  background: rgba(176, 141, 63, 0.3);
}
body.theme-light-ingame .hand-effect-option-picker-btn.is-unusable {
  background: rgba(120, 100, 60, 0.1);
  color: rgba(58, 51, 38, 0.4);
  border-color: rgba(120, 100, 60, 0.3);
}

/* ランク結果モーダル（ranked-result-modal.js）はゴールドの祝祭モーダルで既定ダーク。
   ユーザー報告#145「ライトモードなのにダーク」に対応し、対戦画面ライト時はクリーム背景＋
   暗色文字にしてテーマに馴染ませる。バッジ/ゲージは画像なので変更不要、ゴールドの縁取り・
   LP・勝利見出しは（暗めの金に落として）そのまま活かす。 */
body.theme-light-ingame #ranked-result-modal .ranked-result-inner {
  background: linear-gradient(160deg, #fffdf5, #f3ecda);
  border-color: #c99a3a;
  box-shadow: 0 0 40px rgba(201, 154, 58, 0.3);
  color: #3a3326;
}
body.theme-light-ingame #ranked-result-modal .ranked-result-heading.is-win { color: #b07d1e; }
body.theme-light-ingame #ranked-result-modal .ranked-result-heading.is-lose { color: #64748b; }
body.theme-light-ingame #ranked-result-modal .ranked-result-note { color: #5b5240; }
body.theme-light-ingame #ranked-result-modal .ranked-result-rank { color: #2e2820; }
body.theme-light-ingame #ranked-result-modal .ranked-result-gauge-text { color: #6b5c3c; }
body.theme-light-ingame #ranked-result-modal .ranked-result-lp { color: #b07d1e; }
body.theme-light-ingame #ranked-result-modal .ranked-result-promote-line {
  color: #b07d1e;
  text-shadow: none;
}

/* ランキングの画面全体版（続き74、src/ranking-page.js）。プロフィール画面全体版と
   同じ器の作り方（フルスクリーン、戻るボタン、中央のカード）を踏襲する。 */
#ranking-page {
  position: fixed;
  inset: 0;
  z-index: 1500;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 2rem 1rem;
  background: radial-gradient(ellipse at top, #1e293b 0%, #0f172a 60%, #060a12 100%);
  font-family: sans-serif;
  color: #e2e8f0;
  overflow-y: auto;
  box-sizing: border-box;
}
#ranking-page-back {
  align-self: flex-start;
  background: none;
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.4rem;
  color: #e2e8f0;
  font-family: sans-serif;
  font-size: 0.8rem;
  padding: 0.4rem 0.8rem;
  cursor: pointer;
  margin-bottom: 1rem;
}
#ranking-page-back:hover {
  border-color: rgba(56, 189, 248, 0.7);
  background: rgba(56, 189, 248, 0.12);
}
#ranking-page-title {
  font-size: 1.2rem;
  font-weight: bold;
  margin-bottom: 1.4rem;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}
#ranking-page-info-btn {
  background: none;
  border: 1px solid rgba(148, 163, 184, 0.5);
  border-radius: 50%;
  color: #94a3b8;
  width: 1.4rem;
  height: 1.4rem;
  line-height: 1;
  font-size: 0.9rem;
  cursor: pointer;
  padding: 0;
}
#ranking-page-info-btn:hover {
  border-color: rgba(56, 189, 248, 0.7);
  color: #38bdf8;
}
#ranking-info-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1611;
  width: min(24rem, 90vw);
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.6rem;
  padding: 1rem 1.2rem;
  font-family: sans-serif;
  color: #e2e8f0;
  box-sizing: border-box;
}
#ranking-info-modal-title {
  font-weight: bold;
  margin-bottom: 0.6rem;
  padding-right: 1.6rem;
}
#ranking-info-modal-body p {
  margin: 0 0 0.7rem;
  font-size: 0.82rem;
  line-height: 1.5;
  opacity: 0.9;
}
#ranking-info-modal-body p:last-child {
  margin-bottom: 0;
}
#ranking-page-card {
  width: min(28rem, 92vw);
  background: rgba(15, 23, 32, 0.7);
  border: 1px solid rgba(148, 163, 184, 0.3);
  border-radius: 0.6rem;
  padding: 1.2rem;
}
/* ユーザー要望「3ランキングを横に同時表示・枠は不要」。囲う枠(#ranking-page-card)は使わず、
   3カラムを横並びにする（各カラムは枠なし、見出し＋行だけ）。画面が狭ければ折り返す。 */
#ranking-page-list {
  display: flex;
  gap: 1.6rem 2.4rem;
  justify-content: center;
  align-items: flex-start;
  flex-wrap: wrap;
  width: 100%;
  max-width: 78rem;
  margin: 0 auto;
}
.ranking-page-column {
  flex: 1 1 20rem;
  min-width: 15rem;
  max-width: 24rem;
}
.ranking-page-column-heading {
  font-weight: bold;
  text-align: center;
  color: #7dd3fc;
  border-bottom: 1px solid rgba(148, 163, 184, 0.3);
  padding-bottom: 0.4rem;
  margin-bottom: 0.4rem;
  font-size: 1rem;
}
#ranking-page-tabs {
  display: flex;
  gap: 0.4rem;
  margin-bottom: 1rem;
}
.ranking-page-tab {
  flex: 1;
  padding: 0.4rem 0.6rem;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(148, 163, 184, 0.3);
  border-radius: 0.4rem;
  color: #94a3b8;
  font-family: sans-serif;
  font-size: 0.8rem;
  cursor: pointer;
}
.ranking-page-tab.is-active {
  background: rgba(56, 189, 248, 0.2);
  border-color: rgba(56, 189, 248, 0.7);
  color: #e2e8f0;
  font-weight: bold;
}
#ranking-page-status {
  text-align: center;
  color: #94a3b8;
  padding: 1rem 0;
  font-size: 0.85rem;
}
.ranking-page-empty {
  text-align: center;
  color: #94a3b8;
  padding: 1rem 0;
  font-size: 0.85rem;
}
/* 対戦終了パネルを「盤面を確認する」で最小化した時、左上に出る復元アイコンの脈動。 */
@keyframes post-game-restore-pulse {
  0%,
  100% {
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.55), 0 0 0 0 rgba(250, 204, 21, 0.55);
  }
  50% {
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.55), 0 0 0 0.5rem rgba(250, 204, 21, 0);
  }
}
.ranking-page-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 0.3rem;
  border-bottom: 1px solid rgba(148, 163, 184, 0.12);
  font-size: 0.85rem;
}
/* ユーザー要望「ランキングで自分の行をハイライト点滅で強調」。自分の行を色付き背景＋
   枠で目立たせ、ゆっくり点滅させる（見つけやすさ優先、うるさすぎない速さ）。 */
.ranking-page-row.is-self {
  border-radius: 0.4rem;
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  border-bottom-color: transparent;
  animation: ranking-self-pulse 1.6s ease-in-out infinite;
}
.ranking-page-row.is-self .ranking-page-row-name {
  font-weight: bold;
  color: #fde68a;
}
@keyframes ranking-self-pulse {
  0%,
  100% {
    background: rgba(250, 204, 21, 0.1);
    box-shadow: 0 0 0 1px rgba(250, 204, 21, 0.35) inset;
  }
  50% {
    background: rgba(250, 204, 21, 0.28);
    box-shadow: 0 0 0 1px rgba(250, 204, 21, 0.8) inset, 0 0 0.8rem rgba(250, 204, 21, 0.35);
  }
}
@media (prefers-reduced-motion: reduce) {
  .ranking-page-row.is-self {
    animation: none;
    background: rgba(250, 204, 21, 0.18);
    box-shadow: 0 0 0 1px rgba(250, 204, 21, 0.6) inset;
  }
}
.ranking-page-row-rank {
  width: 1.6rem;
  text-align: center;
  font-weight: bold;
  color: #94a3b8;
}
.ranking-page-row.is-top1 .ranking-page-row-rank {
  color: #facc15;
}
.ranking-page-row.is-top2 .ranking-page-row-rank {
  color: #cbd5e1;
}
.ranking-page-row.is-top3 .ranking-page-row-rank {
  color: #d97706;
}
.ranking-page-row-avatar {
  width: 1.8rem;
  height: 1.8rem;
  border-radius: 50%;
  object-fit: cover;
}
.ranking-page-row-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ranking-page-row-value {
  font-weight: bold;
}

/* 「いつでも使える」割り込みモーダル（続き76、src/main.js）。予約制の廃止に伴い新設。
   非ブロッキング（数秒で自動的に閉じる、進行を止めない）ため、他の承認モーダルと違い
   背景を暗くするbackdropは持たない——通知的な位置づけのcontact-result-modalと同じ
   考え方。.contact-approval-titleクラスを流用している。 */
#anytime-interrupt-modal {
  position: fixed;
  top: 6rem;
  left: 50%;
  transform: translate(-50%, 0);
  width: min(22rem, 90vw);
  z-index: 10450;
  background: linear-gradient(160deg, #422006, #1c1004);
  border: 2px solid #facc15;
  border-radius: 0.7rem;
  padding: 0.9rem 1.2rem;
  text-align: center;
  font-family: sans-serif;
  color: #fef3c7;
  box-shadow: 0 0 1.5rem rgba(250, 204, 21, 0.5), 0 20px 50px rgba(0, 0, 0, 0.6);
}
#anytime-interrupt-modal-list {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-bottom: 0.6rem;
}
.anytime-interrupt-modal-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  background: rgba(0, 0, 0, 0.25);
  border-radius: 0.4rem;
  padding: 0.3rem 0.6rem;
}
.anytime-interrupt-modal-row-name {
  font-size: 0.85rem;
  font-weight: bold;
}
.anytime-interrupt-modal-row-use {
  font-size: 0.8rem;
  font-weight: bold;
  padding: 0.3rem 0.9rem;
  background: rgba(34, 172, 56, 0.85);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 0.4rem;
  color: #f0fdf4;
  cursor: pointer;
}
.anytime-interrupt-modal-row-use:hover {
  background: rgba(34, 172, 56, 1);
}
#anytime-interrupt-modal-optout {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  font-size: 0.7rem;
  color: rgba(254, 243, 199, 0.75);
  cursor: pointer;
}
/* 「割り込みモーダル再開」ボタン（続き76）。フェイズ案内板の中に置き、「今後この
   モーダルを出さない」を選んでいる間だけ表示する。 */
#anytime-interrupt-resume-button {
  /* ユーザー要望（続き87）「割り込みモーダル再開ボタンはフェイズ案内板の右では
     なく左側に表示したい」。#phase-guide-bar末尾にappendChildされるため素の
     flexだと右端に来てしまう——DOM順は変えず、CSSのorderだけで先頭（左端）に
     見せる。 */
  order: -1;
  align-self: center;
  padding: 0.4rem 0.8rem;
  background: rgba(250, 204, 21, 0.15);
  border: 1px solid rgba(250, 204, 21, 0.5);
  border-radius: 0.4rem;
  color: #facc15;
  font-family: sans-serif;
  font-size: 0.75rem;
  font-weight: bold;
  cursor: pointer;
  white-space: nowrap;
}
#anytime-interrupt-resume-button:hover {
  background: rgba(250, 204, 21, 0.3);
}

/* ユーザー要望（続き83）「スマホ画面での画面中央に出てくるモーダルが全体的に小さく
   感じる。管理者モードで大きさを調整できるようにしたい」。この手のモーダルは
   ファイルをまたいで20種類以上あるが、ほぼ全て「position:fixed; top:50%; left:50%;
   transform:translate(-50%, -50%)」という同じ中央固定パターンを個別に持っている
   （各ファイルの元のルールはそのまま、ここでは一括りにできる新しい共通の悩み
   （拡大率）にだけ対応する）。1つ1つに個別のスマホ用ルールを書くと20箇所以上の
   重複になるため、ここで対象を1つのセレクタ一覧にまとめ、body.is-phone-device
   限定で拡大率を掛け合わせるだけの薄い上書きにした。.hand-effect-option-picker・
   .card-received-modalは元々scale(0.9)/scale(0.85)を個別に持っているため、
   その基準倍率を保ったまま追加で掛け合わせる（calc()での乗算）。 */
body.is-phone-device #options-menu-card-preview-demo,
body.is-phone-device #stack-modal,
body.is-phone-device #lock-pick-modal,
body.is-phone-device #confirm-modal,
body.is-phone-device #contact-confirm-modal,
body.is-phone-device #contact-result-modal,
body.is-phone-device #card-note-modal,
body.is-phone-device #icon-action-detail-modal,
body.is-phone-device #currency-about-panel,
body.is-phone-device #help-panel,
body.is-phone-device #help-item-modal,
body.is-phone-device #background-picker-modal,
body.is-phone-device #avatar-picker-modal,
body.is-phone-device #first-login-profile-modal,
body.is-phone-device #final-lock-approval-banner,
body.is-phone-device #contact-approval-modal,
body.is-phone-device #touch-action-confirm-modal,
body.is-phone-device #generic-confirm-modal,
body.is-phone-device #sleight-ritual-modal,
body.is-phone-device .phase-skip-modal,
body.is-phone-device .declare-colors-modal,
body.is-phone-device #victory-modal,
body.is-phone-device #rank-up-modal,
body.is-phone-device #opening-login-info-modal,
body.is-phone-device #ranking-info-modal,
body.is-phone-device #currency-award-modal,
body.is-phone-device #rank-reveal-modal,
body.is-phone-device #match-personal-result-modal,
body.is-phone-device #online-panel {
  transform: translate(-50%, -50%) scale(var(--center-modal-scale-phone, 1));
}
/* ユーザー報告2026-08-17「スマホで部屋作成モーダルの上下が見切れる」「対戦結果（MVP等）モーダルが
   小さすぎて見えない」。どちらも情報が多く縦に長い。横長にして（結果モーダルは中身を横に並べ）、
   縦のはみ出しは内部スクロールで受ける。scale(2)適用後の高さが画面を超えないよう、vwは使わず
   pure rem で幅を決め（ステージ幅1600px＝100remに対して安全な範囲）、max-heightを26remに抑える。 */
body.is-phone-device #online-panel {
  width: 40rem;
  max-height: 26rem;
  overflow-y: auto;
}
body.is-phone-device #match-personal-result-modal {
  width: 42rem;
  max-height: 26rem;
  overflow-y: auto;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-content: flex-start;
  gap: 0.6rem 1rem;
  /* pop-inアニメ（0.7→1）はscale(2)のphone拡大と衝突し、終了時に1→2へ跳ねるため無効化する。 */
  animation: none;
}
/* タイトルは横幅いっぱい、各セクション（順位表・自分の結果・MVP・グラフ）は横に並べて横長に。 */
body.is-phone-device #match-personal-result-modal .match-personal-result-title {
  flex-basis: 100%;
}
body.is-phone-device #match-personal-result-modal .match-personal-result-standings,
body.is-phone-device #match-personal-result-modal .match-personal-result-self,
body.is-phone-device #match-personal-result-modal .match-personal-result-mvp,
body.is-phone-device #match-personal-result-modal .match-personal-result-chart {
  flex: 0 1 auto;
  min-width: 11rem;
  max-width: 19rem;
}
body.is-phone-device .card-received-modal {
  transform: translate(-50%, -50%) scale(calc(0.85 * var(--center-modal-scale-phone, 1)));
}
body.is-phone-device .hand-effect-option-picker {
  transform: translate(-50%, -50%) scale(calc(0.9 * var(--center-modal-scale-phone, 1)));
}
/* ユーザー報告（続き92）「『パーティ』『選べる罠』の選択モーダルで『盤面を見る』を
   押したとき、選択モーダルが画面左端で見切れてしまう」。原因はCSS詳細度の逆転:
   `.hand-effect-option-picker.is-peeking`（クラス2つ、詳細度0,2,0）が本来は
   `left: 1.5rem; transform: translate(0, -50%) scale(1);`で左端に「くっつける」
   はずが、すぐ上のスマホ用ルール`body.is-phone-device .hand-effect-option-picker`
   （要素1つ+クラス2つ、詳細度0,2,1）の方が詳細度で勝ってしまい、スマホでは
   常にtranslate(-50%, -50%)（中心基準）が適用され続けていた。left:1.5remを
   「中心」ではなく「左端」の基準点にしてしまうため、モーダル幅の半分が画面外
   （左）へはみ出していた。translate(0, -50%)に戻しつつ、スマホの拡大率だけは
   維持する専用ルールをさらに詳細度の高いセレクタで追加する。
   さらに、translate(0, -50%)だけに直しても、transform-originが既定の中心（50% 50%）
   のままだと、scale()は要素自身の中心を基準に左右均等へ膨らむため、画面左端近くに
   置かれたこの要素は依然として左半分が画面外にはみ出してしまう（実機確認で発覚）。
   transform-originを左端（0% 50%）にし、拡大が常に右方向にだけ伸びるようにする。 */
body.is-phone-device .hand-effect-option-picker.is-peeking {
  transform-origin: 0% 50%;
  transform: translate(0, -50%) scale(calc(0.9 * var(--center-modal-scale-phone, 1)));
}
/* オプションのドロップダウン（#options-menu-panel）は画面中央固定ではなく右上
   アイコンから吊り下がる別方式のため、上の一括調整には含まれない——ユーザー
   要望通り、こちらだけ別のつまみ（--options-menu-scale-phone）を用意する。
   アンカー（右上の呼び出しボタン直下）を基準点に保つためtransform-originは
   右上にする。 */
/* 【2026-09-06・ユーザー要望】スマホで基本設定の各項目が小さすぎて押しづらい。
   このアプリは 1600x900 の「ステージ」を画面に合わせて縮めて描くので、ここの 1rem は
   スマホでは実画面 7px 前後にしかならない。実測（iPhone 横向き 844x390・倍率0.433）では
   チェックの行の高さ 11.7px・チェックボックス本体 5.6px・選択ボタン 11.1px しかなく、
   目安（Apple 44pt / Google 48dp）の 1/4〜1/8 だった。
   **パネル全体を transform で拡大するのは不可**（実測: 画面外へ 826px はみ出し、全行が
   画面外・スクロールも届かない）。行そのものを大きくして、増えた分は縦スクロールに任せる。
   狙いは主要な行が実画面で 40px 前後、補助的なもの（「戻す」等）が 32px 前後。 */
body.is-phone-device .options-menu-details > summary,
body.is-phone-device .options-menu-checkbox-row,
body.is-phone-device .options-menu-item,
body.is-phone-device .options-menu-detail-entry {
  font-size: 1.9rem;
  line-height: 1.35;
  padding: 1.1rem 1.2rem;
  min-height: 5.2rem;
  box-sizing: border-box;
  align-items: center;
}
body.is-phone-device .options-menu-detail-entry {
  flex-direction: column;
  align-items: flex-start;
  gap: 0.3rem;
}
body.is-phone-device .options-menu-detail-entry-hint {
  font-size: 1.4rem;
}
/* チェックボックス本体も指で押せる大きさに（行全体が <label> なのでどこを押しても効くが、
   見た目が小さいと「押せる」と分からないため）。 */
body.is-phone-device .options-menu-checkbox-row > input[type="checkbox"] {
  width: 2.6rem;
  height: 2.6rem;
  margin-top: 0;
}
body.is-phone-device .options-menu-volume-row,
body.is-phone-device .options-menu-shortcut-row {
  font-size: 1.7rem;
  padding: 0.9rem 1.2rem;
}
/* スライダーのつまみと帯（実測で帯の高さ 6.9px しかなかった）。 */
body.is-phone-device .options-menu-volume-row input[type="range"] {
  height: 3.4rem;
}
body.is-phone-device .options-menu-segment-btn,
body.is-phone-device .options-menu-section-reset-btn {
  font-size: 1.6rem;
  padding: 0.7rem 1.2rem;
  min-height: 4rem;
}
body.is-phone-device .options-menu-section-title,
body.is-phone-device .options-menu-sub-label {
  font-size: 1.5rem;
}

body.is-phone-device #options-menu-panel {
  transform-origin: top right;
  transform: scale(var(--options-menu-scale-phone, 1));
}

/* ── マイデッキ編成ページ（my-deck-builder.js）MTGA風 ────────────────────────
   上段=所持カードのコレクション（アート＋所持◇ピップ、クリックで追加）、下段=現在のデッキ
   （色順の ×N ミニカード、クリックで削除）、上部バーに合計・有効判定・完了。ダーク基調＋light-theme対応。 */
#my-deck-page {
  position: fixed;
  inset: 0;
  z-index: 1500;
  display: flex;
  flex-direction: column;
  padding: 0;
  background: radial-gradient(ellipse at 50% -10%, #22303f 0%, #0f172a 55%, #05080e 100%);
  font-family: sans-serif;
  color: #e2e8f0;
  overflow: hidden;
  box-sizing: border-box;
}
/* 上部バー */
#mdb-header {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.7rem 1.1rem;
  background: linear-gradient(180deg, rgba(13, 20, 30, 0.96), rgba(13, 20, 30, 0.75));
  border-bottom: 1px solid rgba(176, 141, 63, 0.35);
}
#my-deck-page-back {
  flex: 0 0 auto;
  background: none;
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.4rem;
  color: #e2e8f0;
  font-family: sans-serif;
  font-size: 0.8rem;
  padding: 0.4rem 0.8rem;
  cursor: pointer;
}
#my-deck-page-back:hover {
  border-color: rgba(212, 175, 90, 0.8);
  background: rgba(212, 175, 90, 0.14);
}
#mdb-title {
  flex: 0 0 auto;
  font-size: 1.1rem;
  font-weight: bold;
  letter-spacing: 0.02em;
}
#mdb-summary {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 1.1rem;
  justify-content: flex-end;
}
.mdb-summary-counts {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  white-space: nowrap;
}
.mdb-summary-total {
  font-size: 0.8rem;
  color: #cbd5e1;
}
.mdb-summary-total b {
  font-size: 1.35rem;
  color: #f2c94c;
}
.mdb-summary-total small {
  font-size: 0.72rem;
  margin-left: 0.1rem;
}
.mdb-summary-sub {
  font-size: 0.75rem;
  color: #94a3b8;
  white-space: nowrap;
}
.mdb-summary-status {
  min-width: 0;
  overflow: hidden;
}
.mdb-summary-status.is-ok {
  font-size: 0.82rem;
  font-weight: bold;
  color: #4ade80;
  white-space: nowrap;
}
.mdb-summary-errors {
  margin: 0;
  padding-left: 1.05rem;
  list-style: disc;
  max-height: 2.6rem;
  overflow-y: auto;
}
.mdb-summary-errors li {
  font-size: 0.7rem;
  color: #fca5a5;
  line-height: 1.35;
}
#mdb-save {
  flex: 0 0 auto;
  padding: 0.6rem 1.5rem;
  border: none;
  border-radius: 0.5rem;
  background: linear-gradient(180deg, #f0a020, #d97706);
  color: #1c1006;
  font-size: 0.92rem;
  font-weight: bold;
  cursor: pointer;
  box-shadow: 0 0.2rem 0.7rem rgba(217, 119, 6, 0.4);
}
#mdb-save:hover:not(:disabled) {
  filter: brightness(1.08);
}
#mdb-save:disabled {
  background: rgba(100, 116, 139, 0.5);
  color: rgba(226, 232, 240, 0.5);
  cursor: default;
  box-shadow: none;
}
#mdb-hint {
  flex: 0 0 auto;
  padding: 0.35rem 1.1rem;
  font-size: 0.72rem;
  color: #8aa0b6;
  background: rgba(6, 10, 18, 0.55);
  border-bottom: 1px solid rgba(148, 163, 184, 0.12);
}
/* 上段: コレクション */
#mdb-collection {
  flex: 1 1 auto;
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
  gap: 0.7rem;
  align-content: start;
  padding: 0.9rem 1.1rem;
}
.mdb-col-card {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.28rem;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  font-family: sans-serif;
  text-align: center;
}
.mdb-col-card-art {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: 0.45rem;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.4);
  border: 2px solid rgba(148, 163, 184, 0.22);
  transition: border-color 0.14s, box-shadow 0.14s, transform 0.1s;
}
.mdb-col-card:hover .mdb-col-card-art {
  border-color: rgba(212, 175, 90, 0.75);
  transform: translateY(-2px);
}
.mdb-col-card.is-in-deck .mdb-col-card-art {
  border-color: rgba(242, 201, 76, 0.9);
  box-shadow: 0 0 0 1px rgba(242, 201, 76, 0.4), 0 0 14px rgba(242, 201, 76, 0.28);
}
.mdb-col-card.is-maxed .mdb-col-card-art {
  filter: saturate(0.85);
}
.mdb-col-card-art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.mdb-col-card-sp {
  position: absolute;
  top: 0.25rem;
  left: 0.25rem;
  background: rgba(250, 204, 21, 0.94);
  color: #3a2c00;
  font-size: 0.58rem;
  font-weight: bold;
  padding: 0.05rem 0.3rem;
  border-radius: 0.3rem;
}
.mdb-col-card-count {
  position: absolute;
  top: 0.25rem;
  right: 0.25rem;
  min-width: 1.35rem;
  background: rgba(242, 201, 76, 0.96);
  color: #241a00;
  font-size: 0.8rem;
  font-weight: bold;
  padding: 0.05rem 0.35rem;
  border-radius: 0.4rem;
  opacity: 0;
  transform: scale(0.7);
  transition: opacity 0.12s, transform 0.12s;
}
.mdb-col-card-count.is-visible {
  opacity: 1;
  transform: scale(1);
}
.mdb-col-card-pips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.18rem;
  min-height: 0.5rem;
}
.mdb-col-card-pip {
  width: 0.42rem;
  height: 0.42rem;
  transform: rotate(45deg);
  background: rgba(148, 163, 184, 0.3);
  border: 1px solid rgba(148, 163, 184, 0.45);
  border-radius: 0.06rem;
}
.mdb-col-card-pip.is-lit {
  background: #f2c94c;
  border-color: #f2c94c;
  box-shadow: 0 0 4px rgba(242, 201, 76, 0.7);
}
.mdb-col-card-name {
  font-size: 0.68rem;
  font-weight: bold;
  line-height: 1.2;
  color: #cbd5e1;
}
/* 下段: 現在のデッキ */
#mdb-deck-panel {
  flex: 0 0 auto;
  height: 30vh;
  min-height: 9rem;
  display: flex;
  flex-direction: column;
  border-top: 1px solid rgba(176, 141, 63, 0.4);
  background: rgba(6, 10, 18, 0.9);
}
#mdb-deck-panel-title {
  flex: 0 0 auto;
  padding: 0.4rem 1.1rem 0.2rem;
  font-size: 0.78rem;
  font-weight: bold;
  color: #d4af5a;
  letter-spacing: 0.04em;
}
#mdb-deck-list {
  flex: 1 1 auto;
  overflow-y: auto;
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 0.55rem;
  padding: 0.4rem 1.1rem 0.9rem;
}
.mdb-deck-empty {
  color: #64748b;
  font-size: 0.82rem;
  padding: 1rem 0.2rem;
}
.mdb-deck-entry {
  width: 4.4rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  font-family: sans-serif;
}
.mdb-deck-entry-art {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: 0.35rem;
  overflow: hidden;
  border: 1px solid rgba(148, 163, 184, 0.3);
  background: rgba(0, 0, 0, 0.4);
}
.mdb-deck-entry.is-special .mdb-deck-entry-art {
  border-color: rgba(250, 204, 21, 0.7);
}
.mdb-deck-entry:hover .mdb-deck-entry-art {
  border-color: rgba(248, 113, 113, 0.85);
}
.mdb-deck-entry-art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.mdb-deck-entry-count {
  position: absolute;
  right: 0.15rem;
  bottom: 0.15rem;
  background: rgba(6, 10, 18, 0.9);
  border: 1px solid rgba(242, 201, 76, 0.7);
  color: #f2c94c;
  font-size: 0.72rem;
  font-weight: bold;
  padding: 0.02rem 0.28rem;
  border-radius: 0.3rem;
}
/* 立体ケースの表紙（メインカード）を選ぶ★（デッキ内カードの左上角）。 */
.mdb-deck-entry-main {
  position: absolute;
  left: 0.1rem;
  top: 0.1rem;
  width: 1.15rem;
  height: 1.15rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 0.3rem;
  background: rgba(6, 10, 18, 0.75);
  color: rgba(226, 232, 240, 0.7);
  font-size: 0.82rem;
  line-height: 1;
  cursor: pointer;
}
.mdb-deck-entry-main:hover {
  color: #fde68a;
}
.mdb-deck-entry-main.is-main {
  color: #f2c94c;
  text-shadow: 0 0 6px rgba(242, 201, 76, 0.8);
  background: rgba(6, 10, 18, 0.92);
}
.mdb-deck-entry-name {
  font-size: 0.58rem;
  line-height: 1.15;
  color: #94a3b8;
  text-align: center;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#my-deck-page-toast {
  position: fixed;
  left: 50%;
  bottom: 32vh;
  transform: translateX(-50%) translateY(0.5rem);
  background: rgba(15, 23, 42, 0.96);
  border: 1px solid rgba(242, 201, 76, 0.55);
  color: #e2e8f0;
  font-size: 0.8rem;
  padding: 0.5rem 1rem;
  border-radius: 0.5rem;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s, transform 0.2s;
  z-index: 1550;
}
#my-deck-page-toast.is-visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
/* light-theme（body.theme-light、ivory/gold基調）対応。 */
body.theme-light #my-deck-page {
  background: url("../assets/backgrounds/page-bg-light.webp") center / cover no-repeat, #f0e8d6;
  color: #3a3326;
}
body.theme-light #mdb-header {
  background: linear-gradient(180deg, rgba(240, 232, 214, 0.97), rgba(240, 232, 214, 0.8));
  border-bottom-color: rgba(176, 141, 63, 0.5);
}
body.theme-light #mdb-title {
  color: #3a3326;
}
body.theme-light #my-deck-page-back {
  border-color: rgba(176, 141, 63, 0.5);
  color: #6b5c3c;
}
body.theme-light #mdb-hint {
  color: #8a7a55;
  background: rgba(255, 251, 240, 0.55);
}
body.theme-light .mdb-summary-total {
  color: #6b5c3c;
}
body.theme-light .mdb-summary-total b {
  color: #b08d3f;
}
body.theme-light .mdb-col-card-name {
  color: #3a3326;
}
body.theme-light .mdb-col-card-art {
  border-color: rgba(176, 141, 63, 0.3);
}
body.theme-light #mdb-deck-panel {
  background: rgba(240, 232, 214, 0.94);
  border-top-color: rgba(176, 141, 63, 0.5);
}
body.theme-light #mdb-deck-panel-title {
  color: #9a7a2f;
}
body.theme-light .mdb-deck-entry-name {
  color: #7a6a48;
}
body.theme-light #mdb-save {
  background: linear-gradient(180deg, #d4af5a, #b08d3f);
  color: #2a2010;
}

/* ── 不具合報告のお願い（オンライン開始時の案内、bug-report-intro.js）───────────────
   画面全体を暗転し、不具合報告アイコンの位置に光るリング＋アイコン複製を重ねてフォーカス。 */
#bug-report-intro {
  position: fixed;
  inset: 0;
  z-index: 10060;
  background: rgba(0, 0, 0, 0.72);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: sans-serif;
}
/* 実アイコン(#self-status-bug-report)そのものを光らせてフォーカスする（#62/#63: 座標計算での
   複製リングは実機で位置がズレたため廃止。実要素を光らせるのでズレない）。ID2つ＋classで
   ベースの見た目に勝つ。position:relative + 高いz-indexで、前面へ上げた#option-area(10062)の
   中でさらに手前に出す。 */
#self-status-bug-report.bug-report-intro-focus {
  /* body直下＋position:fixed＋z-index:100055（上の#self-status-bug-report）で既に暗転(10060)より
     前面。フォーカス時は見た目の強調（枠・光）だけを足す。 */
  z-index: 100056;
  border-radius: 0.5rem;
  outline: 2px solid rgba(56, 189, 248, 0.95);
  outline-offset: 3px;
  box-shadow: 0 0 0 4px rgba(56, 189, 248, 0.35), 0 0 22px 6px rgba(56, 189, 248, 0.55);
  animation: bug-report-intro-pulse 1.3s ease-in-out infinite;
}
@keyframes bug-report-intro-pulse {
  0%, 100% { box-shadow: 0 0 0 4px rgba(56, 189, 248, 0.3), 0 0 18px 4px rgba(56, 189, 248, 0.45); }
  50% { box-shadow: 0 0 0 6px rgba(56, 189, 248, 0.5), 0 0 30px 10px rgba(56, 189, 248, 0.7); }
}
#bug-report-intro-modal {
  position: relative;
  width: min(30rem, 90vw);
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(56, 189, 248, 0.4);
  border-radius: 0.7rem;
  padding: 1.4rem 1.3rem 1.2rem;
  color: #e2e8f0;
  box-shadow: 0 20px 55px rgba(0, 0, 0, 0.65);
  margin-top: 3rem;
}
.bug-report-intro-title {
  font-size: 1.15rem;
  font-weight: bold;
  margin-bottom: 0.7rem;
}
.bug-report-intro-desc {
  font-size: 0.9rem;
  line-height: 1.65;
  color: #cbd5e1;
  margin-bottom: 1.2rem;
}
.bug-report-intro-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.7rem;
  flex-wrap: wrap;
}
.bug-report-intro-hide {
  background: none;
  border: 1px solid rgba(148, 163, 184, 0.5);
  border-radius: 0.45rem;
  color: #94a3b8;
  font-size: 0.82rem;
  padding: 0.5rem 0.9rem;
  cursor: pointer;
}
.bug-report-intro-hide:hover {
  border-color: rgba(148, 163, 184, 0.85);
  color: #cbd5e1;
}
.bug-report-intro-ok {
  background: linear-gradient(180deg, #38bdf8, #0ea5e9);
  border: none;
  border-radius: 0.45rem;
  color: #04121f;
  font-size: 0.9rem;
  font-weight: bold;
  padding: 0.5rem 1.5rem;
  cursor: pointer;
}
.bug-report-intro-ok:hover {
  filter: brightness(1.08);
}

/* マイデッキ戦フェーズ5: マイデッキ札ホバー時の所有者アバター＋名前ツールチップ。 */
#my-deck-owner-tooltip {
  position: fixed;
  z-index: 10080;
  display: none;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  padding: 0.5rem 0.7rem;
  background: rgba(15, 23, 32, 0.96);
  border: 1px solid rgba(56, 189, 248, 0.55);
  border-radius: 0.55rem;
  box-shadow: 0 0.4rem 1rem rgba(0, 0, 0, 0.55);
  pointer-events: none;
  font-family: sans-serif;
}
#my-deck-owner-tooltip.is-visible {
  display: flex;
}
.my-deck-owner-tooltip-avatar {
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 50%;
  overflow: hidden;
  background: rgba(148, 163, 184, 0.25);
  display: flex;
  align-items: center;
  justify-content: center;
}
.my-deck-owner-tooltip-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.my-deck-owner-tooltip-name {
  font-size: 0.8rem;
  font-weight: bold;
  color: #e2e8f0;
  max-width: 8rem;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.my-deck-owner-tooltip-label {
  font-size: 0.62rem;
  font-weight: bold;
  color: #38bdf8;
  letter-spacing: 0.04em;
}

/* ── マイデッキ名入力（エディタのヘッダー、my-deck-builder.js）─────────────── */
#mdb-name-input {
  flex: 0 0 auto;
  width: 12rem;
  max-width: 30vw;
  background: rgba(6, 10, 18, 0.6);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.4rem;
  color: #f1f5f9;
  font-family: sans-serif;
  font-size: 0.95rem;
  font-weight: bold;
  padding: 0.4rem 0.7rem;
}
#mdb-name-input:focus {
  outline: none;
  border-color: rgba(242, 201, 76, 0.8);
  background: rgba(6, 10, 18, 0.85);
}
body.theme-light #mdb-name-input {
  background: rgba(255, 251, 240, 0.9);
  border-color: rgba(176, 141, 63, 0.5);
  color: #3a3326;
}

/* ── マイデッキ一覧（複数デッキ、my-deck-list.js）MTGAのデッキ箱一覧風 ──────── */
#my-deck-list-page {
  position: fixed;
  inset: 0;
  z-index: 1500;
  display: flex;
  flex-direction: column;
  background: radial-gradient(ellipse at 50% -10%, #22303f 0%, #0f172a 55%, #05080e 100%);
  font-family: sans-serif;
  color: #e2e8f0;
  overflow: hidden;
  box-sizing: border-box;
}
#mdl-header {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.8rem 1.2rem;
  border-bottom: 1px solid rgba(176, 141, 63, 0.35);
  background: linear-gradient(180deg, rgba(13, 20, 30, 0.96), rgba(13, 20, 30, 0.7));
}
#mdl-back {
  background: none;
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.4rem;
  color: #e2e8f0;
  font-family: sans-serif;
  font-size: 0.8rem;
  padding: 0.4rem 0.8rem;
  cursor: pointer;
}
#mdl-back:hover {
  border-color: rgba(212, 175, 90, 0.8);
  background: rgba(212, 175, 90, 0.14);
}
#mdl-title {
  font-size: 1.15rem;
  font-weight: bold;
  letter-spacing: 0.02em;
}
#mdl-grid {
  flex: 1 1 auto;
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(13.5rem, 1fr));
  gap: 1rem;
  align-content: start;
  padding: 1.2rem;
}
.mdl-deck {
  position: relative;
  border-radius: 0.6rem;
  overflow: hidden;
  /* 箱の背景（シーン）をタイル下端まで伸ばす（ユーザー要望2026-08-12: 名前/枚数の白背景が
     ダサい→箱背景を下まで＋中央ぞろえ）。--mdl-accent はJSで .mdl-deck に付与。 */
  background: radial-gradient(135% 125% at 32% 6%, color-mix(in srgb, var(--mdl-accent, #334155) 26%, #0b1220), #05080f);
  border: 1px solid rgba(148, 163, 184, 0.25);
  cursor: pointer;
  transition: border-color 0.14s, transform 0.1s, box-shadow 0.14s;
  display: flex;
  flex-direction: column;
}
.mdl-deck:hover {
  border-color: rgba(212, 175, 90, 0.8);
  transform: translateY(-2px);
  box-shadow: 0 0.4rem 1rem rgba(0, 0, 0, 0.5);
}
/* MTGAデッキボックス風の立体カードケース（ユーザー要望2026-08-12）。正方形タイルの中に、
   斜めに立てた“箱”を置く。箱=前面(メインカード)＋右側のカードの束の厚み＋上から覗くカード＋
   少し開いた蓋。表面(.mdl-deck-front)に選んだメインカードを貼る。 */
.mdl-deck-art {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1; /* 正方形（ユーザー要望） */
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.1rem 0.7rem 0.4rem;
  perspective: 1000px;
  background: transparent; /* タイル(.mdl-deck)のシーン背景を下まで通す */
}
.mdl-deck-case {
  position: relative;
  width: 66%;
  aspect-ratio: 1 / 1; /* 前面を正方形に（ユーザー要望2026-08-12） */
  transform-style: preserve-3d;
  transform: rotateX(9deg) rotateY(-18deg);
  /* filter はここに置かない: preserve-3d要素に filter を付けると仕様上flatに潰れ、
     箱が平面になる（＝「箱感が薄い」原因）。落ち影は下の .mdl-deck-front の box-shadow で出す。 */
}
/* 箱の前面（＝顔。メインカードを貼る）。右側にカードの束の厚みを重ねる。 */
.mdl-deck-front {
  position: absolute;
  inset: 0;
  border-radius: 0.35rem 0.35rem 0.45rem 0.45rem;
  overflow: hidden;
  background: #0b1220;
  border: 1px solid color-mix(in srgb, var(--mdl-accent, #334155) 62%, #ffffff 12%);
  transform: translateZ(16px);
  box-shadow: 2px 0 0 #1b2740, 4px 1px 0 #151f33, 6px 1px 0 #101827, 8px 2px 0 #0b111d, 10px 2px 0 #080d16,
    -11px 16px 16px rgba(0, 0, 0, 0.5); /* 落ち影（filterを使わないのでbox-shadowで） */
}
.mdl-deck-front img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
}
.mdl-deck-front::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--mdl-accent, #334155) 55%, transparent),
    inset 0 -1.5rem 1.3rem -1rem rgba(5, 9, 16, 0.9);
  background: linear-gradient(118deg, rgba(255, 255, 255, 0.16), transparent 42%);
}
/* 上から覗くカードの束（前面の後ろ・上端から少しだけ出る）。方眼紙に見えないよう、細かい
   縦縞ではなく“数枚重なったカードの上端”＝横方向の層で表現する（ユーザー要望2026-08-12）。 */
.mdl-deck-peek {
  position: absolute;
  left: 9%;
  right: 9%;
  top: -6%;
  height: 13%;
  border-radius: 0.18rem 0.18rem 0 0;
  transform: translateZ(9px);
  background: linear-gradient(
    180deg,
    #f4f7fb 0 30%,
    #d7deea 30% 34%,
    #eef2f8 34% 64%,
    #d1d9e6 64% 68%,
    #e7edf5 68% 100%
  );
  border: 1px solid rgba(120, 130, 145, 0.75);
  border-bottom: none;
  box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.35), inset 0 2px 2px rgba(255, 255, 255, 0.6);
}
/* 少し開いた蓋（背面上端にヒンジ、後ろへパカッと開く）。 */
.mdl-deck-lid {
  position: absolute;
  left: -2%;
  right: -2%;
  top: 0;
  height: 25%;
  transform-origin: top center;
  transform: translateZ(16px) rotateX(-58deg);
  border-radius: 0.35rem 0.35rem 0.15rem 0.15rem;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--mdl-accent, #334155) 68%, #000 22%),
    color-mix(in srgb, var(--mdl-accent, #334155) 42%, #000 42%)
  );
  border: 1px solid color-mix(in srgb, var(--mdl-accent, #334155) 60%, #ffffff 10%);
  box-shadow: inset 0 -0.5rem 0.6rem rgba(0, 0, 0, 0.5);
}
.mdl-deck:hover .mdl-deck-case {
  transform: rotateX(6deg) rotateY(-11deg);
}
.mdl-deck-name {
  padding: 0.35rem 0.7rem 0.15rem;
  font-size: 0.92rem;
  font-weight: bold;
  color: #f1f5f9;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: center; /* 中央ぞろえ（ユーザー要望2026-08-12） */
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}
.mdl-deck-meta {
  display: flex;
  align-items: center;
  justify-content: center; /* 中央ぞろえ */
  gap: 0.45rem;
  padding: 0 0.7rem 0.55rem;
  font-size: 0.78rem;
  color: #cbd5e1;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}
.mdl-deck-chip {
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  display: inline-block;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.35);
}
.mdl-deck-count {
  font-weight: bold;
  color: #cbd5e1;
}
.mdl-deck-valid {
  margin-left: auto;
  font-weight: bold;
}
.mdl-deck-valid.is-ok {
  color: #4ade80;
}
.mdl-deck-valid.is-ng {
  color: #fbbf24;
}
.mdl-deck-actions {
  position: absolute;
  top: 0.35rem;
  right: 0.35rem;
  display: flex;
  gap: 0.3rem;
  opacity: 0;
  transition: opacity 0.14s;
}
.mdl-deck:hover .mdl-deck-actions {
  opacity: 1;
}
.mdl-deck-action {
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 0.35rem;
  border: 1px solid rgba(148, 163, 184, 0.5);
  background: rgba(6, 10, 18, 0.85);
  color: #e2e8f0;
  font-size: 0.85rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.mdl-deck-action:hover {
  border-color: rgba(212, 175, 90, 0.85);
  background: rgba(212, 175, 90, 0.2);
}
.mdl-deck-action-del:hover {
  border-color: rgba(248, 113, 113, 0.85);
  background: rgba(248, 113, 113, 0.2);
}
/* 新規作成の箱 */
.mdl-deck-new {
  align-items: center;
  justify-content: center;
  min-height: 13rem;
  border: 2px dashed rgba(148, 163, 184, 0.4);
  background: rgba(15, 23, 42, 0.4);
  color: #94a3b8;
  font-family: sans-serif;
}
.mdl-deck-new:hover {
  border-color: rgba(242, 201, 76, 0.8);
  color: #f2c94c;
}
.mdl-deck-new-plus {
  font-size: 2.6rem;
  font-weight: bold;
  line-height: 1;
}
.mdl-deck-new-label {
  margin-top: 0.5rem;
  font-size: 0.85rem;
  font-weight: bold;
}
body.theme-light #my-deck-list-page {
  background: url("../assets/backgrounds/page-bg-light.webp") center / cover no-repeat, #f0e8d6;
  color: #3a3326;
}
body.theme-light #mdl-header {
  background: linear-gradient(180deg, rgba(240, 232, 214, 0.97), rgba(240, 232, 214, 0.8));
  border-bottom-color: rgba(176, 141, 63, 0.5);
}
body.theme-light #mdl-title,
body.theme-light .mdl-deck-name {
  color: #3a3326;
}
body.theme-light #mdl-back {
  border-color: rgba(176, 141, 63, 0.5);
  color: #6b5c3c;
}
/* ライトモード（ユーザー報告「マイデッキ一覧がライトモードなのにダーク」）: ページ背景・
   タイルのシーン背景・メタ文字を明色にする。箱の前面(メインカード)自体はそのまま。
   タイル(.mdl-deck)がシーン背景を持ち、.mdl-deck-art は透明で下端まで通す（中央ぞろえ）。 */
body.theme-light .mdl-deck {
  background: radial-gradient(135% 125% at 32% 6%, color-mix(in srgb, var(--mdl-accent, #334155) 22%, #efe6d2), #d8c9a9);
  border-color: rgba(176, 141, 63, 0.35);
}
body.theme-light #my-deck-list-page {
  background: radial-gradient(ellipse at 50% -10%, #fbf6ea 0%, #efe6d2 55%, #e6d9bd 100%);
  color: #3a3326;
}
body.theme-light .mdl-deck-art {
  background: transparent;
}
body.theme-light .mdl-deck-name {
  text-shadow: none;
}
body.theme-light .mdl-deck-meta,
body.theme-light .mdl-deck-count {
  color: #6b5c3c;
  text-shadow: none;
}

/* ── マイデッキ編集: ホバー拡大プレビュー＋右クリックのカード補足メニュー ─────────── */
#mdb-preview {
  position: fixed;
  z-index: 10090;
  width: var(--card-preview-size, 17rem);
  height: var(--card-preview-size, 17rem);
  border-radius: 0.6rem;
  overflow: hidden;
  box-shadow: 0 0.6rem 1.6rem rgba(0, 0, 0, 0.7);
  border: 1px solid rgba(0, 0, 0, 0.5);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.1s;
  display: none;
}
#mdb-preview.is-visible {
  display: block;
  opacity: 1;
}
#mdb-preview img,
#mdb-preview .mdb-preview-face {
  position: relative;
  width: 100%;
  height: 100%;
  object-fit: cover;
  overflow: hidden;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  display: block;
}
#mdb-note-menu {
  position: fixed;
  z-index: 10095;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.5);
  border-radius: 0.45rem;
  box-shadow: 0 0.4rem 1rem rgba(0, 0, 0, 0.6);
  padding: 0.25rem;
}
.mdb-note-menu-btn {
  display: block;
  width: 100%;
  white-space: nowrap;
  background: none;
  border: none;
  color: #e2e8f0;
  font-family: sans-serif;
  font-size: 0.85rem;
  padding: 0.45rem 0.8rem;
  border-radius: 0.3rem;
  cursor: pointer;
  text-align: left;
}
.mdb-note-menu-btn:hover {
  background: rgba(56, 189, 248, 0.2);
}

/* ── マイページ右下：メインデッキのビジュアル＋「マイデッキ編集」ボタン（profile-page.js）──
   ユーザー要望2026-08-16。ビジュアル・ボタンどちらを押してもデッキ一覧（編集画面）へ。
   位置・サイズは管理者モードのCSS変数(--profile-maindeck-*, admin.js)で調整。fallbackはadmin.jsの
   defaultと一致させること。 */
#profile-maindeck {
  position: absolute;
  right: var(--profile-maindeck-right, 2.5rem);
  bottom: var(--profile-maindeck-bottom, 0rem);
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.45rem;
  width: var(--profile-maindeck-width, 10.5rem);
  transform: scale(var(--profile-maindeck-scale, 2.25));
  transform-origin: bottom right;
}
/* 「メインデッキ」ラベル・デッキ名・「マイデッキ編集」ボタンは、それぞれ管理者モードで
   位置(x/y)・サイズ(scale)を個別調整できる（ユーザー要望2026-08-16）。transformは兄弟の
   フロー配置に影響しないので、1つ動かしても他は動かない。fallbackはadmin.jsのdefaultと一致。 */
.profile-maindeck-badge {
  font-size: 0.78rem;
  font-weight: 700;
  color: #ffe6a8;
  letter-spacing: 0.05em;
  transform: translate(var(--profile-maindeck-badge-x, 0rem), var(--profile-maindeck-badge-y, 2.1rem)) scale(var(--profile-maindeck-badge-scale, 0.7));
}
body.theme-light #profile-maindeck .profile-maindeck-badge { color: #92660a; }
/* カバー = 3D立体ケース(.mdl-deck-art)＋デッキ名を包むだけのフレームレスなボタン
   （ユーザー要望2026-08-16「ビジュアルを3Dの箱に」）。枠・背景は付けず、箱の落ち影・
   perspective をクリップしないよう overflow:visible のまま。 */
.profile-maindeck-cover {
  width: 100%;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}
/* カバー内の3D箱のホバー回転（.mdl-deck の外なので専用ルールで再現）。 */
.profile-maindeck-cover:hover .mdl-deck-case { transform: rotateX(6deg) rotateY(-11deg); }
.profile-maindeck-name {
  margin-top: 0.3rem;
  font-size: 0.85rem;
  font-weight: 700;
  color: #f1f5f9;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
  transform: translate(var(--profile-maindeck-name-x, 0rem), var(--profile-maindeck-name-y, -1.9rem)) scale(var(--profile-maindeck-name-scale, 1));
}
body.theme-light #profile-maindeck .profile-maindeck-name { color: #1e293b; }
.profile-maindeck-cover.is-empty {
  height: 6rem;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #94a3b8;
  font-size: 0.9rem;
  border: 2px dashed rgba(148, 163, 184, 0.4);
  border-radius: 0.6rem;
}
/* 「マイデッキ編集」= テキストリンク調（ユーザー要望2026-08-16「ボタン枠は不要」）。枠・背景なし。 */
#profile-maindeck-edit {
  width: 100%;
  padding: 0.2rem;
  border: none;
  background: none;
  color: #f2c94c;
  font-family: sans-serif;
  font-size: 0.9rem;
  font-weight: bold;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  text-decoration: underline;
  text-underline-offset: 3px;
  transform: translate(var(--profile-maindeck-edit-x, 0.3rem), var(--profile-maindeck-edit-y, -1.7rem)) scale(var(--profile-maindeck-edit-scale, 0.8));
}
#profile-maindeck-edit:hover { filter: brightness(1.12); }
body.theme-light #profile-maindeck-edit { color: #b45309; }
#profile-maindeck-edit .profile-maindeck-edit-icon { font-size: 1.05em; line-height: 1; text-decoration: none; display: inline-block; }

/* デッキ一覧: メインデッキ（getSelectedDeckId）のバッジ＋★トグル（my-deck-list.js）。 */
.mdl-deck.is-main { box-shadow: 0 0 0 2px #f2c94c, 0 0.4rem 1rem rgba(0, 0, 0, 0.35); }
.mdl-deck-main-badge {
  position: absolute;
  top: 0.4rem;
  left: 0.4rem;
  z-index: 3;
  background: linear-gradient(150deg, #f2c94c, #d97706);
  color: #241300;
  font-size: 0.7rem;
  font-weight: 800;
  padding: 0.15rem 0.5rem;
  border-radius: 0.4rem;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
}
.mdl-deck-action-main.is-main { color: #f2c94c; }

/* ── マイデッキ編集: デッキ設定バー（ファースト色・駒スキン・ペット・裏面）─────────── */
#mdb-settings {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.7rem 1.2rem;
  padding: 0.5rem 1.1rem;
  background: rgba(6, 10, 18, 0.5);
  border-bottom: 1px solid rgba(148, 163, 184, 0.14);
}
.mdb-setting {
  display: flex;
  align-items: center;
  gap: 0.45rem;
}
.mdb-setting-label {
  font-size: 0.74rem;
  font-weight: bold;
  color: #94a3b8;
  white-space: nowrap;
}
.mdb-color-chips {
  display: flex;
  align-items: center;
  gap: 0.3rem;
}
.mdb-color-chip {
  width: 1.4rem;
  height: 1.4rem;
  border-radius: 50%;
  border: 2px solid rgba(148, 163, 184, 0.4);
  cursor: pointer;
  padding: 0;
}
.mdb-color-chip.is-selected {
  border-color: #f2c94c;
  box-shadow: 0 0 0 2px rgba(242, 201, 76, 0.4);
}
.mdb-color-chip-random {
  width: auto;
  height: 1.4rem;
  border-radius: 0.7rem;
  padding: 0 0.5rem;
  background: rgba(30, 41, 59, 0.8);
  color: #cbd5e1;
  font-size: 0.68rem;
  font-weight: bold;
  font-family: sans-serif;
}
.mdb-setting-btn {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.25rem 0.6rem 0.25rem 0.3rem;
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.5rem;
  background: rgba(15, 23, 42, 0.7);
  color: #e2e8f0;
  font-family: sans-serif;
  cursor: pointer;
}
.mdb-setting-btn:hover {
  border-color: rgba(242, 201, 76, 0.8);
  background: rgba(242, 201, 76, 0.12);
}
.mdb-setting-thumb {
  width: 1.8rem;
  height: 1.8rem;
  border-radius: 0.3rem;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.3);
  display: flex;
  align-items: center;
  justify-content: center;
}
.mdb-setting-thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.mdb-setting-face {
  width: 1.8rem;
  height: 1.8rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.2rem;
}
.mdb-setting-face img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
body.theme-light #mdb-settings {
  background: rgba(255, 251, 240, 0.5);
}
body.theme-light .mdb-setting-btn {
  background: rgba(255, 251, 240, 0.9);
  border-color: rgba(176, 141, 63, 0.4);
  color: #3a3326;
}

/* ── マイデッキ戦: デッキ選択オーバーレイ（my-deck-select.js）───────────────── */
#my-deck-select {
  position: fixed;
  inset: 0;
  z-index: 10070;
  background: rgba(3, 6, 12, 0.9);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: sans-serif;
}
#mds-panel {
  width: min(56rem, 94vw);
  max-height: 88vh;
  overflow-y: auto;
  background: radial-gradient(ellipse at 50% -10%, #22303f 0%, #0f172a 70%);
  border: 1px solid rgba(176, 141, 63, 0.45);
  border-radius: 0.8rem;
  padding: 1.2rem 1.3rem 1.4rem;
  box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.7);
  color: #e2e8f0;
}
#mds-title {
  font-size: 1.25rem;
  font-weight: bold;
  text-align: center;
}
#mds-subtitle {
  font-size: 0.85rem;
  color: #94a3b8;
  text-align: center;
  margin-top: 0.3rem;
}
#mds-countdown {
  margin: 0.6rem auto 0;
  text-align: center;
  font-size: 0.95rem;
  font-weight: bold;
  color: #f2c94c;
}
#mds-body {
  margin-top: 1rem;
}
.mds-empty {
  color: #94a3b8;
  font-size: 0.9rem;
  text-align: center;
  padding: 1.5rem 0.5rem;
}
.mds-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  gap: 0.8rem;
}
.mds-deck {
  display: flex;
  flex-direction: column;
  padding: 0;
  border: 1px solid rgba(148, 163, 184, 0.3);
  border-radius: 0.55rem;
  overflow: hidden;
  background: rgba(15, 23, 42, 0.7);
  cursor: pointer;
  font-family: sans-serif;
  color: #e2e8f0;
}
.mds-deck:hover:not(:disabled) {
  border-color: rgba(242, 201, 76, 0.85);
  transform: translateY(-2px);
}
.mds-deck.is-invalid {
  opacity: 0.55;
  cursor: not-allowed;
}
.mds-deck-art {
  width: 100%;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background: linear-gradient(135deg, var(--mds-accent, #334155), #0b1220);
}
.mds-deck-art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 22%;
}
.mds-deck-name {
  padding: 0.35rem 0.5rem 0.1rem;
  font-size: 0.82rem;
  font-weight: bold;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mds-deck-meta {
  padding: 0 0.5rem 0.45rem;
  font-size: 0.72rem;
  color: #94a3b8;
}
#mds-actions {
  display: flex;
  gap: 0.7rem;
  justify-content: center;
  margin-top: 1.2rem;
  flex-wrap: wrap;
}
.mds-action {
  padding: 0.6rem 1.3rem;
  border: none;
  border-radius: 0.5rem;
  font-family: sans-serif;
  font-size: 0.92rem;
  font-weight: bold;
  cursor: pointer;
}
.mds-action-random {
  background: linear-gradient(180deg, #38bdf8, #0ea5e9);
  color: #04121f;
}
.mds-action-new {
  background: rgba(30, 41, 59, 0.9);
  border: 1px solid rgba(148, 163, 184, 0.5);
  color: #e2e8f0;
}
/* デッキ選択からホームへ戻る逃げ道（対局開始前の入口だけに出す）。おまかせ/新規作成より
   目立たない控えめな配色にして、誤って押しにくくする。 */
.mds-action-home {
  background: rgba(15, 23, 42, 0.85);
  border: 1px solid rgba(148, 163, 184, 0.35);
  color: #94a3b8;
}
.mds-action:hover {
  filter: brightness(1.08);
}

/* ── マイデッキ編集: ファーストカード選択（画像を並べる＋ホバー拡大）───────────── */
.mdb-first-picks {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  flex-wrap: wrap;
}
.mdb-first-pick {
  padding: 0;
  border: 2px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.35rem;
  background: rgba(15, 23, 42, 0.6);
  cursor: pointer;
  overflow: hidden;
}
.mdb-first-pick.is-selected {
  border-color: #f2c94c;
  box-shadow: 0 0 0 2px rgba(242, 201, 76, 0.4);
}
.mdb-first-card {
  width: 2.7rem;
  height: 2.7rem;
  line-height: 0;
}
.mdb-first-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.mdb-first-pick-random {
  height: 2.7rem;
  padding: 0 0.5rem;
  color: #cbd5e1;
  font-size: 0.7rem;
  font-weight: bold;
  font-family: sans-serif;
}

/* ── マイデッキ一覧: 各デッキの選択スキン（駒・ペット・裏面）を画像で表示 ───────── */
.mdl-deck-cosmetics {
  display: flex;
  align-items: center;
  justify-content: center; /* 中央ぞろえ（ユーザー要望2026-08-12） */
  gap: 0.35rem;
  padding: 0 0.7rem 0.65rem;
}
.mdl-cos {
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 0.3rem;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.3);
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(148, 163, 184, 0.25);
}
.mdl-cos img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.mdl-cos.mdl-cos-pet {
  font-size: 1rem;
}

/* アプリ内スモークテスト（ユーザー要望2026-08-14）。タイトル画面(opening-screen-active)で
   管理者ログイン中(is-admin-user、main.jsがonAuthChangeで付け外し)の時だけ右下に出す起動ボタン。 */
/* 位置(right/bottom)・大きさ(font-size)は管理者モードの「📐 位置合わせ」で調整可能
   （admin.jsがvarを書き込む。既定値は下のfallbackと一致させること）。padding/border-radiusは
   emにして、font-sizeを変えるとボタン全体が連動して拡縮するようにしてある。 */
#smoke-test-launch-btn {
  display: none;
  position: fixed;
  right: var(--smoke-test-btn-right, 1rem);
  bottom: var(--smoke-test-btn-bottom, 7.3rem);
  z-index: 50200;
  padding: 0.59em 1.06em;
  font-family: sans-serif;
  font-size: var(--smoke-test-btn-font-size, 0.85rem);
  font-weight: 700;
  color: #0e1218;
  background: linear-gradient(160deg, #e3b64a, #c99a2e);
  border: 1px solid rgba(0, 0, 0, 0.3);
  border-radius: 0.7em;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
  cursor: pointer;
}
body.opening-screen-active.is-admin-user #smoke-test-launch-btn {
  display: block;
}
/* 管理者モードで位置・サイズのスライダーを触っている間は、タイトル画面以外でも一時的に
   ボタンを見せて調整結果を確認できるようにする（admin.jsのpreviewSmokeTestBtnが付け外し）。 */
#smoke-test-launch-btn.force-preview {
  display: block;
}
#smoke-test-launch-btn:hover {
  filter: brightness(1.08);
}
/* ユーザー要望2026-08-14: 全画面の暗いバックドロップは廃止し、ドラッグで動かせる浮遊ウィンドウに
   した（自己対戦の盤面を見られるように）。既定位置は邪魔にならない左上。JS(smoke-test-runner.js)が
   left/topを書き換えてドラッグ移動する。 */
.smoke-test-panel {
  position: fixed;
  top: 0.8rem;
  left: 0.8rem;
  z-index: 100091;
  width: min(30rem, 92vw);
  background: linear-gradient(160deg, #161c25, #0e1218);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 0.8rem;
  padding: 1.2rem 1.3rem;
  color: #e9eef4;
  font-family: sans-serif;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
}
.smoke-test-title {
  font-size: 1.05rem;
  font-weight: 700;
  margin-bottom: 0.4rem;
  cursor: move; /* タイトルバーを掴んでドラッグ移動 */
  user-select: none;
  touch-action: none;
}
.smoke-test-desc {
  font-size: 0.82rem;
  color: #aeb8c4;
  margin-bottom: 0.8rem;
  line-height: 1.5;
}
.smoke-test-invariants {
  margin-bottom: 0.8rem;
  font-size: 0.8rem;
}
.smoke-test-invariants > summary {
  cursor: pointer;
  color: #cbd5e1;
  user-select: none;
  padding: 0.15rem 0;
}
.smoke-test-invariants-list {
  margin-top: 0.4rem;
  max-height: 12rem;
  overflow-y: auto;
  background: rgba(0, 0, 0, 0.25);
  border-radius: 0.4rem;
  padding: 0.4rem 0.6rem;
}
.smoke-test-invariant-group {
  margin: 0.4rem 0 0.1rem;
  color: #7dd3fc;
  font-weight: 600;
  font-size: 0.76rem;
}
.smoke-test-invariant-group:first-child {
  margin-top: 0;
}
.smoke-test-invariant-row {
  padding: 0.2rem 0;
  line-height: 1.4;
  border-bottom: 1px solid rgba(148, 163, 184, 0.12);
}
.smoke-test-invariant-row:last-child {
  border-bottom: none;
}
.smoke-test-invariant-name {
  display: inline-block;
  color: #93c5fd;
  font-weight: 600;
  margin-right: 0.4rem;
}
.smoke-test-invariant-desc {
  color: #aeb8c4;
  font-size: 0.75rem;
}
.smoke-test-log {
  height: 11rem;
  overflow-y: auto;
  background: rgba(0, 0, 0, 0.35);
  border-radius: 0.4rem;
  padding: 0.5rem 0.7rem;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.76rem;
  color: #cbd5e1;
  white-space: pre-wrap;
}
.smoke-test-result {
  margin-top: 0.7rem;
  font-weight: 700;
  font-size: 0.95rem;
  min-height: 1.2em;
}
.smoke-test-result.is-pass {
  color: #4ade80;
}
.smoke-test-result.is-fail {
  color: #f87171;
}
.smoke-test-repeat-row {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 0.7rem;
  flex-wrap: wrap;
  font-size: 0.82rem;
  color: #cbd5e1;
}
.smoke-test-repeat-label {
  display: inline-flex;
  align-items: center;
}
.smoke-test-repeat-input {
  width: 3.4rem;
  margin: 0 0.15rem;
  padding: 0.2rem 0.3rem;
  border-radius: 0.35rem;
  border: 1px solid rgba(255, 255, 255, 0.2);
  background: #0f1520;
  color: #f1f5f9;
  font-size: 0.85rem;
  text-align: center;
}
.smoke-test-repeat-full {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  color: #aeb8c4;
}
.smoke-test-actions {
  display: flex;
  gap: 0.6rem;
  margin-top: 0.9rem;
  justify-content: flex-end;
  flex-wrap: wrap;
}
.smoke-test-run,
.smoke-test-close,
.smoke-test-copy,
.smoke-test-download {
  padding: 0.45rem 1rem;
  border-radius: 0.5rem;
  font-weight: 700;
  cursor: pointer;
  border: 1px solid rgba(255, 255, 255, 0.18);
  font-family: sans-serif;
  font-size: 0.85rem;
}
.smoke-test-run {
  background: #2563eb;
  color: #fff;
  border-color: transparent;
}
.smoke-test-run.smoke-test-online {
  background: #0d9488;
}
.smoke-test-run.smoke-test-all {
  background: #7c3aed;
  box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.35);
}
.smoke-test-close {
  background: rgba(255, 255, 255, 0.06);
  color: #e9eef4;
}
.smoke-test-copy {
  background: #f59e0b;
  color: #1a1206;
  border-color: transparent;
}
.smoke-test-download {
  background: rgba(255, 255, 255, 0.06);
  color: #e9eef4;
}
.smoke-test-run:disabled {
  opacity: 0.5;
  cursor: default;
}
.smoke-test-copy-fallback {
  width: 100%;
  height: 8rem;
  margin-top: 0.6rem;
  font-size: 0.7rem;
  font-family: monospace;
  color: #e9eef4;
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 0.4rem;
  white-space: pre;
  overflow: auto;
}

/* ランク戦マッチメイキング（フェーズ2b、ranked-match.js）。待機画面とレディチェック。 */
#ranked-waiting {
  position: fixed;
  inset: 0;
  z-index: 20000; /* ホーム(1500)・オプションエリア等より前面。オープニング(50000)は関与しない */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.2rem;
  background: radial-gradient(circle at 50% 40%, #1a2130, #0a0d14 80%);
  color: #f1f5f9;
  text-align: center;
  padding: 2rem;
}
.ranked-waiting-title {
  font-size: 1.5rem;
  font-weight: 700;
  color: #ffd77a;
  text-shadow: 0 0 12px rgba(255, 200, 90, 0.4);
}
.ranked-waiting-spinner {
  width: 3.4rem;
  height: 3.4rem;
  border: 0.35rem solid rgba(255, 215, 122, 0.25);
  border-top-color: #ffd77a;
  border-radius: 50%;
  animation: ranked-spin 0.9s linear infinite;
}
@keyframes ranked-spin { to { transform: rotate(360deg); } }
.ranked-waiting-status { font-size: 1.05rem; }
.ranked-waiting-count { font-size: 0.95rem; color: #9fb0c8; }
.ranked-waiting-hint { font-size: 0.8rem; color: #7c8aa0; max-width: 26rem; line-height: 1.6; }
.ranked-waiting-cancel {
  margin-top: 0.6rem;
  padding: 0.6rem 1.6rem;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 0.6rem;
  background: rgba(255, 255, 255, 0.06);
  color: #f1f5f9;
  font-size: 0.95rem;
  cursor: pointer;
}
.ranked-waiting-cancel:hover { background: rgba(255, 255, 255, 0.12); }

/* 待機中CPU練習ボタン（ranked-match.js） */
.ranked-waiting-practice {
  margin-top: 1rem;
  padding: 0.6rem 1.6rem;
  border: 1px solid rgba(255, 215, 120, 0.4);
  border-radius: 0.6rem;
  background: rgba(255, 215, 120, 0.12);
  color: #ffe08a;
  font-size: 0.95rem;
  font-weight: bold;
  cursor: pointer;
}
.ranked-waiting-practice:hover { background: rgba(255, 215, 120, 0.2); }

/* 練習中に画面上部に出す小さな「探し中」バナー（盤面は下で見える） */
#ranked-practice-banner {
  position: fixed;
  top: 0.6rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 40000;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 0.8rem;
  background: rgba(20, 26, 38, 0.92);
  border: 1px solid rgba(255, 215, 120, 0.45);
  border-radius: 0.6rem;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
  color: #ffe08a;
  font-size: 0.8rem;
  font-weight: bold;
  max-width: 92vw;
}
#ranked-practice-banner button {
  padding: 0.25rem 0.7rem;
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 0.4rem;
  background: rgba(255, 255, 255, 0.08);
  color: #f1f5f9;
  font-size: 0.72rem;
  cursor: pointer;
  white-space: nowrap;
}
#ranked-practice-banner button:hover { background: rgba(255, 255, 255, 0.16); }

#ranked-ready-modal {
  position: fixed;
  inset: 0;
  z-index: 20010;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.7);
}
.ranked-ready-inner {
  background: linear-gradient(160deg, #232c3d, #141a26);
  border: 2px solid #ffd77a;
  border-radius: 1rem;
  box-shadow: 0 0 40px rgba(255, 200, 90, 0.35);
  padding: 1.8rem 2rem;
  text-align: center;
  color: #f1f5f9;
  max-width: 24rem;
  animation: ranked-ready-pop 0.3s ease;
}
@keyframes ranked-ready-pop { from { transform: scale(0.85); opacity: 0; } to { transform: scale(1); opacity: 1; } }
.ranked-ready-heading {
  font-size: 1.3rem;
  font-weight: 700;
  color: #ffd77a;
  margin-bottom: 1rem;
}
.ranked-ready-opponent {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.9rem;
  margin-bottom: 0.9rem;
}
.ranked-ready-avatar {
  width: 3.4rem;
  height: 3.4rem;
  border-radius: 50%;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2rem;
  background: #2a3446;
  border: 1px solid rgba(255, 215, 122, 0.35);
  flex: 0 0 auto;
}
.ranked-ready-avatar img { width: 100%; height: 100%; object-fit: cover; }
.ranked-ready-info { text-align: left; }
.ranked-ready-name { font-size: 1.05rem; font-weight: 600; }
.ranked-ready-rank { font-size: 0.85rem; color: #9fb0c8; }
.ranked-ready-note { font-size: 0.8rem; color: #9fb0c8; line-height: 1.6; margin-bottom: 0.8rem; }
.ranked-ready-countdown { font-size: 1.1rem; font-weight: 700; color: #ff9e9e; margin-bottom: 1rem; }
.ranked-ready-start {
  padding: 0.7rem 2.4rem;
  border: none;
  border-radius: 0.7rem;
  background: linear-gradient(160deg, #ffd77a, #f0a93a);
  color: #241a08;
  font-size: 1.1rem;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(240, 169, 58, 0.5);
}
.ranked-ready-start:hover { filter: brightness(1.08); }
.ranked-ready-start:disabled { opacity: 0.6; cursor: default; filter: grayscale(0.4); }

/* レディチェックで複数の相手（3〜4人）を並べる（続き190） */
.ranked-ready-opponents {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-bottom: 0.9rem;
  align-items: stretch;
}
.ranked-ready-opponents .ranked-ready-opponent {
  justify-content: flex-start;
  gap: 0.7rem;
  margin-bottom: 0;
  background: rgba(255, 255, 255, 0.04);
  border-radius: 0.5rem;
  padding: 0.35rem 0.6rem;
}

/* 人数選択モーダル（続き190、ranked-match.js） */
#ranked-size-modal {
  position: fixed;
  inset: 0;
  z-index: 20010;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.7);
}
.ranked-size-note { font-size: 0.82rem; color: #9fb0c8; line-height: 1.6; margin-bottom: 1rem; }
.ranked-size-btns { display: flex; gap: 0.7rem; justify-content: center; margin-bottom: 1rem; }
.ranked-size-btn {
  padding: 0.9rem 1.2rem;
  min-width: 5.2rem;
  border: 2px solid rgba(255, 215, 122, 0.5);
  border-radius: 0.7rem;
  background: linear-gradient(160deg, #2a3446, #1a2130);
  color: #f1f5f9;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  line-height: 1.3;
}
.ranked-size-btn:hover { border-color: #ffd77a; filter: brightness(1.1); }
.ranked-size-btn .ranked-size-btn-sub { display: block; font-size: 0.68rem; color: #9fb0c8; font-weight: 400; margin-top: 0.2rem; }
.ranked-size-cancel {
  padding: 0.5rem 1.4rem;
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 0.6rem;
  background: transparent;
  color: #cbd5e1;
  font-size: 0.85rem;
  cursor: pointer;
}

/* ランク対局の結果モーダル（フェーズ3、ranked-result-modal.js） */
#ranked-result-modal {
  position: fixed;
  inset: 0;
  z-index: 20110;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none; /* モーダル自身は透過。inner(auto)がボタンを拾い、外側は下のbackdropへ */
}
.ranked-result-inner {
  pointer-events: auto;
  background: linear-gradient(160deg, #232c3d, #141a26);
  border: 2px solid #ffd77a;
  border-radius: 1rem;
  box-shadow: 0 0 40px rgba(255, 200, 90, 0.35);
  padding: 1.8rem 2.2rem;
  text-align: center;
  color: #f1f5f9;
  min-width: 18rem;
  max-width: 24rem;
  animation: ranked-ready-pop 0.3s ease;
}
.ranked-result-heading { font-size: 1.25rem; font-weight: 700; margin-bottom: 0.9rem; }
.ranked-result-heading.is-win { color: #ffd77a; }
.ranked-result-heading.is-lose { color: #9fb0c8; }
.ranked-result-note { font-size: 0.9rem; color: #cbd5e1; margin-bottom: 0.9rem; line-height: 1.4; }
.ranked-result-rank { font-size: 1.6rem; font-weight: 800; letter-spacing: 0.04em; margin-bottom: 0.9rem; }
.ranked-result-gauge { display: flex; justify-content: center; gap: 0.4rem; margin-bottom: 0.5rem; }
.ranked-result-dot {
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.2);
}
.ranked-result-dot.is-lit {
  background: var(--dot-color);
  border-color: transparent;
  box-shadow: 0 0 8px var(--dot-color);
}
.ranked-result-gauge-text { font-size: 0.9rem; color: #9fb0c8; margin-bottom: 1.1rem; }
.ranked-result-lp { font-size: 1.1rem; color: #ffd77a; margin-bottom: 1.1rem; }
.ranked-result-ok {
  padding: 0.55rem 2.2rem;
  border: none;
  border-radius: 0.6rem;
  background: linear-gradient(160deg, #ffd77a, #f0a93a);
  color: #241a08;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
}
.ranked-result-ok:hover { filter: brightness(1.08); }

/* ── シーズン終了報酬モーダル（ranked-season-reward-modal.js） ── */
#season-reward-modal {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 20110;
  display: flex;
  flex-direction: column;
  align-items: center;
  overflow: hidden;
  width: min(22rem, 92vw);
  background: linear-gradient(160deg, #2a3346, #141a26);
  border: 2px solid #ffd77a;
  border-radius: 1.1rem;
  box-shadow: 0 0 44px rgba(255, 200, 90, 0.4);
  padding: 1.6rem 1.8rem 1.9rem;
  text-align: center;
  color: #f1f5f9;
  animation: ranked-ready-pop 0.32s ease;
}
.season-reward-title {
  font-size: 1.4rem;
  font-weight: 800;
  color: #ffe08a;
  text-shadow: 0 0 14px rgba(255, 210, 110, 0.55);
  z-index: 1;
}
.season-reward-sub { font-size: 0.9rem; color: #cbd5e1; margin-top: 0.3rem; z-index: 1; }
.season-reward-burst {
  position: absolute;
  top: 42%;
  left: 50%;
  width: 150%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  background: repeating-conic-gradient(from 0deg, rgba(255, 214, 120, 0.3) 0deg 8deg, transparent 8deg 20deg);
  border-radius: 50%;
  filter: blur(1px);
  pointer-events: none;
  z-index: 0;
  opacity: 0.5;
  mask-image: radial-gradient(circle, #000 26%, transparent 72%);
  -webkit-mask-image: radial-gradient(circle, #000 26%, transparent 72%);
}
#season-reward-modal.is-celebrating .season-reward-burst {
  animation: ranked-burst-spin 9s linear infinite, ranked-burst-pulse 2.4s ease-in-out infinite;
}
.season-reward-badge { margin-top: 0.8rem; z-index: 1; }
.season-reward-rankname {
  font-size: 1.35rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  color: #ffd77a;
  margin-top: 0.3rem;
  z-index: 1;
}
.season-reward-amount {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  margin-top: 0.8rem;
  z-index: 1;
}
.season-reward-coin { width: 2rem; height: 2rem; object-fit: contain; }
.season-reward-amount-num {
  font-size: 1.8rem;
  font-weight: 900;
  color: #ffe08a;
  text-shadow: 0 0 12px rgba(255, 210, 110, 0.5);
}
.season-reward-note { font-size: 0.82rem; color: #9fb0c8; margin-top: 0.7rem; line-height: 1.45; z-index: 1; }
.season-reward-claim {
  margin-top: 1.1rem;
  padding: 0.55rem 2.4rem;
  border: none;
  border-radius: 0.6rem;
  background: linear-gradient(160deg, #ffd77a, #f0a93a);
  color: #241a08;
  font-size: 1.02rem;
  font-weight: 700;
  cursor: pointer;
  z-index: 1;
}
.season-reward-claim:hover { filter: brightness(1.08); }

/* ── ランク戦の説明モーダル（rank-explain.js）。ホーム/マイページのランク表示クリック＆ヘルプから ──
   タイトル・閉じる×・OKは固定、本文だけスクロール（内容が長いので）。 */
#rank-explain-modal {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 20200;
  width: min(30rem, 92vw);
  max-height: 84vh;
  display: flex;
  flex-direction: column;
  background: linear-gradient(160deg, #232c3d, #141a26);
  border: 2px solid #ffd77a;
  border-radius: 1rem;
  box-shadow: 0 0 40px rgba(255, 200, 90, 0.3);
  padding: 1.5rem 1.5rem 1.8rem;
  color: #f1f5f9;
}
.rank-explain-title {
  flex: 0 0 auto;
  font-size: 1.3rem;
  font-weight: 800;
  color: #ffe08a;
  text-align: center;
  margin-bottom: 0.8rem;
  text-shadow: 0 0 12px rgba(255, 210, 110, 0.5);
}
.rank-explain-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding-right: 0.3rem;
}
.rank-explain-section-title {
  font-size: 1.02rem;
  font-weight: 700;
  color: #ffd77a;
  margin: 1.1rem 0 0.4rem;
  border-left: 3px solid #ffd77a;
  padding-left: 0.5rem;
}
.rank-explain-section-title:first-child {
  margin-top: 0.2rem;
}
.rank-explain-paragraph {
  font-size: 0.9rem;
  line-height: 1.65;
  color: #dfe6f0;
  margin: 0 0 0.5rem;
}
.rank-explain-ok {
  flex: 0 0 auto;
  align-self: center;
  margin-top: 1.2rem;
  padding: 0.55rem 2.4rem;
  border: none;
  border-radius: 0.6rem;
  background: linear-gradient(160deg, #ffd77a, #f0a93a);
  color: #241a08;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
}
.rank-explain-ok:hover { filter: brightness(1.08); }

/* ── ランクアップ昇格演出（ranked-result-modal.js の promotedFrom 指定時） ──
   「七色ゲージ完成→称号変化」を before(旧ランク＋ゲージ満杯)→after(新ランク) のクロスフェードで見せる。 */
.ranked-result-heading.is-promote {
  color: #ffe08a;
  font-size: 1.5rem;
  text-shadow: 0 0 14px rgba(255, 210, 110, 0.7);
  animation: ranked-promote-heading 1.6s ease infinite;
}
@keyframes ranked-promote-heading {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.06); }
}
.ranked-result-showcase-stage {
  position: relative;
  display: flex;
  justify-content: center;
  margin: 0.4rem 0 0.2rem;
}
.ranked-result-showcase-stage.is-promote .ranked-result-before,
.ranked-result-showcase-stage.is-promote .ranked-result-after {
  z-index: 1;
  transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.ranked-result-showcase-stage.is-promote .ranked-result-before {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  opacity: 1;
}
.ranked-result-showcase-stage.is-promote .ranked-result-after {
  opacity: 0;
  transform: scale(0.55);
}
.ranked-result-showcase-stage.is-promote.is-transitioned .ranked-result-before {
  opacity: 0;
  transform: translateX(-50%) scale(1.2);
}
.ranked-result-showcase-stage.is-promote.is-transitioned .ranked-result-after {
  opacity: 1;
  transform: scale(1);
}
/* 背後で回転する金色の放射状バースト。 */
.ranked-result-burst {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 150%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  background: repeating-conic-gradient(from 0deg, rgba(255, 214, 120, 0.34) 0deg 8deg, transparent 8deg 20deg);
  border-radius: 50%;
  filter: blur(1px);
  animation: ranked-burst-spin 9s linear infinite, ranked-burst-pulse 2.4s ease-in-out infinite;
  pointer-events: none;
  z-index: 0;
  mask-image: radial-gradient(circle, #000 28%, transparent 72%);
  -webkit-mask-image: radial-gradient(circle, #000 28%, transparent 72%);
}
.ranked-result-burst.is-static { animation: none; opacity: 0.5; }
@keyframes ranked-burst-spin { to { transform: translate(-50%, -50%) rotate(360deg); } }
@keyframes ranked-burst-pulse { 0%, 100% { opacity: 0.55; } 50% { opacity: 0.85; } }
.ranked-result-promote-line {
  font-size: 1.05rem;
  font-weight: 800;
  color: #ffe08a;
  letter-spacing: 0.03em;
  margin: 0.2rem 0 0.7rem;
  text-shadow: 0 0 10px rgba(255, 210, 110, 0.6);
}
body.reduce-glow .ranked-result-heading.is-promote { animation: none; }

/* ランク称号バッジの共通部品（rank-badge.js、フェーズ6）。段位バッジ画像＋段位名＋七色ゲージ。 */
.rank-badge-box { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; }
.rank-badge-image { object-fit: contain; filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.45)); }

/* ── エフェクト付き静止バッジ（対戦終了後のランク結果・シーズン報酬のヒーロー表示。
   ユーザー要望2026-08-18「アニメバッジはやめて、静止バッジの周りに湯気のような幻想的な
   オーラ＋表面に光沢EFFECT」）。buildRankBadgeImage(effects:true) が
   div.rank-badge-fx > (.rank-badge-aura, img.rank-badge-image, .rank-badge-gloss) を生成する。 */
.rank-badge-fx { position: relative; display: inline-block; }
.rank-badge-fx .rank-badge-image { position: relative; z-index: 2; display: block; width: 100%; height: 100%; }

/* 湯気のような幻想的なオーラ（バッジ背面でゆっくり漂う）。2層をずらして重ね、うねりを出す。 */
.rank-badge-fx .rank-badge-aura {
  position: absolute; left: 50%; top: 52%; width: 150%; height: 150%;
  transform: translate(-50%, -50%); z-index: 1; pointer-events: none; border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
    rgba(255, 240, 190, 0.55), rgba(255, 214, 120, 0.30) 38%, rgba(255, 200, 90, 0) 68%);
  filter: blur(7px);
  animation: rank-badge-aura-drift 6.5s ease-in-out infinite;
}
.rank-badge-fx .rank-badge-aura::before,
.rank-badge-fx .rank-badge-aura::after {
  content: ""; position: absolute; inset: -8%; border-radius: 50%;
  background: radial-gradient(circle at 46% 44%,
    rgba(255, 236, 175, 0.42), rgba(255, 210, 120, 0) 60%);
}
.rank-badge-fx .rank-badge-aura::before { animation: rank-badge-aura-wisp1 8s ease-in-out infinite; }
.rank-badge-fx .rank-badge-aura::after  { animation: rank-badge-aura-wisp2 9.5s ease-in-out infinite; }
@keyframes rank-badge-aura-drift {
  0%   { transform: translate(-50%, -50%) scale(1)    rotate(0deg);  opacity: 0.7; }
  50%  { transform: translate(-51%, -49%) scale(1.09) rotate(6deg);  opacity: 1; }
  100% { transform: translate(-50%, -50%) scale(1)    rotate(0deg);  opacity: 0.7; }
}
@keyframes rank-badge-aura-wisp1 {
  0%   { transform: translate(0, 0)      scale(1);    opacity: 0.35; }
  50%  { transform: translate(6%, -7%)   scale(1.12); opacity: 0.7; }
  100% { transform: translate(0, 0)      scale(1);    opacity: 0.35; }
}
@keyframes rank-badge-aura-wisp2 {
  0%   { transform: translate(0, 0)      scale(1.05); opacity: 0.5; }
  50%  { transform: translate(-7%, 6%)   scale(0.94); opacity: 0.2; }
  100% { transform: translate(0, 0)      scale(1.05); opacity: 0.5; }
}

/* 表面の光沢: 斜めの光が定期的に横切る「シャインスイープ」＋左上の柔らかいスペキュラ。
   バッジ本体（円形メダリオン）の丸い面に収めるため円形maskでクリップし、四隅の透明部分に
   光が漏れないようにする。 */
.rank-badge-fx .rank-badge-gloss {
  position: absolute; inset: 0; z-index: 3; pointer-events: none; overflow: hidden; border-radius: 50%;
  -webkit-mask: radial-gradient(circle at 50% 50%, #000 60%, transparent 64%);
          mask: radial-gradient(circle at 50% 50%, #000 60%, transparent 64%);
}
.rank-badge-fx .rank-badge-gloss::before {
  content: ""; position: absolute; top: -30%; left: -60%; width: 55%; height: 160%;
  background: linear-gradient(100deg,
    rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.7) 50%, rgba(255, 255, 255, 0) 100%);
  transform: rotate(10deg); filter: blur(1px);
  animation: rank-badge-gloss-sweep 4.8s ease-in-out infinite;
}
.rank-badge-fx .rank-badge-gloss::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: radial-gradient(circle at 34% 28%, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0) 42%);
  mix-blend-mode: screen; animation: rank-badge-gloss-breathe 3.6s ease-in-out infinite;
}
@keyframes rank-badge-gloss-sweep {
  0%, 100% { left: -60%; opacity: 0; }
  12%      { opacity: 1; }
  50%      { left: 105%; opacity: 1; }
  62%      { opacity: 0; }
}
@keyframes rank-badge-gloss-breathe {
  0%, 100% { opacity: 0.4; }
  50%      { opacity: 0.85; }
}
/* 「アニメーションを減らす」設定では静止させる（オーラ・光沢とも動きを止める）。 */
body.reduce-glow .rank-badge-fx .rank-badge-aura,
body.reduce-glow .rank-badge-fx .rank-badge-aura::before,
body.reduce-glow .rank-badge-fx .rank-badge-aura::after,
body.reduce-glow .rank-badge-fx .rank-badge-gloss::before,
body.reduce-glow .rank-badge-fx .rank-badge-gloss::after { animation: none; }
body.reduce-glow .rank-badge-fx .rank-badge-gloss::before { opacity: 0; }
.rank-badge-name { font-size: 1.2rem; font-weight: 800; letter-spacing: 0.04em; color: #ffe6a8; }
.rank-badge-gauge { display: flex; justify-content: center; gap: 0.35rem; }
.rank-badge-dot {
  width: 0.9rem;
  height: 0.9rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.2);
}
.rank-badge-dot.is-lit {
  background: var(--dot-color);
  border-color: transparent;
  box-shadow: 0 0 7px var(--dot-color);
}
.rank-badge-lp { font-size: 1rem; font-weight: 700; color: #ffd77a; }

/* U型の七色ゲージ（rank-badge.js buildUGauge、ユーザー提供素材2026-08-16）。豪華な枠に7つの
   宝石ソケット。ゲージが貯まるほど左（赤）→右（紫）へ宝石が点灯する。 */
.rank-ugauge {
  position: relative;
  aspect-ratio: 1 / 1;
  background-image: url("../assets/rank-gauge/frame-u.webp");
  background-size: 100% 100%;
  background-repeat: no-repeat;
  filter: drop-shadow(0 3px 10px rgba(0, 0, 0, 0.5));
}
.rank-ugauge-gem {
  position: absolute;
  /* 枠幅に対する宝石の大きさ（ソケットにフィット）。--rank-ugauge-gem で微調整可。 */
  width: var(--rank-ugauge-gem, 15%);
  height: auto;
  transform: translate(-50%, -50%);
  filter: drop-shadow(0 0 6px rgba(255, 220, 150, 0.55));
  animation: rank-ugauge-gem-in 0.4s var(--gem-delay, 0s) both;
}
@keyframes rank-ugauge-gem-in {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.4); }
  70% { transform: translate(-50%, -50%) scale(1.12); }
  100% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
body.reduce-glow .rank-ugauge-gem { animation: none; }

/* バッジ背景＋U型ゲージ枠＋宝石＋バッジの合成ヒーロー表示（rank-showcase.js）。
   枠は正方形（1254×1254）。レイヤーはDOM順（後ろ→前）: 背景 → 枠 → 宝石 → バッジ。 */
.rank-showcase {
  position: relative;
  aspect-ratio: 1 / 1;
  filter: drop-shadow(0 3px 12px rgba(0, 0, 0, 0.5));
}
/* U型ゲージ枠は枠いっぱいに敷く（掴めない＝座標基準）。 */
.rank-showcase-frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
}
/* バッジ背景（魔法陣、最背面）。既定は枠いっぱい（width100%・中央）だが、調整モードで
   大きさ・位置をずらせる（宝石・バッジと同じく枠中央基準。正方形画像なのでheightは自動）。 */
.rank-showcase-bg {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 100%;
  height: auto;
  transform: translate(-50%, -50%);
  object-fit: contain;
  pointer-events: none;
}
.rank-showcase-gem {
  position: absolute;
  width: var(--rank-ugauge-gem, 15%);
  height: auto;
  transform: translate(-50%, -50%);
  filter: drop-shadow(0 0 6px rgba(255, 220, 150, 0.55));
}
/* ランク結果モーダルの「ジェムを1個ずつもらう」演出（続き165）。点灯＝pop＋光のフラッシュ、
   消灯＝縮んで消える。transform に translate(-50%,-50%) を含めないと中心がズレるので各キーで明記。 */
.rank-showcase-gem.is-lighting {
  animation: rank-gem-light 0.55s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
@keyframes rank-gem-light {
  0% {
    transform: translate(-50%, -50%) scale(0.2);
    opacity: 0;
    filter: drop-shadow(0 0 2px rgba(255, 220, 150, 0.4)) brightness(2.4);
  }
  55% {
    transform: translate(-50%, -50%) scale(1.38);
    opacity: 1;
    filter: drop-shadow(0 0 18px rgba(255, 236, 172, 0.95)) brightness(1.8);
  }
  100% {
    transform: translate(-50%, -50%) scale(1);
    opacity: 1;
    filter: drop-shadow(0 0 6px rgba(255, 220, 150, 0.55)) brightness(1);
  }
}
.rank-showcase-gem.is-dimming {
  animation: rank-gem-dim 0.42s ease-in both;
}
@keyframes rank-gem-dim {
  0% {
    transform: translate(-50%, -50%) scale(1);
    opacity: 1;
  }
  100% {
    transform: translate(-50%, -50%) scale(0.3);
    opacity: 0;
  }
}
body.reduce-glow .rank-showcase-gem.is-lighting,
body.reduce-glow .rank-showcase-gem.is-dimming {
  animation: none;
}
.rank-showcase-badge {
  position: absolute;
  transform: translate(-50%, -50%);
  object-fit: contain;
  filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.5));
}
/* 編集モードでは掴めるようにカーソル＋当たり判定を明示（背景も最背面で掴めるように）。 */
.rank-showcase.is-editable .rank-showcase-gem,
.rank-showcase.is-editable .rank-showcase-badge,
.rank-showcase.is-editable .rank-showcase-bg {
  cursor: grab;
  touch-action: none;
}
.rank-showcase.is-editable .rank-showcase-bg {
  pointer-events: auto;
}
/* 編集時のみ宝石をバッジより前面にして、バッジ下に隠れる宝石（下中央のソケット等）も
   確実に掴めるようにする。表示（ホーム/マイページ/結果モーダル）はバッジが前面のまま。 */
.rank-showcase.is-editable .rank-showcase-gem {
  z-index: 5;
}
.rank-showcase.is-editable .rank-showcase-gem:active,
.rank-showcase.is-editable .rank-showcase-badge:active,
.rank-showcase.is-editable .rank-showcase-bg:active {
  cursor: grabbing;
}

/* 調整モードのオーバーレイ（rank-showcase.js openRankShowcaseEditor）。 */
#rank-showcase-editor {
  position: fixed;
  inset: 0;
  z-index: 21000;
  background: rgba(6, 8, 12, 0.82);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  padding: 1rem;
  box-sizing: border-box;
}
.rank-showcase-editor-bar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  justify-content: center;
  color: #e2e8f0;
  font-size: 0.85rem;
}
.rank-showcase-editor-bar button {
  padding: 0.35rem 0.8rem;
  border-radius: 0.4rem;
  border: 1px solid rgba(148, 163, 184, 0.4);
  background: #1e293b;
  color: #f1f5f9;
  cursor: pointer;
  font-size: 0.8rem;
}
.rank-showcase-editor-bar button:hover { background: #334155; }
.rank-showcase-size-ctl {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.1rem 0.35rem;
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.4rem;
}
.rank-showcase-size-ctl > span { font-size: 0.78rem; color: #cbd5e1; }
.rank-showcase-size-ctl button {
  width: 1.6rem;
  height: 1.6rem;
  padding: 0;
  line-height: 1;
  font-size: 1rem;
  border-radius: 0.3rem;
  border: 1px solid rgba(148, 163, 184, 0.4);
  background: #1e293b;
  color: #f1f5f9;
  cursor: pointer;
}
.rank-showcase-size-ctl button:hover { background: #334155; }
/* 等倍縮小ラッパー（ホーム/マイページのコンパクト表示、rank-showcase.js buildRankShowcase scale）。 */
.rank-showcase-scale { position: relative; }
.rank-showcase-editor-out {
  width: min(46rem, 92vw);
  height: 7rem;
  background: #0f1520;
  color: #f1f5f9;
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.4rem;
  font-family: monospace;
  font-size: 0.75rem;
  padding: 0.5rem;
  box-sizing: border-box;
}
.rank-showcase-editor-stage {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
}
/* 枠は正方形なので、幅＝高さ。ステージの縦に収まるよう vh でも上限する。 */
.rank-showcase-editor-stage .rank-showcase { width: min(32rem, 66vh); }

/* ===== カード面レンダラ（card-renderer.js）＝テキスト無しブランク画像＋アプリ側テキスト ===== */
/* 【重要】このブロックは tools/gen-cardface-css.mjs が丸ごと差し替える自動生成領域。
   ここを直接編集しても、次に再生成した時に消える（#188 で実際に3つのルールが消えた）。
   直したい時は tools/gen-cardface-css.mjs の BASE を編集してから再生成すること。 */
/* 位置・サイズは種別ごとに要素単位で --cf-{n|e|f}-{要素}-{x|y|w|s} で調整（既定は各var()第2引数）。
   この位置ルールは tools/gen-cardface-css.mjs が src/card-layout-config.js から自動生成する。
   文字サイズは全て var(--cf-fit, 1) 倍される＝効果文が枠からはみ出すカードだけ、card-renderer.js の
   自動フィット（fitCardFace）がこの変数を 1 未満にして収める。 */
.card-face {
  position: relative; aspect-ratio: 1 / 1; container-type: size;
  background-size: cover; background-position: center;
  border-radius: 4cqw; overflow: hidden; color: #2a2320;
  box-shadow: 0 0.6cqw 1.6cqw rgba(0, 0, 0, 0.35); user-select: none;
  /* 手札等の祖先コンテナが text-align:center を持つため、明示的に左寄せへ戻す
     （効果本文が中央寄せになる不具合の修正。フレーバー/サブタイトル/ファーストの
     効果本文は各自 text-align:center を明示しているので影響しない）。 */
  text-align: left;
}
/* カードスロット（.hand-card/.board-card/.hand-reveal-card/#card-preview/.stack-top 等）に
   inset:0 で被せるマウント。スロットの角丸に合わせてクリップ、スロット自身の影・比率に従う。
   container-type:size は基底 .card-face から継承（inset:0 で確定サイズになり cqw が解決する）。
   【重要】border-radius: inherit を外すと、基底の border-radius: 4cqw が「マウント自身ではなく
   祖先のコンテナ（無ければビューポート）」を基準に解決されるため、カードが丸く切り抜かれて
   見える（#188 の症状）。ここは必ず inherit のままにすること。 */
.card-face.card-face-mount {
  position: absolute; inset: 0; width: 100%; height: 100%;
  aspect-ratio: auto; border-radius: inherit; box-shadow: none;
  pointer-events: none;
}
/* モーダル等の「1枚のカード表示箱」（buildCardBox）。正方形・角丸/枠/影は呼び出し側のCSSで指定。
   テキストモードは .card-face-mount を内包、画像モードは背景画像（cover）。 */
.card-box {
  position: relative; aspect-ratio: 1 / 1; overflow: hidden;
  background-size: cover; background-position: center; background-repeat: no-repeat;
  border-radius: inherit;
}
/* 効果セット（fx）＝基本/到達/手札を上から順に詰める。間に仕切り線（各効果の中間）。 */
.card-face-fx { display: flex; flex-direction: column; gap: 1.4cqw; }
.card-face-effect { display: flex; gap: 0.45em; align-items: flex-start; font-family: "Noto Sans JP", "ヒラギノ角ゴ Pro W6", "Hiragino Kaku Gothic Pro", "ヒラギノ角ゴ ProN W6", "Hiragino Kaku Gothic ProN", "Yu Gothic", sans-serif; font-weight: 900; }
.card-face-effect-body { flex: 1; min-width: 0; }
.card-face-textline { font-size: 1em; line-height: 1.28; }
/* 「・」選択肢行は左インデントを設けず、普通に左詰め（ユーザー要望）。 */
.card-face-subline { font-size: 1em; line-height: 1.28; padding-left: 0; }
/* ●到達・■手札は正式アイコン（CSSはsrc/なので ../assets/ 参照）。★基本はマーカー無し。 */
.card-face-marker { flex: 0 0 auto; width: 1.25em; height: 1.25em; margin-top: 0.12em; background-size: contain; background-repeat: no-repeat; background-position: center; }
.card-face-marker.is-basic { display: none; }
.card-face-marker.is-arrival { background-image: url("../assets/icons/effect-arrival.png"); }
.card-face-marker.is-hand { background-image: url("../assets/icons/effect-hand.png"); }
/* ファーストの■アイコンは独立要素（左位置Xで配置・中央ではない。サイズは handicon-s）。 */
.card-face-hand-icon { background-image: url("../assets/icons/effect-hand.png"); background-size: contain; background-repeat: no-repeat; background-position: center; }
/* 仕切り線は原本に合わせて黒系で統一（各効果の間に置く。中央のダイヤは無し＝黒線のみ）。 */
.card-face-divider { display: flex; align-items: center; height: 1.2cqw; }
.card-face-divider::before { content: ""; height: 0.22cqw; flex: 1; background: rgba(20, 20, 20, 0.8); }
/* タイトルは全て黒文字（ユーザー指定）。ファーストのみ中央寄せ。フォント＝FOT-マティス。 */
.card-face-title { font-family: "Shippori Mincho B1", "FOT-マティス ProN M", "FOT-Matisse ProN M", "Yu Mincho", serif; font-weight: 800; line-height: 1.08; letter-spacing: 0.2cqw; color: #141414; text-align: left; }
.card-face[data-card-type="first"] .card-face-title { text-align: center; }
/* ルビは絶対配置で base の「上」に浮かせる＝タイトルの行高に影響しない
   （ルビ有無でタイトルの縦位置がズレない）。各漢字ランを基準に中央に置き、
   translateY(=--cf-*-ruby-oy) で上下微調整（正=下）。
   【#194・重要】以前は <ruby>/<rt> で組んでいたが、**Safari は ruby 内部ボックス(rt)への
   position 指定を尊重しない**ため、iOS ではルビが行の高さを食ってタイトルが下へ押し出され、
   効果文と重なっていた（Chromiumでは正常なので手元では再現しなかった）。ruby 内部ボックスを
   やめて普通の span (.card-face-title-run / -rt) で組むことで、どのエンジンでも同じになる。
   古い ruby/rt セレクタも残してあるので、万一 ruby で組まれても崩れない。 */
.card-face-title ruby,
.card-face-title .card-face-title-run { position: relative; display: inline-block; }
.card-face-title rt,
.card-face-title .card-face-title-rt { position: absolute; left: 50%; bottom: 100%; white-space: nowrap; font-weight: 700; line-height: 1; color: #141414; font-family: "Shippori Mincho B1", "FOT-マティス ProN M", "FOT-Matisse ProN M", "Yu Mincho", serif; }
/* 能力名《》はタイトルと同じフォント。 */
.card-face-subtitle { font-family: "Shippori Mincho B1", "FOT-マティス ProN M", "FOT-Matisse ProN M", "Yu Mincho", serif; font-weight: 800; text-align: center; color: color-mix(in srgb, var(--card-accent, #555) 55%, #201a16); }
/* フレーバーは斜体にしない（ユーザー指摘）。フォント＝FOT-マティス。 */
.card-face-flavor { font-family: "Shippori Mincho B1", "FOT-マティス ProN M", "FOT-Matisse ProN M", "Yu Mincho", serif; font-weight: 600; font-style: normal; text-align: center; color: #fff; line-height: 1.25; text-shadow: 0 0.4cqw 1cqw rgba(0, 0, 0, 0.85), 0 0 0.4cqw rgba(0, 0, 0, 0.6); }
/* ファーストは文字が全て白（ユーザー指定）。効果は全て中央揃え・ただしアイコン(マーカー)は左揃え。 */
.card-face[data-card-type="first"] .card-face-title,
.card-face[data-card-type="first"] .card-face-title rt,
.card-face[data-card-type="first"] .card-face-title .card-face-title-rt,
.card-face[data-card-type="first"] .card-face-subtitle,
.card-face[data-card-type="first"] .card-face-effect { color: #fff; text-shadow: 0 0.3cqw 0.8cqw rgba(0, 0, 0, 0.7), 0 0 0.4cqw rgba(0, 0, 0, 0.5); }
.card-face[data-card-type="first"] .card-face-effect-body { text-align: center; }
/* 2026-09-26 の新しい絵から、タイトルの帯が「薄い色」→「濃い色」に変わった。
   以前の絵では濃い文字が正しかったが、そのままだと濃い帯に濃い文字で読めない。
   印字カードと同じく、通常・エターナルのタイトル（ふりがな含む）を白にする。
   ファーストは元から全て白（上の規則）。 */
.card-face[data-card-type=normal] .card-face-title,
.card-face[data-card-type=eternal] .card-face-title { color: #fff; text-shadow: 0 0.25cqw 0.7cqw rgba(0, 0, 0, 0.55); }
/* 【#415・2026-10-16】ユーザー報告「カードタイトルのルビが白色ですが**背景が白**なので黒文字が
   いいかと！」。**ふりがなだけは題の帯の外（真上）に浮いている**（`bottom: 100%`。実測でも
   ルビの下端が題の箱の上端より上にある）。帯そのものは濃いので題の白は正しいが、**ルビが乗って
   いるのは帯の上の淡い所**なので、白いままだと読めない。ルビだけ濃い字に戻す
   （基底の `.card-face-title-rt` が持っている #141414 と同じ）。
   ⚠ **ファーストは触らない**——あちらは題もルビも**絵の上**に乗る作りで、全部白が正しい
   （上の `[data-card-type="first"]` の規則）。 */
.card-face[data-card-type=normal] .card-face-title rt,
.card-face[data-card-type=normal] .card-face-title .card-face-title-rt,
.card-face[data-card-type=eternal] .card-face-title rt,
.card-face[data-card-type=eternal] .card-face-title .card-face-title-rt { color: #141414; text-shadow: none; }

/* エディタ用：要素ごとの枠（どこに何があるか分かるように） */
.card-face.cf-outline .card-face-title,
.card-face.cf-outline .card-face-flavor,
.card-face.cf-outline .card-face-subtitle,
.card-face.cf-outline .card-face-fx,
.card-face.cf-outline .card-face-hand-icon,
.card-face.cf-outline .card-face-effect { outline: 0.25cqw dashed rgba(0, 150, 255, 0.85); outline-offset: 0.3cqw; }

/* --- 各テキスト要素の絶対配置（自動生成：card-layout-config.js） --- */
.card-face[data-card-type="normal"] .card-face-flavor { position: absolute; left: var(--cf-s-flavor-x, 6cqw); top: var(--cf-s-flavor-y, 5cqw); width: var(--cf-s-flavor-w, 88cqw); font-size: calc(var(--cf-s-flavor-s, 2.5cqw) * var(--cf-fit, 1)); }
.card-face[data-card-type="normal"] .card-face-title { position: absolute; left: var(--cf-s-title-x, 6cqw); top: var(--cf-s-title-y, 52.5cqw); width: var(--cf-s-title-w, 90cqw); font-size: calc(var(--cf-s-title-s, 3.9cqw) * var(--cf-fit, 1)); }
.card-face[data-card-type="normal"] .card-face-title rt,
.card-face[data-card-type="normal"] .card-face-title .card-face-title-rt { font-size: calc(var(--cf-s-ruby-s, 1.8cqw) * var(--cf-fit, 1)); transform: translateX(-50%) translateY(var(--cf-s-ruby-oy, 0cqw)); }
.card-face[data-card-type="normal"] .card-face-fx { position: absolute; left: var(--cf-s-fx-x, 6cqw); top: var(--cf-s-fx-y, 58cqw); width: var(--cf-s-fx-w, 88cqw); font-size: calc(var(--cf-s-fx-s, 3.2cqw) * var(--cf-fit, 1)); }
.card-face[data-card-type="normal"] .card-face-fx .card-face-effect.is-basic { font-size: calc(var(--cf-s-fxbasic-s, 2.7cqw) * var(--cf-fit, 1)); width: var(--cf-s-fxbasic-w, 80cqw); margin-left: var(--cf-s-fxbasic-mx, 8cqw); }
.card-face[data-card-type="normal"] .card-face-marker { width: var(--cf-s-icon-s, 6.6cqw); height: var(--cf-s-icon-s, 6.6cqw); }
.card-face[data-card-type="normal"] .card-face-effect { gap: var(--cf-s-gap-s, 1.5cqw); }
.card-face[data-card-type="eternal"] .card-face-flavor { position: absolute; left: var(--cf-s-flavor-x, 6cqw); top: var(--cf-s-flavor-y, 5cqw); width: var(--cf-s-flavor-w, 88cqw); font-size: calc(var(--cf-s-flavor-s, 2.5cqw) * var(--cf-fit, 1)); }
.card-face[data-card-type="eternal"] .card-face-title { position: absolute; left: var(--cf-s-title-x, 6cqw); top: var(--cf-s-title-y, 52.5cqw); width: var(--cf-s-title-w, 90cqw); font-size: calc(var(--cf-s-title-s, 3.9cqw) * var(--cf-fit, 1)); }
.card-face[data-card-type="eternal"] .card-face-title rt,
.card-face[data-card-type="eternal"] .card-face-title .card-face-title-rt { font-size: calc(var(--cf-s-ruby-s, 1.8cqw) * var(--cf-fit, 1)); transform: translateX(-50%) translateY(var(--cf-s-ruby-oy, 0cqw)); }
.card-face[data-card-type="eternal"] .card-face-fx { position: absolute; left: var(--cf-s-fx-x, 6cqw); top: var(--cf-s-fx-y, 58cqw); width: var(--cf-s-fx-w, 88cqw); font-size: calc(var(--cf-s-fx-s, 3.2cqw) * var(--cf-fit, 1)); }
.card-face[data-card-type="eternal"] .card-face-fx .card-face-effect.is-basic { font-size: calc(var(--cf-s-fxbasic-s, 2.7cqw) * var(--cf-fit, 1)); width: var(--cf-s-fxbasic-w, 80cqw); margin-left: var(--cf-s-fxbasic-mx, 8cqw); }
.card-face[data-card-type="eternal"] .card-face-marker { width: var(--cf-s-icon-s, 6.6cqw); height: var(--cf-s-icon-s, 6.6cqw); }
.card-face[data-card-type="eternal"] .card-face-effect { gap: var(--cf-s-gap-s, 1.5cqw); }
.card-face[data-card-type="first"] .card-face-effect.is-basic { position: absolute; left: 50%; transform: translateX(-50%); top: var(--cf-f-basic-y, 6.5cqw); width: var(--cf-f-basic-w, 58cqw); font-size: calc(var(--cf-f-basic-s, 2.8cqw) * var(--cf-fit, 1)); }
.card-face[data-card-type="first"] .card-face-title { position: absolute; left: 50%; transform: translateX(-50%); top: var(--cf-f-title-y, 23.5cqw); width: var(--cf-f-title-w, 90cqw); font-size: calc(var(--cf-f-title-s, 3.5cqw) * var(--cf-fit, 1)); }
.card-face[data-card-type="first"] .card-face-title rt,
.card-face[data-card-type="first"] .card-face-title .card-face-title-rt { font-size: calc(var(--cf-f-ruby-s, 1.6cqw) * var(--cf-fit, 1)); transform: translateX(-50%) translateY(var(--cf-f-ruby-oy, 0cqw)); }
.card-face[data-card-type="first"] .card-face-subtitle { position: absolute; left: 50%; transform: translateX(-50%); top: var(--cf-f-sub-y, 58.5cqw); width: var(--cf-f-sub-w, 90cqw); font-size: calc(var(--cf-f-sub-s, 3.8cqw) * var(--cf-fit, 1)); }
.card-face[data-card-type="first"] .card-face-hand-icon { position: absolute; left: var(--cf-f-handicon-x, 9cqw); top: var(--cf-f-handicon-y, 69cqw); width: var(--cf-f-handicon-s, 8cqw); height: var(--cf-f-handicon-s, 8cqw); }
.card-face[data-card-type="first"] .card-face-effect.is-hand-cost { position: absolute; left: 50%; transform: translateX(-50%); top: var(--cf-f-handcost-y, 69cqw); width: var(--cf-f-handcost-w, 64.5cqw); font-size: calc(var(--cf-f-handcost-s, 2.9cqw) * var(--cf-fit, 1)); }
.card-face[data-card-type="first"] .card-face-effect.is-hand { position: absolute; left: 50%; transform: translateX(-50%); top: var(--cf-f-hand-y, 82.5cqw); width: var(--cf-f-hand-w, 61.5cqw); font-size: calc(var(--cf-f-hand-s, 2.9cqw) * var(--cf-fit, 1)); }

/* ===== カード面エディタ（card-render-preview.js・開発用） ===== */
#card-render-editor {
  position: fixed; inset: 3vh 3vw; z-index: 2760;
  background: #0b0f14; color: #e2e8f0; border-radius: 12px;
  box-shadow: 0 12px 48px rgba(0,0,0,0.6); display: flex; flex-direction: column;
  overflow: hidden; border: 1px solid #2a3446;
}
.cf-ed-header { flex: 0 0 auto; display: flex; align-items: center; gap: 0.8rem; padding: 0.6rem 1rem; border-bottom: 1px solid #2a3446; background: #0f141b; flex-wrap: wrap; }
.cf-ed-title { font-weight: 700; font-size: 0.95rem; }
.cf-ed-picker { background: #0f1520; color: #f1f5f9; border: 1px solid #2a3446; border-radius: 6px; padding: 0.3rem 0.5rem; font-size: 0.85rem; max-width: 16rem; }
.cf-ed-outline-toggle { font-size: 0.82rem; display: inline-flex; align-items: center; gap: 0.3rem; cursor: pointer; }
.cf-ed-body { flex: 1; display: flex; min-height: 0; }
.cf-ed-stage { flex: 1; min-width: 0; display: flex; align-items: center; justify-content: center; padding: 1.5rem; background: radial-gradient(circle at 50% 40%, #1a2230, #0b0f14 70%); overflow: auto; }
/* 1枚を最大限大きく（正方形。左エリアと画面高に収まる範囲で最大） */
.cf-ed-cardwrap { width: min(56rem, 82vh, 100%); }
.cf-ed-cardwrap .card-face { width: 100%; }
.cf-ed-side { flex: 0 0 24rem; overflow-y: auto; padding: 0.6rem 0.9rem 1.2rem; border-left: 1px solid #2a3446; background: #0d1219; }
.cf-ed-group { margin-bottom: 0.7rem; padding-bottom: 0.5rem; border-bottom: 1px solid #1c2634; }
.cf-ed-group-title { font-weight: 700; color: #93c5fd; margin: 0.35rem 0; font-size: 0.9rem; }
.cf-ed-prop { display: flex; align-items: center; gap: 0.4rem; margin: 0.2rem 0; }
.cf-ed-prop-label { flex: 0 0 5rem; font-size: 0.78rem; color: #cbd5e1; }
.cf-ed-prop input[type="range"] { flex: 1; min-width: 4rem; }
.cf-ed-prop-num { flex: 0 0 3.6rem; background: #0f1520; color: #f1f5f9; border: 1px solid #2a3446; border-radius: 5px; padding: 0.15rem 0.3rem; font-size: 0.78rem; }
.cf-ed-prop-unit { flex: 0 0 2rem; font-size: 0.72rem; color: #64748b; }
.cf-ed-btns { display: flex; gap: 0.6rem; margin-top: 0.8rem; }
.cf-ed-btns button { background: #1d283a; color: #e2e8f0; border: 1px solid #2a3446; border-radius: 6px; padding: 0.4rem 0.8rem; cursor: pointer; font-size: 0.82rem; }
.cf-ed-btns button:hover { background: #24324a; }
@media (max-width: 900px) { .cf-ed-body { flex-direction: column; } .cf-ed-side { flex: 0 0 auto; border-left: none; border-top: 1px solid #2a3446; } .cf-ed-cardwrap { width: min(72vw, 60vh); } }
.cf-ed-groupnote { font-size: 0.78rem; color: #93c5fd; background: #10202e; border: 1px solid #1c3a52; border-radius: 6px; padding: 0.3rem 0.5rem; margin-bottom: 0.5rem; }

/* ===== 英語表示のカード面レイアウト微調整 ===== */
/* 英語(en)は日本語より行長が違うため、効果文・フレーバーを少し縮めて枠に収める。
   [data-lang="en"][data-card-type=…] は生成側 (0,3,0/0,4,0) より高い特異性なので、
   ファイル内の位置に関係なく上書きできる（実機で見ながら要微調整）。 */
.card-face[data-lang="en"][data-card-type="normal"] .card-face-fx,
.card-face[data-lang="en"][data-card-type="eternal"] .card-face-fx { font-size: calc(2.75cqw * var(--cf-fit, 1)); }
.card-face[data-lang="en"][data-card-type="normal"] .card-face-flavor,
.card-face[data-lang="en"][data-card-type="eternal"] .card-face-flavor { font-size: calc(2.2cqw * var(--cf-fit, 1)); }
.card-face[data-lang="en"][data-card-type="normal"] .card-face-fx .card-face-effect.is-basic,
.card-face[data-lang="en"][data-card-type="eternal"] .card-face-fx .card-face-effect.is-basic { font-size: calc(2.4cqw * var(--cf-fit, 1)); }
.card-face[data-lang="en"][data-card-type="first"] .card-face-title { font-size: calc(3.2cqw * var(--cf-fit, 1)); }
.card-face[data-lang="en"][data-card-type="first"] .card-face-effect.is-basic { font-size: calc(2.5cqw * var(--cf-fit, 1)); }
.card-face[data-lang="en"][data-card-type="first"] .card-face-effect.is-hand,
.card-face[data-lang="en"][data-card-type="first"] .card-face-effect.is-hand-cost { font-size: calc(2.5cqw * var(--cf-fit, 1)); }

/* ============================================================================
   #4「このターンの出来事」ストック（獲得／捨て／ドロー／奪取の中央フラッシュ →
   右下ボタン列の上の横帯へ積み、ターンが変わるたび掃除）。
   ユーザー確定仕様: 中央で一瞬フラッシュ→ストックへ飛ばす／全員分／ターンが変わるたび掃除／
   置き場所は右下ボタン列のさらに上の横帯／「何によって」も簡単に添える。
   ============================================================================ */
:root {
  --turn-event-stock-bottom: 16.5rem;
  --turn-event-stock-right: 1.2rem;
  --turn-event-stock-chip: 3rem;
}
/* 中央フラッシュ（従来は右下に出しっぱなしのトーストだった）。position:fixed のまま、
   中央寄せだけ上書きする。飛翔中(is-flying)は実座標に固定して transform で飛ばすので、
   中央寄せの transform を打ち消す必要がある＝クラスを外して is-flying を付ける実装。 */
.hand-pickup-toast.is-flash {
  top: 50%;
  left: 50%;
  right: auto;
  bottom: auto;
  transform: translate(-50%, -50%) scale(0.94);
}
.hand-pickup-toast.is-flash.show {
  transform: translate(-50%, -50%) scale(1);
}
.hand-pickup-toast.is-flying {
  opacity: 1;
  pointer-events: none;
  transition: transform 0.42s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.42s ease;
}
/* 【ユーザー要望2026-09-05・#282「このモーダルの余白が気になる！」】
   中央のフラッシュは、見出しや「〜の効果」の一行の方がカードより横に長いことが多く、
   カード（4.2rem）だけが左端に寄って右側に大きな空白ができていた（右下トーストのままの
   左揃えを中央でもそのまま使っていたため）。中央に出ている間だけ中身を横位置の中央に
   揃え、カード名もカードの幅に縛られず折り返しにくくする。右下へ畳んだ後のチップや、
   従来の右下トーストの見た目は変えない。 */
.hand-pickup-toast.is-flash .hand-pickup-toast-title,
.hand-pickup-toast.is-flash .hand-pickup-toast-reason,
.hand-pickup-toast.is-flash .hand-pickup-toast-hidden-note {
  text-align: center;
}
.hand-pickup-toast.is-flash .hand-pickup-toast-cards {
  justify-content: center;
}
.hand-pickup-toast.is-flash .hand-pickup-toast-card {
  width: auto;
  min-width: calc(4.2rem * var(--hand-pickup-toast-scale));
  max-width: calc(9rem * var(--hand-pickup-toast-scale));
}
/* カード画像は display:block の固定幅なので、幅が広がった枠の中では中央に置き直す。 */
.hand-pickup-toast.is-flash .hand-pickup-toast-card img,
.hand-pickup-toast.is-flash .hand-pickup-toast-img {
  margin-left: auto;
  margin-right: auto;
}
/* ✕ボタンのぶん右だけ広くしていた余白は、中央揃えでは左右の差が目立つので揃える。 */
.hand-pickup-toast.is-flash {
  padding-left: calc(1.6rem * var(--hand-pickup-toast-scale));
}
/* 【ユーザー報告2026-09-05・#281】対戦が終わってホームに戻っても、右上にターン数・
   ラウンド数が残っていた。#turn-round-counter は対局中の情報なので、全画面ページ
   （ホーム/マイページ/ランキング/図鑑/マイデッキ）が開いている間は隠す。JS側は
   inline style で display:block を書くため !important が要る。 */
body.full-screen-page-active #turn-round-counter {
  display: none !important;
}

/* 「〜の効果で」等、何によって起きたかの一行。 */
.hand-pickup-toast-reason {
  font-size: calc(0.66rem * var(--hand-pickup-toast-scale));
  color: #93c5fd;
  margin-bottom: 0.3rem;
}
/* 横帯本体。右下のアクションボタン列（#hand-shuffle-button 等、bottom:12.6rem〜）の上。 */
#turn-event-stock {
  position: fixed;
  right: var(--turn-event-stock-right);
  bottom: var(--turn-event-stock-bottom);
  z-index: 1000;
  display: none;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.25rem;
  font-family: sans-serif;
  pointer-events: auto;
}
#turn-event-stock-label {
  font-size: 0.6rem;
  color: rgba(226, 232, 240, 0.65);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
  letter-spacing: 0.04em;
}
#turn-event-stock-list {
  display: flex;
  flex-direction: row;
  gap: 0.3rem;
  /* 【#332・2026-09-07】以前は max-width: min(46rem, 62vw) だった。vw は**実画面**の幅で
     測られるのに、この帯は 1600x900 の「ステージ」の中に置かれている（変形した祖先の中では
     position:fixed が absolute のように振る舞う）ため、vw の値がステージ座標では実質2倍前後に
     効いてしまい、帯が左へ伸びて**盤面に食い込んで**いた（実測: 盤面の右端はステージ座標で
     約1253px、帯の右端は約1581px＝空きは約20rem しかないのに、上限は最大36rem相当だった）。
     ステージは端末に関係なく同じ大きさなので、正しい単位は rem。チップ5枚ぶんで打ち止めにし、
     それ以上は横スワイプで見る。 */
  max-width: 17rem;
  overflow-x: auto;
  overflow-y: hidden;
  /* 指で横に払って見られるようにする。盤面側は touch-action:none で掴む操作を拾うので、
     この帯だけは横方向のスワイプとして扱うと明示する。 */
  touch-action: pan-x;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  padding: 0.25rem;
  border-radius: 0.45rem;
  background: rgba(15, 23, 32, 0.72);
  border: 1px solid rgba(148, 163, 184, 0.32);
  scrollbar-width: thin;
}
.turn-event-stock-chip {
  position: relative;
  flex: 0 0 auto;
  width: var(--turn-event-stock-chip);
  height: var(--turn-event-stock-chip);
  padding: 0;
  border: 1px solid rgba(148, 163, 184, 0.45);
  border-radius: 0.3rem;
  background: rgba(30, 41, 59, 0.9);
  cursor: pointer;
  overflow: hidden;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 0.2s ease, transform 0.2s ease, border-color 0.15s ease;
}
.turn-event-stock-chip.show {
  opacity: 1;
  transform: scale(1);
}
.turn-event-stock-chip:hover {
  border-color: rgba(56, 189, 248, 0.85);
}
.turn-event-stock-chip-face {
  position: relative;
  width: 100%;
  height: 100%;
  background-size: cover;
  background-position: center;
  overflow: hidden;
}
.turn-event-stock-chip-face.is-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.1rem;
}
.turn-event-stock-chip-badge {
  position: absolute;
  right: 0;
  bottom: 0;
  font-size: 0.62rem;
  line-height: 1;
  padding: 0.1rem 0.15rem;
  background: rgba(15, 23, 32, 0.85);
  border-top-left-radius: 0.25rem;
}
/* 【ユーザー要望2026-09-05】「このターンの出来事」の**下**に、前のターンの出来事も残す行。
   普段は畳んで2〜3枚だけ半透明で重ね、押すと開く。帯全体は画面下端からの距離で位置が
   決まっているので、この行が増えても下のボタン列を押し下げず、上へ伸びる。 */
#turn-event-stock-prev {
  display: none;
  flex-direction: row;
  align-items: center;
  gap: 0.3rem;
}
#turn-event-stock-prev-label {
  flex: 0 0 auto;
  font-size: 0.58rem;
  line-height: 1;
  padding: 0.2rem 0.35rem;
  color: rgba(226, 232, 240, 0.62);
  background: rgba(15, 23, 32, 0.62);
  border: 1px solid rgba(148, 163, 184, 0.28);
  border-radius: 0.35rem;
  cursor: pointer;
  font-family: inherit;
  white-space: nowrap;
}
#turn-event-stock-prev-label:hover {
  color: rgba(226, 232, 240, 0.95);
  border-color: rgba(56, 189, 248, 0.7);
}
#turn-event-stock-prev-list {
  display: flex;
  flex-direction: row;
  gap: 0.3rem;
  /* 【#332・2026-09-07】以前は max-width: min(46rem, 62vw) だった。vw は**実画面**の幅で
     測られるのに、この帯は 1600x900 の「ステージ」の中に置かれている（変形した祖先の中では
     position:fixed が absolute のように振る舞う）ため、vw の値がステージ座標では実質2倍前後に
     効いてしまい、帯が左へ伸びて**盤面に食い込んで**いた（実測: 盤面の右端はステージ座標で
     約1253px、帯の右端は約1581px＝空きは約20rem しかないのに、上限は最大36rem相当だった）。
     ステージは端末に関係なく同じ大きさなので、正しい単位は rem。チップ5枚ぶんで打ち止めにし、
     それ以上は横スワイプで見る。 */
  max-width: 17rem;
  overflow-x: auto;
  overflow-y: hidden;
  /* 指で横に払って見られるようにする。盤面側は touch-action:none で掴む操作を拾うので、
     この帯だけは横方向のスワイプとして扱うと明示する。 */
  touch-action: pan-x;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  padding: 0.2rem;
  border-radius: 0.4rem;
  background: rgba(15, 23, 32, 0.5);
  border: 1px solid rgba(148, 163, 184, 0.2);
  scrollbar-width: thin;
}
#turn-event-stock-prev-count {
  flex: 0 0 auto;
  font-size: 0.58rem;
  color: rgba(226, 232, 240, 0.6);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
}
/* 畳んでいる時: 先頭3枚だけを少し重ねて小さく・薄く見せる（枚数は JS の
   COLLAPSED_PREVIEW_COUNT と必ず揃えること）。 */
#turn-event-stock-prev.is-collapsed #turn-event-stock-prev-list {
  gap: 0;
  overflow: visible;
  cursor: pointer;
}
#turn-event-stock-prev.is-collapsed .turn-event-stock-chip {
  width: calc(var(--turn-event-stock-chip) * 0.72);
  height: calc(var(--turn-event-stock-chip) * 0.72);
  opacity: 0.42;
  pointer-events: none;
}
#turn-event-stock-prev.is-collapsed .turn-event-stock-chip + .turn-event-stock-chip {
  margin-left: calc(var(--turn-event-stock-chip) * -0.42);
}
#turn-event-stock-prev.is-collapsed .turn-event-stock-chip:nth-child(n + 4) {
  display: none;
}
#turn-event-stock-prev.is-collapsed:hover .turn-event-stock-chip {
  opacity: 0.75;
}
/* 開いている時も、今のターンの行より一段控えめにして主役を取り違えないようにする。 */
#turn-event-stock-prev:not(.is-collapsed) .turn-event-stock-chip {
  opacity: 0.85;
}
body.theme-light #turn-event-stock-prev-list,
body.theme-light-ingame #turn-event-stock-prev-list,
body.theme-light #turn-event-stock-prev-label,
body.theme-light-ingame #turn-event-stock-prev-label {
  background: rgba(255, 253, 245, 0.8);
  border-color: rgba(120, 113, 108, 0.35);
  color: #3a3326;
}
body.theme-light #turn-event-stock-prev-count,
body.theme-light-ingame #turn-event-stock-prev-count {
  color: #3a3326;
  text-shadow: none;
}
/* チップをクリックした時に、飛ばす前と同じ中身をもう一度見せる小さなモーダル。 */
#turn-event-stock-detail {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 10701;
  max-width: min(24rem, 92vw);
  background: rgba(15, 23, 32, 0.97);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.5rem;
  padding: 1.1rem 1.6rem 0.9rem 0.9rem;
  color: #e2e8f0;
  font-family: sans-serif;
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.55);
}
/* ライトモード（body.theme-light / 盤面だけの theme-light-ingame）配色。 */
body.theme-light #turn-event-stock-list,
body.theme-light-ingame #turn-event-stock-list,
body.theme-light #turn-event-stock-detail,
body.theme-light-ingame #turn-event-stock-detail {
  background: rgba(255, 253, 245, 0.96);
  border-color: rgba(120, 113, 108, 0.4);
  color: #1e293b;
}
body.theme-light #turn-event-stock-label,
body.theme-light-ingame #turn-event-stock-label {
  color: #475569;
  text-shadow: 0 1px 2px rgba(255, 255, 255, 0.8);
}
body.theme-light .turn-event-stock-chip,
body.theme-light-ingame .turn-event-stock-chip {
  background: rgba(255, 255, 255, 0.92);
  border-color: rgba(120, 113, 108, 0.45);
}
body.theme-light .turn-event-stock-chip-badge,
body.theme-light-ingame .turn-event-stock-chip-badge {
  background: rgba(255, 253, 245, 0.9);
}

/* ユーザー報告2026-08-28（続き302）「マイデッキ編集画面で上部の名前設定とかがオプション
   エリアの黒帯に隠れちゃってる。ほかのページも同様になってないか確認しておいて」。
   #option-area（上端のアイコン列＋その装飾帯 ::before、高さ --option-area-band-height）は
   全画面ページより手前に出す設計（body.full-screen-page-active 等でz-indexを引き上げる）なので、
   各ページの一番上にある要素（← 戻る・タイトル・完了ボタン・枚数表示など）が帯の下に潜り込む。
   ページ側で帯の高さぶんの余白を必ず確保して防ぐ。スマホは #option-area 全体が
   --option-area-scale-phone 倍に拡大されるため、余白も同じ倍率にする。
   ここに置いてあるのは、各ページの元の padding 指定（ファイル内の別の場所）より後に効かせて
   確実に上書きするため（同じ詳細度なので後勝ち）。 */
:root {
  --page-top-safe: calc(var(--option-area-band-height, 3rem) + 0.4rem);
}
body.is-phone-device {
  --page-top-safe: calc((var(--option-area-band-height, 3rem) + 0.4rem) * var(--option-area-scale-phone, 1.65));
}
/* 上端に余白を持っていなかったページ（自前のヘッダーバーが画面最上部に密着していた）。 */
#my-deck-page,
#my-deck-list-page,
#shop-panel {
  padding-top: var(--page-top-safe);
  box-sizing: border-box;
}
/* 元々 2rem の余白があるページ。帯より狭い時だけ広げる（見た目を大きく変えない）。 */
#home-screen,
#profile-page,
#ranking-page,
#codex-page {
  padding-top: max(2rem, var(--page-top-safe));
  box-sizing: border-box;
}

/* ── マイデッキ編成ページの「デッキの箱」（続き302、ユーザー要望2026-08-28）──────────
   「マイデッキ編集画面に、マイデッキの箱絵を追加したい。箱にカードをドラッグすると箱絵が
   そのカードになるようにしたい」。下段（現在のデッキ）の左に立体ケースを置き、カードを
   ドロップすると deck.mainCardId ＝箱の表紙になる。箱の見た目はマイデッキ一覧・マイページと
   同じ部品(.mdl-deck-art)の使い回し。 */
#mdb-deck-body {
  flex: 1 1 auto;
  display: flex;
  align-items: stretch;
  gap: 0.9rem;
  min-height: 0;
  padding-left: 1.1rem;
}
#mdb-deck-body #mdb-deck-list {
  flex: 1 1 auto;
  min-width: 0;
  padding-left: 0;
}
#mdb-deck-case {
  flex: 0 0 auto;
  align-self: flex-start;
  width: 9rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  padding: 0.3rem 0.3rem 0.45rem;
  border: 1px dashed rgba(212, 175, 90, 0.45);
  border-radius: 0.5rem;
  background: rgba(255, 255, 255, 0.03);
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
#mdb-deck-case.is-drop-target {
  border-color: #d4af5a;
  border-style: solid;
  background: rgba(212, 175, 90, 0.18);
  box-shadow: 0 0 0 2px rgba(212, 175, 90, 0.35);
}
/* .mdl-deck-art は width:100% + 1:1 なので、箱の枠側で実寸を決める。 */
#mdb-deck-case .mdl-deck-art {
  width: 100%;
  pointer-events: none; /* 中身の画像がドラッグのドロップ判定を邪魔しないように */
}
#mdb-deck-case-hint {
  order: 2; /* 箱（先頭に挿入される）の下に置く */
  font-size: 0.6rem;
  line-height: 1.35;
  color: #94a3b8;
  text-align: center;
}
body.theme-light #mdb-deck-case {
  border-color: rgba(176, 141, 63, 0.5);
  background: rgba(176, 141, 63, 0.08);
}
body.theme-light #mdb-deck-case-hint {
  color: #6b5c3c;
}

/* ── 称号（続き313、src/titles.js）──────────────────────────────────────────
   マイページの「称号コレクション」と、左下ステータスに出るお気に入りの称号。
   取得済みは金の縁取り、未取得は🔒で沈めて「あと何をすれば取れるか」をtitleで見せる。 */
.my-page-titles {
  margin-top: 0.8rem;
}
.my-page-titles-heading {
  font-size: 0.8rem;
  font-weight: bold;
  color: #d4af5a;
  letter-spacing: 0.08em;
  margin-bottom: 0.2rem;
}
.my-page-titles-status {
  font-size: 0.7rem;
  color: #94a3b8;
  margin-bottom: 0.4rem;
}
.my-page-titles-group {
  margin-bottom: 0.5rem;
}
.my-page-titles-group-name {
  font-size: 0.65rem;
  font-weight: bold;
  color: #94a3b8;
  margin-bottom: 0.2rem;
}
/* 続き318: ユーザー要望「各称号が不ぞろいに並んでいるのが気になる。碁盤目状にきれいに
   並んでいると美しい。モーダルは大きくなっても構わない」。幅バラバラの折り返し(flex-wrap)を
   やめ、同じ幅の升目に揃うグリッドにする。列数は幅に応じて自動（auto-fill）。 */
.my-page-titles-row {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(10.5rem, 1fr));
  gap: 0.4rem;
}
.my-page-title-chip {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  width: 100%;
  min-width: 0;
  min-height: 2.1rem;
  padding: 0.25rem 0.5rem;
  /* 升目に並べるので、丸ピルより角丸の方が整って見える */
  border-radius: 0.4rem;
  white-space: nowrap;
  overflow: hidden;
  border: 1px solid rgba(212, 175, 90, 0.45);
  background: rgba(212, 175, 90, 0.12);
  color: #f1e6cd;
  font-size: 0.72rem;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, transform 0.1s;
}
.my-page-title-chip:hover {
  background: rgba(212, 175, 90, 0.22);
}
.my-page-title-chip.is-selected {
  background: linear-gradient(180deg, rgba(212, 175, 90, 0.55), rgba(212, 175, 90, 0.3));
  border-color: #d4af5a;
  color: #fffdf8;
  box-shadow: 0 0 10px rgba(212, 175, 90, 0.5);
  font-weight: bold;
}
.my-page-title-chip.is-locked {
  border-color: rgba(148, 163, 184, 0.3);
  background: rgba(148, 163, 184, 0.08);
  color: #94a3b8;
  cursor: default;
}
.my-page-title-chip.is-locked:hover {
  background: rgba(148, 163, 184, 0.12);
}
.my-page-title-icon {
  font-size: 0.85rem;
  line-height: 1;
}
/* 左下ステータスのお気に入り称号（名前の下） */
.self-status-title {
  font-size: 0.62rem;
  color: #d4af5a;
  letter-spacing: 0.04em;
  margin-top: 0.05rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
body.theme-light .self-status-title {
  color: #a67c1a;
}
body.theme-light .my-page-title-chip {
  color: #3a3326;
}
body.theme-light .my-page-title-chip.is-locked {
  color: #8a7d63;
}

/* ── マイページの名前編集ボタン（続き316）────────────────────────────────
   ユーザー報告「保存/取消ボタンが実績のところに食い込む」「取消が白文字で見にくい」。
   この行(name)はレイアウト上3倍前後に拡大されるため、横に伸びると隣の実績へ大きくはみ出す。
   幅は my-page.js 側で抑え（折り返し）、色はここでテーマ別に当てる。以前は取消ボタンが
   インラインstyleで「白文字＋ほぼ透明の背景」だったため、明るい背景のマイページでは
   ほとんど読めなかった。 */
.my-page-name-save,
.my-page-name-cancel {
  flex: 0 0 auto;
  /* 続き318: 横一列に戻したぶん、左右の余白を詰めて全体の幅を抑える */
  padding: 0.2rem 0.4rem;
  white-space: nowrap;
  border-radius: 0.3rem;
  font-size: 0.75rem;
  font-family: inherit;
  cursor: pointer;
  line-height: 1.4;
}
.my-page-name-save {
  background: #be185d;
  border: 1px solid #be185d;
  color: #ffffff;
}
.my-page-name-save:hover {
  background: #9d174d;
}
.my-page-name-cancel {
  background: rgba(148, 163, 184, 0.18);
  border: 1px solid rgba(148, 163, 184, 0.55);
  color: #e2e8f0;
}
.my-page-name-cancel:hover {
  background: rgba(148, 163, 184, 0.3);
}
/* 明るい配色のマイページでは、取消ボタンを白文字ではなく濃い文字＋白背景にする */
body.theme-light .my-page-name-cancel {
  background: #ffffff;
  border-color: rgba(120, 100, 60, 0.55);
  color: #3a3326;
}
body.theme-light .my-page-name-cancel:hover {
  background: #f3ece0;
}
/* 続き318: 称号はマイページ本体ではなくモーダル専用になったため、続き316で入れていた
   幅の上限（実績の枠が横に広がるのを防ぐためのもの）は不要になった（上限があるとグリッドが
   1列に潰れてしまう）。 */

/* ユーザー報告2026-08-28（続き316）「お知らせページの戻るボタンが上の黒帯に干渉している」。
   #changelog-modal も inset:0 の全画面ページなのに、続き302の余白確保の一覧から漏れていた。 */
#changelog-modal {
  padding-top: var(--page-top-safe);
  box-sizing: border-box;
}

/* ===========================================================================
   【続き575・2026-10-11】スマホの文字サイズの総点検（第1弾）
   ユーザー「スマホだと文字が小さいと感じるものが多くあります。総点検して！」

   前提: 1600x900 のステージを画面に収まる倍率へ縮めて表示するので、iPhone の横持ち
   （932x430）では倍率 0.478 ＝ **1rem が実画面で 7.6px** にしかならない。だから
   「CSSで0.85rem」は実画面では 6.5px になる。**実画面のpxで測ってから決める。**
   目安: 読ませるものは13px以上／押すものは高さ28px以上／補助的な文字も11px以上。
   PCの見え方は変えない（すべて body.is-phone-device / is-tablet-device の中）。

   ★ついでに見つけた罠: `max-height: 90vh` は**実画面の43%しか無い**。`vh` はビューポート
   基準なのに、描かれる大きさはステージ倍率（0.478）が掛かるため。ステージ基準の **rem**
   （ステージの高さ = 56.25rem）で書くのが正しい。ここで直す2つのモーダルはそうした。
   =========================================================================== */

/* ① お知らせ／更新情報（全画面ページ。中身は .changelog-list が縦に巻ける＝字を大きくしても
      はみ出さない）。横幅も広げる——50rem（実画面382px）だと画面の41%しか使えず、字を
      大きくすると折り返しばかりになる。 */
body.is-phone-device .changelog-list,
body.is-tablet-device .changelog-list {
  width: min(86rem, 100%);
}
body.is-phone-device .changelog-modal-title,
body.is-tablet-device .changelog-modal-title {
  font-size: 2.35rem;
  margin-bottom: 1.4rem;
  padding-right: 3rem;
}
body.is-phone-device .changelog-date,
body.is-tablet-device .changelog-date {
  font-size: 1.45rem;
  margin-bottom: 0.7rem;
}
body.is-phone-device .changelog-items,
body.is-tablet-device .changelog-items {
  padding-left: 2rem;
}
body.is-phone-device .changelog-items li,
body.is-tablet-device .changelog-items li {
  font-size: 1.7rem;
  margin-bottom: 0.5rem;
}
body.is-phone-device .changelog-fixes-label,
body.is-tablet-device .changelog-fixes-label {
  font-size: 1.7rem;
}
body.is-phone-device .changelog-fixes-toggle,
body.is-tablet-device .changelog-fixes-toggle {
  font-size: 1.5rem;
  padding: 0.4rem 0.9rem;
  border-radius: 0.5rem;
}
body.is-phone-device #changelog-back,
body.is-tablet-device #changelog-back {
  font-size: 1.7rem;
  padding: 0.8rem 1.5rem;
  border-radius: 0.7rem;
}

/* ② サウンドの初回設定モーダル（first-run-bgm.js）。初めて遊ぶ人が最初に触る画面なのに
      実測 説明6.5px／ラベル6.5px／「次へ」7.3px だった。縦の上限も vh から rem へ。 */
body.is-phone-device #first-run-bgm-modal,
body.is-tablet-device #first-run-bgm-modal {
  width: min(60rem, 94vw);
  padding: 2rem 2.2rem 1.8rem;
  border-radius: 1rem;
  max-height: 50rem; /* 90vh では実画面の43%しか無い（上の★参照） */
}
body.is-phone-device .first-run-bgm-title,
body.is-tablet-device .first-run-bgm-title {
  font-size: 2.2rem;
  margin-bottom: 1.1rem;
}
body.is-phone-device .first-run-bgm-desc,
body.is-tablet-device .first-run-bgm-desc {
  font-size: 1.5rem;
  line-height: 1.7;
  margin-bottom: 1.1rem;
}
body.is-phone-device .first-run-bgm-label,
body.is-tablet-device .first-run-bgm-label,
body.is-phone-device .first-run-bgm-value,
body.is-tablet-device .first-run-bgm-value {
  font-size: 1.6rem;
}
/* つまみも指で動かせる太さにする。 */
body.is-phone-device .first-run-bgm-row input[type="range"],
body.is-tablet-device .first-run-bgm-row input[type="range"] {
  height: 2.6rem;
}
body.is-phone-device .first-run-bgm-preview,
body.is-tablet-device .first-run-bgm-preview {
  font-size: 1.6rem;
  padding: 0.7rem 1.3rem;
  border-radius: 0.6rem;
}
body.is-phone-device .first-run-bgm-ok,
body.is-tablet-device .first-run-bgm-ok {
  font-size: 1.9rem;
  padding: 0.9rem 2rem;
  border-radius: 0.7rem;
}

/* ③ オープニングの物語テロップ（初めての人が最初に読む文章なのに実測7.6px）。
      **字を大きくするより先に横幅を広げる**のが肝: 移動量 -160rem は「文章の高さ+ステージの
      高さ」から決め打ちしてあるので（@keyframes opening-story-crawl-scroll のコメント参照）、
      字だけ大きくすると文章が高くなって**上まで抜けきらない**。横幅を 34rem→86rem（2.5倍）に
      広げてから字を1.7倍にすれば、行数が減るので全体の高さはほぼ変わらない。実測で確認する。 */
body.is-phone-device .opening-story-crawl-text,
body.is-tablet-device .opening-story-crawl-text {
  width: min(86rem, 94vw);
  font-size: 1.7rem;
  /* 字を大きくすると文章が高くなるので、**移動量も作り直す**（下の @keyframes）。
     横幅を広げても高さは変わらなかった——実測で幅60/70/76/82/90rem のどれでも 156.3rem で、
     **行が折り返していない**（高さ＝行数×行間）ことが分かった。最初は「幅を広げれば行数が
     減って高さが戻る」と考えたが、それは外れだった。 */
  animation-name: opening-story-crawl-scroll-phone;
}
/* スマホ用の移動量: 文章の高さ156.3rem ＋ ステージの高さ56.25rem ＝ 212.6rem 必要
   （PC用の -160rem では上まで抜けきらず、最後の数行が画面に残る＝実測で確認）。
   余裕を見て216rem。時間は40秒のままなので速くはなるが、**1秒あたりに流れる行数は減る**
   （字1.7倍・距離1.35倍）＝読みやすくなる方向。文章を増減したらこの値も調整が必要。 */
@keyframes opening-story-crawl-scroll-phone {
  from { transform: translateX(-50%) translateY(0); opacity: 0; }
  5% { opacity: 1; }
  95% { opacity: 1; }
  to { transform: translateX(-50%) translateY(-216rem); opacity: 0; }
}
body.is-phone-device .opening-story-crawl-skip-hint,
body.is-tablet-device .opening-story-crawl-skip-hint {
  font-size: 1.6rem;
}

/* 【続き575・第2弾】対局中ずっと目に入る表示と、押すものの文字（実測値は下の各コメント）。 */

/* アイコンの下の小さな文字（ロック／ハンド／ムーブ／自動送り／盤面拡大 等）。実測5.0px。
   `white-space: nowrap` で横に伸びるので、隣のアイコンの文字と重なり得る。当てた後に
   重なりを測って確かめる。 */
body.is-phone-device .icon-action-button-caption,
body.is-tablet-device .icon-action-button-caption {
  font-size: 1.45rem;
  padding: 0.1rem 0.5rem;
  border-radius: 0.5rem;
}
/* 【訂正】このクラスは**2か所で実効倍率が違う**: オプションエリア（倍率0.477）と
   フェイズ案内板（倍率1.002＝案内板自体が2.1倍に拡大されている）。一律に1.45remへ
   上げたら案内板側が**実画面23.2px**になった（元は10.5pxで十分だった）。
   案内板の中だけ元の大きさへ戻す。 */
body.is-phone-device #phase-guide-bar .icon-action-button-caption,
body.is-tablet-device #phase-guide-bar .icon-action-button-caption {
  font-size: 0.65rem;
  padding: 0.05rem 0.4rem;
}

/* 自動送りのON/OFFバッジ。実測3.8px。丸アイコンの右下に重ねる小さな印で、枠の色
   （緑=ON／赤=OFF）でも分かるようにはなっているが、3.8pxは何が書いてあるか読めない。 */
body.is-phone-device .phase-auto-skip-toggle .phase-auto-skip-toggle-state,
body.is-tablet-device .phase-auto-skip-toggle .phase-auto-skip-toggle-state {
  /* 1rem まで大きくしたら **⏭アイコンの半分以上を覆った**（実測16x10の重なり）。
     少し小さくして、丸の外側へ押し出す。ON/OFFは枠の色（緑／赤）でも分かるので、
     ここは「読めれば十分」。 */
  /* 右下へ逃がしたら、今度は下の「自動送り」の文字に乗った（実測32x7の重なり）。
     丸の**上の角**へ移す——上には何も無い（ターン表示はバーの0.6rem上で、しかも右寄せ）。 */
  /* 【訂正】案内板自体が2.1倍なので 1rem では実画面16.2px（元は8.1px）。
     0.62rem＝約10pxに収め、位置も元の右下へ戻す（小さければアイコンを覆わない）。 */
  font-size: 0.62rem;
  padding: 0.03rem 0.25rem;
}

/* ターン数・ラウンド数（画面右上）。実測5.0px。管理者モードのスライダー
   （--turn-round-counter-font-size）はPC用の値なので、スマホ用の変数を別に用意して
   既定を大きくする（続き570 で「既定を変える時に人の設定を踏まないように」と学んだ形）。
   行間が1だと文字の上下が切れていた（実測 113x13 > 113x10 のはみ出し）ので直す。 */
body.is-phone-device #turn-round-counter,
body.is-tablet-device #turn-round-counter {
  /* 【訂正】この表示はオプションエリア（1.65倍）の中にあり実効倍率0.79。1.45remでは
     実画面18.3pxで大きすぎた（元は8.2px）。1rem＝約12.6px。 */
  font-size: var(--turn-round-counter-font-size-phone, 1rem);
  line-height: 1.4;
}

/* 自分の名前と手札の枚数（自席のステータス）。実測6.5px／5.7px。
   どちらも inline-block で自前の帯を持つので、大きくすると帯も一緒に広がる。 */
/* 【訂正】自席のステータスは元から1.75倍に拡大されており（実効倍率0.836）、名前は
   実画面11.4px・手札は10.0pxで**ほぼ足りていた**。1.5remにしたら20.1pxで大きすぎた。 */
body.is-phone-device .self-status-name,
body.is-tablet-device .self-status-name {
  font-size: 0.9rem;
  padding: 0.1rem 0.5rem;
}
body.is-phone-device .self-status-hand-count,
body.is-tablet-device .self-status-hand-count {
  font-size: 0.85rem;
  padding: 0.1rem 0.5rem;
}

/* 「自分のターンです」「相手のターンです」。実測6.1px。案内板バーの真上に浮いていて
   右端基準で左へ伸びる作りなので、大きくしても画面の外へは出ない方向に伸びる。 */
/* 【訂正】実効倍率0.602（案内板の一部が拡大されている）。1.6remでは15.4pxで少し大きい。 */
body.is-phone-device #phase-automation-turn-status,
body.is-tablet-device #phase-automation-turn-status {
  font-size: 1.3rem;
  padding: 0.5rem 0.9rem;
}

/* モーダルを閉じる ✕。実測8.4px・当たり判定も小さい（1.8rem＝実画面14px）。
   指で押せる大きさにする。 */
body.is-phone-device .modal-close-x,
body.is-tablet-device .modal-close-x {
  /* 3.2rem まで大きくしたら、モーダルの見出しの箱に被る量が増えた（実測 11x11 → 21x11）。
     見出し側は padding-right 1.6rem 程度しか空けていないものが多いので 2.6rem に抑える
     （実画面20px。文字は12.4pxで読める）。 */
  width: 2.6rem;
  height: 2.6rem;
  font-size: 1.6rem;
}

/* 【続き575・第3弾】「今回のメンバー」と「スタートプレイヤー決定」。 */

/* 「あなた／対戦相手」。実測6.5px。管理者モードのつまみ（--match-intro-seat-size）は
   PC用の値なので、スマホ用の変数を別に用意する（つまみも admin.js に足した）。 */
body.is-phone-device .match-intro-seat,
body.is-tablet-device .match-intro-seat {
  font-size: var(--match-intro-seat-size-phone, 1.5rem);
}
/* 「画面をタップすると飛ばせます」。実測4.6px＝ほぼ読めない。 */
body.is-phone-device .match-intro-hint,
body.is-tablet-device .match-intro-hint {
  font-size: 1.4rem;
  bottom: 1rem;
}

/* 「３：スタートプレイヤー決定」の発表モーダル（game-setup.js の showStartPlayerModal）。
   見出し7.3px・本文6.9px、しかも幅が min(20rem, 92vw) ＝ **実画面153px** しか無く、
   10文字ほどで折り返していた。文字サイズはクラスへ移したので素直に指定できる。
   幅と高さの上限は inline style で書かれているため `!important` でないと上書きできない
   （buildSimpleModal が cssText で当てている）。 */
.start-player-title {
  font-size: 0.95rem;
}
.start-player-body {
  font-size: 0.9rem;
}
body.is-phone-device .start-player-title,
body.is-tablet-device .start-player-title {
  font-size: 1.8rem;
}
body.is-phone-device .start-player-body,
body.is-tablet-device .start-player-body {
  font-size: 1.6rem;
}
body.is-phone-device .start-player-announce,
body.is-tablet-device .start-player-announce {
  width: min(56rem, 94vw) !important;
  max-height: 46rem !important; /* 85vh は実画面の41%しか無い（vhの罠。上の★参照） */
  padding: 1.6rem !important;
}

/* ===========================================================================
   【続き576・2026-10-11】スマホの文字サイズ（第2弾・訂正後）
   **実効倍率 = getBoundingClientRect().width / offsetWidth** で測り直した結果だけを入れる。
   最初は「CSSのfont-size × ステージ倍率」で測っていて、**パネル自体がさらに transform で
   拡大されている場合を見落としていた**。そのため直す必要の無い所（ヘルプ＝元から2倍で
   12.6〜16px、マイページの名前＝元から3倍で12.2px）まで大きくしてしまい、23〜37pxに
   なっていた。それらは撤去した。
   =========================================================================== */

/* 【訂正2】α版のお知らせは**触らない**。正しい式（rem × 16 × 実効倍率0.861）で計算すると
   見出し17.2px／アイコン17.9px／導入12.4px／小見出し12.7px／本文11.3px／お礼11.7px／
   「わかりました」13.8px／「今後表示しない」10.3px で、**元から十分読める大きさだった**。
   一度2.35remまで上げて32.3pxにしてしまったので撤去した（前回は ×16 を落として計算して
   いたため「7.1px」と誤読していた）。 */

/* 【続き585・2026-10-12】**マイページのスマホ用の文字拡大は取り消した。**
   ユーザー「スマホでのホーム画面とマイページ画面ですが昨日文字サイズ調整してるんだけど、
   する前のがよかったです」。続き576 で着せ替え・段位まわり（7.9〜9.1px）を12px台へ、
   続き577 でパネルの幅・余白・題名を大きくしていたが、**読みやすさより見た目の
   まとまりの方が大事だった**という判断。数字の上では「11px以上」の目安を満たしていても、
   ユーザーが見て良くないなら戻す。
   ⚠ **`#my-page-panel` の基本の規則（上）は残す。**あれは続き577 で my-page.js の
   inline style から移してきたもので、PC の値は移す前とまったく同じ。消すとパネルの
   見た目そのものが無くなる（my-page.js 側はもう inline で持っていない）。
   共通の ✕ だけは全モーダル向けの拡大（2.6rem）が効いてしまうので、ヘルプと同じ形で
   このパネルの中だけ元の大きさに戻す。 */
body.is-phone-device #my-page-panel .modal-close-x,
body.is-tablet-device #my-page-panel .modal-close-x {
  width: 1.8rem;
  height: 1.8rem;
  font-size: 1.1rem;
}

/* 【訂正2】ヘルプのパネルは**元から2倍**（実効倍率0.956）なので、共通の ✕ の拡大が効いて
   実画面24.2pxになっていた。ヘルプの中だけ元の大きさへ戻す。
   ——同じクラスでも**入れ物の倍率が違えば見え方が違う**。共通クラスを触る時は、
   そのクラスが出てくる場所を全部測ること（アイコンの下の文字でも同じ罠を踏んだ）。 */
body.is-phone-device #help-panel .modal-close-x,
body.is-tablet-device #help-panel .modal-close-x {
  width: 1.8rem;
  height: 1.8rem;
  font-size: 1.1rem;
}

/* 【続き585・2026-10-12】**ホーム画面のスマホ用の文字拡大も取り消した**（上と同じ理由）。
   取り消したのは3つの塊: ①続き576 の文字の拡大（タイルの名前・「次にやること」・
   副題・各バッジ・「ホーム画面に追加」の案内バー）②続き578 の #402 対応（①で縦に
   伸びた分を取り返すために余白・タイルの間隔・絵の大きさを詰めたもの）。
   🔴 **②は①があって初めて必要になったものなので、①だけ戻すと余白が詰まったままの
   中途半端な見た目になる。必ず両方まとめて戻す。**
   ③ #402「ホーム画面がスクロールするようになった」は①を戻すことで一緒に解消する
   （実測: はみ出し 30px＝続き575 より前と同じ値に戻った。この30pxは元からある
   丸め誤差で、見た目には出ていない）。 */

/* 【続き577・2026-10-11】マイページのパネル。**my-page.js の inline style から移してきた**
   （inline だと CSS から上書きできず、スマホ用の文字サイズを足せなかった）。
   PC の値は移す前とまったく同じ。 */
#my-page-panel {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(24rem, 92vw);
  max-height: 85vh;
  overflow-y: auto;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(148, 163, 184, 0.4);
  border-radius: 0.6rem;
  padding: 1.2rem;
  z-index: 2301;
  font-family: sans-serif;
  font-size: 0.85rem;
  color: #e2e8f0;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
/* 【続き578】#403「こういった中央のモーダル、スマホだと文字小さいかも！」。
   画面中央に出る効果の説明（main.js の showEffectReasonModal）。文字は名前0.75rem・
   本文0.8rem で、幅も min(20rem, 80vw)＝実画面153pxしか無かった。 */
body.is-phone-device .effect-reason-modal,
body.is-tablet-device .effect-reason-modal {
  width: min(60rem, 92vw);
  padding: 1.2rem 1.6rem;
  border-radius: 1rem;
}
body.is-phone-device .effect-reason-modal-name,
body.is-tablet-device .effect-reason-modal-name {
  font-size: 1.5rem;
  margin-bottom: 0.6rem;
}
body.is-phone-device .effect-reason-modal-text,
body.is-tablet-device .effect-reason-modal-text {
  font-size: 1.6rem;
  line-height: 1.6;
}

/* 【続き583】サマリー（早見表）を見る画面（summary-sheet.js）とフェイズ案内板のボタン。
   早見表は横長（1500x750）なので、画面の幅いっぱいに置いて読ませる。文字の大きさは
   画像なので、**どれだけ大きく映せるか**がそのまま読みやすさになる。 */
#summary-sheet-panel {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 10461; /* backdrop(10460)より前面 */
  width: min(96rem, 96vw);
  max-height: 54rem;
  overflow-y: auto;
  box-sizing: border-box;
  padding: 1rem 1.2rem 1.2rem;
  border-radius: 0.8rem;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(251, 191, 36, 0.5);
  color: #e2e8f0;
  font-family: sans-serif;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
  text-align: center;
}
.summary-sheet-title {
  font-size: 1.1rem;
  font-weight: bold;
  color: #fbbf24;
  margin-bottom: 0.6rem;
  padding-right: 2rem;
}
.summary-sheet-tabs {
  display: flex;
  justify-content: center;
  gap: 0.5rem;
  margin-bottom: 0.7rem;
}
.summary-sheet-tab {
  padding: 0.4rem 0.9rem;
  border-radius: 0.4rem;
  border: 1px solid rgba(148, 163, 184, 0.5);
  background: rgba(2, 6, 12, 0.35);
  color: inherit;
  font-family: sans-serif;
  font-size: 0.85rem;
  cursor: pointer;
}
.summary-sheet-tab.is-current {
  border-color: rgba(251, 191, 36, 0.9);
  background: rgba(251, 191, 36, 0.18);
  font-weight: 700;
}
.summary-sheet-image {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 0.4rem;
  cursor: pointer;
}
.summary-sheet-hint {
  margin-top: 0.5rem;
  font-size: 0.72rem;
  opacity: 0.75;
}
/* ライトテーマでも読めるように（他のモーダルと同じ扱い）。 */
body.theme-light #summary-sheet-panel {
  background: #fdfbf5;
  border-color: rgba(176, 141, 63, 0.6);
  color: #3a3326;
}
body.theme-light .summary-sheet-title { color: #8a6d2f; }
body.theme-light .summary-sheet-tab { background: rgba(176, 141, 63, 0.12); border-color: rgba(176, 141, 63, 0.45); }
/* スマホ: 1rem が実画面7.6pxにしかならないので、文字は2倍前後に（続き575 の物差し）。
   早見表そのものは幅いっぱいに出す。 */
body.is-phone-device #summary-sheet-panel,
body.is-tablet-device #summary-sheet-panel {
  /* 【vwの罠】`96vw` だと実画面の46%にしかならない（vwはビューポート基準なのに、描かれる
     大きさにはステージ倍率0.478が掛かるため＝続き575 の★）。ステージ基準の rem で書く
     （ステージの幅は100rem）。実測 405px → 734px に広がる。 */
  width: 96rem;
  max-height: 52rem;
  padding: 0.9rem 1rem 1rem;
}
/* 早見表は横長（幅の半分の高さ）。横持ちのスマホは**高さが足りない**ので、見出しを詰めて
   注記は省き、1画面に収める（画像を押せば表裏が切り替わることはタブを見れば分かる）。 */
body.is-phone-device .summary-sheet-hint,
body.is-tablet-device .summary-sheet-hint { display: none; }
body.is-phone-device .summary-sheet-title,
body.is-tablet-device .summary-sheet-title { font-size: 1.7rem; margin-bottom: 0.5rem; padding-right: 3.4rem; }
body.is-phone-device .summary-sheet-tab,
body.is-tablet-device .summary-sheet-tab { font-size: 1.5rem; padding: 0.45rem 1rem; border-radius: 0.6rem; }
body.is-phone-device .summary-sheet-hint,
body.is-tablet-device .summary-sheet-hint { font-size: 1.3rem; margin-top: 0.8rem; }


/* 【#408・2026-10-12】ユーザー報告「スマホでの、サマリー表示で、**バツボタンが小さい**です。
   あと**少しスクロールになるので動かない方がいい**です！」
   実測（932x430）: ✕ は **19.9x19.9px**（押すものの目安28pxに足りない）、パネルは中身894に対して
   見える830＝**はみ出し64** で、早見表の下がわずかに切れて巻かれていた（932x320 では ✕ が14.8px）。

   巻かないようにする直し方: パネルを縦の3段（題／タブ／絵）にして**絵だけが縮む**ようにする。
   `object-fit: contain` なので縮んでも形は崩れない。`min-height: 0` が無いと flex の子は縮まず、
   結局はみ出す（#412 と同じ）。 */
#summary-sheet-panel {
  display: flex;
  flex-direction: column;
  overflow: hidden; /* 巻かない（中の絵が縮んで収まる） */
}
.summary-sheet-title,
.summary-sheet-tabs,
.summary-sheet-hint {
  flex: 0 0 auto;
}
.summary-sheet-image {
  flex: 0 1 auto;
  min-height: 0;
  object-fit: contain;
}
/* ✕ は全モーダル共通のクラスなので、**この画面の中だけ**大きくする（他のモーダルに波及させない）。
   3.8rem ＝ 実画面約29px で「押すものは28px以上」を満たす。題の右の余白も一緒に広げないと
   題の文字に被る（続き575 で実際に被らせた）。 */
body.is-phone-device #summary-sheet-panel .modal-close-x,
body.is-tablet-device #summary-sheet-panel .modal-close-x {
  width: 3.8rem;
  height: 3.8rem;
  font-size: 2.3rem;
}
body.is-phone-device #summary-sheet-panel .summary-sheet-title,
body.is-tablet-device #summary-sheet-panel .summary-sheet-title {
  padding-right: 4.6rem;
}
/* フェイズ案内板の「サマリー」ボタン（自動送りの左隣）。絵文字を中央に置くだけ。 */
.phase-summary-glyph {
  font-size: 1.2rem;
  line-height: 1;
}

/* ── マイページ: 名前の上のお気に入り称号（続き317）────────────────────────
   ユーザー要望「マイページの称号はもうスペースがないので名前の上を称号にしませんか？
   そこに載せるのはお気に入りだけで、クリックすると称号モーダルが出るようにします」。
   名前と同じレイアウトブロック（data-layout-key="name"）の中に1行だけ置く。 */
.my-page-name-block {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1rem;
}
/* ユーザー報告2026-08-28（続き318）「名前の上の称号は、背景がアバターになることで文字が
   読みづらい」。マイページの背面には巨大な半透明アバターが敷かれており、その絵柄の上に
   小さな金文字を置くと溶けてしまう。**半透明の下地（帯）＋文字の影**を付けて、どんな絵柄の
   上でも読めるようにする。下地は文字にぴったり付く小さな角丸にして、装飾が主張しすぎない
   ようにした。 */
.my-page-fav-title {
  display: inline-block;
  background: rgba(12, 9, 6, 0.55);
  border: 1px solid rgba(212, 175, 90, 0.35);
  border-radius: 0.3rem;
  padding: 0.08rem 0.4rem;
  font-family: inherit;
  font-size: 0.6rem;
  font-weight: bold;
  letter-spacing: 0.04em;
  color: #f0d9a4;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.85);
  cursor: pointer;
  white-space: nowrap;
  line-height: 1.35;
  backdrop-filter: blur(2px);
}
.my-page-fav-title:hover {
  background: rgba(12, 9, 6, 0.72);
  border-color: rgba(212, 175, 90, 0.7);
}
/* 未設定のときは控えめな誘い文句にする（押せば称号モーダルが開く） */
.my-page-fav-title.is-empty {
  color: #d8d3c8;
  font-weight: normal;
  border-color: rgba(148, 163, 184, 0.3);
  background: rgba(12, 9, 6, 0.4);
}
/* 明るい配色では、暗い帯ではなく生成りの帯＋濃い金文字にする（同じく絵柄の上でも読める） */
body.theme-light .my-page-fav-title {
  background: rgba(255, 253, 248, 0.88);
  border-color: rgba(176, 141, 63, 0.5);
  color: #8a6413;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.9);
}
body.theme-light .my-page-fav-title:hover {
  background: #fffdf8;
  border-color: rgba(176, 141, 63, 0.85);
}
body.theme-light .my-page-fav-title.is-empty {
  color: #6b5c3c;
  background: rgba(255, 253, 248, 0.75);
}
/* 称号コレクションのモーダル（名前の上のバッジをクリックで開く） */
#title-collection-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 10701;
  /* 続き318: 称号を碁盤目に並べるため、ユーザーの許可を得て広げた（3〜4列になる幅） */
  width: min(42rem, 94vw);
  max-height: 82vh;
  overflow-y: auto;
  padding: 1.1rem 1.2rem 1.3rem;
  border-radius: 0.7rem;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(212, 175, 90, 0.45);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.5);
  color: #e2e8f0;
  font-family: sans-serif;
}
body.theme-light #title-collection-modal {
  background: #fffdf8;
  border-color: rgba(176, 141, 63, 0.5);
  color: #3a3326;
}
body.theme-light #title-collection-modal .my-page-titles-status {
  color: #6b5c3c;
}
body.theme-light #title-collection-modal .my-page-titles-group-name {
  color: #8a7d63;
}

/* 長い称号名（例「ファルベンドの守り手」）が升目からはみ出さないよう、ラベルだけ省略する */
.my-page-title-chip > span:last-child {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── マイページの見た目調整（続き319）──────────────────────────────────────
   ユーザー報告「着せ替え項目の枠がなんかダサい」「称号とアバター変更の枠がダサい」。
   (1) 着せ替え: ボタンを囲っていた四角い箱（ライト配色の beige な塗り）をやめ、
       **サムネイル自体を主役**にする。細い金の縁＋やわらかい影、ホバーで金が灯って少し浮く。
   (2) 称号: 枠線付きのピルをやめ、**端に向かって溶ける半透明の下地**＋文字の影にする
       （読みやすさは保ったまま「枠」に見えないようにする）。
   (3) アバター変更ボタンは撤去（アバターを直接クリックする方式へ。my-page.js参照）。 */
.my-page-cosmetic-btn {
  padding: 0.15rem 0.3rem;
  gap: 0.25rem;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  background: none;
  border: none;
}
.my-page-cosmetic-thumb {
  width: 3rem;
  height: 3rem;
  border-radius: 0.5rem;
  border: 1px solid rgba(212, 175, 90, 0.45);
  background: rgba(0, 0, 0, 0.22);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.28);
  transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s;
}
.my-page-cosmetic-btn:hover .my-page-cosmetic-thumb {
  border-color: rgba(212, 175, 90, 0.95);
  box-shadow: 0 0 0 1px rgba(212, 175, 90, 0.35), 0 6px 14px rgba(0, 0, 0, 0.35);
  transform: translateY(-2px);
}
.my-page-cosmetics-title {
  color: #d4af5a;
  letter-spacing: 0.12em;
  font-size: 0.78rem;
}
/* 明るい配色でも「箱」は作らない（塗りはサムネの中だけ） */
body.theme-light .my-page-cosmetic-btn {
  background: none;
  border: none;
  color: #6b5c3c;
}
body.theme-light .my-page-cosmetic-btn:hover {
  background: none;
}
body.theme-light .my-page-cosmetic-thumb {
  background: #fffdf8;
  border-color: rgba(176, 141, 63, 0.5);
  box-shadow: 0 2px 6px rgba(120, 100, 60, 0.2);
}
body.theme-light .my-page-cosmetics-title {
  color: #a67c1a;
}
/* 称号バッジ: 枠線をやめ、右へ溶けていく下地にする（枠に見せずに読みやすさだけ確保） */
.my-page-fav-title {
  border: none;
  border-radius: 0.2rem;
  padding: 0.1rem 0.7rem 0.1rem 0.35rem;
  background: linear-gradient(90deg, rgba(10, 8, 5, 0.62), rgba(10, 8, 5, 0.38) 65%, rgba(10, 8, 5, 0));
}
.my-page-fav-title:hover {
  background: linear-gradient(90deg, rgba(10, 8, 5, 0.78), rgba(10, 8, 5, 0.5) 65%, rgba(10, 8, 5, 0));
}
.my-page-fav-title.is-empty {
  border: none;
  background: linear-gradient(90deg, rgba(10, 8, 5, 0.45), rgba(10, 8, 5, 0.22) 65%, rgba(10, 8, 5, 0));
}
body.theme-light .my-page-fav-title,
body.theme-light .my-page-fav-title.is-empty {
  border: none;
  background: linear-gradient(90deg, rgba(255, 253, 248, 0.92), rgba(255, 253, 248, 0.6) 65%, rgba(255, 253, 248, 0));
}
body.theme-light .my-page-fav-title:hover {
  background: linear-gradient(90deg, rgba(255, 253, 248, 1), rgba(255, 253, 248, 0.75) 65%, rgba(255, 253, 248, 0));
}
/* アバター画像: profile-layout-editor が親ブロックへ pointer-events:none を当てている
   （巨大な四角い当たり判定が周囲のクリックを奪う「見えない枠」対策）ので、画像だけ
   クリックできるように戻す。**円形に切り抜いて四隅では反応しない**ようにするのが要点
   （clip-path は当たり判定にも効く。mask-image は見た目だけで当たり判定には効かない）。 */
.my-page-avatar-img {
  pointer-events: auto;
  clip-path: circle(62%);
}

/* ── マイデッキ編成ページのチュートリアル（続き320、deck-builder-tutorial.js）──────
   盤面のチュートリアル（#tutorial-overlay）と違い、**座標計算をしない**方式。
   対象要素にクラスを付けて光らせ、説明は画面下部の固定パネルに出す。bodyのステージ
   transform(scale)が何倍でもズレようがないのが利点。暗幕は張らない＝説明を読みながら
   実際に触って試せる。 */
#mdb-tutorial {
  position: fixed;
  left: 50%;
  bottom: 1.2rem;
  transform: translateX(-50%);
  z-index: 2450; /* #my-deck-page(1500) より前面、オプションエリアの帯(2700)よりは後ろ */
  width: min(34rem, 92vw);
  font-family: sans-serif;
  pointer-events: none; /* パネル本体だけ操作を受け取る（下の .mdb-tut-panel で auto に戻す） */
}
.mdb-tut-panel {
  pointer-events: auto;
  background: linear-gradient(180deg, rgba(24, 20, 16, 0.97), rgba(14, 12, 9, 0.97));
  border: 1px solid rgba(212, 175, 90, 0.55);
  border-radius: 0.6rem;
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.5);
  padding: 0.8rem 1rem 0.7rem;
  color: #f1e6cd;
}
.mdb-tut-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem;
  margin-bottom: 0.35rem;
}
.mdb-tut-title {
  font-size: 0.95rem;
  font-weight: bold;
  color: #e6c67f;
  letter-spacing: 0.04em;
}
.mdb-tut-progress {
  font-size: 0.7rem;
  color: #94a3b8;
  font-family: monospace;
  flex: 0 0 auto;
}
.mdb-tut-body {
  font-size: 0.8rem;
  line-height: 1.65;
}
.mdb-tut-body p {
  margin: 0 0 0.35rem;
}
.mdb-tut-body b {
  color: #f0d9a4;
}
.mdb-tut-buttons {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.5rem;
}
.mdb-tut-spacer {
  flex: 1 1 auto;
}
.mdb-tut-buttons button {
  font-family: inherit;
  font-size: 0.78rem;
  padding: 0.3rem 0.9rem;
  border-radius: 0.35rem;
  cursor: pointer;
  border: 1px solid rgba(212, 175, 90, 0.5);
  background: rgba(212, 175, 90, 0.12);
  color: #f1e6cd;
}
.mdb-tut-buttons button:hover {
  background: rgba(212, 175, 90, 0.25);
}
.mdb-tut-buttons button:disabled {
  opacity: 0.4;
  cursor: default;
}
.mdb-tut-next {
  background: linear-gradient(180deg, #d4af5a, #b8873f) !important;
  border-color: #d4af5a !important;
  color: #241c11 !important;
  font-weight: bold;
}
.mdb-tut-skip {
  border-color: rgba(148, 163, 184, 0.35) !important;
  background: none !important;
  color: #94a3b8 !important;
}
/* 説明中の対象を光らせる（位置は要素自身が持っているのでズレない） */
.mdb-tut-highlight {
  position: relative;
  outline: 2px solid #d4af5a;
  outline-offset: 2px;
  border-radius: 0.4rem;
  box-shadow: 0 0 0 4px rgba(212, 175, 90, 0.18), 0 0 22px rgba(212, 175, 90, 0.45);
  animation: mdb-tut-pulse 1.8s ease-in-out infinite;
}
@keyframes mdb-tut-pulse {
  0%, 100% { box-shadow: 0 0 0 4px rgba(212, 175, 90, 0.18), 0 0 22px rgba(212, 175, 90, 0.45); }
  50% { box-shadow: 0 0 0 6px rgba(212, 175, 90, 0.28), 0 0 30px rgba(212, 175, 90, 0.7); }
}
/* ヘッダーの「？ 使い方」ボタン */
#mdb-help {
  flex: 0 0 auto;
  background: none;
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.4rem;
  color: #e2e8f0;
  font-family: sans-serif;
  font-size: 0.78rem;
  padding: 0.35rem 0.7rem;
  cursor: pointer;
}
#mdb-help:hover {
  border-color: rgba(212, 175, 90, 0.8);
  background: rgba(212, 175, 90, 0.14);
}

/* ============================================================================
   ライトモードの取りこぼし一括対応（ユーザー指摘2026-08-30「ライトモードなのにモーダルが
   ダークなものが多々ある」）。既存の上書き（#contact-result-modal / #action-log-info-modal）と
   同じ「アイボリー地＋ゴールド枠＋濃い文字」に揃える。
   ここに載せていないのは、暗いままが正しいもの——背景を落とすための幕(backdrop)、スポットライト、
   開発者しか開かない画面、物語シーン、そして色そのものに意味があるお祝いのモーダル
   （勝利=金／通貨=緑／ランク=青）。
   ========================================================================= */
body.theme-light #afk-cpu-banner,
body.theme-light #anytime-interrupt-modal,
body.theme-light #avatar-picker-modal,
body.theme-light #background-picker-modal,
body.theme-light #bug-report-intro-modal,
body.theme-light #card-back-skin-modal,
body.theme-light #card-context-menu,
body.theme-light #card-effect-picker-hint,
body.theme-light #card-note-modal,
body.theme-light #confirm-modal,
body.theme-light #contact-approval-modal,
body.theme-light #contact-confirm-modal,
body.theme-light #cpu-step-hint,
body.theme-light #currency-about-panel,
body.theme-light #currency-dropdown-menu,
body.theme-light #daily-bonus-toast,
body.theme-light #emote-mute-menu,
body.theme-light #emote-picker,
body.theme-light #final-lock-approval-banner,
body.theme-light #first-login-profile-modal,
body.theme-light #generic-confirm-modal,
body.theme-light #hand-drop-hint,
body.theme-light #home-screen-toast,
body.theme-light #icon-action-detail-modal,
body.theme-light #mdb-note-menu,
body.theme-light #mds-panel,
body.theme-light #my-deck-owner-tooltip,
body.theme-light #my-deck-page-toast,
body.theme-light #opening-login-info-modal,
body.theme-light #pet-picker-modal,
body.theme-light #phase-guide-modal,
body.theme-light #piece-skin-modal,
body.theme-light #playmat-picker-modal,
body.theme-light #priority-transfer-modal,
body.theme-light #rank-explain-modal,
body.theme-light #ranked-practice-banner,
body.theme-light #ranking-page-card,
body.theme-light #timer-toggle-approval-banner,
body.theme-light #touch-action-confirm-modal,
body.theme-light .card-arrival-modal-keep,
body.theme-light .card-arrival-modal-pin,
body.theme-light .card-open-prompt,
body.theme-light .game-intro-modal,
body.theme-light .glossary-popup,
body.theme-light .hand-effect-option-picker-peek-hint,
body.theme-light .hand-pickup-toast,
body.theme-light .mdb-tut-panel,
body.theme-light .phase-guide-tooltip,
body.theme-light .phase-skip-modal,
body.theme-light .piece-name-bubble,
body.theme-light .quick-note-toast,
body.theme-light .setup-animation-skip-hint,
body.theme-light #auto-processing-toggle-approval-banner {
  background: #fdfbf5;
  border-color: rgba(176, 141, 63, 0.5);
  color: #3a3326;
}
/* 上のパネルをライト地にしたことで、中の「自前で明るい文字色を持つボタン」だけが読めなく
   なるので、そこも合わせて濃い文字にする（地が透明なボタン＝パネルの色をそのまま背負う）。 */
body.theme-light #card-context-menu button,
body.theme-light #currency-dropdown-menu button {
  color: #3a3326;
}
body.theme-light #card-context-menu button:hover,
body.theme-light #currency-dropdown-menu button:hover {
  background: rgba(176, 141, 63, 0.16);
}
body.theme-light #ranked-practice-banner button {
  background: rgba(176, 141, 63, 0.14);
  border-color: rgba(176, 141, 63, 0.45);
  color: #3a3326;
}

/* ライトモードで地をアイボリーにしたパネルの中に残っていた、薄すぎて読めない文字色を直す
   （ユーザー指摘2026-08-30「文字が薄くて読めません」）。アイボリー地とのコントラスト比が
   4.5を下回っていたものだけを、本文は濃い墨色・補足はやや薄い墨色に置き換える。 */
body.theme-light .bug-report-attach-remove,
body.theme-light .card-arrival-modal-label,
body.theme-light .game-intro-alloff,
body.theme-light .game-intro-alloff:hover,
body.theme-light .game-intro-body,
body.theme-light .glossary-popup-close,
body.theme-light .glossary-popup-paragraph,
body.theme-light .glossary-popup-title,
body.theme-light .glossary-term,
body.theme-light .glossary-term:hover,
body.theme-light .hand-effect-use-modal-label,
body.theme-light .hand-pickup-toast-reason,
body.theme-light .lobby-icon-toggle-state,
body.theme-light .mdb-tut-body b,
body.theme-light .mdb-tut-buttons button,
body.theme-light .mdb-tut-progress,
body.theme-light .mdb-tut-skip,
body.theme-light .mdb-tut-title,
body.theme-light .afk-cpu-tag,
body.theme-light #anytime-interrupt-modal-optout,
body.theme-light #anytime-interrupt-resume-button,
body.theme-light .bug-report-intro-desc,
body.theme-light .bug-report-intro-hide,
body.theme-light .bug-report-intro-hide:hover,
body.theme-light .bug-report-status,
body.theme-light .bug-report-status.is-error,
body.theme-light .changelog-empty,
body.theme-light .changelog-ja-only,
body.theme-light .changelog-dev-head,
body.theme-light .changelog-dev-items li,
body.theme-light .changelog-fix-items li,
body.theme-light .currency-about-title,
body.theme-light .currency-award-float,
body.theme-light .emote-mute-menu-btn,
body.theme-light .emote-picker-btn,
body.theme-light .first-login-profile-name-input,
body.theme-light .mdb-deck-entry-main:hover,
body.theme-light .mdb-first-pick-random,
body.theme-light .mdb-note-menu-btn,
body.theme-light .mdb-setting-label,
body.theme-light .mdb-summary-errors li,
body.theme-light .mdb-summary-status.is-ok,
body.theme-light .mdb-summary-sub,
body.theme-light #mdb-help,
body.theme-light .mds-deck-meta,
body.theme-light .mds-empty,
body.theme-light #mds-countdown,
body.theme-light #mds-subtitle,
body.theme-light .my-deck-owner-tooltip-label,
body.theme-light .my-deck-owner-tooltip-name,
body.theme-light .opening-login-info-btn,
body.theme-light .options-menu-shortcut-preset,
body.theme-light .pet-picker-label,
body.theme-light .priority-transfer-label:hover,
body.theme-light .profile-maindeck-badge,
body.theme-light .profile-maindeck-cover.is-empty,
body.theme-light .profile-maindeck-name,
body.theme-light .rank-explain-paragraph,
body.theme-light .rank-explain-section-title,
body.theme-light .rank-explain-title,
body.theme-light .ranking-page-row.is-top3 .ranking-page-row-rank,
body.theme-light .ranking-page-tab,
body.theme-light .ranking-page-tab.is-active,
body.theme-light .stack-top,
body.theme-light #touch-action-confirm-dontshow,
body.theme-light #touch-action-confirm-dontshow:hover {
  color: #3a3326;
}

/* 勝利演出シミュレーター（victory-preview.js、管理者モードから開く）。
   dissolve-preview と同じ「右側に寄せた薄いパネル＋スライダー」の作り。再生中はパネルを
   薄くして演出そのものを見えるようにする。 */
#victory-preview {
  position: fixed;
  inset: 0;
  z-index: 10700;
  pointer-events: none;
}
.victory-preview-panel {
  position: absolute;
  right: 0.8rem;
  top: 3.6rem;
  bottom: 0.8rem;
  width: min(24rem, 92vw);
  overflow-y: auto;
  pointer-events: auto;
  background: rgba(15, 23, 32, 0.96);
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.6rem;
  padding: 0.7rem 0.8rem 0.9rem;
  color: #e2e8f0;
  font-family: sans-serif;
  font-size: 0.8rem;
  box-shadow: 0 0.6rem 2rem rgba(0, 0, 0, 0.55);
  transition: opacity 200ms ease-out;
}
#victory-preview.is-playing .victory-preview-panel { opacity: 0.14; }
.victory-preview-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.5rem; }
.victory-preview-title { font-weight: 700; font-size: 0.92rem; }
.victory-preview-close {
  background: transparent; border: none; color: #cbd5e1; font-size: 1rem; cursor: pointer; padding: 0.1rem 0.3rem;
}
.victory-preview-stages { display: flex; flex-wrap: wrap; gap: 0.25rem; margin-bottom: 0.6rem; }
.victory-preview-stage {
  font-size: 0.62rem; letter-spacing: 0.06em; padding: 0.12rem 0.38rem; border-radius: 999px;
  background: rgba(148, 163, 184, 0.16); color: #94a3b8;
}
.victory-preview-stage.is-current { background: #b45309; color: #fff; font-weight: 700; }
.victory-preview-row { display: flex; align-items: center; flex-wrap: wrap; gap: 0.35rem; margin-bottom: 0.5rem; }
.victory-preview-row > span { font-size: 0.72rem; color: #94a3b8; }
.victory-preview-row select,
.victory-preview-row button,
.victory-preview-play button,
.victory-preview-foot button {
  background: #1e293b; color: #e2e8f0; border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.3rem; padding: 0.22rem 0.5rem; font-size: 0.74rem; cursor: pointer;
}
.victory-preview-play { display: flex; align-items: center; gap: 0.35rem; margin-bottom: 0.6rem; }
.victory-preview-status { font-size: 0.7rem; color: #fbbf24; }
.victory-preview-body { display: flex; flex-direction: column; gap: 0.3rem; }
.victory-preview-slider { display: grid; grid-template-columns: 1fr 7rem 2.6rem; align-items: center; gap: 0.35rem; }
.victory-preview-slider-label { font-size: 0.7rem; color: #cbd5e1; line-height: 1.3; }
.victory-preview-slider-value { font-size: 0.68rem; color: #94a3b8; text-align: right; }
.victory-preview-foot { display: flex; gap: 0.4rem; margin-top: 0.7rem; }
.victory-preview-out {
  width: 100%; height: 8rem; margin-top: 0.4rem; background: #0f1520; color: #a5f3fc;
  border: 1px solid rgba(148, 163, 184, 0.3); border-radius: 0.3rem; font-size: 0.7rem;
}
body.theme-light .victory-preview-panel { background: #fdfbf5; border-color: rgba(176, 141, 63, 0.5); color: #3a3326; }
body.theme-light .victory-preview-slider-label,
body.theme-light .victory-preview-slider-value,
body.theme-light .victory-preview-row > span { color: #6b6152; }

/* 「このマスでいいですか？」の確認（cell-confirm.js、ユーザー要望2026-09-01）。
   【重要】盤面を隠さないこと。背景は暗くせず、パネルは選んだマスと反対側の端に出す
   （マスが上半分なら .is-bottom、下半分なら .is-top）。これで確認中も対象マスが必ず見える。 */
#cell-confirm-modal {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10615;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  padding: 0.7rem 1.1rem;
  border-radius: 0.7rem;
  background: rgba(15, 23, 32, 0.94);
  border: 1px solid rgba(148, 163, 184, 0.45);
  box-shadow: 0 0.8rem 2rem rgba(0, 0, 0, 0.6);
  font-family: sans-serif;
  max-width: min(28rem, 92vw);
}
/* 選んだマスの隣に置く時（既定）。left/top は JS(placeNextToCell)がローカル座標で入れる。
   中央寄せの transform は打ち消す。 */
#cell-confirm-modal.is-anchored {
  transform: none;
}
/* 保険: マスの位置が測れなかった時だけ画面下端の中央に出す。 */
#cell-confirm-modal.is-fallback {
  bottom: 1rem;
}
.cell-confirm-title {
  font-weight: bold;
  font-size: 1.05rem;
  color: #e2e8f0;
}
.cell-confirm-hint {
  font-size: 0.8rem;
  color: #94a3b8;
  text-align: center;
  line-height: 1.4;
}
.cell-confirm-dontshow {
  background: none;
  border: none;
  color: #94a3b8;
  font-size: 0.72rem;
  text-decoration: underline;
  cursor: pointer;
  padding: 0.1rem 0.3rem;
}
.cell-confirm-dontshow:hover { color: #e2e8f0; }
/* 確認中のマスを強く光らせる（どのマスの話か一目で分かるように）。 */
.cell-confirm-target {
  outline: 0.22rem solid #fbbf24 !important;
  outline-offset: 0.1rem;
  box-shadow: 0 0 1.4rem 0.35rem rgba(251, 191, 36, 0.85) !important;
  animation: cell-confirm-pulse 1.1s ease-in-out infinite;
}
@keyframes cell-confirm-pulse {
  0%, 100% { outline-color: #fbbf24; }
  50% { outline-color: #fff7d6; }
}
body.theme-light #cell-confirm-modal {
  background: #fdfbf5;
  border-color: rgba(176, 141, 63, 0.5);
}
body.theme-light .cell-confirm-title { color: #3a3326; }
body.theme-light .cell-confirm-hint,
body.theme-light .cell-confirm-dontshow { color: #6b6153; }

/* 色宣言で必要数ちょうどまで選び終えた時、残りの色を沈める（ユーザー要望2026-09-01）。
   #193 で撤去したのは「1色でも選べば沈める」形。こちらは**必要数に達した時だけ**なので、
   「もう選べない」という正しい情報になる（この状態では実際に押しても増えない）。 */
.declare-colors-modal-grid.is-full .declare-colors-modal-swatch:not(.is-selected) {
  /* アニメーション等が opacity を設定しているため !important が要る（実測で確認）。 */
  opacity: 0.3 !important;
  filter: grayscale(0.85);
  cursor: default;
}

/* 自動処理モード中の「自分の手札公開エリア」を隠す（ユーザー要望2026-09-01）。
   公開カードは手札の扇の中に出しているので、ここは常に空で場所だけ取っていた。
   display:none にはしない——ドロップ先であり、周りのレイアウトの基準でもあるため（#199の教訓）。 */
.hand-reveal-area.is-hidden-by-auto {
  visibility: hidden;
  pointer-events: none;
}

/* α版のお知らせ（alpha-notice.js、ユーザー要望2026-09-01）。ホーム画面(z-index:1500)より
   手前・オプションエリアの帯より奥に出す。専門知識の無い人が読む前提なので、
   1項目＝アイコン＋見出し＋やさしい説明の3点セットを大きめの行間で並べる。 */
#alpha-notice-modal {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 2701;
  width: min(34rem, 92vw);
  max-height: 86vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 1.4rem 1.5rem 1.1rem;
  border-radius: 0.9rem;
  background: #101820;
  border: 1px solid rgba(251, 191, 36, 0.45);
  box-shadow: 0 1.2rem 3rem rgba(0, 0, 0, 0.7);
  font-family: sans-serif;
  color: #e2e8f0;
}
.alpha-notice-title {
  font-size: 1.25rem;
  font-weight: bold;
  color: #fbbf24;
  text-align: center;
}
.alpha-notice-lead { font-size: 0.9rem; line-height: 1.65; }
.alpha-notice-list { display: flex; flex-direction: column; gap: 0.7rem; }
.alpha-notice-item {
  display: flex;
  gap: 0.65rem;
  align-items: flex-start;
  padding: 0.6rem 0.7rem;
  border-radius: 0.5rem;
  background: rgba(255, 255, 255, 0.05);
}
.alpha-notice-icon { font-size: 1.3rem; line-height: 1.2; flex: none; }
.alpha-notice-item-head { font-weight: bold; font-size: 0.92rem; margin-bottom: 0.15rem; }
.alpha-notice-item-body { font-size: 0.82rem; line-height: 1.6; color: #cbd5e1; }
.alpha-notice-thanks { font-size: 0.85rem; line-height: 1.6; color: #fde68a; text-align: center; }
.alpha-notice-ok {
  align-self: center;
  padding: 0.55rem 2.2rem;
  border-radius: 0.45rem;
  border: none;
  background: #0ea5e9;
  color: #fff;
  font-size: 1rem;
  font-weight: bold;
  cursor: pointer;
}
.alpha-notice-ok:hover { background: #0284c7; }
.alpha-notice-never {
  align-self: center;
  background: none;
  border: none;
  color: #94a3b8;
  font-size: 0.75rem;
  text-decoration: underline;
  cursor: pointer;
  padding: 0.1rem 0.3rem;
}
.alpha-notice-never:hover { color: #e2e8f0; }
body.theme-light #alpha-notice-modal {
  background: #fdfbf5;
  border-color: rgba(176, 141, 63, 0.5);
  color: #3a3326;
}
body.theme-light .alpha-notice-title { color: #a97c33; }
body.theme-light .alpha-notice-item { background: rgba(176, 141, 63, 0.1); }
body.theme-light .alpha-notice-item-body { color: #6b6153; }
body.theme-light .alpha-notice-thanks { color: #8a6d2f; }
body.theme-light .alpha-notice-never { color: #6b6153; }

/* α版のお知らせ・スマホ（ユーザー報告2026-09-01「スマホだとモーダルをスクロールしなくちゃ
   いけません」）。原因は2つ:
   ①この要素は body（ステージ変形 translate+scale あり）の中にあるので、max-height:86vh の
     vh は**実画面**を指すのに、その値は**ステージ単位**として解釈される。932x430の実機では
     86vh=370ステージpx＝実画面ではわずか177px（画面の4割）にしか使われず、中身605pxが
     入らずスクロールになっていた。→ vh/vw をやめ、他の中央モーダルと同じく **pure rem**
     （ステージ1600x900＝100rem x 56.25rem 基準）で寸法を決める。
   ②そのままでは文字が実画面で9.6pxしかなく読めない。他の中央モーダルと同じ
     --center-modal-scale-phone（既定2倍）に乗せる。拡大後も収まるよう幅46rem/高さ27rem。
   3つの項目は縦積みだと高さが足りないので、スマホでは横並び（3列）にする。 */
body.is-phone-device #alpha-notice-modal {
  width: 46rem;
  /* 拡大率は 0.9 倍（既定なら 1.8 倍）。2倍のままだと縦がステージ(900px)を超えて
     結局スクロールになるため、少しだけ抑えて全文が一度に見えるようにした。 */
  max-height: 30rem;
  padding: 1rem 1.2rem 0.8rem;
  gap: 0.5rem;
  transform: translate(-50%, -50%) scale(calc(0.9 * var(--center-modal-scale-phone, 1)));
}
body.is-phone-device .alpha-notice-list {
  flex-direction: row;
  align-items: stretch;
  gap: 0.5rem;
}
body.is-phone-device .alpha-notice-item {
  flex: 1 1 0;
  flex-direction: column;
  gap: 0.25rem;
}

/* 不具合報告#202「スマホのホーム画面で、画面全体を上下にスワイプできてしまう」。
   ホーム画面の中身はすべて rem（＝1600x900の固定ステージ基準）なので、本来どの端末でも
   同じ高さで収まる。にもかかわらず**スマホでだけ**縦にはみ出していた原因は2つの合わせ技:
     ①`#home-screen-grid` の飾りの下方向オフセット（--home-grid-pos-y、既定6.4rem）が
       スクロール可能領域には効いてしまう（transformはレイアウトに影響しないが、
       スクロール範囲の計算には含まれる）
     ②スマホでは上端の帯を避ける余白 --page-top-safe が約90px（PCは約54px）と大きく、
       その差ぶんだけ中身が下へ寄る
   結果、たった2pxはみ出して `overflow-y: auto` のスクロール対象になり、画面全体を
   スワイプできてしまっていた。ホーム画面は中身を上下中央に置く作りで、タイトルは
   もともと帯よりずっと下（実測129px＞帯90px）に来るため、この画面に限っては
   スマホ用の広い上余白は不要。PCと同じ2remに揃えて、はみ出しを解消する。 */
body.is-phone-device #home-screen {
  padding-top: 2rem;
}

/* ランク戦の通知を勧めるバッジ／行（ユーザー要望2026-09-02「ランク戦通知を促したい。ホームの
   ランクマッチのところに“通知をオンにしませんか”的なバッジを。あとランク戦待ちの時にも促す」）。
   ホーム側は「今◯人が募集中」のバッジ(.home-screen-tile-waiting-badge)の下に重ねる小さなチップ。 */
.home-screen-tile-notify-badge {
  position: absolute;
  left: 50%;
  bottom: -0.5rem;
  transform: translateX(-50%);
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  background: #0ea5e9;
  color: #fff;
  font-size: 0.62rem;
  font-weight: bold;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.35);
}
.home-screen-tile-notify-badge:hover {
  background: #0284c7;
}
.ranked-waiting-notify {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.9rem;
  padding: 0.7rem 1rem;
  border-radius: 0.6rem;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.14);
}
.ranked-waiting-notify-text {
  font-size: 0.85rem;
  color: #cbd5e1;
  text-align: center;
}
.ranked-waiting-notify-button {
  padding: 0.4rem 1.2rem;
  border-radius: 0.4rem;
  border: none;
  background: #0ea5e9;
  color: #fff;
  font-size: 0.85rem;
  font-weight: bold;
  cursor: pointer;
}
.ranked-waiting-notify-button:hover {
  background: #0284c7;
}
body.theme-light .ranked-waiting-notify {
  background: rgba(176, 141, 63, 0.1);
  border-color: rgba(176, 141, 63, 0.35);
}
/* （続き495で削除）待機画面は常に暗いので、ライト配色でも文字は明るいままにする。 */

/* 全員へのお知らせ（announcement.js、ユーザー要望2026-09-02）。α版のお知らせと同じ見た目の
   系統にして、運営からの連絡だと一目で分かるようにする。中身は管理者が書いた自由文なので、
   長文でもスクロールで読める（vh/vwは使わない——ステージ変形の中では実画面と食い違うため。
   #alpha-notice-modal のコメント参照）。 */
#announcement-modal {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 2701;
  width: 34rem;
  max-width: 92%;
  max-height: 40rem;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
  padding: 1.4rem 1.5rem 1.1rem;
  border-radius: 0.9rem;
  background: #101820;
  border: 1px solid rgba(251, 191, 36, 0.45);
  box-shadow: 0 1.2rem 3rem rgba(0, 0, 0, 0.7);
  font-family: sans-serif;
  color: #e2e8f0;
}
.announcement-badge {
  align-self: center;
  padding: 0.15rem 0.7rem;
  border-radius: 999px;
  background: rgba(251, 191, 36, 0.18);
  border: 1px solid rgba(251, 191, 36, 0.5);
  color: #fbbf24;
  font-size: 0.7rem;
  font-weight: bold;
}
.announcement-title {
  font-size: 1.2rem;
  font-weight: bold;
  color: #fbbf24;
  text-align: center;
}
.announcement-body {
  font-size: 0.9rem;
  line-height: 1.7;
  white-space: pre-wrap; /* 管理者が入れた改行をそのまま活かす */
  word-break: break-word;
}
.announcement-ok {
  align-self: center;
  padding: 0.55rem 2.2rem;
  border-radius: 0.45rem;
  border: none;
  background: #0ea5e9;
  color: #fff;
  font-size: 1rem;
  font-weight: bold;
  cursor: pointer;
}
.announcement-ok:hover { background: #0284c7; }
body.theme-light #announcement-modal {
  background: #fdfbf5;
  border-color: rgba(176, 141, 63, 0.5);
  color: #3a3326;
}
body.theme-light .announcement-title { color: #a97c33; }
body.theme-light .announcement-badge { color: #a97c33; background: rgba(176, 141, 63, 0.12); }
/* スマホでは他の中央モーダルと同じ拡大に乗せる（#alpha-notice-modal と同じ考え方。
   vh/vwを使わず rem で寸法を決め、拡大後もステージ(1600x900)に収まる大きさにする）。 */
body.is-phone-device #announcement-modal {
  width: 44rem;
  max-width: none;
  max-height: 28rem;
  padding: 1rem 1.2rem 0.8rem;
  transform: translate(-50%, -50%) scale(calc(0.9 * var(--center-modal-scale-phone, 1)));
}

/* 「ホーム画面に追加 / アプリとしてインストール」の案内（pwa-install.js）。
   ユーザー報告2026-09-05「メニューアイコン群に被っている。右上のオプションエリアの下あたりは
   どうですか？」——以前はホーム画面の流れの中（中央）に置いていたため、タイル群と重なって
   いた。右上のオプションアイコン列のすぐ下へ、控えめな小さいカードとして固定する。
   追加済み／「あとで」を押した端末では display:none のまま残る。 */
#pwa-install-banner {
  display: none;
  position: fixed;
  top: calc(var(--option-area-band-height, 3rem) + 0.6rem);
  right: 1rem;
  z-index: 1600; /* ホーム画面(1500)より手前・モーダル類より奥 */
  max-width: 21rem;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 0.4rem 0.6rem;
  padding: 0.5rem 0.9rem;
  border-radius: 0.6rem;
  background: rgba(12, 20, 32, 0.7);
  border: 1px solid rgba(148, 163, 184, 0.35);
  font-size: 0.8rem;
  color: #cbd5e1;
  text-align: right;
  line-height: 1.5;
}
/* 案内文は1行まるごと使う（ボタンと同じ行を取り合うと、日本語が2文字だけ次の行へ
   こぼれて見栄えが悪くなる。ユーザー報告2026-09-05「『すぐに開けます』の改行がダサい」）。 */
.pwa-install-text {
  flex: 1 0 100%;
}
body.theme-light #pwa-install-banner {
  background: #fdfbf5;
  border-color: rgba(176, 141, 63, 0.45);
  color: #3a3326;
}
.pwa-install-add {
  padding: 0.3rem 0.8rem;
  border-radius: 0.4rem;
  border: none;
  background: #15803d;
  color: #fff;
  font-size: 0.8rem;
  cursor: pointer;
}
.pwa-install-later {
  padding: 0.3rem 0.6rem;
  border-radius: 0.4rem;
  border: 1px solid rgba(148, 163, 184, 0.4);
  background: transparent;
  color: inherit;
  font-size: 0.75rem;
  cursor: pointer;
}

/* iPhone/iPad 向けの手順モーダル（ボタン一つでは追加できないため案内する）。 */
#pwa-install-modal {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 10460;
  width: min(26rem, 92%);
  padding: 1.1rem 1.3rem 1.2rem;
  border-radius: 0.7rem;
  background: #0f172a;
  border: 1px solid rgba(148, 163, 184, 0.4);
  color: #e2e8f0;
  box-shadow: 0 1rem 2.5rem rgba(0, 0, 0, 0.55);
}
body.theme-light #pwa-install-modal {
  background: #fdfbf5;
  border-color: rgba(176, 141, 63, 0.5);
  color: #3a3326;
}
.pwa-install-modal-title {
  font-size: 1rem;
  font-weight: 700;
  margin-bottom: 0.7rem;
  text-align: center;
}
.pwa-install-modal-steps {
  margin: 0 0 0.7rem 1.1rem;
  padding: 0;
  font-size: 0.85rem;
  line-height: 1.65;
}
.pwa-install-modal-note {
  font-size: 0.75rem;
  line-height: 1.5;
  opacity: 0.8;
}

/* 更新中の進捗表示（update-checker.js の showUpdateProgress）。ユーザー要望2026-09-02
   「更新中の時、たまに長い時があるので進捗が分かるようにしたい」。バナーの「更新する」から
   でも、起動直後の自動更新でも同じものが出る。画面上部中央の細い帯で、更新バナー
   (#update-available-banner) より手前に出す。リロードで消えるので後片付けは不要。 */
#update-progress {
  position: fixed;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  z-index: 60000;
  min-width: min(22rem, 80vw);
  padding: 0.45rem 0.9rem 0.55rem;
  border-radius: 0 0 0.5rem 0.5rem;
  background: rgba(12, 20, 32, 0.94);
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-top: none;
  color: #e2e8f0;
  font-size: 0.78rem;
  text-align: center;
  box-shadow: 0 0.4rem 1.2rem rgba(0, 0, 0, 0.45);
  pointer-events: none;
}
body.theme-light #update-progress {
  background: #fdfbf5;
  border-color: rgba(176, 141, 63, 0.5);
  color: #3a3326;
}
.update-progress-text {
  margin-bottom: 0.35rem;
  line-height: 1.35;
}
.update-progress-track {
  height: 0.35rem;
  border-radius: 0.2rem;
  background: rgba(148, 163, 184, 0.25);
  overflow: hidden;
}
.update-progress-bar {
  height: 100%;
  width: 0%;
  border-radius: 0.2rem;
  background: linear-gradient(90deg, #38bdf8, #22d3ee);
  transition: width 0.2s ease;
}

/* #215（ユーザー報告2026-09-03「スマホでカウンターロックの時、『ロックするカードを選んで
   ください』の上部の案内が表示されていなかった」）。原因は「出ていない」のではなく
   **小さすぎて読めない**こと——この案内は position:fixed だが、body 自体にステージ変形
   （縮小）が掛かっているため、0.9rem の文字がスマホ実機では7px程度まで縮んでいた。
   他の中央モーダルと同じ --center-modal-scale-phone に乗せて拡大する。上端を起点に拡大
   すれば画面外へはみ出さない。横に長い文言は折り返す（nowrapのままだと画面幅を超える）。 */
body.is-phone-device #card-effect-picker-hint {
  transform-origin: top center;
  transform: translate(-50%, -0.6rem) scale(var(--center-modal-scale-phone, 1));
  white-space: normal;
  max-width: 46rem;
  text-align: center;
}
body.is-phone-device #card-effect-picker-hint.show {
  transform: translateX(-50%) scale(var(--center-modal-scale-phone, 1));
}

/* ロックの出来事だけ、隅のバッジの代わりに「鎖」をカード画像の上に重ねる
   （ユーザー要望2026-09-03「ロック演出時に使っている鎖アイコンをカード画像に重ねる感じは
   どう？」）。ロックの瞬間に盤面へ焼き付く刻印（.lock-stamp）と同じ assets/lock-icon.webp を
   使うので、演出とお知らせで見た目が揃う。中央フラッシュ・右下のチップ・チップを開いた時の
   モーダルはどれも同じHTMLを使い回すため、この1つのルールで3か所とも同じ見た目になる。 */
.hand-pickup-toast-img.is-locked::after,
.turn-event-stock-chip-face.is-locked::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("../assets/lock-icon.webp");
  background-size: 108% 108%;
  background-position: center;
  background-repeat: no-repeat;
  pointer-events: none;
}
/* 「＋」「∞」のような文字記号のバッジは、絵文字より線が細く小さく見えるので少しだけ大きく太く
   する（🗑 などの絵文字バッジは従来のまま）。 */
.turn-event-stock-chip-badge {
  font-weight: bold;
}

/* 「捨てた」バッジの線画ゴミ箱（絵文字の🗑から差し替え。＋/∞ と同じ単色トーンに揃えるため）。 */
.turn-event-stock-chip-badge.is-svg {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 0.95rem;
  height: 0.95rem;
  padding: 0.08rem;
  box-sizing: content-box;
}

/* はじめての人の「次にやること」カード（first-steps.js）。ホームのタイル群の上に1枚だけ出る。
   タイルと同じ枠の中で「これが今やること」と一目で分かるよう、金の縁と大きなボタンで
   1つだけ強調する。3歩を終える／✕を押すと出なくなるので、慣れた人の邪魔にはならない。 */
#home-first-step {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.9rem;
  width: min(46rem, 92vw);
  margin: 0.2rem auto 1rem;
  padding: 0.85rem 3rem 0.85rem 1rem;
  border: 2px solid rgba(250, 204, 21, 0.75);
  border-radius: 0.7rem;
  background: linear-gradient(140deg, rgba(66, 46, 8, 0.92), rgba(28, 22, 10, 0.92));
  box-shadow: 0 0 1.6rem rgba(250, 204, 21, 0.22), 0 10px 24px rgba(0, 0, 0, 0.45);
  color: #fef3c7;
  font-family: sans-serif;
  text-align: left;
}
.home-first-step-badge {
  flex: 0 0 auto;
  width: 2.1rem;
  height: 2.1rem;
  border-radius: 50%;
  background: #facc15;
  color: #3a2c05;
  font-weight: bold;
  font-size: 1.1rem;
  display: flex;
  align-items: center;
  justify-content: center;
}
.home-first-step-body {
  flex: 1 1 auto;
  min-width: 0;
}
.home-first-step-label {
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  opacity: 0.75;
}
.home-first-step-title {
  font-size: 1.05rem;
  font-weight: bold;
  line-height: 1.3;
}
.home-first-step-desc {
  font-size: 0.78rem;
  line-height: 1.45;
  opacity: 0.85;
}
.home-first-step-go {
  flex: 0 0 auto;
  padding: 0.55rem 1.3rem;
  border: none;
  border-radius: 0.45rem;
  background: #facc15;
  color: #3a2c05;
  font-size: 0.95rem;
  font-weight: bold;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
}
.home-first-step-go:hover {
  background: #fde047;
}
.home-first-step-close {
  position: absolute;
  top: 0.3rem;
  right: 0.4rem;
  border: none;
  background: transparent;
  color: rgba(254, 243, 199, 0.65);
  font-size: 0.85rem;
  line-height: 1;
  padding: 0.25rem 0.35rem;
  cursor: pointer;
}
.home-first-step-close:hover {
  color: #fef3c7;
}
/* 狭い画面では縦積み（ボタンを下いっぱいに）。 */
@media (max-width: 34rem) {
  #home-first-step {
    flex-wrap: wrap;
    padding-right: 1rem;
  }
  .home-first-step-go {
    width: 100%;
  }
}
/* ライト配色。 */
body.theme-light #home-first-step {
  background: linear-gradient(140deg, rgba(255, 249, 230, 0.97), rgba(250, 240, 214, 0.97));
  color: #3a3326;
  border-color: rgba(176, 141, 63, 0.8);
}
body.theme-light .home-first-step-close {
  color: rgba(58, 51, 38, 0.6);
}

/* ライトモードの「奪った／受け取った」中央モーダル（.card-received-modal と複数枚版）。
   ユーザー報告2026-09-04「カウンターロック関係のモーダルでライトモードなのにダークのものが
   ある」。接触・カウンターロックの一連の流れで最後に出るこのモーダルだけ、暗い緑の箱のまま
   だった（続き338/339のライト総点検は id 指定のモーダルを中心に洗ったため、**クラス指定**の
   このモーダルが漏れていた）。他のライトモーダルと同じアイボリー地＋濃い文字に揃えつつ、
   「受け取った」ことを示す緑のアクセント（枠・見出し）は残す。 */
body.theme-light .card-received-modal {
  background: linear-gradient(160deg, #f3fbf6, #fdfbf5);
  border-color: #1c7a3a;
  box-shadow: 0 0 1.2rem rgba(28, 122, 58, 0.28), 0 20px 50px rgba(0, 0, 0, 0.2);
}
body.theme-light .card-received-modal-label {
  color: #15803d;
  text-shadow: none;
}
body.theme-light .card-received-modal-name {
  color: #3a3326;
  text-shadow: none;
}
body.theme-light .card-received-modal-subtitle {
  color: #5b6b5f;
}
body.theme-light .card-received-modal img,
body.theme-light .card-received-modal .card-box,
body.theme-light .card-received-modal-card .card-box {
  border-color: rgba(28, 122, 58, 0.45);
  box-shadow: 0 0.8rem 2rem rgba(0, 0, 0, 0.18);
}
/* 同じ見落とし（クラス指定なのでライト上書きが無い）を、明るい箱の"中"に入る3つにも適用する。
   ・.anytime-interrupt-modal-row … いつでも割り込みモーダル（本体はライト対応済み）の中の行
   ・.options-menu-segment-btn … 基本設定パネル（同上）の中の切り替えボタン
   ・.card-preview-note … 拡大プレビューに添える注意書き
   これら以外の「クラス指定で暗いまま」の要素（タイトル画面・物語メニュー・盤面のカード裏・
   開発者用パネル等）は、暗いことに意味があるので意図的にそのままにしている。 */
body.theme-light .anytime-interrupt-modal-row {
  background: rgba(176, 141, 63, 0.1);
  border-color: rgba(176, 141, 63, 0.45);
  color: #3a3326;
}
body.theme-light .options-menu-segment-btn {
  background: rgba(176, 141, 63, 0.12);
  border-color: rgba(176, 141, 63, 0.45);
  color: #3a3326;
}
body.theme-light .card-preview-note {
  background: #fdfbf5;
  border-color: rgba(176, 141, 63, 0.6);
  color: #3a3326;
}

/* ===== フレンド機能（2026-09-04、friends-ui.js） ===================================== */
/* マイページのボタン（称号と同じく「押したらモーダル」方式。マイページのレイアウトは
   絶対配置＋width:max-content なので、幅の広いリストは直接置かない。続き316の教訓）。 */
.my-page-friends-btn {
  padding: 0.35rem 0.8rem;
  background: rgba(15, 23, 42, 0.55);
  border: 1px solid rgba(148, 163, 184, 0.5);
  border-radius: 0.4rem;
  color: #e2e8f0;
  font-size: 0.8rem;
  cursor: pointer;
  white-space: nowrap;
}
.my-page-friends-btn:hover {
  border-color: #facc15;
  color: #fde68a;
}
/* 届いている申請があるときは押す前に分かるように光らせる。 */
.my-page-friends-btn.has-incoming {
  border-color: #facc15;
  color: #fde68a;
  box-shadow: 0 0 0.7rem rgba(250, 204, 21, 0.45);
}
body.theme-light .my-page-friends-btn {
  background: #fdfbf5;
  border-color: #cbb779;
  color: #3a3326;
}

#friends-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 10701;
  width: min(30rem, 92vw);
  max-height: 82vh;
  overflow-y: auto;
  padding: 1.2rem 1.2rem 1rem;
  background: linear-gradient(160deg, #111827, #0b1120);
  border: 1px solid rgba(148, 163, 184, 0.45);
  border-radius: 0.7rem;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.55);
  color: #e2e8f0;
}
body.theme-light #friends-modal {
  background: #fdfbf5;
  border-color: #cbb779;
  color: #3a3326;
}
.friend-modal-title {
  font-size: 1rem;
  font-weight: bold;
  margin-bottom: 0.6rem;
}
.friend-section-heading {
  margin: 0.9rem 0 0.35rem;
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  color: #94a3b8;
}
body.theme-light .friend-section-heading {
  color: #7a6f56;
}
.friend-empty-note {
  font-size: 0.78rem;
  color: #94a3b8;
  line-height: 1.5;
  padding: 0.3rem 0;
}
body.theme-light .friend-empty-note {
  color: #6b6250;
}
.friend-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 0;
  border-bottom: 1px solid rgba(148, 163, 184, 0.18);
}
body.theme-light .friend-row {
  border-bottom-color: rgba(203, 183, 121, 0.4);
}
.friend-row-avatar {
  flex: none;
  width: 2.1rem;
  height: 2.1rem;
  border-radius: 50%;
  background: rgba(148, 163, 184, 0.25) center / cover no-repeat;
  border: 1px solid rgba(148, 163, 184, 0.4);
}
.friend-row-info {
  flex: 1;
  min-width: 0;
}
.friend-row-name {
  font-size: 0.85rem;
  font-weight: bold;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.friend-row-sub {
  font-size: 0.7rem;
  color: #94a3b8;
}
body.theme-light .friend-row-sub {
  color: #6b6250;
}
/* オンラインの人だけ緑にする（「今なら誘える」が一目で分かるように）。 */
.friend-row-sub.is-online {
  color: #4ade80;
}
body.theme-light .friend-row-sub.is-online {
  color: #15803d;
}
.friend-row-actions {
  flex: none;
  display: flex;
  gap: 0.3rem;
}
.friend-btn {
  padding: 0.25rem 0.6rem;
  background: rgba(148, 163, 184, 0.18);
  border: 1px solid rgba(148, 163, 184, 0.45);
  border-radius: 0.3rem;
  color: #e2e8f0;
  font-size: 0.72rem;
  cursor: pointer;
  white-space: nowrap;
}
.friend-btn.is-primary {
  background: #be185d;
  border-color: #be185d;
  color: #fff;
}
.friend-btn.is-small {
  font-size: 0.68rem;
  padding: 0.15rem 0.45rem;
}
.friend-btn:disabled {
  opacity: 0.6;
  cursor: default;
}
body.theme-light .friend-btn {
  background: #fffdf8;
  border-color: #cbb779;
  color: #3a3326;
}
body.theme-light .friend-btn.is-primary {
  background: #be185d;
  border-color: #be185d;
  color: #fff;
}

/* 対戦終了パネルの中に出す一行（モーダルを増やさない、というユーザーとの合意）。 */
.post-game-friend-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.7rem;
  font-size: 0.78rem;
}
.post-game-friend-label {
  color: #94a3b8;
}
body.theme-light .post-game-friend-label {
  color: #6b6250;
}
.post-game-friend-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.15rem 0.4rem;
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.3rem;
}
body.theme-light .post-game-friend-chip {
  border-color: rgba(203, 183, 121, 0.6);
}

/* ===== 盤面のWebGL描画（three.js, board-3d.js）================================== */
/* 3D描画がONの間は、WebGLが描く担当の要素から**絵だけ**を剥がす。箱（大きさ・位置）は
   そのまま残すので、タップ判定（elementsFromPoint ベースの自前方式）も、管理者モードの
   位置調整も、今までどおり一切変わらない。ここで消しているのは背景画像と、それに付いて
   いた影・光彩だけ——つまりiOSで合成レイヤーとGPUメモリを食っていた分。 */
/* 【重要】必ず #game-table の中だけに限る。WebGLが描くのは #game-table の中を走査して
   見つけた要素だけなので、盤面の外にある同じクラスの要素——ドラッグ中のゴーストや、
   駒の移動演出のゴースト（body直下に置く .piece-hop-ghost の中の .piece-face）、左下の
   駒スキンのプレビュー——まで絵を剥がしてしまうと、**代わりに描くものが無いので
   まっさらな箱になる**（実測: 3D描画ONの間、移動/ドラッグ中の駒が無地になっていた）。 */
body.board-3d-on #game-table .board-card,
body.board-3d-on #game-table .piece-face,
body.board-3d-on #game-table .stack-top,
body.board-3d-on #game-table .lock-area-bar-image,
body.board-3d-on #game-table .playmat-bg,
body.board-3d-on #game-table .table-background-bg {
  background-image: none !important;
  background-color: transparent !important;
  /* 影・光彩も剥がす（iOSで重い合成の元）。ただし !important は付けない——「今ここに置いた」
     「この候補が選ばれている」等の**ハイライトの光**（.cell.card-effect-just-placed .board-card 等、
     このルールより詳細度が高い）は、3D描画中でも今までどおり見えてほしいため。 */
  box-shadow: none;
}
/* WebGLのキャンバス自体。.scene の中に敷き、盤面のDOMより手前・UIより奥に置く。 */
#board-3d-canvas {
  position: absolute;
  left: 0;
  top: 0;
  pointer-events: none;
}
body.board-3d-on #game-table { position: relative; z-index: 1; }

/* WebGL描画中に、DOMの「光る演出」をキャンバスの手前へ逃がす層（board-3d.js の
   syncGlowLayer）。キャンバス(z-index:5)より手前に置くことで、手番の駒のグローや
   ホバーの光が、WebGLの描くカード・マスの板に隠されなくなる（ユーザー報告2026-09-05
   「自ターン時の駒のカラーEFFECTがしっかり光っていない」）。中身は空の箱で、
   位置と大きさだけJS側が元の要素に合わせる。 */
#board-3d-glow-layer {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  pointer-events: none;
  z-index: 6;
}
.board-3d-glow {
  position: absolute;
  border-radius: 0.2rem;
  pointer-events: none;
}
.board-3d-glow.is-turn {
  animation: piece-own-turn-glow 2.6s ease-in-out infinite;
}
.board-3d-glow.is-hover {
  box-shadow: 0 0 0.4rem 0.12rem rgba(56, 189, 248, 0.9);
}
.board-3d-glow.is-turn.is-hover {
  animation: piece-own-turn-glow-hover 2.6s ease-in-out infinite;
}
/* 手番プレイヤーのロックエリアの光。元の .lock-area.is-turn-player と同じアニメーションを
   逃がし先で再生する（色は --turn-glow-rgb をJS側が引き継ぐ）。 */
.board-3d-glow.is-lock-turn {
  animation: lock-area-turn-glow 2.6s ease-in-out infinite;
}
/* 「常時光る演出をやめる」設定は、逃がした先でも同じように効かせる。 */
body.reduce-glow .board-3d-glow.is-turn,
body.reduce-glow .board-3d-glow.is-turn.is-hover,
body.reduce-glow .board-3d-glow.is-lock-turn {
  animation: none !important;
}

/* 盤面のWebGL描画中に、一時的な演出（到達バースト・ロックの刻印など）をキャンバスの手前へ
   逃がすための入れ物（board-3d-overlay.js の mountOverlayEffect）。中身は元の置き場所と
   同じく inset:0 で広がるので、この箱に元の要素と同じ位置・大きさを与えるだけでよい。 */
.board-3d-effect-box {
  position: absolute;
  pointer-events: none;
}
/* ロック中でも使えるカードの光を逃がした先の箱。カードと同じ角丸にして、
   きらめき（帯）は箱の中で切り、周回する球は箱の外へはみ出せるようにする。 */
.board-3d-glow.is-usable-while-locked {
  border-radius: 0.2rem;
  overflow: hidden;
}
.board-3d-glow.is-usable-while-locked.effect-orbit {
  overflow: visible;
}

/* ===== 【演出①】「防いだ！」の瞬間（カウンターロック／ゴメンナサイ） =====================
   ユーザー要望2026-09-06「演出で強化できるところ→おすすめ順で実装しよう！」の1位。
   接触をカウンターロックで無効化した瞬間・最後のロックをゴメンナサイで止めた瞬間は、この
   ゲームで一番の見せ場なのに、今までは文章のモーダルが出るだけだった（専用の動きは0件）。
   守った側の駒（ゴメンナサイは守った人のロックエリア）に六角形の盾が張られ、衝撃波が広がり、
   画面中央に「防いだ！」が一瞬だけ出る。攻めた側の駒は弾かれてよろける。
   盤面のDOMに載せる演出なので、キャンバス（奥）より自然に手前に描かれる（appendEffectHost）。 */
/* 【ユーザー要望2026-09-08・続き487】「ゴメンナサイの手札効果が使用されるタイミングはど派手に」。
   このゲームで一番の逆転（相手の7色目＝勝利宣言を止めたうえに、完成間近のロックを1枚奪う）なので、
   止まったことを見せる「防いだ！」(.block-shield)の**前**に、発動そのものを宣言する。
   ★重い装飾は使わない: filter / backdrop-filter は全画面に掛けるとGPUの弱い端末で1フレームを
     丸ごと持っていく（続き450で勝利演出のぼかしを外した実測、#339でも重さが問題になっている）。
     ここは**塗りと transform だけ**で組む。 */
/* 【ユーザー要望2026-09-08・続き489】「もっとかっこよくしたい。画面全体を4等分したような感じで！
   アバターも丸枠無くしてがっつりめいっぱい表示で！」（例として挙がったのはApexの「チャンピオン部隊」）。
   前の版は暗幕の上に小さなカードを並べる形だったので、**画面まるごとを人数ぶんに縦割りし、
   1人1枚の縦長パネルにアバターを全面で敷く**形へ作り直した。2人戦なら2分割、3人なら3分割になる。
   ★ここでも重い装飾（filter / backdrop-filter）は使わない——塗りと transform だけ（続き450・#339）。 */
/* ★管理者モードの「対戦開始前の『今回のメンバー』紹介」から調整する値。
   admin.js の default と必ず同じ数字にしておくこと（リセットで戻る先がここになる）。 */
:root {
  --match-intro-duration: 3.8;        /* 見せる長さ（秒）。main.js が読む */
  --match-intro-pet-size: 18rem;      /* ペットの大きさ */
  --match-intro-pet-lift: 0.4rem;     /* ペットがふわふわ浮く高さ */
  --match-intro-name-size: 2rem;      /* 名前 */
  --match-intro-seat-size: 0.85rem;   /* 「あなた／対戦相手」 */
  --match-intro-title-size: 1.5rem;   /* 見出し「今回のメンバー」 */
  --match-intro-emoji-size: 9rem;     /* 絵文字アバターの大きさ（画像アバターには効かない） */
  --match-intro-info-bottom: 2.4rem;  /* 名前まわりを下端からどれだけ上げるか */
  --match-intro-portrait-pos-x: 50%;  /* アバター画像の見せる位置（0%=左寄り・100%=右寄り） */
  /* 【続き498】位置の微調整（ユーザー要望）。既定はすべて0＝今までと同じ見た目。
     ※ transform を持つ要素（見出し・名前まわり・ペット）は animation が transform を
     　最後まで保持する（fill-mode both / infinite）ので、**キーフレーム側に含めないと効かない**。 */
  --match-intro-title-pos-y: 0rem;    /* 見出し「今回のメンバー」の上下 */
  --match-intro-info-pos-x: 0rem;     /* 名前まわり（段位・ペット・席・名前）の左右 */
  --match-intro-pet-pos-x: -4.7rem;   /* ペットの左右 */
  --match-intro-pet-pos-y: 0.7rem;    /* ペットの上下 */
  --match-intro-rank-pos-x: 6.6rem;   /* 段位バッジの左右 */
  --match-intro-rank-pos-y: 18.2rem;  /* 段位バッジの上下 */
  --match-intro-veil-top: 62%;        /* 下の暗い幕がどこまで上がるか */
  --match-intro-rank-size: 11.2rem;   /* 段位バッジ（ランク戦のときだけ出る） */
  --match-intro-rank-name-size: 1.3rem; /* 段位名 */
}

.match-intro {
  position: fixed;
  inset: 0;
  z-index: 10670;
  display: flex; /* 人数ぶんの縦割り。パネルが flex:1 で等分される */
  background: #05070d;
  cursor: pointer; /* タップで飛ばせる */
  animation: match-intro-in 0.28s ease-out;
}
.match-intro.is-leaving { animation: match-intro-out 0.32s ease-in forwards; }
/* 【続き494・ユーザー報告「プレビューが表示されない」】続き493 でプレビューだけ z-index を
   1500 まで下げたのが誤りだった——管理者パネルはホーム画面等では 2800 / その背景は 2799 へ
   **引き上げられる**（上の body.full-screen-page-active 参照）ので、プレビューはその背景の下に
   潜って見えなくなる。**固定の数字で場所取りをしない**——プレビューは本番と同じ高さのまま置き、
   出ている間だけ管理者パネルをその上へ出す（下の body.match-intro-preview-active）。 */
.match-intro.is-preview { z-index: 10670; }
body.match-intro-preview-active #admin-panel { z-index: 10680; }
@keyframes match-intro-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes match-intro-out { from { opacity: 1; } to { opacity: 0; } }

/* 1人ぶんの縦長パネル。下から迫り上がるように現れる（席の順に少しずつ遅らせる）。 */
.match-intro-panel {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  animation: match-intro-panel 0.62s cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: var(--intro-delay, 0s);
}
.match-intro-panel + .match-intro-panel { border-left: 1px solid rgba(255, 255, 255, 0.14); }
@keyframes match-intro-panel {
  from { opacity: 0; transform: translateY(7%); }
  to { opacity: 1; transform: none; }
}
/* 上の色帯と、下からにじむその人の色。「誰が何色か」が一目で分かる。 */
.match-intro-panel::before {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 0.34rem;
  background: var(--intro-color, #f6c945);
  z-index: 3;
}
.match-intro-panel::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 42%;
  background: linear-gradient(to top, var(--intro-color, #f6c945), transparent);
  opacity: 0.3;
  z-index: 2;
  pointer-events: none;
}

/* ★アバターは丸枠なしで全面。画像は cover で敷き詰め、絵文字のアバターは特大で中央に置く。 */
.match-intro-portrait {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--match-intro-emoji-size, 9rem); /* 絵文字アバター用 */
  line-height: 1;
  color: #e2e8f0;
  animation: match-intro-portrait 3.4s ease-out both; /* ゆっくり寄っていく */
  animation-delay: var(--intro-delay, 0s);
}
.match-intro-portrait .avatar-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* 【ユーザー報告2026-09-08・続き495】「アバター画像の見せる位置が変わりません」。
     原因は動かす向きが逆だったこと——パネルは**縦長**、アバターの絵は**正方形**なので、
     object-fit: cover では**左右がはみ出して切り取られ、上下は切り取られない**（実測: 4人戦の
     パネル 319x720 に 768x768 の絵→ 720x720 に拡大され、横に401px はみ出す／縦は0）。
     つまり上下をいくら動かしても1pxも変わらない。動かせる向き＝左右にした。 */
  object-position: var(--match-intro-portrait-pos-x, 50%) 50%;
  display: block;
}
@keyframes match-intro-portrait {
  from { transform: scale(1.14); }
  to { transform: scale(1); }
}
/* 下半分を暗くして名前を読ませる（アバターの上に重ねるだけ。ぼかしは使わない）。 */
.match-intro-veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(to top, rgba(3, 5, 10, 0.95) 10%, rgba(3, 5, 10, 0.55) calc(var(--match-intro-veil-top, 62%) * 0.55), rgba(3, 5, 10, 0) var(--match-intro-veil-top, 62%));
}

.match-intro-info {
  position: relative;
  z-index: 4;
  padding: 0 0.7rem var(--match-intro-info-bottom, 2.4rem);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  text-align: center;
  animation: match-intro-info 0.5s ease-out both;
  animation-delay: calc(var(--intro-delay, 0s) + 0.2s);
}
@keyframes match-intro-info {
  from { opacity: 0; transform: translate(var(--match-intro-info-pos-x, 0rem), 0.8rem); }
  to { opacity: 1; transform: translateX(var(--match-intro-info-pos-x, 0rem)); }
}
.match-intro-seat {
  font-size: var(--match-intro-seat-size, 0.85rem);
  font-weight: 800;
  letter-spacing: 0.22em;
  color: var(--intro-color, #f6c945);
}
.match-intro-name {
  font-size: var(--match-intro-name-size, 2rem);
  font-weight: 900;
  color: #ffffff;
  text-shadow: 0 0.12rem 0.5rem rgba(0, 0, 0, 0.9);
  word-break: break-word;
  line-height: 1.2;
}
/* 【ユーザー要望2026-09-08・続き495】「ランクバッチは？」。ランク戦のときだけ、名前の上に
   段位バッジを出す（ランク戦でない対戦では段位そのものが動かないので出さない）。 */
.match-intro-rank {
  /* 【続き500】ペットより手前に描く。DOM順ではペットが後（＝上）に来るので、
     重なり順だけをここで明示する（並び順は変えない＝位置調整の変数の意味を保つ）。 */
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
  transform: translate(var(--match-intro-rank-pos-x, 0rem), var(--match-intro-rank-pos-y, 0rem));
}
.match-intro-rank .rank-badge-image {
  width: var(--match-intro-rank-size, 4rem);
  height: var(--match-intro-rank-size, 4rem);
  object-fit: contain;
}
.match-intro-rank-name {
  font-size: var(--match-intro-rank-name-size, 0.8rem);
  font-weight: 800;
  letter-spacing: 0.12em;
  color: #fde68a;
}

/* ペット（ユーザー要望「ペットも入れれるといい」）。名前の上でふわふわさせる。 */
.match-intro-pet {
  position: relative;
  z-index: 1; /* 段位バッジ（z-index:2）より後ろ＝続き500 */
  width: var(--match-intro-pet-size, 7rem);
  height: var(--match-intro-pet-size, 7rem);
  object-fit: contain;
  animation: match-intro-pet 1.6s ease-in-out infinite;
}
/* 素材が無くて追従スプライト（ドット絵）へ落ちた時だけ、拡大でぼやけないようにする。 */
.match-intro-pet.is-sprite { image-rendering: pixelated; }
@keyframes match-intro-pet {
  0%,
  100% {
    transform: translate(var(--match-intro-pet-pos-x, 0rem), var(--match-intro-pet-pos-y, 0rem));
  }
  50% {
    transform: translate(
      var(--match-intro-pet-pos-x, 0rem),
      calc(var(--match-intro-pet-pos-y, 0rem) - var(--match-intro-pet-lift, 0.4rem))
    );
  }
}

/* 見出しと案内は画面全体に横断させる（パネルの分割線をまたぐ）。 */
.match-intro-title {
  position: absolute;
  left: 0; right: 0; top: 0;
  z-index: 6;
  padding: 0.85rem 0 1.5rem;
  text-align: center;
  font-size: var(--match-intro-title-size, 1.5rem);
  font-weight: 900;
  letter-spacing: 0.32em;
  color: #fde68a;
  text-shadow: 0 0 1rem rgba(250, 204, 21, 0.45);
  background: linear-gradient(to bottom, rgba(3, 5, 10, 0.92), rgba(3, 5, 10, 0));
  animation: match-intro-title 0.5s ease-out both;
}
@keyframes match-intro-title {
  from { opacity: 0; transform: translateY(calc(var(--match-intro-title-pos-y, 0rem) - 0.6rem)); }
  to { opacity: 1; transform: translateY(var(--match-intro-title-pos-y, 0rem)); }
}
.match-intro-hint {
  position: absolute;
  left: 0; right: 0; bottom: 0.55rem;
  z-index: 6;
  text-align: center;
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  color: rgba(226, 232, 240, 0.5);
}
/* 「今回のメンバー」は画面全体を使う演出なので、常に最前面に居るボタン（不具合報告など、
   紹介より高い z-index を持つもの）はこの数秒だけ引っ込める。z-index の競り合いにすると
   壊れやすいので、印が付いている間だけ隠す形にした。 */
body.match-intro-active #self-status-bug-report { opacity: 0; pointer-events: none; }

body.reduce-glow .match-intro-title { text-shadow: none; }
body.reduce-glow .match-intro-pet { animation: none; }
body.reduce-glow .match-intro-portrait { animation: none; }

/* ゴメンナサイの発動演出（続き487）。見せる長さは管理者モードから変えられる。
   キーフレームの%はこの長さに対する割合なので、伸ばすとカードが止まっている時間も
   そのぶん長くなる（30%〜78%が「大きく止まっている」区間）。
   既定値は admin.js の default と必ず同じ数字にしておくこと。 */
:root {
  --gomennasai-declare-duration: 2.2; /* 見せる長さ（秒）。main.js が読む */
}
.gomennasai-declare {
  position: fixed;
  inset: 0;
  z-index: 10680;
  pointer-events: none;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: gomennasai-wash calc(var(--gomennasai-declare-duration, 2.2) * 1s) ease-out forwards;
}
@keyframes gomennasai-wash {
  0% { background: rgba(88, 28, 135, 0); }
  12% { background: rgba(88, 28, 135, 0.82); }
  70% { background: rgba(88, 28, 135, 0.62); }
  100% { background: rgba(88, 28, 135, 0); }
}
/* 中央のカード。斜めに滑り込んで一度大きく止まり、最後に手前へ抜ける。 */
.gomennasai-declare-card {
  width: 13rem;
  height: 13rem;
  border-radius: 0.6rem;
  background-size: cover;
  background-position: center;
  box-shadow: 0 0 2.4rem rgba(216, 180, 254, 0.85), 0 0.6rem 1.6rem rgba(0, 0, 0, 0.55);
  animation: gomennasai-card calc(var(--gomennasai-declare-duration, 2.2) * 1s) cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes gomennasai-card {
  0% { transform: translateX(-38%) rotate(-16deg) scale(0.5); opacity: 0; }
  18% { transform: translateX(0) rotate(-6deg) scale(1.18); opacity: 1; }
  30% { transform: translateX(0) rotate(0deg) scale(1); opacity: 1; }
  78% { transform: translateX(0) rotate(0deg) scale(1); opacity: 1; }
  100% { transform: translateX(0) rotate(0deg) scale(1.5); opacity: 0; }
}
/* 「ゴメンナサイッ！」の文字。カードの少し下で、叩きつけるように出る。 */
.gomennasai-declare-text {
  position: absolute;
  left: 50%;
  top: calc(50% + 8.2rem);
  transform: translateX(-50%);
  font-size: 2.6rem;
  font-weight: 900;
  letter-spacing: 0.08em;
  color: #f5f3ff;
  text-shadow: 0 0 1.2rem rgba(192, 132, 252, 0.95), 0 0.25rem 0 rgba(59, 7, 100, 0.9);
  white-space: nowrap;
  animation: gomennasai-text calc(var(--gomennasai-declare-duration, 2.2) * 1s) cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes gomennasai-text {
  0% { opacity: 0; transform: translateX(-50%) scale(2.2); }
  20% { opacity: 1; transform: translateX(-50%) scale(1); }
  80% { opacity: 1; transform: translateX(-50%) scale(1); }
  100% { opacity: 0; transform: translateX(-50%) scale(1.15); }
}
/* 外へ弾ける紫の輪。2枚を時間差で。 */
.gomennasai-declare-ring {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 13rem;
  height: 13rem;
  margin: -6.5rem 0 0 -6.5rem;
  border-radius: 50%;
  border: 0.28rem solid rgba(216, 180, 254, 0.9);
  animation: gomennasai-ring 1.1s ease-out forwards;
}
.gomennasai-declare-ring.is-delayed { animation-delay: 0.16s; border-color: rgba(250, 204, 21, 0.75); }
@keyframes gomennasai-ring {
  0% { opacity: 0.95; transform: scale(0.35); }
  100% { opacity: 0; transform: scale(2.6); }
}
body.reduce-glow .gomennasai-declare-card { box-shadow: none; }
.block-shield {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 260%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%) scale(0.2);
  border: 0.22rem solid var(--block-color, #f6c945);
  /* 六角形。物理的な「盾」に見せたいので円ではなく角のある形にする。 */
  clip-path: polygon(50% 0%, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%);
  background: radial-gradient(circle at 50% 50%, color-mix(in srgb, var(--block-color, #f6c945) 42%, transparent), transparent 72%);
  box-shadow: 0 0 1.4rem color-mix(in srgb, var(--block-color, #f6c945) 70%, transparent);
  opacity: 0;
  pointer-events: none;
  animation: block-shield-pop 620ms cubic-bezier(0.16, 0.9, 0.3, 1) forwards;
}
@keyframes block-shield-pop {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.2); }
  18% { opacity: 1; transform: translate(-50%, -50%) scale(1.12); }
  38% { opacity: 0.95; transform: translate(-50%, -50%) scale(0.96); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.3); }
}

.block-shockwave {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 120%;
  aspect-ratio: 1;
  border-radius: 50%;
  border: 0.16rem solid color-mix(in srgb, var(--block-color, #f6c945) 85%, white);
  transform: translate(-50%, -50%) scale(0.3);
  opacity: 0;
  pointer-events: none;
  animation: block-shockwave-out 700ms ease-out forwards;
}
.block-shockwave.is-delayed { animation-delay: 130ms; }
@keyframes block-shockwave-out {
  0% { opacity: 0.9; transform: translate(-50%, -50%) scale(0.3); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(3.2); }
}

/* 攻めた側の駒が弾かれてよろける（駒は5面の立方体なので、回さずに横揺れだけにする）。 */
.piece.is-block-recoil { animation: block-recoil 520ms cubic-bezier(0.36, 0.07, 0.19, 0.97) both; }
@keyframes block-recoil {
  0% { translate: 0 0; }
  18% { translate: -0.42rem -0.12rem; }
  40% { translate: 0.28rem 0; }
  62% { translate: -0.16rem 0; }
  82% { translate: 0.08rem 0; }
  100% { translate: 0 0; }
}

/* 画面中央の「防いだ！」。盤面の外（body直下・position:fixed）なので、ステージ変形にも
   WebGL描画のON/OFFにも左右されない。操作を邪魔しないよう pointer-events は無し。 */
.board-flash-label {
  position: fixed;
  left: 50%;
  top: 42%;
  transform: translate(-50%, -50%);
  z-index: 10680;
  pointer-events: none;
  font-size: 3.1rem;
  font-weight: 900;
  letter-spacing: 0.16em;
  color: #fff;
  text-shadow:
    0 0 0.7rem var(--block-color, #f6c945),
    0 0 1.6rem var(--block-color, #f6c945),
    0 0.2rem 0.5rem rgba(0, 0, 0, 0.7);
  animation: block-flash-label 900ms cubic-bezier(0.16, 0.9, 0.3, 1) forwards;
}
/* 山札の作り直しは「新しい山ができた」お知らせなので、少し控えめ・少し下に出す。 */
.board-flash-label.is-deck-refill { font-size: 2.3rem; top: 46%; letter-spacing: 0.1em; }
/* 【#375】ゴメンナサイで奪われた色のお知らせ。「防いだ！」のような一語ではなく**文**なので、
   小さく・画面の下寄りに・長めに出す（この行を読ませるのが目的。上に出ると中央のモーダルと
   かぶる）。基底の 900ms の animation はすぐ消えてしまうので、専用の animation で上書きする。 */
.board-flash-label.is-lock-robbed {
  top: auto;
  bottom: 13%;
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  max-width: min(92vw, 44rem);
  text-align: center;
  line-height: 1.5;
  padding: 0.5rem 1.1rem;
  border-radius: 0.7rem;
  background: rgba(16, 10, 26, 0.82);
  border: 0.1rem solid var(--block-color, var(--color-purple, #a855f7));
  text-shadow:
    0 0 0.6rem var(--block-color, var(--color-purple, #a855f7)),
    0 0.2rem 0.5rem rgba(0, 0, 0, 0.8);
  animation: lock-robbed-label 2600ms ease-out forwards;
}
/* 【#407・2026-10-12】オンラインの移動がサーバーに反映されなかった時の一行。
   ゴメンナサイの一行（is-lock-robbed）と同じ置き方・同じ動きで、色だけ赤にする。
   ⚠ 幅に `vw` を使わない（ビューポート基準なのに描画にはステージ倍率が掛かるので、
   92vw が実画面の46%にしかならない＝続き575 の★）。親基準の % で書く。 */
.board-flash-label.is-move-failed {
  top: auto;
  bottom: 13%;
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  max-width: min(44rem, 92%);
  text-align: center;
  line-height: 1.5;
  padding: 0.5rem 1.1rem;
  border-radius: 0.7rem;
  background: rgba(26, 10, 10, 0.86);
  border: 0.1rem solid var(--color-red, #ef4444);
  text-shadow:
    0 0 0.6rem rgba(239, 68, 68, 0.85),
    0 0.2rem 0.5rem rgba(0, 0, 0, 0.8);
  animation: lock-robbed-label 3400ms ease-out forwards;
}
/* スマホでは 1.5rem が**実画面10.8px**にしかならなかった（実測。この一行は中に居て
   ステージ倍率0.449が掛かる）。これは「もう一度マスを選んでください」という**指示**なので、
   読めないと意味が無い。目安の13px以上になるまで上げる（1.9rem＝約13.7px）。 */
body.is-phone-device .board-flash-label.is-move-failed,
body.is-tablet-device .board-flash-label.is-move-failed {
  font-size: 1.9rem;
}
@keyframes lock-robbed-label {
  0% { opacity: 0; transform: translate(-50%, 0.8rem) scale(0.94); }
  8% { opacity: 1; transform: translate(-50%, 0) scale(1); }
  86% { opacity: 1; transform: translate(-50%, 0) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -0.4rem) scale(1); }
}
@keyframes block-flash-label {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.6); }
  16% { opacity: 1; transform: translate(-50%, -50%) scale(1.14); }
  32% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  76% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.06); }
}
/* 「ずっと光り続ける演出をやめる」設定は常時光るものが対象。ここは一瞬の演出なので止めない。 */


/* ===== 【演出②】山札切れ → 捨て場をひっくり返して新しい山札にする ==========================
   ユーザー要望2026-09-06（おすすめ順の2位）。ルール上とても大きな出来事（docs/rulebook.md
   「こんな時は」）なのに、今までは黙って山が入れ替わるだけで、気づかないまま進んでいた。
   捨て場の山がまるごと裏返りながら山札の位置へ移動し、着いたところで山札が「トン」と沈む
   （沈み込みは既存の .stack.is-settling が枚数の増加で自動的に付く）。
   カードのめくれ演出（.card-flip-*）の部品をそのまま使い、向きだけ逆（表→裏）にしている。 */
.card-flip-flipper.is-closing { animation-direction: reverse; }
.deck-refill-ghost { z-index: 10590; }
.deck-refill-burst {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 320%;
  aspect-ratio: 1;
  border-radius: 50%;
  border: 0.14rem solid rgba(255, 245, 210, 0.9);
  background: radial-gradient(circle at 50% 50%, rgba(255, 240, 190, 0.35), transparent 70%);
  transform: translate(-50%, -50%) scale(0.2);
  opacity: 0;
  pointer-events: none;
  animation: deck-refill-burst 620ms ease-out forwards;
}
@keyframes deck-refill-burst {
  0% { opacity: 0.95; transform: translate(-50%, -50%) scale(0.2); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.9); }
}


/* ===== 【演出③-1】ばらまく（増殖する樹々・合同建設・白の意思ほか） ==========================
   ユーザー要望2026-09-06（おすすめ順の3位・⑧の残り）。カードが何枚も盤面に配られる効果は
   「1枚ずつ着地する」だけで、ばらまいている感じが無かった（.scatter 系のCSSは0件だった）。
   着地したマスから、そのカードの色の粒が四方に弾ける。1枚ごとに出るので、複数マスに置く
   効果では自然に「順番に光が飛ぶ」流れになる。 */
.scatter-spark {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 22%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%, #fff, var(--scatter-color, #f6c945) 55%, transparent 72%);
  box-shadow: 0 0 0.5rem var(--scatter-color, #f6c945);
  transform: translate(-50%, -50%) scale(0.4);
  opacity: 0;
  pointer-events: none;
  animation: scatter-spark-out 620ms cubic-bezier(0.2, 0.7, 0.3, 1) forwards;
}
@keyframes scatter-spark-out {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.3); }
  14% { opacity: 1; }
  100% {
    opacity: 0;
    transform: translate(calc(-50% + var(--scatter-dx, 0px)), calc(-50% + var(--scatter-dy, 0px))) scale(0.15);
  }
}

/* ===== 【演出③-2】賭ける（ザ・ギャンブル・試練の儀式）＝宣言色の当たり／外れ ==================
   宣言色の丸（.declared-colors-indicator）は「結果が出た瞬間に黙って消える」だけだった。
   当たった色は一度大きく輝いて割れ、外れた色はひび割れて灰色に沈む。どちらに転んだのかが
   一目で分かるようにする（当たり＝宣言色が出た。ザ・ギャンブルでは手札全捨て、試練の儀式では
   もう一度続く）。 */
.declared-colors-indicator.is-resolving { transition: none; }
.declared-colors-indicator-swatch.is-hit {
  animation: declared-swatch-hit 760ms cubic-bezier(0.16, 0.9, 0.3, 1) forwards;
  z-index: 2;
}
@keyframes declared-swatch-hit {
  0% { transform: scale(1); filter: brightness(1); }
  22% { transform: scale(1.9); filter: brightness(2.1) saturate(1.4); }
  46% { transform: scale(1.25); filter: brightness(1.3); }
  100% { transform: scale(2.6); filter: brightness(2.4); opacity: 0; }
}
.declared-colors-indicator-swatch.is-miss {
  animation: declared-swatch-miss 760ms ease-in forwards;
}
@keyframes declared-swatch-miss {
  0% { transform: translate(0, 0) rotate(0deg); filter: grayscale(0); opacity: 1; }
  18% { transform: translate(0, -0.16rem) rotate(-8deg); }
  100% { transform: translate(0, 0.9rem) rotate(24deg); filter: grayscale(1); opacity: 0; }
}
.declared-colors-indicator-swatch.is-miss:nth-child(2n) { animation-delay: 70ms; }
.declared-colors-indicator-swatch.is-miss:nth-child(3n) { animation-delay: 130ms; }


/* 【#304】合同建設などの「山札から／手札から」を選ぶモーダル（儀式ピックの見た目を流用して
   いる）が、ライトモードでも暗いままだった。ここはカードを見せない純粋な二択なので、他の
   ライトモーダルと同じアイボリー地＋濃い文字に揃える。儀式ピック本体（裏向きのカードを並べて
   選ぶ場面）は暗いことに意味があるので、そちらは変えない。 */
body.theme-light #sleight-ritual-modal.is-place-source-choice {
  background: linear-gradient(160deg, #f6f3fd, #fdfbf5);
  border-color: #6d4bc4;
  color: #3a3326;
  box-shadow: 0 0 1.2rem rgba(109, 75, 196, 0.25), 0 20px 50px rgba(0, 0, 0, 0.2);
}
body.theme-light #sleight-ritual-modal.is-place-source-choice .sleight-ritual-title {
  color: #4c2f8f;
  text-shadow: none;
}
body.theme-light #sleight-ritual-modal.is-place-source-choice .place-source-choice-buttons button {
  background: rgba(109, 75, 196, 0.12);
  color: #3a3326;
  border-color: rgba(109, 75, 196, 0.55);
}
body.theme-light #sleight-ritual-modal.is-place-source-choice .place-source-choice-buttons button:hover {
  background: rgba(109, 75, 196, 0.24);
}

/* 降参の確認モーダル（続き456・オンラインの2人戦のみ）。
   取り返しがつかない操作なので、他の確認モーダルより「重い」見た目にしてある
   （赤系のアクセント＋「降参する」ボタンだけを赤で塗る）。ライトモードにも対応。 */
.resign-confirm-modal {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 10621;
  width: min(26rem, 92vw);
  padding: 1.4rem 1.5rem 1.2rem;
  border-radius: 0.9rem;
  background: rgba(17, 20, 28, 0.98);
  border: 1px solid rgba(220, 60, 70, 0.5);
  box-shadow: 0 22px 55px rgba(0, 0, 0, 0.55);
  color: #e2e8f0;
  font-size: 0.95rem;
  line-height: 1.6;
}
body.theme-light .resign-confirm-modal,
body.theme-light-ingame .resign-confirm-modal {
  background: rgba(253, 251, 245, 0.99);
  border-color: rgba(190, 40, 50, 0.55);
  color: #1e293b;
  box-shadow: 0 22px 55px rgba(15, 23, 42, 0.28);
}
.resign-confirm-title {
  font-size: 1.1rem;
  font-weight: 700;
  color: #fca5a5;
  margin-bottom: 0.6rem;
}
body.theme-light .resign-confirm-title,
body.theme-light-ingame .resign-confirm-title {
  color: #b91c1c;
}
.resign-confirm-body {
  margin-bottom: 1.1rem;
}
.resign-confirm-actions {
  display: flex;
  gap: 0.6rem;
  justify-content: flex-end;
}
.resign-confirm-actions button {
  padding: 0.5rem 1.1rem;
  border-radius: 0.5rem;
  font-size: 0.95rem;
  cursor: pointer;
  border: 1px solid rgba(148, 163, 184, 0.5);
  background: rgba(51, 65, 85, 0.6);
  color: inherit;
}
body.theme-light .resign-confirm-actions button,
body.theme-light-ingame .resign-confirm-actions button {
  background: rgba(226, 232, 240, 0.8);
  border-color: rgba(100, 116, 139, 0.5);
}
.resign-confirm-actions .resign-confirm-ok {
  background: #b91c1c;
  border-color: #b91c1c;
  color: #fff;
  font-weight: 700;
}
/* オプションメニューの「降参する」だけ赤字にして、他の項目と取り違えないようにする。 */
.options-menu-item-danger {
  color: #fca5a5;
}
body.theme-light .options-menu-item-danger,
body.theme-light-ingame .options-menu-item-danger {
  color: #b91c1c;
}

/* 接触の申し込み中（相手の返事待ち）のにらみ合い（2026-09-07）。
   攻めた駒は相手の方向へ前のめりに脈打ち、狙われた駒は自分の色で警戒する。
   「ずっと光り続ける演出をやめる」設定では止める。 */
.piece.is-contact-attacker .piece-face,
.piece.is-contact-defender .piece-face {
  animation: contact-standoff-pulse 0.9s ease-in-out infinite alternate;
}
.piece.is-contact-defender .piece-face {
  animation-name: contact-standoff-alarm;
  animation-duration: 0.55s;
}
@keyframes contact-standoff-pulse {
  from { box-shadow: 0 0 0.1rem var(--contact-standoff-color, #fff); }
  to { box-shadow: 0 0 0.9rem 0.16rem var(--contact-standoff-color, #fff); }
}
@keyframes contact-standoff-alarm {
  from { box-shadow: 0 0 0.14rem rgba(255, 255, 255, 0.5); }
  to { box-shadow: 0 0 1rem 0.2rem var(--contact-standoff-color, #fff); }
}
body.reduce-glow .piece.is-contact-attacker .piece-face,
body.reduce-glow .piece.is-contact-defender .piece-face {
  animation: none;
}

/* ゲート侵攻③「自ゲートへ帰還」（2026-09-07）。外から内へ締まっていく輪で
   「自分の陣地が迎え入れる」感じを出す（侵攻された側に出る gate-intrusion-ring とは
   逆向きの動き＝役割の違いが動きで分かる）。 */
.gate-return-ring {
  position: absolute;
  inset: -60%;
  border-radius: 50%;
  border: 0.2rem solid var(--gate-return-color, #f6c945);
  box-shadow: 0 0 1.2rem var(--gate-return-color, #f6c945);
  opacity: 0;
  pointer-events: none;
  animation: gate-return-close 1.2s ease-out forwards;
}
.gate-return-ring.is-delayed {
  animation-delay: 0.22s;
  animation-duration: 1.25s;
}
@keyframes gate-return-close {
  0% { transform: scale(1); opacity: 0; }
  25% { opacity: 0.85; }
  100% { transform: scale(0.28); opacity: 0; }
}

/* ゴメンナサイで相手のロックエリアから1枚を引き抜く瞬間（2026-09-07）。
   スロットに亀裂が走ってから、その色の光の筋（.steal-beam）が奪った人の手札へ飛ぶ。 */
.lock-pull-crack {
  position: absolute;
  inset: -10%;
  pointer-events: none;
  border-radius: 0.2rem;
  background:
    linear-gradient(105deg, transparent 46%, var(--lock-pull-color, #e2e8f0) 49%, transparent 52%),
    linear-gradient(72deg, transparent 44%, var(--lock-pull-color, #e2e8f0) 47%, transparent 50%);
  filter: drop-shadow(0 0 0.35rem var(--lock-pull-color, #e2e8f0));
  opacity: 0;
  animation: lock-pull-crack 0.75s ease-out forwards;
}
@keyframes lock-pull-crack {
  0% { transform: scale(0.6); opacity: 0; }
  30% { transform: scale(1.05); opacity: 1; }
  100% { transform: scale(1.5); opacity: 0; }
}

/* 手札が0枚でハンドフェイズが飛ばされた時、空の手札エリアを一度だけ淡く光らせる
   （2026-09-07。従来は文字のモーダルだけで、手札エリア自体には何も起きていなかった）。 */
.hand-area.is-hand-empty-flash {
  animation: hand-empty-flash 0.9s ease-out;
}
@keyframes hand-empty-flash {
  0% { box-shadow: 0 0 0 rgba(148, 163, 184, 0); }
  35% { box-shadow: 0 0 1.6rem 0.3rem rgba(148, 163, 184, 0.55); }
  100% { box-shadow: 0 0 0 rgba(148, 163, 184, 0); }
}

/* 【報告#323】連続タイムアップの見える化。ランク戦ではしきい値（既定3回）で敗北、
   それ以外はおまかせ交代になるのに、何回目かがどこにも出ていなかった。
   フェイズ案内板の中に小さく添え、あと1回になったら赤く強めに出す。 */
.turn-timeout-strike {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.18rem 0.5rem;
  border-radius: 999px;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
  color: #fde68a;
  background: rgba(120, 53, 15, 0.55);
  border: 1px solid rgba(251, 191, 36, 0.55);
}
.turn-timeout-strike.is-critical {
  color: #fee2e2;
  background: rgba(127, 29, 29, 0.75);
  border-color: rgba(248, 113, 113, 0.9);
  box-shadow: 0 0 0.5rem rgba(248, 113, 113, 0.5);
}
.turn-timeout-strike.is-bump {
  animation: turn-timeout-strike-bump 0.6s ease-out 1;
}
@keyframes turn-timeout-strike-bump {
  0% { transform: scale(1); }
  30% { transform: scale(1.22); }
  100% { transform: scale(1); }
}
body.reduce-glow .turn-timeout-strike {
  box-shadow: none;
}

/* 【ユーザー要望2026-09-07】ランク戦のマッチング待ち画面に流す「豆知識」（waiting-tips.js）。
   ボタン類の下に置き、待っている間だけ読むもの。切り替わりは瞬間的に入れ替わると読んでいる
   途中で消えて驚くので、いったん薄くしてから差し替える（JS側の FADE_MS と揃えること）。 */
/* 【ユーザー要望2026-09-08・続き485】「豆知識が小さすぎます。むしろ豆知識を中央に大きく
   持ってきましょう！」。以前は 0.66rem でボタン類の下に置いていたが、このアプリは
   1600x900 のステージを画面に合わせて縮めて描くので、スマホでは **実画面で約5px** にしか
   ならず読めなかった（続き439で基本設定の行を大きくしたのと同じ話）。待ち時間の主役に
   格上げして、囲みを付けて中央に大きく出す。 */
/* 【ユーザー指摘2026-09-08・続き487】「この豆知識の背景はさすがにダサくないですか？w
   分量によって枠のサイズが変わるのも嫌です。枠ない方が良いんじゃ？」——そのとおりなので
   囲み（背景・枠線・角丸・内側の余白）をやめ、**文字だけ**を大きく中央に置く。
   ★背景をぼかす案は採らない。全画面のぼかしはGPUの弱い端末で1フレームまるごと持っていく
     （続き450で勝利演出の blur を外した時の実測）。塗りも敷かない方が軽くて綺麗。
   ★高さは**固定**にする（文の長さで枠が伸び縮みして下のボタンが動くのを止めたい、という指摘）。
     3行ぶんを確保し、中で上下中央に置くので、1行の豆知識でも3行の豆知識でも箱は動かない。 */
.waiting-tips {
  margin-top: 1.4rem;
  margin-bottom: 1.4rem;
  max-width: 34rem;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
}
.waiting-tips-label {
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  color: rgba(250, 204, 21, 0.95);
}
.waiting-tips-body {
  font-size: 1.15rem;
  line-height: 1.75;
  color: rgba(241, 245, 249, 0.95);
  transition: opacity 0.42s ease;
  opacity: 1;
  height: 5.25em; /* 1.75em × 3行ぶんで固定。文の長さが変わっても箱は動かない */
  display: flex;
  align-items: center;
  justify-content: center;
}
.waiting-tips-body.is-fading {
  opacity: 0;
}
/* 【ユーザー報告2026-09-08・続き495】「豆知識の文字が見にくいです。コントラストの問題？」
   ——そのとおりで、原因は続き487で私が足したライト配色の文字色だった。**待機画面
   (#ranked-waiting) は配色設定に関わらず常に暗い**（背景は固定の暗い放射グラデーション）のに、
   ライト配色の時だけ文字を暗い色（#3a3326 / #8a6d1f）にしていたため、暗い地に暗い字＝読めない。
   ライト配色用の上書きは丸ごとやめ、常に明るい文字（上の .waiting-tips-* の指定）を使う。
   ★教訓: 「配色設定がライトだから文字を暗く」ではなく、**その文字が乗る面が明るいかどうか**で
     決める。全画面の覆いは配色に追従しないものが多い。 */
/* 同じ理由で、待機画面の「お知らせON」の案内文もライト配色の上書きをやめる（地は暗いまま）。 */

/* 【ユーザー報告2026-09-07】「対戦開始」を押さずに締め切りを迎えるとキューから外れるが、
   待機画面のぐるぐるが回ったままで「まだ探している」ように見えていた（実測: その後は
   サーバーへの問い合わせ0回＝入り直さない限り絶対にマッチしない）。止まったことを見た目でも
   示し、その場で入り直せるボタンを出す（ranked-match.js の showSearchStopped）。 */
#ranked-waiting.is-search-stopped .ranked-waiting-spinner {
  display: none;
}
#ranked-waiting.is-search-stopped .ranked-waiting-status {
  color: #fca5a5;
}
.ranked-waiting-requeue {
  margin-top: 0.7rem;
  padding: 0.5rem 1.5rem;
  font-family: sans-serif;
  font-size: 0.78rem;
  font-weight: 700;
  color: #0e1218;
  background: linear-gradient(160deg, #e3b64a, #c99a2e);
  border: 1px solid rgba(0, 0, 0, 0.3);
  border-radius: 0.6rem;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
  cursor: pointer;
}
.ranked-waiting-requeue:disabled {
  opacity: 0.55;
  cursor: default;
}
body.theme-light .ranked-waiting-requeue,
body.theme-light-ingame .ranked-waiting-requeue {
  color: #2a220e;
}

/* 【ユーザー要望2026-09-07】オプションエリアのランキングアイコンをタップした時の2択
   （ランキングを見る／戦績システムへ）。ranking-page.js の openRankingChoiceMenu。
   他の簡易モーダル（#ranking-info-modal 等）と同じ位置・同じ地の色にそろえる。 */
#ranking-choice-menu {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 1611;
  min-width: 15rem;
  padding: 1.2rem 1.2rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  background: rgba(15, 23, 32, 0.96);
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 0.7rem;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.55);
  font-family: sans-serif;
  color: #e2e8f0;
}
.ranking-choice-title {
  font-size: 0.85rem;
  font-weight: 700;
  text-align: center;
  margin-bottom: 0.2rem;
  color: #fde68a;
}
.ranking-choice-item {
  padding: 0.62rem 0.9rem;
  font-family: sans-serif;
  font-size: 0.8rem;
  text-align: left;
  color: #e2e8f0;
  background: rgba(30, 41, 59, 0.85);
  border: 1px solid rgba(148, 163, 184, 0.3);
  border-radius: 0.5rem;
  cursor: pointer;
}
.ranking-choice-item:hover {
  border-color: rgba(56, 189, 248, 0.7);
  background: rgba(56, 189, 248, 0.14);
}
body.theme-light #ranking-choice-menu,
body.theme-light-ingame #ranking-choice-menu {
  background: rgba(253, 250, 242, 0.98);
  border-color: rgba(146, 118, 52, 0.4);
  color: #3a3326;
}
body.theme-light .ranking-choice-title,
body.theme-light-ingame .ranking-choice-title {
  color: #8a6d1f;
}
body.theme-light .ranking-choice-item,
body.theme-light-ingame .ranking-choice-item {
  background: rgba(255, 255, 255, 0.9);
  border-color: rgba(146, 118, 52, 0.35);
  color: #3a3326;
}

/* 【2026-09-29・続き546】ロックカードを捨てる順に選ぶモーダル（ユーザー要望「ロックエリア風に」）。
   盤面のロックエリア（.lock-area / .lock-slot）と同じ7色の並び・同じ色枠にして、どの色を
   手放すことになるのかが一目で分かるようにする。文字色は #lock-pick-modal から継承させる
   （明暗2テーマあるので決め打ちしない＝続き535の実害）。 */
.lock-pick-row {
  display: flex;
  gap: 0.35rem;
  justify-content: center;
  flex-wrap: wrap;
  margin: 0.5rem 0;
}
.lock-pick-slot {
  position: relative;
  width: 5rem;
  height: 5rem;
  flex-shrink: 0;
  border-radius: 0.2rem;
  border: 2px solid rgba(226, 232, 240, 0.6);
  /* 盤面のスロットと同じく、JS が color にその色を入れて currentColor で光らせる。 */
  box-shadow: 0 0 0.2rem 0.05rem currentColor;
}
.lock-pick-card {
  position: absolute;
  inset: 0;
  border-radius: 0.2rem;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
.lock-pick-card.is-pickable {
  cursor: pointer;
}
.lock-pick-card.is-pickable:hover {
  outline: 2px solid rgba(250, 204, 21, 0.75);
}
/* ファースト・エターナルは他のカードの効果の対象にならない＝見えるけれど押せない。 */
.lock-pick-card.is-locked-out {
  opacity: 0.45;
}
.lock-pick-card.is-discard-picked {
  outline: 3px solid #facc15;
}

/* 【2026-09-29・続き546・ユーザー要望】「盤面を見る」——モーダルを一時的にどかす。
   背景の覆いは**外さず透明にするだけ**（インラインstyleなのでJS側で切り替える）。見るだけに
   して、選んでいる最中に盤面を触らせない＝選択の前提が変わらないようにする。 */
.modal-peek-board {
  display: block;
  margin: 0.5rem auto 0;
  padding: 0.25rem 0.9rem;
  border: 1px solid rgba(148, 163, 184, 0.5);
  border-radius: 0.4rem;
  background: rgba(148, 163, 184, 0.16);
  color: inherit;
  font-family: sans-serif;
  font-size: 0.75rem;
  cursor: pointer;
}
.modal-peek-board:hover {
  background: rgba(148, 163, 184, 0.3);
}
#stack-modal.is-peeking,
#lock-pick-modal.is-peeking {
  opacity: 0;
  pointer-events: none;
}
/* 戻るボタンはモーダル（z-index:10002）より手前に出す。 */
.modal-peek-return {
  position: fixed;
  left: 50%;
  bottom: 1.2rem;
  transform: translateX(-50%);
  z-index: 10003;
  padding: 0.4rem 1.2rem;
  border: 1px solid rgba(250, 204, 21, 0.7);
  border-radius: 0.5rem;
  background: rgba(28, 16, 4, 0.95);
  color: #fde68a;
  font-family: sans-serif;
  font-size: 0.85rem;
  font-weight: bold;
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.5);
}
.modal-peek-return:hover {
  filter: brightness(1.12);
}

/* 【続き548】アグレッシブモードの「▶ 見本」。対戦中でないと押せない。 */
.options-menu-demo-btn {
  margin-left: 0.4rem;
}
.options-menu-demo-btn:disabled {
  opacity: 0.4;
  cursor: default;
}

/* 【#377・2026-10-06】対戦中に「戻る」を押した時の案内。黙って握り潰すと「戻るが壊れている」と
   思われるので、短く理由を出す。盤面の当たり判定には関わらせない（pointer-events: none）。 */
#back-guard-notice {
  position: fixed;
  left: 50%;
  bottom: 4.5rem;
  transform: translateX(-50%);
  z-index: 10050;
  padding: 0.45rem 1.1rem;
  border: 1px solid rgba(250, 204, 21, 0.7);
  border-radius: 0.5rem;
  background: rgba(28, 16, 4, 0.95);
  color: #fde68a;
  font-family: sans-serif;
  font-size: 0.85rem;
  font-weight: bold;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.5);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease;
}
#back-guard-notice.show {
  opacity: 1;
}

/* 【#389】手札の「捨てる順」モーダルの「おまかせ」。確定ボタンと同じ形だが、主役ではない
   ので少し控えめにする（色は決め打ちせず、既存の確定ボタンの指定をそのまま継ぐ）。 */
#stack-modal .stack-modal-confirm.is-auto {
  margin-top: 0.35rem;
  opacity: 0.85;
  font-size: 0.85em;
}

/* 【#385・2026-10-07】勝利演出を派手にする追加2つ。どちらも DOM＋CSS だけで描くので、
   キャンバスの描画量（＝#348 の発熱の主因）を増やさない。数値は --vic-rings / --vic-rays
   から読むので、管理者モードのシミュレーター（victory-preview.js）でそのまま詰められる。 */

/* ① 脈動のたびにキューブから広がる七色の衝撃波。 */
.vic-pulse-ring {
  position: absolute;
  left: 0;
  top: 0;
  width: 2rem;
  height: 2rem;
  margin-left: -1rem;
  margin-top: -1rem;
  border-radius: 50%;
  border: 0.22rem solid rgba(var(--vic-ring-rgb, 255, 255, 255), 0.95);
  box-shadow:
    0 0 1.2rem rgba(var(--vic-ring-rgb, 255, 255, 255), 0.8),
    inset 0 0 1.2rem rgba(var(--vic-ring-rgb, 255, 255, 255), 0.55);
  pointer-events: none;
  opacity: 0;
  animation: vic-pulse-ring var(--vic-ring-ms, 900ms) cubic-bezier(0.1, 0.8, 0.3, 1) forwards;
}
@keyframes vic-pulse-ring {
  0% {
    opacity: 0;
    transform: scale(0.4) rotateX(var(--table-tilt, 42deg));
  }
  12% {
    opacity: calc(0.9 * var(--vic-ring-power, 1));
  }
  100% {
    opacity: 0;
    transform: scale(calc(22 * var(--vic-ring-power, 1))) rotateX(var(--table-tilt, 42deg));
  }
}

/* ② VICTORY の文字の背後でゆっくり回る七色の光芒。文字より奥に置く。 */
.vic-rays {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 170vmax;
  height: 170vmax;
  margin-left: -85vmax;
  margin-top: -85vmax;
  z-index: -1;
  pointer-events: none;
  opacity: 0;
  background: conic-gradient(
    from 0deg,
    rgba(255, 64, 92, 0.5) 0deg 6deg, transparent 6deg 26deg,
    rgba(255, 138, 50, 0.5) 26deg 32deg, transparent 32deg 52deg,
    rgba(255, 216, 74, 0.5) 52deg 58deg, transparent 58deg 78deg,
    rgba(66, 229, 138, 0.5) 78deg 84deg, transparent 84deg 104deg,
    rgba(73, 168, 255, 0.5) 104deg 110deg, transparent 110deg 130deg,
    rgba(255, 116, 200, 0.5) 130deg 136deg, transparent 136deg 156deg,
    rgba(168, 117, 255, 0.5) 156deg 162deg, transparent 162deg 180deg,
    rgba(255, 64, 92, 0.5) 180deg 186deg, transparent 186deg 206deg,
    rgba(255, 138, 50, 0.5) 206deg 212deg, transparent 212deg 232deg,
    rgba(255, 216, 74, 0.5) 232deg 238deg, transparent 238deg 258deg,
    rgba(66, 229, 138, 0.5) 258deg 264deg, transparent 264deg 284deg,
    rgba(73, 168, 255, 0.5) 284deg 290deg, transparent 290deg 310deg,
    rgba(255, 116, 200, 0.5) 310deg 316deg, transparent 316deg 336deg,
    rgba(168, 117, 255, 0.5) 336deg 342deg, transparent 342deg 360deg
  );
  /* 中心ほど薄く＝文字の下が濁らないように抜く。 */
  mask-image: radial-gradient(circle, transparent 0%, transparent 14%, #000 42%, #000 100%);
  -webkit-mask-image: radial-gradient(circle, transparent 0%, transparent 14%, #000 42%, #000 100%);
  animation: vic-rays-in 900ms ease-out forwards, vic-rays-spin 26s linear infinite;
}
@keyframes vic-rays-in {
  from { opacity: 0; }
  to { opacity: calc(0.5 * var(--vic-rays-power, 1)); }
}
@keyframes vic-rays-spin {
  from { rotate: 0deg; }
  to { rotate: 360deg; }
}
/* 【重さ対策】見せ場が終わって結果画面の背景になったら、回し続けるのをやめる
   （既存の .is-settled と同じ考え方＝数分間残る画面で重い装飾を続けない）。 */
#victory-celebration.is-settled .vic-rays {
  animation: vic-rays-in 1ms linear forwards;
  opacity: calc(0.28 * var(--vic-rays-power, 1));
}
body.reduce-glow .vic-pulse-ring,
body.reduce-glow .vic-rays {
  display: none;
}

/* 【2026-10-08】試遊ページからクラウドファンディングのページへ戻るテキストリンク
   （opening-screen.js の試遊パネルの最後）。ボタン3つより控えめにして、
   「遊ぶ」導線の邪魔をしない。色は決め打ちせず、周りから継ぐ（明暗2テーマがあるため＝教訓15）。 */
/* 【続き573】試遊パネルの「いま誰でログインしているか」の1行。色は決め打ちせず周りから
   継ぐ（明暗2テーマがあるため＝教訓15）。**スマホでは他の文字と同じく拡大する**
   ——続き566 で CAMPFIRE リンクをこの拡大規則に入れ忘れて 6.3px のままだった。 */
.opening-trial-auth {
  margin: 0.2rem 0 0.1rem;
  font-size: 0.72rem;
  line-height: 1.5;
  opacity: 0.85;
}
body.is-phone-device .opening-trial-auth {
  font-size: 1.5rem;
  line-height: 1.6;
  margin: 0.4rem 0 0.2rem;
}

.opening-trial-cf {
  display: block;
  margin: 0.9rem auto 0;
  text-align: center;
  font-size: 0.82rem;
  line-height: 1.6;
  opacity: 0.85;
  text-decoration: underline;
  text-underline-offset: 0.18em;
  cursor: pointer;
}
.opening-trial-cf:hover,
.opening-trial-cf:focus-visible {
  opacity: 1;
}
/* 【重要・2026-10-08】スマホでは**これも大きくする**。このアプリは1600x900を縮めて描くので
   1remが実画面で7px前後にしかならず、上の 0.82rem は実機で**6.3px**しか無かった（実測）。
   同じ画面のボタンはスマホ用に 2.5rem（実機19px）へ拡大され、小さい説明文（.opening-trial-desc）は
   **非表示**にする作りになっている——その仕組みにこのリンクを入れ忘れていた。10/17の公開で
   支援者候補が通る導線なので、読めないままにはできない。ボタンより控えめ・説明文より大きい
   位置づけにし、指で押せるように当たり判定も広げる。 */
body.is-phone-device .opening-trial-cf {
  font-size: 1.7rem;
  padding: 0.5rem 1.2rem;
  margin-top: 0.5rem;
  opacity: 0.95;
}

/* 【続き602・2026-10-20】不具合報告をくれた人へのお礼（bug-report-thanks.js）。
   起動時の案内モーダル（#opening-setup-modal）と同じ「自前で地と文字色を持つ暗いパネル」に
   する——明暗2テーマのどちらでも読めるようにするいちばん確実な形（教訓15。色を書かずに
   継承させると、片方のテーマで文字が消える事故が起きる）。
   ホーム画面はスマホでも開くので、スマホ用の寸法も一緒に決める（1600x900 のステージを
   縮めて描くため、iPhone 横持ちでは 1rem が実画面 7〜8px にしかならない）。 */
#bug-thanks-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(34rem, 92vw);
  z-index: 100061; /* 上の覆い(100060)より前面 */
  box-sizing: border-box;
  padding: 1.3rem;
  border-radius: 0.6rem;
  background: rgba(15, 23, 32, 0.98);
  border: 1px solid rgba(251, 191, 36, 0.5);
  color: #e2e8f0;
  font-family: sans-serif;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
  /* ⚠ vh ではなく rem（ステージの高さ = 56.25rem）。vh はビューポート基準なので、
     スマホの実画面では意図の半分以下にしかならない（続き575 の★の罠）。 */
  max-height: 44rem;
  overflow-y: auto;
}
.bug-thanks-title {
  font-size: 1.2rem;
  font-weight: bold;
  color: #fbbf24;
  text-align: center;
  margin-bottom: 0.6rem;
}
.bug-thanks-lead {
  font-size: 0.9rem;
  line-height: 1.6;
  text-align: center;
  margin-bottom: 0.9rem;
}
.bug-thanks-list {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}
.bug-thanks-item {
  padding: 0.6rem 0.7rem;
  border-radius: 0.4rem;
  background: rgba(148, 163, 184, 0.12);
  border-left: 3px solid rgba(251, 191, 36, 0.6);
}
.bug-thanks-when {
  font-size: 0.72rem;
  opacity: 0.7;
}
.bug-thanks-quote {
  font-size: 0.82rem;
  line-height: 1.5;
  margin: 0.25rem 0 0.4rem;
  opacity: 0.85;
  font-style: italic;
}
.bug-thanks-fixed {
  font-size: 0.92rem;
  line-height: 1.55;
  color: #bbf7d0;
}
.bug-thanks-close {
  display: block;
  width: 100%;
  margin-top: 1rem;
  padding: 0.5rem;
  border-radius: 0.4rem;
  border: 1px solid rgba(251, 191, 36, 0.5);
  background: rgba(251, 191, 36, 0.18);
  color: inherit;
  font-size: 0.95rem;
  cursor: pointer;
}
/* スマホ: 実画面のpxで測ってから決める（読ませるもの13px以上／押すもの高さ28px以上）。 */
body.is-phone-device #bug-thanks-modal {
  width: min(78rem, 94vw);
  padding: 1.3rem;
  max-height: 49rem;
}
body.is-phone-device .bug-thanks-title { font-size: 2.3rem; }
body.is-phone-device .bug-thanks-lead { font-size: 1.75rem; }
body.is-phone-device .bug-thanks-when { font-size: 1.4rem; }
body.is-phone-device .bug-thanks-quote { font-size: 1.62rem; }
body.is-phone-device .bug-thanks-fixed { font-size: 1.7rem; }
body.is-phone-device .bug-thanks-close { font-size: 1.7rem; padding: 0.8rem; }
