/* Trez Hunt — フェーズ0+1+2
 *
 * 単一ファイルにまとめている。Propshaft の `stylesheet_link_tag :app` は
 * app/assets/stylesheets 配下を全て読み込むが、その順序に依存したくないため。
 */

:root {
  --bg: #0d1117;
  --bg-elevated: #161b22;
  --line: #30363d;
  --text: #e6edf3;
  --text-dim: #8b949e;
  --accent: #ffd166;
  --accent-ink: #3d2c00;
  --ok: #3fb950;
  --warn: #d29922;
  --err: #f85149;
  --info: #58a6ff;

  /* --- フォーム部品の基準 ---
     入力欄・選択欄・ボタンは全部この高さに揃える。揃えないと、
     隣り合った欄で下端がずれて、説明文の行も食い違う。 */
  --control-h: 36px;
  --control-radius: 8px;

  /* 幅は「1行に何個並ぶか」で決める。文字数で決めると画面ごとに揃わない。
       s … PC 8個 / スマホ 4個
       m … PC 4個 / スマホ 2個
       l … PC 2個 / スマホ 1個 */
  /* アイコンの入力欄。絵文字2文字ぶん。
     ch（＝「0」の幅）で測ると、絵文字はその倍以上あるので小さすぎる。 */
  --w-icon: 2.4em;

  /* フロートのメニュー（ハンバーガー）の一辺。画面のてっぺんに置く帯は、
     この分だけ右を空けて重なりを避ける。 */
  --menu-size: 38px;

  /* iPhone のノッチ / ホームバー */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);

  /* AR ペインの「画面幅に対する高さ」の比。1 = 正方形。
     大きくすると AR が縦長になり、その分だけ地図が狭くなる。
     縦の長さではなく幅を基準にしているので、端末が変わっても見え方が変わらない。

     0.8（横長）にしてある。1 だと半々の表示で AR と地図がほぼ同じ高さになり、
     歩いている間に見たい地図が窮屈だった。カメラ映像は object-fit: cover で
     切り取っているので、縦を削っても見える範囲はほとんど変わらない。 */
  --ar-aspect: 0.8;
  /* ただし横長の端末では画面を埋め尽くしてしまうので、縦にも上限を設ける。
     単位は body / .play と揃えること（混ぜると足し算が合わなくなる）。 */
  --ar-max-height: 50svh;
}

* { box-sizing: border-box; }

/* JS で hidden を立てて隠す要素があるが、.ar-start { display: grid } のような
 * author スタイルは UA スタイルシートの [hidden] { display: none } に勝ってしまう。
 * （author > UA という優先順位のため）
 * 個別に書き足すと漏れるので、ここで一括して打ち勝たせる。 */
[hidden] { display: none !important; }

/* 同じ理由で、閉じている <dialog> も守る。
 * .shop { display: flex } のように author スタイルで display を書くと、
 * UA の dialog:not([open]) { display: none } に勝ってしまい、閉じたままの
 * ダイアログがレイアウトに居座る。プレイ画面は grid なので、行の割り当てごと
 * ずれて、お宝カードが画面外へ押し出された。 */
dialog:not([open]) { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  /* 100% でも 100dvh でもなく 100svh。
     100% は「初期包含ブロック＝URLバーが隠れた状態の高さ」を指すので、
     URLバーが出ている間は body のほうが実際の表示領域より背が高くなり、
     その差だけページがスクロールできてしまう（プレイ画面で顕著）。
     dvh は「今見えている高さ」だが、読み込み直後はURLバーの出入りと
     食い違うことがあり、同じはみ出しが起きた。svh は「URLバーが出ている
     ＝一番狭い状態」の高さなので、どの瞬間でもはみ出さない。 */
  height: 100svh;

  /* 引っ張って更新（プルダウン更新）を止める。
     プレイ中は地図を掴んで動かすので、下へ大きく引くつもりが更新になって、
     遊んでいる途中で画面が作り直される。

     **body だけでは足りない。** この指定を画面全体（viewport）へ効かせるのは
     ルート（html）の値で、body の値が引き継がれるかはブラウザ任せ。
     両方に書いておく。 */
  overscroll-behavior: none;

  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Hiragino Sans", "Noto Sans JP", sans-serif;
  -webkit-text-size-adjust: 100%;
}

body {
  /* ゲーム画面。ページ自体のスクロールとプルダウン更新を止める（html にも同じ指定） */
  overscroll-behavior: none;
  touch-action: manipulation;

  /* スマホでボタンやカードを押したときに、文字が選択されたり
     iOS の「コピー / 調べる」メニューが出たりするのを止める。
     押しているつもりが選択になるので、操作感が壊れる。
     タップ対象の多くが <button> ではなく <div> や <a> なので、
     個別に書くと必ず漏れる。ここで一括して切り、
     「選択したい中身」だけ下で戻す。 */
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;

  /* 押した瞬間の灰色の点滅も消す。代わりに :active を自前で当てる
     （既定の点滅は角丸のカードだと四角く出て不格好なため）。 */
  -webkit-tap-highlight-color: transparent;
}

/* ここだけは選択・コピーできる必要がある。
   座標やセンサーの値は、控えたり貼り付けたりする対象なので。 */
input,
textarea,
code,
.kv dd,
.diag__log,
.admin__rowmeta,
.collected__meta,
.collected__where {
  user-select: text;
  -webkit-user-select: text;
  -webkit-touch-callout: default;
}

/* ===================== Play 画面：上下分割 ===================== */

.play {
  /* svh: URLバーが出ている一番狭い状態を基準にする。dvh だと読み込み直後に
     はみ出して、下端のお宝カードが見切れることがあった。 */
  height: 100svh;

  /* 絶対配置の子（メニューなど）の基準をここにする。
     positioned な祖先が無いと初期包含ブロック基準になり、この箱の
     overflow: hidden が効かない。 */
  position: relative;
  display: grid;
  /* 上=AR（画面幅から決まる高さ）/ 中=ディバイダ / 下=MAP（残り全部） */
  grid-template-rows: auto auto 1fr;

  /* 列は1本だけだが、明示して minmax(0, 1fr) にする。
     省略すると暗黙の auto 列になり、その幅は「中の要素がこれ以上は縮めないと
     主張する幅」で決まる。主張する要素が1つでもあると、列が画面より広くなり、
     中の3段（AR・帯・地図）ごと右へはみ出して切り取られる（実機で発生）。
     minmax(0, 1fr) なら下限が 0 になるので、画面幅を超えられない。 */
  grid-template-columns: minmax(0, 1fr);

  overflow: hidden;
}

/* AR ペインの高さは、端末の縦の長さではなく「画面幅に対する比率」で決める。
   縦を等分すると、端末が長いほど AR だけが間延びする。カメラ映像は
   object-fit: cover で切り取っているので、縦に伸ばしても得られる情報は増えない。
   余った縦は全部 MAP に回る（歩いている間に見たいのは地図のほう）。
   比率は :root の --ar-aspect で調整できる。 */
.pane--ar {
  height: min(calc(100vw * var(--ar-aspect)), var(--ar-max-height));
}

/* AR / MAP どちらかを広げる3状態。バッテリー節約と見やすさの両立用。
   広げる側・畳む側は比率ではなく残り全部を使うので、高さの指定は外す。 */
.play[data-layout="ar"]  { grid-template-rows: 1fr auto 0fr; }
.play[data-layout="map"] { grid-template-rows: 0fr auto 1fr; }
.play[data-layout="ar"]  .pane--ar,
.play[data-layout="map"] .pane--ar { height: auto; }

.pane {
  position: relative;
  overflow: hidden;
  min-height: 0; /* grid 内で子要素がはみ出さないように必須 */
}

.pane--ar  { background: #000; }
.pane--map { background: #1b2430; }

/* --- AR ペイン --- */

/* AR ペイン内の重ね順。
 *
 * 絶対配置の要素は、同じスタッキングコンテキスト内の「静的配置のブロック」より
 * 後に描画される。つまり canvas を静的配置のままにすると、絶対配置の video が
 * canvas の上に乗ってしまい、AR のお宝がカメラ映像の裏に隠れる。
 * そのため両方を絶対配置にして z-index を明示する。
 *
 *   video (0) < canvas (1) < HUD・ガイド矢印・GET告知 (2) < 起動画面 (3) < エラー (4)
 */
.ar-canvas {
  display: block;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
}

.pane--ar video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}

/* 拡張探索モードの周辺減光。CSS の filter にビネットが無いので別に重ねる。
   映像(0)の上・お宝(1)の下に置きたいので、z-index は 0 のまま DOM 順で前に出す。 */
.ar-vignette {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s ease;
  background: radial-gradient(ellipse at center,
    rgba(0, 0, 0, 0) 45%, rgba(0, 0, 0, 0.55) 80%, rgba(0, 0, 0, 0.85) 100%);
}

.ar-hud {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  padding: calc(var(--safe-top) + 8px) 8px 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.ar-hud__row {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

/* 最上段は右上のメニューボタンと重なるので、その分だけ空ける */
.ar-hud__row:first-child { padding-right: 46px; }

.chip {
  font-size: 11px;
  line-height: 1;
  padding: 5px 8px;
  border-radius: 999px;
  background: rgba(13, 17, 23, 0.72);
  border: 1px solid var(--line);
  color: var(--text-dim);
  backdrop-filter: blur(4px);
  white-space: nowrap;

  /* お宝の名前をそのまま入れるので、長い名前でも幅を持たせない。
     nowrap のままにすると、この1個で HUD の行が画面幅を超える。 */
  max-width: 100%;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.chip b { color: var(--text); font-weight: 600; }

/* HUD は pointer-events: none なので、押せるチップは自分で戻す */
.chip--button {
  appearance: none;
  font: inherit;
  font-size: 11px;
  color: var(--text-dim);
  pointer-events: auto;
  cursor: pointer;
}

.chip--button:active { transform: translateY(1px); }

/* AR 領域の右下に置く、状態を持つボタン（千里眼・チャージ）。
   HUD の並びに混ぜると、他の表示に埋もれて見つけにくい。 */
.ar-modebutton {
  position: absolute;
  right: 8px;
  bottom: calc(var(--safe-bottom) + 8px);
  z-index: 2;

  appearance: none;
  display: grid;
  gap: 5px;
  min-width: 96px;
  padding: 7px 10px 8px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: rgba(13, 17, 23, 0.72);
  backdrop-filter: blur(4px);
  color: var(--text-dim);
  font: inherit;
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
}

.ar-modebutton:active { transform: translateY(1px); }

/* 残量。数字より、減っていく様子が見えるほうが分かりやすい。 */
.ar-modebutton__gauge {
  height: 3px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.16);
  overflow: hidden;
}

.ar-modebutton__fill {
  display: block;
  height: 100%;
  width: 100%;
  background: currentColor;
  transition: width 0.3s linear;
}

/* チャージは戦闘中だけ出す。千里眼は戦闘中だけ引っ込める。 */
.ar-charge { display: none; }

/* 戦いの最中に何度も押すボタン。他のモードボタンの倍の高さにして、
   撃つ操作（画面タップ）のついでに指が届くようにする。 */
.play[data-battle="on"] .ar-charge {
  display: grid;
  align-content: center;
  gap: 8px;
  min-height: 68px;
  padding: 10px 12px;
  font-size: 12px;
}
.play[data-battle="on"] .ar-explore { display: none; }

/* 満タンなら押せる。使用中は色が変わる。 */
.ar-charge[data-state="ready"] { border-color: #f2c956; color: #f2c956; }
.ar-charge[data-state="on"] { border-color: #ff9f43; color: #ff9f43; }
.ar-charge[data-state="empty"] { opacity: 0.5; }

/* 使用中の段階。色で一目で分かるようにする（ゲージも同じ色になる）。 */
.ar-explore[data-level="1"] {
  border-color: var(--accent);
  color: var(--accent);
}

.ar-explore[data-level="2"] {
  border-color: #58a6ff;
  color: #58a6ff;
}
.chip--ok   { border-color: rgba(63, 185, 80, 0.5); }
.chip--warn { border-color: rgba(210, 153, 34, 0.6); }
.chip--err  { border-color: rgba(248, 81, 73, 0.6); color: var(--err); }

/* AR 起動ボタン。iOS は「ユーザー操作起点」でないと方位センサー許可を出せないため、
   自動起動ではなく必ずこのボタンを経由する。 */
.ar-start {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  gap: 14px;
  justify-items: center;
  padding: 24px;
  text-align: center;
  background: radial-gradient(circle at 50% 40%, #1b2430, #000);
  z-index: 3;
  pointer-events: auto;
}

.ar-start__title { font-size: 15px; font-weight: 600; }
.ar-start__note  { font-size: 12px; color: var(--text-dim); max-width: 30em; line-height: 1.6; }

.btn {
  appearance: none;
  border: 1px solid var(--line);
  background: var(--bg-elevated);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  padding: 10px 16px;
  border-radius: 10px;
  cursor: pointer;
}

.btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 700;
}

.btn:active { transform: translateY(1px); }

.ar-error {
  position: absolute;
  inset: auto 8px 8px 8px;
  z-index: 4;
  background: rgba(248, 81, 73, 0.14);
  border: 1px solid rgba(248, 81, 73, 0.5);
  color: #ffb3ae;
  font-size: 12px;
  line-height: 1.6;
  padding: 10px 12px;
  border-radius: 10px;
  pointer-events: auto;
}

.ar-error[hidden] { display: none !important; }

/* --- 画面外ガイド矢印と GET 演出（フェーズ2） --- */

/* お宝が画面外にあるときに、その方向の画面端に出す矢印。
   位置と回転は JS が毎フレーム transform で書き換える。
   left/top を書き換えるとその都度レイアウトが走るので transform に寄せてある。 */
.ar-guide {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 2;
  pointer-events: none;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px 6px 8px;
  border-radius: 999px;
  background: rgba(13, 17, 23, 0.78);
  border: 1px solid var(--accent);
  backdrop-filter: blur(4px);
  white-space: nowrap;
  will-change: transform;

  /* 名前が長いと吹き出しが伸びる。ペインからはみ出さない幅で止める。 */
  max-width: calc(100% - 16px);
  overflow: hidden;
}

/* 矢印だけが回る。ラベルは常に水平のまま読めるようにする。 */
.ar-guide__arrow {
  display: inline-block;
  font-size: 15px;
  line-height: 1;
  color: var(--accent);
}

.ar-guide__label {
  font-size: 12px;
  font-weight: 700;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 出現範囲に入った瞬間の合図。カメラ映像を一瞬白く飛ばす。
   起動画面(3)やエラー(4)より手前に出す。見逃されては意味が無いので。 */
.ar-flash {
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  background: #fff;
  opacity: 0;
}

.ar-flash.is-on { animation: ar-flash 0.45s ease-out; }

@keyframes ar-flash {
  0%   { opacity: 0; }
  12%  { opacity: 0.8; }
  100% { opacity: 0; }
}

/* 手下を一掃したときの合図。こちらは薄く青く。
   お宝に着いた合図（白）と混ざらない色にして、何が起きたかを色で見分けられるようにする。 */
.ar-flash--sweep { background: #66c8ff; }
.ar-flash--sweep.is-on { animation: ar-flash-sweep 0.55s ease-out; }

@keyframes ar-flash-sweep {
  0%   { opacity: 0; }
  15%  { opacity: 0.42; }
  100% { opacity: 0; }
}

/* ボム。敵ぜんぶを殴るので、合図も一番強くする。 */
.ar-flash--bomb { background: #ff9f43; }
.ar-flash--bomb.is-on { animation: ar-flash-bomb 0.5s ease-out; }

@keyframes ar-flash-bomb {
  0%   { opacity: 0; }
  8%   { opacity: 0.85; }
  100% { opacity: 0; }
}

/* 倒し切れずに自爆されたときの合図。薄い赤で数回チカチカさせる。
   良いこと（青・1回）と悪いこと（赤・明滅）を、色と点き方の両方で分ける。 */
.ar-flash--damage { background: #ff5a5a; }
.ar-flash--damage.is-on { animation: ar-flash-damage 0.66s ease-out; }

@keyframes ar-flash-damage {
  0%   { opacity: 0; }
  8%   { opacity: 0.38; }
  24%  { opacity: 0; }
  40%  { opacity: 0.38; }
  56%  { opacity: 0; }
  72%  { opacity: 0.3; }
  100% { opacity: 0; }
}

/* 強い明滅が苦手な人には控えめに。合図そのものは消さない。 */
@media (prefers-reduced-motion: reduce) {
  @keyframes ar-flash {
    0%   { opacity: 0; }
    12%  { opacity: 0.3; }
    100% { opacity: 0; }
  }

  @keyframes ar-flash-sweep {
    0%   { opacity: 0; }
    15%  { opacity: 0.18; }
    100% { opacity: 0; }
  }

  /* 明滅の回数も減らす。チカチカが一番きついのはここ。 */
  @keyframes ar-flash-damage {
    0%   { opacity: 0; }
    12%  { opacity: 0.18; }
    100% { opacity: 0; }
  }

  @keyframes ar-flash-bomb {
    0%   { opacity: 0; }
    8%   { opacity: 0.3; }
    100% { opacity: 0; }
  }
}

/* GET したときの告知。演出そのもの（お宝が大きくなって消える）は three 側。 */
.ar-banner {
  position: absolute;
  left: 50%;
  top: 38%;
  z-index: 2;
  pointer-events: none;
  padding: 12px 18px;
  border-radius: 14px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 15px;
  font-weight: 700;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5);
  transform: translate(-50%, -50%);
  animation: banner-pop 0.35s ease-out;
}

@keyframes banner-pop {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.8); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

/* --- 中央のディバイダ（最寄りのお宝情報＋レイアウト切替） --- */

.divider {
  display: flex;
  align-items: center;
  gap: 10px;

  /* グリッドの子は既定で min-width: auto ＝「中身の最小幅までは縮まない」。
     AR と地図のペインは overflow: hidden なので 0 を主張するが、
     この帯は overflow が visible なので中身の幅をそのまま主張してしまう。 */
  min-width: 0;
  padding: 8px 10px;
  background: var(--bg-elevated);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  z-index: 5;
}

.divider__main { flex: 1; min-width: 0; }

.divider__label {
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.06em;
}

.divider__target {
  display: flex;
  align-items: baseline;
  gap: 8px;
  overflow: hidden;
}

.divider__name {
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.divider__dist {
  font-size: 18px;
  font-weight: 700;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.divider__dir { font-size: 11px; color: var(--text-dim); white-space: nowrap; }

/* MAP だけの表示では、この帯が画面のてっぺんに来る。そこはフロートの
   メニューの場所なので、切替ボタンがメニューの下に潜って重なる。
   メニュー1個ぶんだけ右を空ける。他の表示では帯が画面の途中にあるので、
   空けると無駄な余白になるだけ。 */
.play[data-layout="map"] .divider {
  padding-right: calc(var(--menu-size) + 18px);
}

.layout-toggle { display: flex; gap: 4px; }

.layout-toggle button {
  appearance: none;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--text-dim);
  font-size: 10px;
  padding: 6px 8px;
  border-radius: 7px;
  cursor: pointer;
}

.layout-toggle button[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 700;
}

/* --- MAP ペイン --- */

.map-canvas { width: 100%; height: 100%; }

/* タイルが取れなかったときの表示（オフラインなど） */
.map-canvas--failed {
  display: grid;
  place-content: center;
  padding: 20px;
  text-align: center;
  font-size: 12px;
  line-height: 1.7;
  color: var(--text-dim);
}

/* MapLibre の帰属表示。OSM のデータを使う以上ここは消せないので、
   暗い画面に馴染むよう色だけ合わせる。 */
.maplibregl-ctrl-attrib {
  background: rgba(13, 17, 23, 0.72) !important;
  color: var(--text-dim);
  font-size: 10px;
}

.maplibregl-ctrl-attrib a { color: var(--text-dim); }
.maplibregl-ctrl-attrib-button { filter: invert(1) opacity(0.6); }

/* 地図上のお宝マーカー（マーカー要素の中に差し込む） */
.marker {
  display: grid;
  place-content: center;
  width: 38px;
  height: 38px;
  font-size: 20px;
  background: rgba(13, 17, 23, 0.88);
  border: 2px solid var(--accent);
  border-radius: 50% 50% 50% 4px;
  transform: rotate(-45deg);
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.5);
}

.marker > span { transform: rotate(45deg); }

/* 位置決めの器（MapLibre に渡す側）。印を重ねる基準にする。 */
.markerpin { position: relative; }

/* 守り人がいるお宝の印（#03）。雫の頭に小さな赤い点を1つ。
   地図は「どこにあるか」を読む場所なので、印は気付ける最小限に留める。
   絶対配置にしてあるのは、雫の大きさを変えると Marker の anchor（bottom）が
   ずれて、ピンの先が指す場所が動くため。 */
.marker__boss {
  position: absolute;
  top: -3px;
  left: 50%;
  transform: translateX(-50%);
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #f85149;
  border: 1.5px solid rgba(13, 17, 23, 0.9);
  pointer-events: none;
}

/* 取り終えたお宝の印は薄く。ピンそのものが薄くなるのに、点だけ赤いままだと浮く。 */
.marker--got + .marker__boss { opacity: 0.4; }

/* 一度見つけたが、周期が来てまた探せるお宝。取れる状態なので薄くはしない。
   「行ったことがある」ことだけ分かればよいので、細い枠で差を付ける。 */
.marker--again { border-style: dashed; }

/* 航路の次の目的地（#06）。脈打たせて、どこへ向かえばよいかを一目で伝える。
   .marker が transform: rotate(-45deg) を持っているので、
   拡大するときも rotate を書き足すこと（上書きすると雫型が崩れる）。 */
.marker--next {
  border-width: 3px;
  animation: marker-next 1.5s ease-in-out infinite;
}

@keyframes marker-next {
  0%, 100% {
    transform: rotate(-45deg) scale(1);
    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.5), 0 0 0 0 rgba(255, 209, 102, 0.55);
  }
  50% {
    transform: rotate(-45deg) scale(1.12);
    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.5), 0 0 0 12px rgba(255, 209, 102, 0);
  }
}

/* 強い明滅が苦手な人には、動かさず枠だけで示す */
@media (prefers-reduced-motion: reduce) {
  .marker--next { animation: none; box-shadow: 0 0 0 4px rgba(255, 209, 102, 0.35); }
}

/* いま取れないお宝。消さずに薄いグレーで残す。
   消してしまうと「そこに何かあった」ことまで分からなくなり、
   地図を見返したときに歩いた履歴として読めない。 */
.marker--got {
  filter: grayscale(1);
  opacity: 0.45;
  border-color: var(--line);
  box-shadow: none;
}

.marker--player {
  border-color: #58a6ff;
  border-radius: 50%;
  transform: none;
  width: 20px;
  height: 20px;
}

/* お宝リスト（地図下部に重ねる横スクロール） */
.treasure-strip {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  gap: 6px;
  padding: 8px 8px calc(var(--safe-bottom) + 8px);
  background: linear-gradient(to top, rgba(13, 17, 23, 0.92), rgba(13, 17, 23, 0));
  pointer-events: auto;

  /* MapLibre のコントロール（帰属表示など）は z-index: 2 を持ち、
     しかも地図コンテナが重ね合わせ文脈を作らないので、この帯より前に出てくる。
     タップを奪われないよう明示的に上へ置く。 */
  z-index: 3;
}

/* ---- 読み物のトースト（航路に誓う／航路を経て／お宝のヒント） ---- */

/* 航路を選んだとき（誓う／経て）、刻み終えた瞬間（経て）、お宝カードを押して
   狙いを定めたとき（ヒント）に流す。
   画面のまんなかに置くのは、上（航路プルダウン・ショップ・所持ポイント）と
   下（お宝カードの帯）が埋まっているため。
   ペインの中ではなく .play 直下に置く。刻み終える瞬間は AR を見ていることが
   多く、地図のペインに入れると AR だけの表示では見えない。
   重ね順はメニュー（30）の下、開発用パネル（20）の上。 */
.toast {
  position: absolute;
  left: 16px;
  right: 16px;
  top: 50%;
  z-index: 25;
  display: grid;
  gap: 8px;
  justify-items: center;
  padding: 16px 18px;
  border-radius: 14px;
  background: rgba(9, 13, 18, 0.88);
  border: 1px solid rgba(255, 209, 102, 0.35);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);

  /* 触れば消せる。地図の操作を奪うのは、出ている数秒のあいだだけ。 */
  pointer-events: auto;
  cursor: pointer;

  /* 出るときも消えるときも、少し浮き上がりながら。
     時間は play コントローラの TOAST_FADE_MS と合わせること。 */
  opacity: 0;
  transform: translateY(-44%);
  transition: opacity 400ms ease, transform 400ms ease;
}

.toast[data-shown="true"] { opacity: 1; transform: translateY(-50%); }

/* 何の文かで枠の色を変える。同じ形のものが3つの意味で出るので、
   色が違えばどれか一目で分かる。
   金＝これから行く（誓い） / 緑＝歩き終えた（結び） / 青＝手がかり（ヒント）。
   青は管理画面の「よく触る項目」と同じ色で、案内の色として揃えてある。 */
.toast[data-kind="epilogue"] { border-color: rgba(63, 185, 80, 0.45); }
.toast[data-kind="epilogue"] .toast__head { color: var(--ok); }

.toast[data-kind="hint"] { border-color: rgba(121, 192, 255, 0.45); }
.toast[data-kind="hint"] .toast__head { color: #79c0ff; }

.toast__head {
  display: flex;
  align-items: baseline;
  gap: 6px;
  color: var(--accent);
  font-size: 12px;
  font-weight: 700;
}

.toast__icon { font-size: 18px; }

/* 改行はそのまま活かす（管理画面で書いたとおりに読ませる）。
   長い誓いでも地図からはみ出さないよう、高さで止めて中で送る。 */
.toast__text {
  margin: 0;
  font-size: 13px;
  line-height: 2;
  text-align: center;
  white-space: pre-line;
  color: #e9e3d6;
  max-height: 34vh;
  overflow-y: auto;
}

/* 横スクロールにはしない。画面外に隠れると、そのお宝の存在に気付けないため。
   出す枚数のほうを画面幅に合わせて絞り（play コントローラ）、
   残ったカードで幅を等分する。 */
.treasure-card {
  /* button の既定の見た目を落とす（中身のレイアウトは自前で組む） */
  appearance: none;
  font: inherit;
  color: var(--text);
  text-align: left;

  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: 12px;
  background: rgba(22, 27, 34, 0.94);
  border: 1px solid var(--line);
  cursor: pointer;
  /* 「また探せる」の印を中に重ねるため */
  position: relative;
}

/* キーボード操作のときだけ枠を出す。タップでは出さない。 */
.treasure-card:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* 航路の次の目的地。カードの側でも分かるようにする。
   地図を見ていないときは、こちらが唯一の手がかりになる。 */
.treasure-card[data-next="true"] {
  border-color: var(--accent);
}

.treasure-card[data-next="true"] .treasure-card__name::before {
  content: "次 ";
  color: var(--accent);
  font-weight: 700;
}

.treasure-card[data-in-range="true"] {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent) inset;
}

.treasure-card:active { transform: translateY(1px); }

/* 追跡中のお宝。AR にはこれだけが出るので、どれを選んでいるかは
   一目で分かる必要がある。 */
.treasure-card[data-focused="true"] {
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: none;
}

.treasure-card[data-focused="true"] .treasure-card__name,
.treasure-card[data-focused="true"] .treasure-card__dist {
  color: var(--accent-ink);
}

.treasure-card__icon { font-size: 20px; flex: 0 0 auto; }

/* アイコンの右上に重ねる小さな印（守り人がいる / また探せる）。
   カードの幅を食わせたくないので、流れから外して浮かせてある。 */
.treasure-card__marks {
  position: absolute;
  top: 2px;
  left: 14px;
  display: flex;
  align-items: center;
  gap: 1px;
  pointer-events: none;
}

/* 守り人がいるお宝（#03）。姿だけを小さく出す。強さや数は出さない
   （カードは「どこへ向かうか」を読む場所なので、印は気付ける大きさで足りる）。 */
.treasure-card__boss { font-size: 10px; line-height: 1; }

/* 一度見つけたお宝の印。周期が来て、また探せるようになっている。 */
.treasure-card__again { font-size: 9px; line-height: 1; }

.treasure-card:not([data-again="true"]) .treasure-card__again { display: none; }

.treasure-card__body { display: grid; gap: 2px; min-width: 0; }

/* 幅を分け合うので、長い名前は畳む */
.treasure-card__name {
  font-size: 11px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.treasure-card__dist {
  font-size: 13px;
  font-weight: 700;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

/* 全部見つけたとき、またはお宝が未登録のときの案内。
   カードは order で並べ替えるので、案内は必ず後ろに来るようにしておく。 */
/* カードが1枚も出せないときの案内。
   ここは「お宝が並ぶはずの場所が空になった」瞬間で、遊ぶ側からは行き止まりに
   見える。薄い字で1行置くだけでは、何が起きたのかも次に何をすればよいのかも
   読み取れない。カード1枚ぶんの箱にして、はっきり出す。 */
.stripnote {
  order: 99;
  display: grid;
  gap: 6px;
  justify-items: center;
  margin: 0;
  padding: 12px;
  text-align: center;
  flex: 1 1 auto;
  /* flex の子は既定で min-width: auto。長い文がそのまま幅になると帯を押し広げる。 */
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--bg-elevated);
}

.stripnote__title { font-size: 13px; font-weight: 700; color: var(--text); }
.stripnote__hint { font-size: 11px; line-height: 1.6; color: var(--text-dim); }

/* 次の一手をその場で押せるようにする。プルダウンは地図の右上にあり、
   帯を見ている目からは離れている。 */
.stripnote__action {
  appearance: none;
  font: inherit;
  font-size: 11px;
  padding: 6px 12px;
  border-radius: 999px;
  border: 1px solid var(--accent);
  background: rgba(255, 209, 102, 0.14);
  color: var(--accent);
  cursor: pointer;
}

.stripnote__action:active { transform: translateY(1px); }

/* ===================== 見つけたときの確認 ===================== */

.getresult {
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--bg-elevated);
  color: var(--text);
  padding: 16px;
  max-width: min(360px, 88vw);
  text-align: center;
}

.getresult::backdrop { background: rgba(0, 0, 0, 0.75); }

.getresult__title {
  font-size: 13px;
  font-weight: 700;
  color: var(--accent);
  margin-bottom: 10px;
}

.getresult__photo {
  display: block;
  width: 100%;
  /* 写真の形は撮る側で上限まで抑えてある（ar の PHOTO_MAX_ASPECT）。
     ここの上限は、撮り方を変えたときにモーダルが画面からはみ出すのを防ぐ
     最後の砦。contain にしてあるので、はみ出しても切れずに縮むだけ。 */
  height: auto;
  max-height: 52svh;
  object-fit: contain;
  border-radius: 12px;
  margin-bottom: 12px;
  background: #000;
}

.getresult__head {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-bottom: 6px;
}

.getresult__icon { font-size: 24px; }
.getresult__name { font-size: 15px; font-weight: 700; }

.getresult__meta {
  font-size: 11px;
  line-height: 1.7;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.getresult__actions {
  display: flex;
  gap: 8px;
  margin-top: 16px;
}

/* 「いただき！」を押しやすいほうに置く。並びは 出直す → いただき。 */
.getresult__actions .btn { flex: 1 1 0; }

/* ===================== 右上のフロートメニュー ===================== */

/* AR の HUD(2)・起動画面(3)・エラー(4)・開発パネル(20) より手前に出す。
   プレイ画面のどのレイアウト状態（AR / 半々 / MAP）でも同じ位置に留まるよう、
   ペインの中ではなく .play 直下に置いてある。 */
/* プレイ画面は 100svh に収まるので absolute でよいが、他の画面はスクロールする。
   一緒に流れていくと戻れなくなるので、そちらでは画面に貼り付ける。 */
.admin .menu,
.collection .menu,
.diag .menu {
  position: fixed;
}

/* 見出しがメニューの下へ潜らないように、その分だけ空ける */
.admin__bar,
.collection__bar,
.diag > h1 {
  padding-right: 48px;
}

.menu {
  position: absolute;
  top: calc(var(--safe-top) + 8px);
  right: 8px;
  z-index: 30;
  display: grid;
  justify-items: end;
  gap: 6px;
}

.menu__button {
  appearance: none;
  width: var(--menu-size);
  height: var(--menu-size);
  display: grid;
  place-content: center;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: rgba(13, 17, 23, 0.72);
  backdrop-filter: blur(4px);
  cursor: pointer;
}

.menu__button[aria-expanded="true"] { border-color: var(--accent); }
.menu__button:active { transform: translateY(1px); }

/* ハンバーガーの3本線。中央の1本を実体、上下を疑似要素で作る。 */
.menu__bars,
.menu__bars::before,
.menu__bars::after {
  display: block;
  width: 16px;
  height: 2px;
  border-radius: 2px;
  background: var(--text);
}

.menu__bars { position: relative; }

.menu__bars::before,
.menu__bars::after {
  content: "";
  position: absolute;
  left: 0;
}

.menu__bars::before { top: -5px; }
.menu__bars::after  { top: 5px; }

.menu__panel {
  display: grid;
  gap: 2px;
  min-width: 172px;
  padding: 6px;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: rgba(22, 27, 34, 0.96);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(6px);
}

.menu__item {
  display: block;
  padding: 10px;
  border-radius: 8px;
  font-size: 12px;
  color: var(--text);
  text-decoration: none;
}

.menu__item[aria-pressed="false"] { color: var(--text-dim); }
.menu__item:disabled { color: var(--text-dim); opacity: 0.5; cursor: default; }
.menu__item:hover { background: rgba(255, 255, 255, 0.07); }
.menu__item:active { background: rgba(255, 255, 255, 0.14); }

/* ===================== 開発用: 位置偽装パネル ===================== */

.devtools {
  position: absolute;
  inset: auto 8px 8px auto;
  z-index: 20;
  display: grid;
  gap: 6px;
  justify-items: end;
}

/* スマホでは出さない。
   このパネルの主役は位置偽装で、それは「歩かずに試す」ためのもの。
   実機では歩けば済むので、狭い画面でお宝カードに重なる邪魔なだけの存在になる。
   hover と pointer の両方を見て、マウスの付いたタッチPCを巻き込まないようにする。 */
@media (hover: none) and (pointer: coarse) {
  .devtools { display: none; }
}

.devtools__panel {
  background: rgba(13, 17, 23, 0.94);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 10px;
  display: grid;
  gap: 8px;
  font-size: 11px;
  min-width: 210px;
}

.devtools__panel[hidden] { display: none !important; }

.devtools__row { display: grid; gap: 3px; }
.devtools__row label { color: var(--text-dim); font-size: 10px; }
.devtools__row input[type="range"] { width: 100%; }

.devtools__hint { color: var(--text-dim); line-height: 1.5; }

/* ===================== 診断ページ ===================== */

.diag {
  max-width: 720px;
  margin: 0 auto;
  padding: calc(var(--safe-top) + 16px) 14px calc(var(--safe-bottom) + 40px);
}

.diag h1 { font-size: 17px; margin: 0 0 4px; }
.diag__lead { font-size: 12px; color: var(--text-dim); line-height: 1.7; margin: 0 0 18px; }

.diag section {
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 12px 14px;
  margin-bottom: 12px;
  background: var(--bg-elevated);
}

.diag h2 {
  font-size: 13px;
  margin: 0 0 10px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.badge {
  font-size: 10px;
  padding: 3px 7px;
  border-radius: 999px;
  border: 1px solid var(--line);
  color: var(--text-dim);
  font-weight: 500;
}

.badge--ok   { color: var(--ok);   border-color: rgba(63, 185, 80, 0.5); }
.badge--warn { color: var(--warn); border-color: rgba(210, 153, 34, 0.5); }
.badge--err  { color: var(--err);  border-color: rgba(248, 81, 73, 0.5); }

.kv {
  display: grid;
  grid-template-columns: minmax(9em, auto) 1fr;
  gap: 4px 12px;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.kv dt { color: var(--text-dim); }
.kv dd { margin: 0; word-break: break-all; }

.diag video {
  width: 100%;
  max-height: 45vh;
  background: #000;
  border-radius: 10px;
  display: block;
  object-fit: cover;
}

.diag__actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }

.diag__log {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10.5px;
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 200px;
  overflow-y: auto;
  background: #000;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 8px;
  color: var(--text-dim);
  margin: 10px 0 0;
}

.nav-link {
  display: inline-block;
  font-size: 11px;
  color: var(--text-dim);
  margin-top: 6px;
}

/* ===================== 管理画面（フェーズ3） ===================== */

/* プレイ画面と違い、こちらは普通に縦スクロールする画面。
   PCでもスマホでも「地図で位置を決めて、下のフォームで詰める」流れになるよう
   1カラムで積んである。 */
.admin {
  max-width: 900px;
  margin: 0 auto;
  padding: calc(var(--safe-top) + 12px) 12px calc(var(--safe-bottom) + 32px);
  display: grid;
  gap: 14px;
}

.admin__bar {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.admin__title { font-size: 15px; font-weight: 700; }
.admin__count { font-size: 12px; color: var(--text-dim); }
.admin__bar .nav-link { margin-left: auto; margin-top: 0; }

.admin__notice {
  padding: 10px 12px;
  border-radius: 10px;
  font-size: 12px;
  background: rgba(63, 185, 80, 0.12);
  border: 1px solid rgba(63, 185, 80, 0.5);
  color: #7ee787;
}

.admin__map {
  position: relative;
  height: 46vh;
  min-height: 260px;
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
  background: #1b2430;
}

.admin__maphint {
  position: absolute;
  inset: auto 8px 8px 8px;
  padding: 7px 10px;
  border-radius: 8px;
  font-size: 11px;
  line-height: 1.6;
  color: var(--text-dim);
  background: rgba(13, 17, 23, 0.82);
  border: 1px solid var(--line);
  backdrop-filter: blur(4px);
  pointer-events: none;
}

/* 登録しようとしている座標を示す下書きピン。
   .marker は -45度回して雫型にしているので、こちらは打ち消して丸にする。 */
.marker--draft {
  transform: none;
  border-radius: 50%;
  border-style: dashed;
  border-color: #58a6ff;
  animation: draft-pulse 1.4s ease-in-out infinite;
}

@keyframes draft-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(88, 166, 255, 0.5); }
  50%      { box-shadow: 0 0 0 8px rgba(88, 166, 255, 0); }
}

/* 道順に入れられないお宝（踏破で出る隠しお宝）。消さずに薄くする。
   消すと、そこに何かあること自体が分からなくなる。 */
.marker--locked { opacity: 0.35; filter: grayscale(1); }

.marker--clickable { cursor: pointer; }

/* --- フォーム --- */

/* 登録・編集の箱。<details> で畳む。
   **新規は閉じた状態から始める。** 開いていると、いま出ている入力欄が
   新規なのか編集なのか分からず、直したつもりで別のものを作ってしまう。
   編集は一覧の鉛筆を押して入る＝そこにワンクッションがあるので、開いて始める。 */
.admin__formbox {
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--bg-elevated);
}

/* 何をしているのかを色で示す。**緑＝新しく作る / 青＝直す。**
   開いているかどうかだけだと、一覧を挟んで戻ってきたときに迷う。
   青は一覧の鉛筆・よく触る項目と同じ色で、「直すもの」の色として揃えてある。 */
.admin__formbox[data-mode="new"]  { border-color: rgba(63, 185, 80, 0.45); }
.admin__formbox[data-mode="edit"] { border-color: rgba(121, 192, 255, 0.45); }

/* 開いているときは、はっきり出す */
.admin__formbox[data-mode="new"][open]  { border-color: var(--ok); }
.admin__formbox[data-mode="edit"][open] { border-color: #79c0ff; }

.admin__formbox[data-mode="new"] .admin__formhead  { color: var(--ok); }
.admin__formbox[data-mode="edit"] .admin__formhead { color: #79c0ff; }

.admin__form {
  display: grid;
  gap: 12px;
  margin-top: 12px;
}

.admin__formhead {
  cursor: pointer;
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: 13px;
  font-weight: 700;
}

.admin__formhead::marker { color: var(--text-dim); font-size: 11px; }

/* 「新たに〜へ戻る」。見出しは summary に出したので、こちらはフォームの中。
   grid の子なので、右端へ寄せるのは justify-self で行う。 */
/* 「新たに〜へ戻る」は見出しの右端。よく使うので、目立つところに置く。
   summary の中に入れても <details> は開閉しない（活性化のふるまいは、
   まず押した要素のものが選ばれるので、リンク側が勝つ）。 */
.admin__formhead { flex-wrap: wrap; }

/* リンクの字だと、真下の「消す」と見分けが付かず押し間違える。
   ボタンの形にして、押す場所そのものを離す。
   色は新規の枠と同じ緑。押した先が「新たに作る」なので、行き先の色を着せる
   （箱の枠が緑＝新しく作る／青＝直す、と同じ読み方になる）。 */
.admin__formback {
  margin-left: auto;
  font-weight: 400;
  text-decoration: none;
  color: var(--ok);
  border-color: rgba(63, 185, 80, 0.55);
  background: rgba(63, 185, 80, 0.10);
}

.admin__errors {
  padding: 10px 12px;
  border-radius: 10px;
  font-size: 12px;
  line-height: 1.8;
  background: rgba(248, 81, 73, 0.12);
  border: 1px solid rgba(248, 81, 73, 0.5);
  color: #ffb3ae;
}

/* 8等分の升目に載せる。欄はその何個分かで幅を決める（s/m/l）。 */
.admin__grid {
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  gap: 10px;
  align-items: start;
}

/* 幅は end 側の span で指定する。`grid-column: span 2` と書くと span が
   start 側に入り、field--break が start を上書きしたときに幅が消える
   （end が auto ＝ 1列になり、s と同じ大きさになってしまう）。 */
.field--s { grid-column-end: span 1; }
.field--m { grid-column-end: span 2; }
.field--l { grid-column-end: span 4; }
.field--wide { grid-column: 1 / -1; }

/* この欄から新しい行にする。前の行に隙間が余っていても、そこへ詰めない。
   欄のまとまり（どこまでが一組か）を目で分けたいときに使う。
   start だけを決めるので、上の s/m/l と組み合わせられる。 */
.field--break { grid-column-start: 1; }

/* スマホは4等分。s は4個、m は2個、l は1個ずつ並ぶ。 */
@media (max-width: 720px) {
  .admin__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.field { display: grid; gap: 4px; align-content: start; }
.field--wide { grid-column: 1 / -1; }
/* 行の高さを決め打ちにする。決めないと「ID」のような欧文だけの見出しと
   「名前」のような和文の見出しで行の高さが変わり、下の入力欄がずれる。 */
.field__label { font-size: 11px; line-height: 1.5; color: var(--text-dim); }
.field__note  { font-size: 10px; line-height: 1.5; color: var(--text-dim); }

/* 入力欄・選択欄は同じ見た目・同じ高さ。select を素のままにすると、
   隣の入力欄と高さも角も揃わない。
   チェックボックスは除く。appearance: none を当てると、ネイティブの
   チェックマークごと消えて「押しても見た目が変わらない」ことになる。

   除外は :where() で包む。素の :not() は中身のぶん特異度が上がり、
   個別の指定（アイコンの幅など）に勝ってしまう。:where() は 0 のまま。 */
.field input:where(:not([type="checkbox"]):not([type="radio"])),
.field select,
.filter input,
.filter select {
  appearance: none;
  width: 100%;
  height: var(--control-h);
  font: inherit;
  font-size: 14px;
  padding: 0 10px;
  border-radius: var(--control-radius);
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--text);
}

.field input:where(:not([type="checkbox"]):not([type="radio"])):focus,
.field select:focus,
.filter input:focus,
.filter select:focus {
  outline: none;
  border-color: var(--accent);
}

.field input:where(:not([type="checkbox"])):disabled,
.field select:disabled { color: var(--text-dim); background: #10151c; }

/* 複数行の欄（航路に誓う）。入力欄と同じ見た目にするが、高さは rows なりで、
   --control-h には揃えない。横は伸ばさない（升目の幅を勝手に超えるため）。 */
.field textarea {
  width: 100%;
  font: inherit;
  font-size: 14px;
  line-height: 1.7;
  padding: 8px 10px;
  border-radius: var(--control-radius);
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--text);
  resize: vertical;
}

.field textarea:focus { outline: none; border-color: var(--accent); }
.field--key textarea { border-color: rgba(88, 166, 255, 0.4); }

/* よく触る項目。設定するときにまず決めるところなので、一目で見つかるようにする。
   太字にすると文字の幅が変わって升目からはみ出すので、色だけで示す。

   青にしてあるのは、金色（--accent）が focus の色だから。同じ色にすると
   「いま触っている欄」と見分けがつかなくなる。
   枠は薄く敷くだけ。濃くすると入力欄そのものより枠が目立つ。 */
.field--key .field__label { color: #79c0ff; }

.field--key input:where(:not([type="checkbox"]):not([type="radio"])),
.field--key select { border-color: rgba(88, 166, 255, 0.4); }

/* アイコンの入力欄。入力欄と「選ぶ」を横に並べる。 */
.iconfield { display: flex; gap: 6px; }

/* 絵文字が主役なので、この欄だけ字を大きくする */
.iconfield__input { flex: 1; min-width: 0; font-size: 18px; }

.iconfield__open {
  flex: none;
  appearance: none;
  /* 入力欄と同じ高さにする。指定しないと中身なりの高さになり、
     隣の欄と下端がずれて、説明文の行も食い違う。 */
  height: var(--control-h);
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--bg-elevated);
  color: var(--text);
  font-size: 12px;
  cursor: pointer;
}

.iconfield__open:hover { border-color: var(--accent); }
.iconfield__open:active { transform: translateY(1px); }

/* アイコンの一覧。アイコンを持つ欄は3つあるので、埋め込まずに1つを浮かせる。 */
.iconpicker {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}

.iconpicker__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
}

.iconpicker__panel {
  position: relative;
  display: flex;
  flex-direction: column;
  width: min(560px, 100%);
  max-height: min(70vh, 520px);
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--bg-elevated);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5);
  overflow: hidden;
}

.iconpicker__head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--line);
}

.iconpicker__title { font-size: 13px; }

/* 今その欄に入っているアイコン。選び直すときの手がかり。 */
.iconpicker__current { flex: 1; font-size: 20px; line-height: 1; }

.iconpicker__close {
  appearance: none;
  border: none;
  background: transparent;
  color: var(--text-dim);
  font-size: 15px;
  cursor: pointer;
  padding: 4px 6px;
}

.iconpicker__close:hover { color: var(--text); }

/* 分類ごとに並べてスクロールさせる。一列に並べると横に伸びるだけで探せない。 */
.iconpicker__list {
  flex: 1;
  overflow-y: auto;
  padding: 4px 12px 12px;
}

/* 分類の見出し。スクロールしても「今どの分類を見ているか」が分かるよう貼り付ける。 */
.iconpicker__category {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 8px 0 4px;
  font-size: 10px;
  color: var(--text-dim);
  background: var(--bg-elevated);
}

.iconpicker__row { display: flex; flex-wrap: wrap; gap: 3px; }

.icon-choice {
  appearance: none;
  border: 1px solid transparent;
  background: transparent;
  border-radius: 8px;
  font-size: 21px;
  line-height: 1;
  padding: 5px 6px;
  cursor: pointer;
}

.icon-choice:hover { border-color: var(--line); }
.icon-choice:active { transform: translateY(1px); }

/* いま入っているもの。一覧が長いので、色だけでなく枠でも分かるようにする。 */
.icon-choice[data-selected="true"] {
  border-color: var(--accent);
  background: rgba(255, 209, 102, 0.16);
}

.admin__actions { display: flex; justify-content: flex-end; }

/* 直している箱から、そのまま消せるようにする行。「戻る」の真下、右端に小さく。
   ボタンの形にすると、隣の「戻る」より目立って押しやすくなってしまう
   （よく使うのは戻るほうで、消すのは取り返しがつかない）。
   フォームの外に出ているのは、button_to が <form> を作るため（入れ子にできない）。 */
.admin__delete {
  display: flex;
  justify-content: flex-end;
  margin-top: 6px;
}

/* 押せるが、見た目はリンク。button_to が作る <button> に着せる。 */
.linklike {
  appearance: none;
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  font-size: 11px;
  line-height: 1.6;
  color: var(--text-dim);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.linklike--danger { color: #ffb3ae; }
.linklike:active { transform: translateY(1px); }

/* --- ショップ（#04） --- */

/* 地図の左上、所持ポイントの左隣。歩きながらでも押せる大きさにする。 */
/* 地図に重ねる部品は、右上の航路プルダウンと同じ寸法に揃える
   （高さ26px・文字12px）。上端で横一列に並ぶので、揃っていないと目立つ。 */
.shopbutton {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 3;
  appearance: none;
  width: 26px;
  height: 26px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(13, 17, 23, 0.82);
  color: var(--text);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}

.shopbutton:active { transform: translateY(1px); }

.shop {
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--bg-elevated);
  color: var(--text);
  padding: 0;
  width: min(420px, 90vw);
  max-height: 80svh;
  overflow: hidden;
}

/* 中身の組み方は開いているときだけに書く。.shop 側に display を書くと、
   閉じた <dialog> がレイアウトに居座る（冒頭の全体ルールで塞いではいる）。 */
.shop[open] {
  display: flex;
  flex-direction: column;
}

.shop__head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--line);
}

.shop__title { font-size: 14px; font-weight: 700; }

/* 残高。買えるかどうかの判断に要るので、閉じるボタンより先に置く。 */
.shop__balance {
  flex: 1;
  text-align: right;
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
}

.shop__close {
  appearance: none;
  border: none;
  background: transparent;
  color: var(--text-dim);
  font-size: 15px;
  padding: 4px 6px;
  cursor: pointer;
}

.shop__list {
  flex: 1;
  overflow-y: auto;
  padding: 10px 14px 14px;
  display: grid;
  gap: 8px;
}

.shop__empty { padding: 20px 14px; font-size: 12px; color: var(--text-dim); text-align: center; }

/* 持ち物の効果ごとの見出し。名前と効き目だけでは、どれが回復でどれがボムなのか
   読み取るのに手間が要る。区切りとして薄く置く（主役は探具そのもの）。
   最初の見出しだけは、上の線と余白を落とす（枠のすぐ下に線が来る）。 */
.baggroup {
  font-size: 10px;
  font-weight: 700;
  color: var(--text-dim);
  letter-spacing: 0.08em;
  padding-top: 8px;
  margin-top: 2px;
  border-top: 1px solid var(--line);
}

.baggroup:first-child { padding-top: 0; margin-top: 0; border-top: 0; }

.shopitem {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 12px;
}

.shopitem__icon { font-size: 24px; flex: none; }
.shopitem__body { display: grid; gap: 2px; min-width: 0; flex: 1; }
.shopitem__name { font-size: 13px; font-weight: 600; }
.shopitem__effect { font-size: 11px; color: var(--text-dim); }
.shopitem__owned { font-size: 10px; color: var(--accent); }

/* 買えないものは押せなくする。押してから「足りません」と言われるより、
   見た時点で分かるほうがよい。 */
.shopitem .btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* --- 所持ポイント --- */

/* 地図の左上に重ねる。MapLibre のコントロールが z-index: 2 を持つので、
   前に出すには明示が要る（地図コンテナは重ね合わせ文脈を作らない）。 */
.wallet {
  position: absolute;
  top: 8px;
  left: 40px; /* ショップボタン（8 + 26）の右隣 */
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 5px 9px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(13, 17, 23, 0.82);
  font-size: 12px;
  font-weight: 700;

  /* 押すと持ち物が開く。button になったので、既定の見た目を落とす。 */
  appearance: none;
  color: var(--text);
  font-family: inherit;
  cursor: pointer;
}

.wallet:active { transform: translateY(1px); }

.wallet__coin { font-size: 12px; line-height: 1; }

/* 桁が増えても幅が飛び跳ねないよう、数字の幅を揃える */
.wallet__value { font-variant-numeric: tabular-nums; }

/* 増えた分。一瞬だけ出して消す。 */
.wallet__gain {
  color: var(--accent);
  font-size: 11px;
}

.wallet__gain.is-on { animation: wallet-gain 2s ease-out; }

@keyframes wallet-gain {
  0%   { opacity: 0; transform: translateY(6px); }
  12%  { opacity: 1; transform: translateY(0); }
  75%  { opacity: 1; }
  100% { opacity: 0; }
}

/* GET の結果に出すもらえるポイント。ここだけ色を起こす。 */
.getresult__reward {
  margin: 2px 0 6px;
  font-size: 14px;
  font-weight: 700;
  color: var(--accent);
}

/* --- コレクションの航路（#06） --- */

/* 左右の余白は .collection が持っている。ここで足すと航路だけ内側に寄る。 */
.collection__courselist { display: grid; gap: 8px; }

.coursecard {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 12px;
}

/* 踏破した航路は色を起こす。一覧の中で見つけやすくする。 */
.coursecard[data-finished="true"] { border-color: var(--accent); }

.coursecard__icon { font-size: 22px; flex: none; }
.coursecard__body { display: grid; gap: 3px; min-width: 0; flex: 1; }
.coursecard__name { font-size: 13px; font-weight: 600; }

/* 道順。お宝が多い航路でも横に伸びないよう折り返す。 */
.coursecard__steps { display: flex; flex-wrap: wrap; gap: 3px; }

.coursecard__step { font-size: 14px; opacity: 0.35; filter: grayscale(1); }
.coursecard__step[data-got="true"] { opacity: 1; filter: none; }

/* 航路に誓う文（#06）。読み物なので、道順より少し落として置く。
   改行はそのまま活かす（プロローグと同じ見え方にする）。 */
.coursecard__oath {
  font-size: 11px;
  line-height: 1.8;
  color: var(--text-dim);
  white-space: pre-line;
  border-left: 2px solid var(--line);
  padding-left: 8px;
  margin-top: 2px;
}

/* 航路を経て。誓いと同じ形にして、線の色だけ変える（プロローグと同じ読み分け）。 */
.coursecard__oath--after { border-left-color: rgba(63, 185, 80, 0.5); }

/* 刻み終えた記録。進み具合より控えめに置く。 */
.coursecard__record { font-size: 10px; color: var(--accent); opacity: 0.85; }

.coursecard__count {
  flex: none;
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}

.coursecard[data-finished="true"] .coursecard__count { color: var(--accent); }

/* --- 刻む航路を選ぶ（#06） --- */

/* 地図の右上。所持ポイントとショップは左上なので、ぶつからない。
   MapLibre のコントロールが z-index: 2 を持つので、明示して前に出す。 */
.coursepick {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 6px;
  /* 左にはショップ（8+26）と所持ポイント（40から約62px）が並ぶ。
     そこへ食い込まない上限。幅360pxの端末でも、プルダウンと切り替えボタンの
     214px が収まる。もっと狭い端末では、足りない分だけプルダウンが縮む。 */
  max-width: calc(100% - 118px);
}

/* 持ち物（#04）。中身の並べ方はショップと同じなので、.shop__* を使い回す。
   別の見た目にすると、同じ「探具の一覧」なのに2通りの読み方を覚えることになる。
   箱そのものの寸法だけ、ここで .shop と同じものを与える。 */
.bag {
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--bg-elevated);
  color: var(--text);
  padding: 0;
  width: min(420px, 90vw);
  max-height: 80svh;
  overflow: hidden;
}

/* 閉じた <dialog> がレイアウトに居座らないよう、display は [open] 側に書く。 */
.bag[open] {
  display: flex;
  flex-direction: column;
}

/* 持ち物の行。どこを押してもチェックが変わるよう、行ごと <label> にしてある
   （戦いの前に急いで触るので、小さな四角を狙わせない）。 */
.bagitem { cursor: pointer; }

/* 置いていく探具は薄く。持っていくものだけが目に入る状態にする。 */
.bagitem:has(.bagitem__bring:not(:checked)) { opacity: 0.45; }

.bagitem__bring {
  flex: none;
  width: 18px;
  height: 18px;
  accent-color: var(--accent);
  cursor: pointer;
}

/* 持っていくものを選べる、と先に伝える。読まないと、外したことに気付かないまま
   戦いに出て「探具が無い」になる。 */
.bag__note {
  margin: 0;
  padding: 0 14px 4px;
  font-size: 11px;
  line-height: 1.7;
  color: var(--text-dim);
}

.bag__note strong { color: var(--text); }

/* 残り回数。買うボタンの位置に置くので、幅の主張だけ揃えておく。 */
.bagitem__left {
  flex: none;
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
}

/* 現在地に戻る。地図の右下、お宝カードのすぐ上。
   地図を触ったあとに自分の位置を見失うので、いつでも戻れるようにしておく。

   お宝カードの帯の子にして、帯の上端（bottom: 100%）を基準に浮かせる。
   カードの高さは中身で変わるので、地図側から px で持ち上げると必ずいつか重なる。
   帯より前に出す（z-index）のは、帯が後ろのカードを覆う grad を持つため。 */
.recenter {
  position: absolute;
  right: 8px;
  bottom: calc(100% + 4px);
  z-index: 1;
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(13, 17, 23, 0.82);
  color: var(--text);
  cursor: pointer;
}

.recenter:active { transform: translateY(1px); }
.recenter .icon { display: block; }

/* 航路をどこまで出しているか。プルダウンの右に丸く置く。
   出しているのは「切り替え先」ではなく「いまの基準」。
   雫＝近くだけ、地球＝ぜんぶ。 */
.coursepick__scope {
  appearance: none;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(13, 17, 23, 0.82);
  color: var(--text-dim);
  cursor: pointer;
}

/* ぜんぶ出しているときは目立たせる。普段の状態と見分けるため。 */
.coursepick__scope[data-scope="all"] { color: var(--accent); border-color: var(--accent); }

.coursepick__scope:active { transform: translateY(1px); }
.coursepick__scope .icon { display: block; }

.coursepick__select {
  appearance: none;
  /* 全角13.5文字ぶんで固定する。素の select は「一番長い選択肢」で幅が決まるので、
     航路を選び直すたびに箱が伸び縮みして落ち着かない。
     文字数で決めたいので em を使う（px だと文字の大きさを変えたときにずれる）。
     border-box なので、余白と枠のぶんを足して「中身が13.5文字」にする。
     溢れた名前は末尾を省略。画面が狭いときだけ縮む。 */
  width: calc(13.5em + 20px);
  max-width: 100%;
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  padding: 5px 9px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(13, 17, 23, 0.82);
  color: var(--text);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  /* 幅を固定したので、短い名前だと左に寄って見える。真ん中に置く。
     （appearance: none なので、右端に矢印のための余白は要らない） */
  text-align: center;
  text-overflow: ellipsis;
}

/* 直したほうがよい状態。消えているわけではないので、赤ではなく黄で示す。 */
.admin__warn { color: var(--warn); }

/* 絵文字1文字。「選ぶ」と合わせて s の1枠に収める。

   入力欄もボタンも縮めるようにしておくこと。どちらかが縮まないと、
   窓が狭いときに合計が枠を超えて、ボタンがはみ出す。 */
.field--icon .iconfield { gap: 4px; min-width: 0; }

/* 入力欄は「選ぶ」を置いた残り全部。固定値にすると、PC で余っていても
   スマホでは足りない、という食い違いが出る。
   下限だけ決めておけば、どの幅でもはみ出さずに一番広く使える。 */
.field--icon .iconfield__input {
  flex: 1 1 var(--w-icon);
  min-width: var(--w-icon);
  width: auto;
  text-align: center;
  padding: 0 2px;
}

/* ボタンは縮めない。ここが縮むと「選ぶ」の字が潰れる。 */
.field--icon .iconfield__open {
  flex: 0 0 auto;
  padding: 0 6px;
  font-size: 11px;
  white-space: nowrap;
}

/* --- 一覧の絞り込みとまとまり --- */

.filter {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.filter select { width: auto; min-width: 10ch; }
.filter input { flex: 1 1 180px; width: auto; }

/* エリアの見出し。一覧の中で段が変わったことが分かればよいので、控えめに。 */
/* 区分けごとのまとまり。既定は閉じておき、見出し（summary）を押すと開く。
   区分けが増えても一覧の全体像が一目で掴める。
   JS を書かずに済むよう <details> を使う。キーボードでも開閉できる。 */
.admin__groupbox { display: block; }

/* 中の行。details を直接 grid にすると、閉じているときの扱いが
   ブラウザによって怪しいので、行はもう一枚内側でまとめる。 */
.admin__grouprows { display: grid; gap: 8px; margin-top: 8px; }

.admin__group {
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  padding: 4px 2px;
  border-bottom: 1px solid var(--line);
  font-size: 11px;
  font-weight: 700;
  color: var(--text-dim);
  letter-spacing: 0.06em;
}

/* 開いている見出しを起こす。既定が閉じなので、開けたものが目に留まるように。 */
.admin__groupbox[open] .admin__group { color: var(--text); }

.admin__group::marker { color: var(--text-dim); font-size: 10px; }

.admin__groupcount { font-weight: 400; font-variant-numeric: tabular-nums; }

/* --- 航路の道順（#06） --- */

.routepick {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--line);
  display: grid;
  gap: 10px;
}

/* 道順は航路の中身そのもの。よく触る項目（.field--key）と同じ色を差す。
   見出しと地図の枠だけ。中身まで色を付けると、地図が読みにくくなる。 */
.route__head { font-size: 12px; font-weight: 700; color: #79c0ff; }

/* 添えの説明まで青くすると、見出しとの主従が消える */
.route__hint { font-weight: 400; font-size: 10px; margin-left: 6px; color: var(--text-dim); }

.routepick .admin__map { border-color: rgba(88, 166, 255, 0.4); }

.routepick__bar { display: flex; align-items: center; gap: 8px; }

.routepick__count {
  flex: 1;
  font-size: 12px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* 選んだ順の一覧。地図だけだと、順番の確認に目を往復させることになる。 */
.routepick__list {
  display: grid;
  gap: 4px;
  max-height: 220px;
  overflow-y: auto;
}

.routepick__row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  border: 1px solid var(--line);
  border-radius: 8px;
}

/* 地図のピンと同じ数字。目を移したときに対応が取れる。 */
.routepick__step {
  flex: none;
  display: grid;
  place-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 11px;
  font-weight: 700;
}

.routepick__name { flex: 1; min-width: 0; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.routepick__remove {
  flex: none;
  appearance: none;
  border: none;
  background: transparent;
  color: var(--text-dim);
  font-size: 13px;
  padding: 2px 6px;
  cursor: pointer;
}

.routepick__remove:hover { color: var(--err); }

.routepick__empty { margin: 0; font-size: 12px; color: var(--text-dim); }

/* 道順に入ったピン。絵文字の代わりに順番の数字を出す。 */
.marker[data-step]:not([data-step=""]) {
  background: var(--accent);
  border-color: var(--accent);
  font-weight: 700;
}

.marker[data-step]:not([data-step=""]) > span { color: var(--accent-ink); }

/* --- 出現期間（#05） --- */

.schedule {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--line);
}

.schedule__head {
  font-size: 12px;
  font-weight: 700;
  color: var(--text-dim);
  margin-bottom: 8px;
}

/* 曜日は横一列。7つ並ぶので、狭い画面では折り返す。 */
.schedule__days { display: flex; flex-wrap: wrap; gap: 4px; }

/* --- 入／切のボタン（曜日・守り人・順路厳守） ---

   ON / OFF は枠の色と文字の色だけで示す。チェックボックスの四角を残すと、
   外枠と二重に見えて落ち着かない。 */
.schedule__day,
.bossform__switch {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--text-dim);
  cursor: pointer;
  user-select: none;
}

.schedule__day { min-width: 34px; padding: 5px 8px; font-size: 12px; }
.bossform__switch { padding: 8px 14px; font-size: 13px; font-weight: 600; }

/* 守り人の上書き欄（#07）。空欄のときに使われる値を薄い字で出している。
   **絵文字は color を無視する**（色付きのグリフなので）。placeholder を
   グレーにしても見た目が変わらず、入力済みと見分けが付かない。
   opacity なら絵文字にも効くので、そちらで薄くする。 */
[data-admin-target~="inherit"]::placeholder {
  opacity: 0.4;
  font-style: italic;
}

/* ファイルを選ぶ欄。
   共通の入力欄の指定（高さ --control-h ・padding 0）がそのまま当たると、
   中のボタンが上に貼り付いて下だけ余る。中身なりの高さにして、
   上下の余白で真ん中に置く。枠のどこを押しても選べるよう cursor も変える。 */
.field input[type="file"] {
  height: auto;
  min-height: var(--control-h);
  /* 6 + 24（ボタン） + 6 = 36px = --control-h に揃う */
  padding: 6px 8px;
  line-height: 24px;
  font-size: 12px;
  color: var(--text-dim);
  cursor: pointer;
}

.field input[type="file"]::file-selector-button {
  appearance: none;
  height: 24px;
  margin-right: 8px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--bg-elevated);
  color: var(--text);
  font: inherit;
  font-size: 11px;
  cursor: pointer;
}

.field input[type="file"]:hover { border-color: var(--line); }
.field input[type="file"]::file-selector-button:hover { border-color: var(--accent); color: var(--accent); }

/* 真の姿の欄（#07）。いま何が付いているかを、見て分かるようにする。
   文字だけだと「上げたつもり」の取り違えが起きる。 */
.artfield {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-top: 6px;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg);
}

/* 市松模様。絵の透過が効いているかどうかが、これで一目で分かる。
   単色の下敷きだと、白い絵と透過の絵が見分けられない。 */
.artfield__thumb {
  flex: none;
  width: 72px;
  height: 72px;
  display: grid;
  place-items: center;
  border-radius: 8px;
  overflow: hidden;
  background-color: #fff;
  background-image:
    linear-gradient(45deg, #c9d1d9 25%, transparent 25%, transparent 75%, #c9d1d9 75%),
    linear-gradient(45deg, #c9d1d9 25%, transparent 25%, transparent 75%, #c9d1d9 75%);
  background-size: 16px 16px;
  background-position: 0 0, 8px 8px;
}

.artfield__thumb img { max-width: 100%; max-height: 100%; display: block; }

.artfield__body { display: grid; gap: 4px; min-width: 0; }
.artfield__name { font-size: 12px; font-weight: 700; overflow-wrap: anywhere; }
.artfield__meta { font-size: 11px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.artfield__remove { justify-self: start; margin-top: 2px; font-size: 11px; padding: 5px 10px; }

/* 上書きの説明。ここから下は「変えたいときだけ入れる欄」だと伝える。 */
.bossform__note {
  margin: 10px 0;
  font-size: 11px;
  line-height: 1.8;
  color: var(--text-dim);
}

.bossform__note strong { color: var(--text); font-weight: 700; }

/* 見た目は消すが、押せる範囲とキーボードのフォーカスは残す。
   display: none にすると Tab で辿れなくなる。 */
.schedule__day input[type="checkbox"],
.bossform__switch input[type="checkbox"] {
  appearance: none;
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.schedule__day:has(input:checked),
.bossform__switch:has(input:checked) {
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 700;
}

/* キーボードで辿っているときだけ枠を出す。タップでは出さない。 */
.schedule__day:has(input:focus-visible),
.bossform__switch:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* --- ボス戦の設定 --- */

/* 名前は bossform。AR の戦闘 HUD が .battle を使っているので、そちらとは分ける。 */
.bossform {
  display: grid;
  gap: 10px;
  padding: 12px;
  border: 1px dashed var(--line);
  border-radius: 10px;
  background: var(--bg);
}

/* --- 一覧 --- */

.admin__list { display: grid; gap: 8px; }

.admin__row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-elevated);
}

.admin__row.is-editing { border-color: var(--accent); }

/* 下に一段ぶら下げるカード（守人研究の「使っているお宝」）。
   縦に積む。折り返し（flex-wrap）で下段を作ると、説明が長いカードでは
   操作ボタンまで一緒に押し出されて、鉛筆とゴミ箱が下の段へ落ちる。
   「カード情報｜操作ボタン」の1行は、中身の長さによらず崩さない。 */
.admin__row--stack {
  display: grid;
  gap: 6px;
  align-items: stretch;
}

.admin__rowmain { display: flex; align-items: center; gap: 10px; min-width: 0; }

/* 押すと下にお宝の一覧が開く。別の画面へ飛ばさないのは、守り人を見比べて
   いる最中に読みたいものだから。飛ぶと、どれを見ていたか見失う。 */
.usedbox__head {
  cursor: pointer;
  font-size: 11px;
  color: var(--text-dim);
  width: fit-content;
}

.usedbox[open] .usedbox__head { color: var(--accent); }
.usedbox__head::marker { font-size: 10px; }

.usedbox__list {
  display: grid;
  gap: 4px;
  margin-top: 6px;
  padding: 8px 10px;
  border-radius: 8px;
  background: var(--bg);
}

.usedbox__row { display: flex; align-items: baseline; gap: 6px; min-width: 0; }
.usedbox__icon { flex: none; font-size: 14px; }

.usedbox__name {
  font-size: 12px;
  flex: none;
  max-width: 45%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 上書きしている欄の並び。長くなるので、ここだけは折り返してよい。 */
.usedbox__meta { font-size: 10px; color: var(--text-dim); min-width: 0; }

.usedbox__none { font-size: 11px; }

.admin__rowicon { font-size: 20px; }
.admin__rowbody { display: grid; gap: 3px; min-width: 0; }

/* カードの本体は「地図をここへ」のボタン。見た目は素のカードのまま。
   タップ対象は <button> にする（<div> はフォーカスを受け取れない）。 */
.admin__rowjump {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.admin__rowjump:active { opacity: 0.6; }

/* 飛ぶ先が無いカード（お宝の入っていない航路）。押せないことを見せる。 */
.admin__rowjump:disabled { cursor: default; opacity: 0.75; }
.admin__rowjump:disabled:active { opacity: 0.75; }

.admin__rowjump:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 8px;
}
/* 名前と ID を横に並べる。長い名前が来ても ID を押し出さないよう、
   縮むのは名前のほうだけにする。 */
.admin__rowtitle { display: flex; align-items: baseline; gap: 6px; min-width: 0; }

.admin__rowname {
  font-size: 13px;
  font-weight: 600;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ID は控えめに。名前が主役で、ID は必要なときだけ拾えればよい。 */
.admin__rowid {
  flex: none;
  font-size: 10px;
  font-weight: 400;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 1px 5px;
  color: var(--accent);
}

/* 出方の印（🌫️ 霧中 / 🗝️ 隠し）。名前の隣に置く。
   絵文字は色を指定しても効かないので、目立たせ方は大きさと濃さで作る。 */
.admin__rowflag { flex: none; font-size: 12px; opacity: 0.9; }

.admin__rowmeta {
  font-size: 11px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.admin__rowmeta code {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 1px 5px;
  color: var(--accent);
}

.admin__rowactions { margin-left: auto; display: flex; gap: 6px; }
.admin__rowactions form { margin: 0; }

.btn--small { font-size: 11px; padding: 6px 10px; border-radius: 8px; }

/* コレクションの「⚔️ 再戦」（#03）。返すの隣に並ぶので、色で取り違えを防ぐ
   （返すのは赤。こちらは金＝遊びを続けるほうの色）。 */
.btn--rematch { color: var(--accent); border-color: rgba(255, 209, 102, 0.5); }
.btn--danger { color: #ffb3ae; border-color: rgba(248, 81, 73, 0.5); }

/* 編集は青、削除は赤。並んでいても取り違えにくい。
   アイコンの線は currentColor なので、ここの色にそのまま従う。 */
.btn--edit { color: #79c0ff; border-color: rgba(88, 166, 255, 0.5); }

/* アイコンだけのボタン。文字が無いぶん、幅と高さを自分で決める。
   指で押す対象なので、小さくしすぎない（一辺 32px）。 */
.btn--icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
}

.btn--icon .icon { display: block; }

.admin__empty { font-size: 12px; color: var(--text-dim); line-height: 1.8; }

/* 入力欄の並びに挟む注意書き。「ここを直すと何が起きるか」が想像しにくいところに
   置く。欄そのものより控えめに、でも読み飛ばされない程度には出す。

   **display を block に戻すこと。** 升目に載せるため .field を付けているが、
   .field は grid なので、そのままだと中の <strong> が1つの升目として扱われ、
   強調したところの前後で必ず行が分かれる（文の途中で改行される）。 */
.admin__hint {
  display: block;
  margin: 0;
  padding: 8px 10px;
  font-size: 11px;
  line-height: 1.8;
  color: var(--text-dim);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
}

.admin__hint strong { color: var(--text); }

.admin__empty code {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 1px 5px;
  color: var(--accent);
}

/* ===================== コレクション画面（フェーズ4） ===================== */

.collection {
  max-width: 720px;
  margin: 0 auto;
  padding: calc(var(--safe-top) + 12px) 12px calc(var(--safe-bottom) + 32px);
  display: grid;
  gap: 14px;
  align-content: start;
}

.collection__bar {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.collection__title { font-size: 15px; font-weight: 700; }

.collection__progress {
  font-size: 12px;
  font-weight: 700;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

.collection__bar .nav-link { margin-left: auto; margin-top: 0; }

.collection__list { display: grid; gap: 8px; }

.collection__empty {
  font-size: 12px;
  line-height: 2;
  color: var(--text-dim);
  text-align: center;
  padding: 32px 0;
}

/* エリアごとの区分け。管理画面（.admin__groupbox）と同じ作りにしてある。
   既定は閉じ。お宝が増えても、まず全体の姿が見える。 */
.collection__group { display: block; }

.collection__grouphead {
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 2px;
  border-bottom: 1px solid var(--line);
  font-size: 11px;
  font-weight: 700;
  color: var(--text-dim);
  letter-spacing: 0.06em;
}

.collection__group[open] .collection__grouphead { color: var(--text); }
.collection__grouphead::marker { color: var(--text-dim); font-size: 10px; }

.collection__groupcount { font-weight: 400; font-variant-numeric: tabular-nums; }

/* 中の行。details を直接 grid にすると、閉じているときの扱いが
   ブラウザによって怪しいので、行はもう一枚内側でまとめる。 */
.collection__grouprows { display: grid; gap: 8px; margin: 8px 0 12px; }

.collection__subtitle {
  font-size: 12px;
  font-weight: 700;
  color: var(--text-dim);
  margin: 0 0 8px;
}

.collection__remaininglist { display: grid; gap: 6px; }

/* --- 取得済みの1件 --- */

.collected {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-elevated);
}

.collected__icon { font-size: 24px; }

/* 見つけたときの写真。撮れていればアイコンの代わりにこれを出す。 */
.collected__photo {
  appearance: none;
  flex: 0 0 auto;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
  background: #000;
  cursor: pointer;
  line-height: 0;
}

.collected__photo img {
  width: 56px;
  height: 56px;
  object-fit: cover;
  display: block;
}

.collected__photo:active { transform: translateY(1px); }

/* 拡大表示 */
.photo-view {
  border: 0;
  border-radius: 14px;
  background: var(--bg-elevated);
  padding: 12px;
  max-width: min(92vw, 560px);
  text-align: center;
}

.photo-view::backdrop { background: rgba(0, 0, 0, 0.75); }

.photo-view__image {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 10px;
  margin-bottom: 12px;
}
/* 余った幅はここが吸う。持たせないと、行の中身の長さで右のボタンの位置が
   前後して、一覧が縦に並んだときに凸凹に見える。 */
.collected__body { display: grid; gap: 3px; min-width: 0; flex: 1 1 auto; }

/* 再戦と返すを縦に積み、行の右端で揃える。横に並べると名前と日付の欄が
   押しつぶされ、左に流すと行の中身の長さで位置が前後する。 */
.collected__actions {
  display: grid;
  gap: 6px;
  justify-items: stretch;
  flex: none;
  margin-left: auto;
}

/* 2つのボタンの大きさを揃える。ただし大きさそのものは中身なりのまま
   （幅を決め打ちすると、その分だけ左の欄が狭くなって表示が崩れる）。
   幅は justify-items: stretch が広いほうに合わせる。
   高さだけは揃わないので、行の高さを決めておく（絵文字を持つほうだけ背が高くなる）。 */
.collected__actions .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1.4;
  white-space: nowrap;
}
.collected__title { display: flex; align-items: center; gap: 5px; min-width: 0; }
.collected__name { font-size: 13px; font-weight: 600; }

/* 初めて見つけた日。今回の取得日時より控えめに置く
   （親が既に薄い色なので、大きさで差を付ける）。 */
.collected__first { font-size: 10px; opacity: 0.8; }

/* また探しに行けるお宝の印。休んでいる間は薄く、解禁されたら色を起こす。 */
.againmark {
  flex: none;
  font-size: 10px;
  line-height: 1;
  padding: 3px 5px;
  border-radius: 6px;
  border: 1px solid var(--accent);
  color: var(--accent);
}

.againmark[data-resting="true"] {
  border-color: var(--line);
  color: var(--text-dim);
}

/* 守り人がいたお宝の印。お宝そのもののアイコンと見間違えないよう、
   枠を付けて小さく置く。 */
.bossmark {
  flex: none;
  font-size: 11px;
  line-height: 1;
  padding: 2px 4px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--bg);
}

.collected__missing {
  font-size: 10px;
  color: var(--warn);
}

.collected__meta {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: var(--text-dim);
}

/* 位置偽装で取ったものは正直に印を付ける。開発中の記録と実際に歩いた記録を
   後から区別できないと、コレクションの意味が薄れるため。 */
.collected__fake {
  font-size: 9px;
  padding: 1px 5px;
  border-radius: 999px;
  border: 1px solid rgba(210, 153, 34, 0.6);
  color: var(--warn);
}

.collected__where {
  font-size: 10px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.collected .btn { margin-left: auto; flex: 0 0 auto; }

/* --- 未取得の1件 --- */

.remaining {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border: 1px dashed var(--line);
  border-radius: 10px;
  opacity: 0.75;
}

.remaining__icon { font-size: 18px; filter: grayscale(1); }
.remaining__body { display: grid; gap: 2px; min-width: 0; }
.remaining__title { display: flex; align-items: center; gap: 5px; min-width: 0; }
.remaining__name { font-size: 12px; font-weight: 600; }
.remaining__hint { font-size: 11px; color: var(--text-dim); }

/* オフラインやサーバー停止で開けない導線。消さずに落とすのは、
   「無い」のではなく「今は使えない」ことを伝えるため。 */
.menu__item--disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* 「📡 拠点につなぐ」（#08）。ここだけがサーバーへ確かめに行く。
   下に並ぶサーバーが要るメニューは、押されるまで落としてある。
   繋がったら色を付けて、いまどちらなのかが読めるようにする。 */
.menu__item--connect[aria-pressed="true"] { color: var(--ok); }

/* メニュー項目のうち、リンクではなくボタンのもの */
.menu__item--button {
  appearance: none;
  width: 100%;
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 12px;
  text-align: left;
  cursor: pointer;
}

/* アイコンを大きく見るだけのダイアログ */
.about {
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--bg-elevated);
  color: var(--text);
  padding: 20px;
  max-width: min(320px, 84vw);
  max-height: 84svh;
  overflow-y: auto;
  text-align: center;
}

.about::backdrop { background: rgba(0, 0, 0, 0.6); }

.about__icon {
  width: min(220px, 56vw);
  height: auto;
  border-radius: 24px;
  display: block;
  margin: 0 auto 14px;
}

.about__name { font-size: 16px; font-weight: 700; }

.about__note {
  margin: 6px 0 16px;
  font-size: 12px;
  line-height: 1.9;
  color: var(--text-dim);
}

/* スタッフロール。口上と切り離したいので、上に細い線を引いて場所を分ける。 */
.about__staff {
  margin: 0 0 16px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
  display: grid;
  gap: 6px;
}

/* 役割と名前を上下に積む。横に並べると、長い役割名で名前が潰れる。 */
.about__staffrow { display: grid; gap: 1px; }

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

.about__staff dd {
  margin: 0;
  font-size: 12px;
  font-weight: 600;
}

/* 口上の締め。ここだけ色を起こして、決め台詞に見せる。 */
.about__note strong {
  color: var(--accent);
  font-weight: 700;
  letter-spacing: 0.04em;
}

/* 役割ごとの区切り。線があるだけで、どこまでがひとかたまりかが読める。 */
.menu__sep {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 5px 0;
}

/* サーバーが要る項目の group の頭に出す。下に続く項目ぜんぶに掛かる文言なので、
   途中に挟むと、どこまでが対象なのか分からない。 */
.menu__note {
  display: block;
  padding: 2px 10px 6px;
  font-size: 10px;
  line-height: 1.5;
  color: var(--warn);
}

/* ===================== ボス戦 ===================== */

/* AR ペインを全画面にする。地図もディバイダも隠して、戦いに集中させる。 */
.play[data-battle="on"] { grid-template-rows: 1fr 0fr 0fr; }
.play[data-battle="on"] .divider,
.play[data-battle="on"] .pane--map { display: none; }
.play[data-battle="on"] .pane--ar { height: auto; }

/* 戦闘の表示。タップを canvas に通したいので、全体は素通しにする。 */
.battle {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  padding: calc(var(--safe-top) + 8px) 10px calc(var(--safe-bottom) + 10px);
  display: flex;
  flex-direction: column;
}

.battle__top {
  display: grid;
  gap: 6px;
}

.battle__row {
  display: flex;
  align-items: center;
  gap: 6px;
}

.battle__rowlabel {
  font-size: 16px;
  width: 20px;
  text-align: center;
}

.battle__gauge {
  flex: 1 1 auto;
  height: 8px;
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.25);
  overflow: hidden;
}

/* 減っていく様子が見えることが大事なので、色は変えず幅だけ動かす。 */
.battle__gaugefill {
  display: block;
  height: 100%;
  width: 100%;
  background: linear-gradient(to right, #f85149, #ff9f43);
  transition: width 0.15s linear;
}

/* 持ち物（#04）。戦闘画面の左下に並べる。

   右下は空けておくこと。チャージボタンがそこに居るうえ、.battle は z-index: 4 で
   その上に乗るので、重ねると覆い隠して押せなくなる。 */
/* 効果ごとに1行。混ざっていると、回復のつもりでボムを押す。
   下端を基準に置いてあるので、種類が増えると上へ伸びる。 */
.battle__items {
  position: absolute;
  left: 10px;
  right: 116px;
  bottom: calc(12px + var(--safe-bottom));
  display: grid;
  gap: 6px;
  justify-items: start;
  /* 隙間は撃つ操作に通す。ここを auto にすると、並びの余白でも弾が出なくなる。 */
  pointer-events: none;
}

.battle__itemrow {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  pointer-events: none;
}

.battleitem {
  /* 親の .battle は pointer-events: none。ボタンだけ押せるように戻す。 */
  pointer-events: auto;
  appearance: none;
  position: relative;
  width: 46px;
  height: 46px;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.35);
  background: rgba(13, 17, 23, 0.72);
  color: var(--text);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
}

.battleitem:active { transform: translateY(1px); }

/* 残り回数。押すたびに減るので、数字が見えていないと使いどきを決められない。 */
.battleitem__left {
  position: absolute;
  right: 2px;
  bottom: 1px;
  font-size: 10px;
  font-weight: 700;
  color: var(--accent);
}

/* 当たった瞬間。ゲージを小さく揺らして光らせる。
   守り人の体力は1発あたりの減りが小さいので、幅の変化だけでは気付けない。 */
.battle__gauge.is-hit { animation: battle-gauge-hit 0.22s ease-out; }

@keyframes battle-gauge-hit {
  0%   { transform: translateX(0);    box-shadow: 0 0 0 0 rgba(255, 255, 255, 0); }
  25%  { transform: translateX(3px);  box-shadow: 0 0 6px 1px rgba(255, 255, 255, 0.8); }
  60%  { transform: translateX(-2px); box-shadow: 0 0 4px 1px rgba(255, 255, 255, 0.4); }
  100% { transform: translateX(0);    box-shadow: 0 0 0 0 rgba(255, 255, 255, 0); }
}

/* 残り時間。ボスの体力より細くして、主従を分ける。 */
.battle__gauge--time { height: 5px; }
.battle__gaugefill--time { background: linear-gradient(to right, #58a6ff, #7ee787); }

.battle__timer {
  min-width: 2.2em;
  text-align: right;
  font-size: 18px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.8);
}

/* 残りが少ないと赤く点滅する。数字を読ませなくても切迫が伝わる。 */
.battle__timer[data-low="true"] {
  color: var(--err);
  animation: battle-timer 0.8s ease-in-out infinite;
}

@keyframes battle-timer {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

/* 弾道。右下からレティクルへ、撃つたびに一瞬だけ走る。 */
.battle__tracer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  pointer-events: none;
}

/* 弾道の太さは JS が撃つたびに決める（アイテムの威力に応じて太くする）。 */
.battle__beam {
  stroke: #ffe7a0;
  stroke-width: var(--tracer-width, 1.5px);
  stroke-linecap: round;
  filter: drop-shadow(0 0 var(--tracer-glow, 2px) rgba(255, 200, 80, 0.9));
  /* px で指定した太さを、viewBox の引き伸ばしに巻き込まれないようにする。
     この指定があるので、--tracer-width の数字はそのまま画面のピクセルになる。 */
  vector-effect: non-scaling-stroke;
}

/* 稲妻はチャージ中だけ。普段は出さない。 */
.battle__bolt {
  fill: none;
  stroke: #bfe4ff;
  stroke-width: 1px;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  filter: drop-shadow(0 0 3px rgba(140, 200, 255, 0.95));
  opacity: 0;
}

.battle__tracer.is-charged .battle__bolt { opacity: 1; }

/* 2本目は逆位相。1本だけだと線が片側へ寄って見える。 */
.battle__tracer.is-charged .battle__bolt--flip { animation-delay: 0.02s; }

.battle__tracer.is-firing { animation: battle-tracer 0.13s ease-out; }

/* チャージ中は少し長く残す。太さではなく「まとわりつく感じ」で強さを出す。 */
.battle__tracer.is-firing.is-charged { animation: battle-tracer-charged 0.2s ease-out; }

@keyframes battle-tracer {
  0%   { opacity: 1; }
  60%  { opacity: 0.8; }
  100% { opacity: 0; }
}

@keyframes battle-tracer-charged {
  0%   { opacity: 1; }
  30%  { opacity: 0.55; }
  55%  { opacity: 1; }
  100% { opacity: 0; }
}

/* 画面中心のレティクル。ここに敵を入れて撃つ。 */
.battle__reticle {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 46px;
  height: 46px;
  margin: -23px 0 0 -23px;
  border: 2px solid rgba(255, 255, 255, 0.8);
  border-radius: 50%;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5), inset 0 0 0 1px rgba(0, 0, 0, 0.5);
}

.battle__reticle::before,
.battle__reticle::after {
  content: "";
  position: absolute;
  background: rgba(255, 255, 255, 0.85);
}

.battle__reticle::before { left: 50%; top: -8px; width: 1px; height: 8px; }
.battle__reticle::after { top: 50%; left: -8px; height: 1px; width: 8px; }

/* 当たったかどうかを一瞬で返す。音が切ってあっても分かるように。 */
.battle__reticle[data-state="hit"] {
  border-color: var(--accent);
  transform: scale(1.25);
}

.battle__reticle[data-state="miss"] { transform: scale(0.9); }

/* 決着の一拍。倒した実感が無いまま写真の確認に移ると、何が起きたか分からない。 */
.battle__result {
  position: absolute;
  left: 50%;
  top: 42%;
  transform: translate(-50%, -50%);
  padding: 14px 22px;
  border-radius: 14px;
  font-size: 22px;
  font-weight: 700;
  white-space: nowrap;
  animation: battle-result 0.45s ease-out;
}

.battle__result[data-result="won"] {
  background: var(--accent);
  color: var(--accent-ink);
  box-shadow: 0 10px 40px rgba(255, 209, 102, 0.5);
}

.battle__result[data-result="appear"] {
  background: rgba(13, 17, 23, 0.9);
  color: #ff9f43;
  border: 1px solid #ff9f43;
  font-size: 18px;
}

.battle__result[data-result="lost"] {
  background: rgba(13, 17, 23, 0.9);
  color: var(--text-dim);
  border: 1px solid var(--line);
}

@keyframes battle-result {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.7); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

.battle__hint {
  margin-top: auto;
  /* 持ち物の並びと重ならないように上げる */
  margin-bottom: 58px;
  text-align: center;
  font-size: 11px;
  color: rgba(255, 255, 255, 0.7);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
}
