/* MEASURE-001: 距離・面積計測 UI
 * 色は 16 進数で直接指定する（CSS 変数のフォールバックが効かず暗転した追補116 P1 事故の再発防止）
 * 全セレクタは .ms- で始める。!important は使わない。
 * z-index 950 = report-fab(900) より上・全モーダル(9999+) より下
 */

.ms-root,
.ms-card,
.ms-toast { box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Meiryo UI", sans-serif; }
.ms-root *,
.ms-card *,
.ms-toast * { box-sizing: border-box; }

/* hidden 属性の復権。
   UA スタイルシートの [hidden]{display:none} は author 側の display 宣言に負けるため、
   display:flex 等を持つ要素では el.hidden = true が効かない（実機で発現した）。
   !important は使わず、属性セレクタの詳細度で打ち消す。 */
.ms-panel[hidden],
.ms-card[hidden],
.ms-toast[hidden],
.ms-btn[hidden],
.ms-root [hidden] { display: none; }

/* ---------- ランチャー / ツールバー ---------- */
.ms-root {
  position: fixed;
  left: 50%;
  bottom: 34px;
  transform: translateX(-50%);
  z-index: 950;
  max-width: calc(100vw - 32px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  pointer-events: none;
}
.ms-root > * { pointer-events: auto; }

.ms-panel { display: flex; flex-direction: column; align-items: stretch; gap: 6px; max-width: 100%; }

.ms-prompt {
  background: #ffffff;
  color: #1f2933;
  border: 1px solid #d9dee5;
  border-radius: 8px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.18);
  padding: 7px 12px;
  font-size: 12.5px;
  line-height: 1.5;
  display: flex;
  align-items: center;
  gap: 7px;
}
.ms-p-ic { color: #0F6E56; flex: 0 0 auto; font-size: 13px; }
.ms-p-tx { white-space: normal; }
.ms-p-tx b, .ms-p-num { font-weight: 700; font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; }
.ms-p-warn { color: #b45309; font-weight: 600; }

.ms-bar {
  background: #ffffff;
  border: 1px solid #d9dee5;
  border-radius: 10px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.18);
  padding: 5px;
  display: flex;
  align-items: center;
  gap: 4px;
  overflow-x: auto;
}
.ms-sep { width: 1px; height: 22px; background: #d9dee5; flex: 0 0 auto; margin: 0 2px; }

.ms-btn {
  appearance: none;
  background: #ffffff;
  color: #1f2933;
  border: 1px solid #d9dee5;
  border-radius: 7px;
  height: 36px;
  padding: 0 10px;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: 0 0 auto;
  white-space: nowrap;
}
.ms-btn:hover { background: #E6F2EE; border-color: #0F6E56; }
.ms-btn:focus-visible { outline: 2px solid #0F6E56; outline-offset: 1px; }
.ms-btn:disabled { opacity: 0.4; cursor: default; background: #ffffff; border-color: #d9dee5; }
.ms-btn.ms-on { background: #0F6E56; color: #ffffff; border-color: #0F6E56; }
.ms-btn.ms-primary { border-color: #0F6E56; color: #0F6E56; }
.ms-btn.ms-primary:disabled { color: #6b7280; border-color: #d9dee5; }
.ms-iconly { padding: 0 9px; }
.ms-ic { font-size: 13px; }
.ms-launcher { box-shadow: 0 2px 12px rgba(0, 0, 0, 0.18); height: 38px; }

/* ---------- 結果カード ---------- */
.ms-card {
  position: fixed;
  /* BUNDLE-A (#325 / #308 統合・2026-09-08): 計測結果カードも右下の同じ列に居る。
   * #308 は split-2v で現在地ボタンと 40.6px² 重なると実測しており、#325 の
   * 完了条件はその解消を含む。凡例と同じ変数で横へ逃がす。
   * 🔴 変数が付かなければ 10px ＝ 今日の姿。覆う側へは転ばない。 */
  right: 10px;                                       /* フォールバック */
  right: calc(10px + var(--maprise-rightcol-clear, 0px));
  top: 236px;
  width: 262px;
  max-height: calc(100vh - 300px);
  z-index: 950;
  background: #ffffff;
  color: #1f2933;
  border: 1px solid #d9dee5;
  border-radius: 10px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.18);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.ms-card-hd {
  display: flex; align-items: center; justify-content: space-between;
  padding: 8px 10px; border-bottom: 1px solid #d9dee5;
  font-size: 12px; font-weight: 700; color: #6b7280;
}
.ms-x { appearance: none; background: transparent; border: 0; color: #6b7280; font-size: 13px; cursor: pointer; padding: 2px 4px; line-height: 1; }
.ms-x:hover { color: #1f2933; }
.ms-card-bd { padding: 10px; overflow-y: auto; }
.ms-big { font-size: 24px; font-weight: 700; line-height: 1.2; color: #0F6E56; font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; }
.ms-sub { font-size: 12px; color: #6b7280; margin-top: 3px; }
.ms-warn { font-size: 11.5px; color: #b45309; background: #fdf6e7; border: 1px solid #e6d5ad; border-radius: 6px; padding: 5px 7px; margin-bottom: 8px; line-height: 1.45; }
.ms-rows { margin-top: 9px; border-top: 1px solid #d9dee5; padding-top: 7px; }
.ms-row { display: flex; justify-content: space-between; font-size: 12px; padding: 2px 0; color: #6b7280; }
.ms-row b { color: #1f2933; font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; }

.ms-units { display: flex; gap: 4px; margin-top: 8px; }
.ms-unit { appearance: none; flex: 1 1 0; background: #ffffff; color: #1f2933; border: 1px solid #d9dee5; border-radius: 6px; height: 28px; font-size: 11.5px; font-weight: 600; cursor: pointer; }
.ms-unit:hover { background: #E6F2EE; }
.ms-unit.ms-on { background: #0F6E56; color: #ffffff; border-color: #0F6E56; }

.ms-seg-hd { margin-top: 10px; font-size: 11px; font-weight: 700; color: #6b7280; border-top: 1px solid #d9dee5; padding-top: 7px; }
.ms-segs { list-style: none; margin: 5px 0 0; padding: 0; max-height: 160px; overflow-y: auto; }
.ms-segs li { display: flex; justify-content: space-between; font-size: 11.5px; padding: 2px 0; }
.ms-seg-n { color: #6b7280; }
.ms-seg-v { font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; }

.ms-copy { appearance: none; width: 100%; margin-top: 10px; height: 32px; background: #ffffff; color: #0F6E56; border: 1px solid #0F6E56; border-radius: 7px; font-size: 12px; font-weight: 600; cursor: pointer; }
.ms-copy:hover { background: #E6F2EE; }
.ms-disc { margin-top: 9px; font-size: 11px; line-height: 1.5; color: #6b7280; border-top: 1px solid #d9dee5; padding-top: 7px; }

/* ---------- 地図上の辺ラベル ---------- */
.ms-labels { position: fixed; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; z-index: 949; }
.ms-lbl {
  position: absolute;
  transform: translate(-50%, -50%);
  background: rgba(255, 255, 255, 0.92);
  color: #1f2933;
  border: 1px solid #0F6E56;
  border-radius: 3px;
  padding: 1px 5px;
  font-size: 11px;
  font-weight: 700;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  white-space: nowrap;
  pointer-events: none;
}

/* ---------- トースト ---------- */
.ms-toast {
  position: fixed; left: 50%; bottom: 96px; transform: translateX(-50%);
  z-index: 951; background: #1f2933; color: #ffffff;
  border-radius: 7px; padding: 7px 14px; font-size: 12px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.18);
}

/* ---------- モバイル ---------- */
@media (max-width: 768px) {
  /* bottom は measure-ui.js の reserveAttributionSpace() が出典の実高さから算出して
     上書きする。ここは JS 到達前の初期値であり、出典を覆う 0 にしてはならない（D-1）。 */
  /* padding-bottom は AI 対話ボタン(.mobile-ai-btn)の帯を空けるための予約である（D-2）。
     同ボタンは position:fixed / z-index 998 で計測 UI(950) より前面に出るため、重なると
     ランチャーの下端と、パネルを開いたときのツールバー左下（＝距離ボタンの下端）を
     持っていかれる（実測 375x812: 480px^2 ＝ ランチャー面積の 15.8%）。
     必要な退避量は「AI ボタン上端 = 24+48 = 72px ＋ safe-area」から root の bottom を
     引いた値。bottom は reserveAttributionSpace() が出典の実高さから決めるが
     MIN_BOTTOM=34 を下回らないので、72-34=38px に余白 8px を足した 46px あれば
     bottom がどの値でも足りる（8px は他のクリアランスと同じ値）。
     🔴 これは padding であって bottom ではない。root 自体の下端は 1px も動かないので、
        D-1 が確保した出典クレアランスは変わらない。pointer-events:none も維持されるため、
        空けた帯にある AI ボタンのタップも塞がない（実測で確認）。
     🔴 右へ逃がす案は採らない。align-self:flex-end は GeolocateControl と重なり
        （実測 704px^2）、左オフセットはツールバーの可視幅を 359px から 301px へ削り
        「消去」を横スクロールの向こうへ追いやるため。 */
  .ms-root { left: 0; right: 0; bottom: 34px; transform: none; max-width: 100%; align-items: stretch; padding: 0 8px 46px; padding-bottom: calc(46px + env(safe-area-inset-bottom, 0px)); gap: 5px; }
  /* BUNDLE-A-RESUME-001 (#351・2026-09-08): 横向きの左右 safe-area 帯（iPhone SE
   * 568x320 実測 left/right 44px）へ、バーの中身（モードラベル・アイコン・
   * プレースホルダー文言）が入っていた。左右とも固定 8px しか無く、44px 帯には
   * 遠く届かない。既存の padding: 0 8px 46px（上記行）の左右成分だけを
   * safe-area 分だけ広げる。⚠️ inset が 0（デスクトップ・縦向き）では 8px の
   * ままで今日の姿と変わらない。 */
  .ms-root { padding-left: calc(8px + env(safe-area-inset-left, 0px)); padding-right: calc(8px + env(safe-area-inset-right, 0px)); }
  .ms-btn { height: 44px; min-width: 44px; font-size: 12px; }
  .ms-launcher { height: 44px; align-self: flex-start; }
  .ms-bar { border-radius: 10px; }
  /* ⚠️ 狭幅では left/right 8px の全幅になる。横へ「動かす」余地は無いので、
   *   右端だけを詰める（幅が縮む）。left は動かさない＝文字の折返しは増えるが
   *   カードが画面外へ出ることはない。 */
  .ms-card { left: 8px; right: 8px; width: auto; top: auto; bottom: 104px; max-height: 44vh; }
  .ms-card { right: calc(8px + var(--maprise-rightcol-clear, 0px)); }
  /* BUNDLE-A-RESUME-001 (#351): カード自身も左右 safe-area 帯へ入りうる
   * （上と同じ理由・同じ端末構成。右端は計測結果の値 <b>（例:「点の数 3」）が
   * 帯へ入っていた・実測 iPhone SE 568x320）。 */
  .ms-card { left: calc(8px + env(safe-area-inset-left, 0px)); }
  .ms-card { right: calc(8px + env(safe-area-inset-right, 0px) + var(--maprise-rightcol-clear, 0px)); }
  .ms-toast { bottom: 160px; }
}


/* ==========================================================================
 * MEASURE-FIX-001 (2026-09-07) — 出典クレジット被覆の是正（#305）と
 *                                 計測ツールバーの離脱経路の確保（#306）
 *
 * 🔴 なぜこのファイルに置くのか
 *   出典を覆っていたのは .mobile-ai-btn / .maplibregl-ctrl-bottom-right /
 *   .ms-root / #ai-chat-panel の 4 つで、CSS は app-main.css / chat-ui.css /
 *   本ファイルへ分散していた。「各要素が独自に bottom を持つ」ことが被覆の
 *   構造的原因なので、単一の変数 --maprise-viewport-bottom-clearance へ集約する。
 *   🔴 [#340] 旧名は --maprise-attrib-clearance。「出典の逃げ量」一般と読めてしまい、
 *      ペイン基準が要る場面で誤って選ばれる恐れがあったため、基準が名前に出る形へ
 *      改めた。ここが持つのは **ビューポート下端**からの量（position:fixed 用）で、
 *      ペイン（.map-frame）下端からの量は --maprise-legend-bottom（別物）。
 *      単画面では両者が一致するため、単画面だけを見ていると同じ量に見える。
 *   ⚠️ var() のフォールバックが新名→旧名→34px の 3 段なのは、本ファイルと
 *      measure-ui.js が別々の S3 オブジェクトで、片方だけ先に配信される瞬間が
 *      あるため。旧名は当面 measure-ui.js が併せて設定している。
 *   本ファイルは stylesheet の読み込み順で最後（実測 index 25 / app-main.css は 2、
 *   chat-ui.css は 14）のため、!important を増やさずに上書きできる。
 *   ⚠️ 責務としては不自然（計測 UI の CSS が全画面の下部レイアウトを担う）。
 *      共通 CSS を新設すると index.html 双子への link 追加が要るため、
 *      変更面積の小ささを優先した。将来 app-main.css へ移すのが望ましい。
 *
 * 変数は measure-ui.js の reserveAttributionSpace() が設定する。
 * 未設定でも既定値（34px）で動く＝JS が落ちても従来より悪化しない。
 * ========================================================================== */

/* --- #305: 下部固定要素は出典の実高さに追従する ------------------------- */

/* AI ボタン（モバイル）。app-main.css は bottom:24px 固定で、
 * 出典が 2〜3 行に折り返すと必ず重なっていた（実測 828px² / 5.03%）。 */
@media (max-width: 768px) {
  .mobile-ai-btn {
    bottom: calc(var(--maprise-viewport-bottom-clearance, var(--maprise-attrib-clearance, 34px))
                 + env(safe-area-inset-bottom, 0px)) !important;
  }
}

/* Geolocate ほか MapLibre 標準コントロール。
 * 📌 実測では出典と同じ z-index 2 で DOM 順が先＝出典の「後ろ」にあり、
 *    出典を覆ってはいなかった。ただし逆に出典へ潜り込んで見えにくくなるため、
 *    同じ変数で退避させて重なり自体をなくす。 */
.maplibregl-ctrl-bottom-right {
  bottom: calc(var(--maprise-viewport-bottom-clearance, var(--maprise-attrib-clearance, 34px))
               + env(safe-area-inset-bottom, 0px)) !important;                  /* フォールバック */
  /* BUNDLE-A (#325・2026-09-08): ペイン基準の逃げ量を採る。
   * 🔴 上の行（ビューポート基準）だけでは上下分割で足りない。NEAR_BOTTOM_GAP により
   *   「画面下端から遠い出典」を意図的に無視するため 34px しか返らず、実測 769px の
   *   上下分割では出典（トグルを避けて縦長へ変形・y343-399）とコントロール
   *   （y326-365）が重なったままになる。ペイン下端基準の量は
   *   --maprise-ctrl-bottom-clear（reserveRightColumn() が要素ごとに実測）。
   * 🔴 効かなかった場合（max()/var() 非対応）は上の行が残り、今日の姿へ戻るだけ。 */
  /* 🔴 BUNDLE-A r4 (2026-09-08): JS の値は **総量** として採る（足さない）。
   *   --maprise-ctrl-bottom-clear は reserveRightColumn() が
   *   「ペイン下端 - 出典の上端 + 余白」で測った値で、出典自体が safe-area 帯の
   *   中まで伸びている（実測 375x812 で出典 y732-812）以上、ここへさらに
   *   env() を足すと 34px 上へ行き過ぎる。実測ではその 34px でツールバー
   *   （y588-644）へ食い込み、そこから逃げようとしてカードの中へ着地していた。
   * ⚠️ JS が値を入れないときは従来式（上の行と同じ）へ落ちる。
   *   さらに max() で「safe-area 帯 + 8px」を最低限として必ず確保する
   *   ＝出典が無い/隠れている場合でも帯へは入らない。 */
  bottom: max(var(--maprise-ctrl-bottom-clear,
                  calc(var(--maprise-viewport-bottom-clearance, var(--maprise-attrib-clearance, 34px))
                       + env(safe-area-inset-bottom, 0px))),
              calc(env(safe-area-inset-bottom, 0px) + 8px)) !important;
}

/* BUNDLE-A (#351・2026-09-08): 左下のコントロール列（ScaleControl）。
 * 🔴 bottom-right だけを退避させていたため、同じ下端に居るスケールバーが
 *   取り残されていた。実測 375x812（縦向き・inset bottom 34）:
 *     .maplibregl-ctrl-scale  y780-802 ＝ safe-area 帯（778-812）へ 24px
 *     同じ矩形が出典（y732-812）の中にも入っている
 *   '30 km' という**文字**なので #351 の対象（背景・装飾のみの箱ではない）。
 * ⚠️ 右列と同じ変数を使う。reserveRightColumn() は左右**両方**の
 *   コントロール列へ --maprise-ctrl-bottom-clear を入れる（同関数のコメント参照）。 */
.maplibregl-ctrl-bottom-left {
  bottom: calc(var(--maprise-viewport-bottom-clearance, var(--maprise-attrib-clearance, 34px))
               + env(safe-area-inset-bottom, 0px)) !important;                  /* フォールバック */
  /* 🔴 BUNDLE-A r4 (2026-09-08): JS の値は **総量** として採る（足さない）。
   *   --maprise-ctrl-bottom-clear は reserveRightColumn() が
   *   「ペイン下端 - 出典の上端 + 余白」で測った値で、出典自体が safe-area 帯の
   *   中まで伸びている（実測 375x812 で出典 y732-812）以上、ここへさらに
   *   env() を足すと 34px 上へ行き過ぎる。実測ではその 34px でツールバー
   *   （y588-644）へ食い込み、そこから逃げようとしてカードの中へ着地していた。
   * ⚠️ JS が値を入れないときは従来式（上の行と同じ）へ落ちる。
   *   さらに max() で「safe-area 帯 + 8px」を最低限として必ず確保する
   *   ＝出典が無い/隠れている場合でも帯へは入らない。 */
  bottom: max(var(--maprise-ctrl-bottom-clear,
                  calc(var(--maprise-viewport-bottom-clearance, var(--maprise-attrib-clearance, 34px))
                       + env(safe-area-inset-bottom, 0px))),
              calc(env(safe-area-inset-bottom, 0px) + 8px)) !important;
}

/* MOBILE-ATTRIB-001 (2026-09-07): AI チャットパネル（モバイルのボトムシート）。
 *
 * 🔴 なぜ x 方向分離では解けないのか
 *   デスクトップのパネルは右 380px なので、出典の右端をパネルの左端で止めれば
 *   避けられる（実測 12/12 で 0%）。ところがモバイルのパネルは **全幅**
 *   （実測 375×812 で x=0 / w=375）で、左へ逃がす余地が無い。
 *   それでも separateFromChatPanel() は right を設定するため、出典は
 *   **幅 16px・高さ 1300px・135 行の縦棒**に潰れて画面外へはみ出していた
 *   （実測 2026-09-07: 被覆 73.4%・判読不能）。
 *
 * 🔴 採った方式: パネルを縮めず、出典の上へ持ち上げる
 *   bottom だけを動かすので **height（65dvh）は変わらない**＝チャット領域も
 *   入力欄も狭くならない。実測 375×812: パネル y=230-758（h=528 のまま）/
 *   出典 y=766-812（w=375・5 行・判読可）/ 被覆 0%。
 *   ⚠️ 高さを削る案は採らない（上下分割で bottom 468px が要り、パネルが
 *      900→432px と半減する。この判断は #305 の既存コメントと同じ）。
 *
 * 🔴 これで separateFromChatPanel() の clip は自然に止まる
 *   持ち上げるとパネル（-758）と出典（766-）が縦に重ならなくなり、
 *   同関数の縦重なり判定が false になるため right を設定しない。
 *   実測でも style.right が (none) のままになる。
 *   ただし「重なったら潰す」経路自体は残るので、measure-ui.js 側にも
 *   フェイルセーフ（全幅の相手は clip しない）を入れてある。
 *
 * ⚠️ カスケード: bottom:0 !important が app-main.css（sheet index 2）と
 *    chat-ui.css（同 14）の双方にある。本ファイルは 25 で最後に読まれるため、
 *    同じ詳細度・同じ !important でも後勝ちで効く（実測で computedBottom=54px）。
 *    本ファイルより後に読まれる CSS を足す場合はここが崩れる。
 *
 * 📌 safe-area を足すのは上の 2 つの消費者と揃えるため。出典自身も
 *    padding-bottom に同じ env() を持つので厳密には二重に見込むが、
 *    余分に持ち上げる方向（＝覆わない側）なので法務要件を壊さない。
 *
 * 🔴🔴 2026-09-07 追記 — セレクタを .open へ限定した理由（初版の退行）
 *   初版は `#ai-chat-panel` 一律に bottom を上げており、**閉じている既定
 *   状態を壊していた**。モバイルのパネルは `transform: translateY(100%)`
 *   で閉じるので、閉時の上端は `ビューポート高 - bottom` になる。
 *   bottom を 54px にすると上端が 812-54=758 へ上がり、パネルの
 *   上端 54px が画面内へ残って出典（y 766-812）を **99.91% 覆った**
 *   （実測 2026-09-07・覆う側は chat-header / chat-history-btn /
 *   chat-maximize-btn）。元の不具合（開時 73.4%）より悪い。
 *   bottom を 0 へ戻すと閉時 0%（パネル y=812）へ戻ることも実測して
 *   おり、因果は確定している。したがって **開いている間だけ** 持ち上げる。
 * ⚠️ `:not(.chat-maximized)` は最大化表示（height:100dvh）を変えないため。
 *    最大化は地図ごと覆う全画面モードで、本件の対象外。
 * ⚠️ 詳細度は #ai-chat-panel.open（id+class）が #ai-chat-panel（id）に勝つので、
 *    同じ !important でも読み込み順に関わらず開時だけ確実に効く。 */
@media (max-width: 768px) {
  #ai-chat-panel.open:not(.chat-maximized) {
    bottom: calc(var(--maprise-viewport-bottom-clearance, var(--maprise-attrib-clearance, 34px))
                 + env(safe-area-inset-bottom, 0px)) !important;
  }
}

/* ATTRIB-READABILITY-001 (#305 残条件 3・2026-09-07): 狭いデスクトップ幅で
 * AI パネルに縦を譲らせ、出典が縦棒へ潰れるのを止める。
 *
 * 🔴 何が起きていたか（実測 2026-09-07・本番・770x800・パネル開）
 *   デスクトップのパネルは右 380px。サイドバー 320px を引くと出典に残るのは
 *   **70px の帯**しかなく、separateFromChatPanel() がそこへ右端を詰めるため
 *   出典が **幅 70px・高さ 329px・25 行・1 行あたり中央値 6 文字**へ潰れる。
 *   ⚠️ このとき DOM 被覆率は **1.25%** にしか見えない。潰れるほど面積が減り
 *      指標はむしろ良くなるため、被覆率では検知できない（#342）。
 *
 * 🔴 なぜ x 方向では解けないのか（実測で確定・定数では届かない）
 *   出典が読める要素幅は 220px 必要（実測: 帯 204px・中央値 22 文字）。
 *   770px では左に 70px しか無く、**どんな定数を入れても 220px は作れない**。
 *   サイドバー（320px）とパネル（380px）の合計 700px が固定である以上、
 *   220px を確保できるのは **920px 以上**の幅だけ（実測 916px で帯 199.7px）。
 *   したがって残る手段は「パネル側が縦を譲る」しかない。
 *
 * 🔴 なぜ bottom ではなく height なのか
 *   モバイルのボトムシートは height:65dvh + bottom:0 なので bottom を上げれば
 *   持ち上がる。**デスクトップのパネルは top:0 + height:100vh（chat-ui.css）**で
 *   過剰指定になっており、bottom を動かしても 1px も動かない（実測）。
 *   高さを削るのが唯一の経路。削る量は出典の実高さから来る
 *   --maprise-viewport-bottom-clearance（ビューポート下端基準＝position:fixed の
 *   パネルに対して正しい基準）。実測 770px: 800px -> 749px（-51px・6.4%）。
 *   🔴 [#340] ここは 100vh から引く量なので、**ビューポート基準でなければならない**。
 *      ペイン基準の --maprise-legend-bottom へ寄せてはいけない（#340 案2 が
 *      不適である具体例）。
 *
 * 🔴 これで separateFromChatPanel() の clip は自然に止まる
 *   譲るとパネル下端が出典の上へ退き、縦の重なり判定が false になるため
 *   right を設定しない。出典は素の 450px 幅（3 行）へ戻る。
 *   ⚠️ 譲るか否かの判定は**横方向の幾何だけ**で行っている（measure-ui.js）。
 *      縦の重なりを条件にすると、譲る→重ならない→やめる→重なる、の発振になる。
 *
 * 🔴 効かなかった場合は今日の姿へ戻るだけ
 *   クラスは measure-ui.js が付ける。JS が落ちればクラスが付かず、
 *   パネルは height:100vh のまま＝現状。悪化する形にはならない。
 * ⚠️ :not(.chat-maximized) は最大化（地図が 1 点も見えない全画面モード）を
 *    変えないため。最大化は本件の対象外（#305 の既存判定を踏襲）。 */
@media (min-width: 769px) {
  html.maprise-attrib-yield #ai-chat-panel.open:not(.chat-maximized) {
    height: calc(100vh - var(--maprise-viewport-bottom-clearance, var(--maprise-attrib-clearance, 34px))) !important;
    max-height: calc(100vh - var(--maprise-viewport-bottom-clearance, var(--maprise-attrib-clearance, 34px))) !important;
  }
}

/* ATTRIB-READABILITY-001 (#305 残条件 6・2026-09-08): 横向き（縦を譲れない）で
 * AI パネルを**横へ**詰め、出典の帯を確保する。
 *
 * 🔴 何が起きていたか（実測 2026-09-08・本番・812x375・パネル開・inset R47/B21）
 *   出典に残る帯が **91.7px・15 行・1 行あたり中央値 10 文字**（R3 不成立）で、
 *   さらにパネルが **10.7%** 覆っていた（R1 不成立）。
 *   上の「縦を譲る」機構は横向きでは使えない。パネルの中身は 371px を必要とし、
 *   高さ 375px から出典ぶんを引くと 299px しか残らず、入力欄・文字数・免責が
 *   パネル外へ出て被覆が 35.9%／54.5% へ**悪化**する（実測・改修前より悪い）。
 *
 * 🔴 横なら成立する（実測・パネル 380 -> 300）
 *   812x375 で 帯 **172.7px / 8 行 / 中央値 19 文字 / 被覆 0%** ＝ R1・R2・R3 充足。
 *   .map-frame は全構成で 320-812 のまま**変化しない**。パネルは position:fixed の
 *   オーバーレイでレイアウトに参加していないため、地図の再計算（map.resize()）が
 *   要らない。サイドバーを詰める案にはこの性質が無いので採らない。
 *   凡例も不変（h:225 / 14 項目 / clipTop:0）＝「凡例側を縮小・非表示にしない」に適合。
 *   文字サイズも変えていない＝「出典の文字縮小は不可」に適合。
 *
 * 🔴 なぜ overflow-y:auto を併せるのか
 *   300px へ詰めると中身の折り返しが増え、**AI 免責文だけ**がパネル下端の外へ出る。
 *   パネル自身をスクロール可能にすると到達できる（実測: scrollTop を末尾へ送ると
 *   免責文が完全に可視 = reach）。入力欄・文字数カウンタは詰めた状態でも可視のまま。
 *   ⚠️ .chat-messages の内部スクロールは温存される（実測で scrollHeight/clientHeight
 *      とも従来どおり）。二重スクロールにはなるが、外側は overscroll-behavior:contain
 *      で親（地図）へ連鎖させない。
 *
 * 🔴 トグルボタンも一緒に動かす必要がある
 *   #ai-chat-toggle.panel-open は chat-ui.css で right: var(--chat-panel-width, 380px)。
 *   パネルだけ詰めるとトグルが元の位置（右から 380px）に取り残され、詰めた
 *   パネルの左端（右から 300px）との間に 80px の隙間が空いて宙に浮く。
 *   ⚠️ 「開いている間トグルは常に display:none」ではない。実測 2026-09-08:
 *      **開閉をクリックで往復させた後は none**（スイートの全 desk シナリオ）だが、
 *      **開いたまま画面幅だけ変えた場合は flex**（desk-1000・right 380px・
 *      パネルと隙間 0）。表示される経路が実在するので追従は要る。
 *   ✅ 効いていることの実測（812x375・compact 300px・display を強制して測定）:
 *      トグルの computed right = 300px / 矩形 468-512 / パネル左端 512 ＝ 隙間 0。
 *      本ルールが無ければ 380px（右端 432）となり 80px の隙間が残る。
 *   ⚠️ chat-ui.js のリサイズ操作はトグルへ**インラインで** right を書くため、
 *      ここは !important が要る（stylesheet の !important はインラインに勝つ）。
 *
 * 🔴 なぜ --chat-panel-width を直接上書きしないのか
 *   measure-ui.js が「素の幅」の読み取り元として同じ変数を使っている。上書きすると
 *   詰めた値を素の幅として読み、判定が反転して発振する。専用変数に分ける。
 *
 * 🔴 効かなかった場合は今日の姿へ戻るだけ
 *   クラスと変数は measure-ui.js が付ける。JS が落ちれば付かず、パネルは
 *   width: var(--chat-panel-width, 380px) のまま＝現状。悪化する形にはならない。
 * ⚠️ ユーザーがパネル幅をドラッグで変えている間、詰めている側が優先される
 *   （幅は 300〜素の幅の範囲に丸められる）。ドラッグは mousedown のみで
 *   高さ 420px 未満のビューポートに限られる条件のため、実機の横向き
 *   （タッチ）では発生しない。法務要件（出典の可読性）を優先する判断。
 * ⚠️ :not(.chat-maximized) は最大化を対象外にするため（上のルールと同じ理由）。 */
@media (min-width: 769px) {
  html.maprise-attrib-compact #ai-chat-panel.open:not(.chat-maximized) {
    width: var(--maprise-chat-compact-width, 300px) !important;
    overflow-y: auto !important;
    overscroll-behavior: contain;
  }
  html.maprise-attrib-compact #ai-chat-toggle.panel-open {
    right: var(--maprise-chat-compact-width, 300px) !important;
  }
}

/* SPLIT-ATTRIB-001 (2026-09-07): 上下分割・4分割（モバイル）で出典を画面下端へ固定する。
 *
 * 🔴 なぜ必要か
 *   `.attribution` は **#mapFrame0 の子が 1 つだけ**（frame1〜3 は持たない）で、
 *   `position:absolute; bottom:0` により **その frame の下端**へ付く。単画面なら
 *   frame0 の下端＝画面下端なので問題ないが、上下分割では frame0 が上半分になるため
 *   出典が **画面中央（実測 375x812 で y=389.5-435.5）** へ来る。
 *   モバイルの AI パネルは全幅・65dvh のボトムシートなので、そこを必ず覆う
 *   （実測 2026-09-07: 被覆 **100%**・JA/EN とも・4分割でも同じく 100%）。
 *
 * 🔴 なぜ clearance だけでは解けないのか
 *   `bottomClearance()`（measure-ui.js）は `NEAR_BOTTOM_GAP = 120` により
 *   **画面下端から遠い出典を意図的に無視する**。避けようとすると下部固定要素が
 *   画面中央まで飛ぶためで、この判断自体は正しい。つまり出典が中央に在る限り、
 *   clearance 機構は原理的に助けに来ない。**出典の側を下端へ持ってくるのが筋**である。
 *
 * 🔴 これで既存機構がそのまま働く
 *   下端へ移ると `vh - r.bottom = 0` となり clearance が 34px -> 54px へ復帰し、
 *   MOBILE-ATTRIB-001 の `#ai-chat-panel.open` ルールがパネルを出典の上へ逃がす。
 *   **パネルの高さ（65dvh）は変えていない**＝チャット領域も入力欄も狭くならない。
 *   実測: パネル y 230.2-758 / 出典 y 766-812（w=375・4 行）/ 被覆 **0%**。
 *
 * ⚠️ z-index 3 が要る理由（2 のままだと 14.36% 残る）
 *   fixed 化すると出典はルートの重ね合わせ文脈へ移り、**DOM 上あとに来る**
 *   frame1 の `coord-display`（z-index 2）や MapLibre のスケールバーに負ける
 *   （実測: 被覆 14.36%・覆う側は coords1 とスケール）。3 へ上げると 0%。
 *   🔴 これは新しい上下関係ではない。**単画面では今日すでに出典が上**で、
 *      coords0 とスケールバーは出典の矩形に 100% 入っている（実測）。
 *      本ルールは分割時にその関係を揃えるだけで、覆う対象は増えていない
 *      （上ペインの coords0/スケールはむしろ 100% -> 0% と解放される）。
 *
 * ⚠️ 適用範囲を 2v / 4 に限っている
 *   左右分割（2h）は frame0 の下端がすでに画面下端で被覆 0%（実測）。
 *   触ると幅が 186.5 -> 375 へ変わるだけで法務上の利得が無いため**対象外**。
 *   単画面も同様に無変更。`!important` は付けない（詳細度 0,3,0 で足りる）。
 *
 * 🔴 効かなかった場合
 *   出典は frame0 の中に留まる＝**今日の姿**へ戻るだけで、覆う側へは転ばない。
 *   z-index だけが効かなかった場合も 14.36% で、100% よりは良い。
 *   前回（MOBILE-ATTRIB-001 初版）の退行は「効いてはいけない状態で効いた」
 *   ものだったので、外し損ねても悪化しない形を選んである。
 *
 * 📌 4分割は Starter 以上のプランゲートがあり guest では UI から到達できない
 *   （実測: `setSplitMode('4')` でアップグレード案内が出る）。検証は
 *   `#mapGrid` のクラスだけを `split-4` にしてレイアウトを再現して行った。 */
@media (max-width: 768px) {
  .map-grid.split-2v .attribution,
  .map-grid.split-4 .attribution {
    position: fixed;
    z-index: 3;
  }
}

/* --- #306: 狭幅でツールバーが横スクロールを要する問題 --------------------
 * 375px 実測: scrollWidth 396 > clientWidth 357。JA では ✕、EN では
 * 「Clear」と ✕ が初期表示されず、モバイルでは ESC も使えないため
 * 離脱手段が「横スクロールして ✕ を押す」だけになっていた。
 * 🔴 ラベルは視覚的に隠すだけで、aria-label と title には残る
 *    （applyLabels() が全ボタンへ設定済み）。読み上げは劣化しない。 */
@media (max-width: 480px) {
  .ms-bar .ms-lb { display: none; }
  .ms-bar .ms-btn { padding: 0 9px; }
}
