    :root {
      --bg: #0f1115;
      --card: #181b22;
      --card2: #20242d;
      --fg: #e7e9ee;
      --muted: #98a0ad;
      --accent: #6aa9ff;
      --good: #4ade80;
      --warn: #fbbf24;
      --bad:  #f87171;
      /* 「失敗」の隣に置く色。--bad から色相だけを橙側へ寄せた同系色で、赤の一味だと
         分かりつつ --bad とは別物だと見分けられる濃さにしてある (位置合わせ失敗バッジ用)。 */
      --bad-soft: #f78a60;
      --border: #2a2f3a;
      /* ドロップゾーンのアイコン (4 棟のピクトグラム)。奥から手前へ 4 階調で、奥ほど背景に
         溶ける = 大気遠近。ダークでは奥を暗く、ライトでは奥を淡くすることで、どちらの
         テーマでも「遠いものほど霞む」が成立する。--icon-win は窓 (夜景の灯り)。 */
      --icon-far:  #42638a;
      --icon-mid:  #5b86bb;
      --icon-mid2: #7aa5d8;
      --icon-near: #9cc4f5;
      --icon-win:  rgba(255, 236, 190, .9);
      --radius: 14px;
    }
    @media (prefers-color-scheme: light) {
      :root {
        --bg: #f6f7fa;
        --card: #ffffff;
        --card2: #f0f2f6;
        --fg: #1a1d24;
        --muted: #5b6371;
        --accent: #2563eb;
        --good: #15803d;
        --warn: #b45309;
        --bad:  #b91c1c;
        --bad-soft: #b04614;
        --border: #e4e7ec;
        --icon-far:  #94b7db;
        --icon-mid:  #6c9ac7;
        --icon-mid2: #4d7cae;
        --icon-near: #2f5f95;
        --icon-win:  rgba(255, 255, 255, .78);
      }
    }
    * { box-sizing: border-box; }
    /* データ検出 (電話番号・住所・日付の自動リンク化) の保険。
       抑止の本体は各 HTML の <meta name="format-detection"> だが、iOS Safari は
       telephone=no しか公式には見ておらず、住所・日付の検出や、選択メニュー経由の
       検出は meta を素通りすることがある。建物名 ("〇〇ビル 3-1-1" のような数字を
       含む名称) が青リンクになると誤タップで電話アプリが開くので、通り抜けた分は
       見た目と操作を殺して素のテキストに戻す。Safari が付ける属性
       (x-apple-data-detectors) で拾うため、本物の <a> には一切影響しない。 */
    a[x-apple-data-detectors],
    a[data-detector-type] {
      color: inherit !important;
      text-decoration: none !important;
      pointer-events: none !important;
      -webkit-touch-callout: none;
    }
    html, body {
      margin: 0; padding: 0;
      background: var(--bg);
      color: var(--fg);
      font: 15px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Hiragino Sans", "Noto Sans CJK JP", sans-serif;
      min-height: 100vh;
      /* iOS Safari のテキスト自動拡大 (既定 `auto`) を切る。**サイト全体に効かせる**。
         WebKit の autosizing はブロック単位ではなくテキストノード単位で倍率を決め、
         はみ出す長い行は拡大しないので、同じ font-size を宣言した兄弟どうしで
         見かけの大きさが食い違う。実害が出ていたのは結果ビューの 1 行情報
         (`.pick-compact` = 名称 (素のテキスト) + `.h` (自治体名/高さ) がすべて 14px)
         で、横持ちにすると短い `.h` だけが拡大され、長い建物名は据え置かれて
         「自治体名のほうが名称より大きい」状態になっていた。名称が「(名称不明)」の
         ときだけ揃って見えたのは、そちらは短くて同じ倍率に入るため。10px の
         `.credits` も同様に膨らむ。倍率はレイアウト幅／可視幅の比で決まるので
         回転で変わり、縦持ちでは目立たない。
         `html` (ルート) に置かないと WebKit は見ない。プリント版は JS が cqw で
         フォントを決めるので、ここを固定しておくと画面と紙の一致も保てる。 */
      -webkit-text-size-adjust: 100%;
      text-size-adjust: 100%;
    }
    main {
      max-width: 640px;
      margin: 0 auto;
      padding: 16px 14px 80px;
      padding-top: max(16px, env(safe-area-inset-top));
    }
    h1 {
      font-size: 18px;
      margin: 4px 0 14px;
      letter-spacing: 0.02em;
    }
    h1 .sub { color: var(--muted); font-weight: 400; font-size: 13px; margin-left: 6px; }
    .brand {
      display: flex;
      align-items: center;
      gap: 12px;
      margin: 4px 0 16px;
    }
    .brand-mark {
      flex: 0 0 auto;
      width: 52px;
      height: 52px;
      border-radius: 12px;
      background: #eef4fb;
      border: 1px solid var(--border);
      padding: 3px;
    }
    .brand-titles {
      display: flex;
      flex-direction: column;
      gap: 3px;
      min-width: 0;
    }
    .brand-ja {
      font-size: 21px;
      font-weight: 800;
      letter-spacing: 0.04em;
      margin: 0;
      line-height: 1.1;
    }
    .brand-en {
      font-size: 11px;
      /* 大文字化はしない。ブランド英名がキャメルケース (MiharashiScope) なので、
         uppercase にすると S の区切りが消えて読みづらい。字間も大文字前提の広さ
         (0.22em) から、混在ケースで自然な控えめの値に戻す。 */
      letter-spacing: 0.04em;
      color: var(--muted);
    }
    .card {
      background: var(--card);
      border: 1px solid var(--border);
      border-radius: var(--radius);
      padding: 14px;
      margin-bottom: 14px;
    }
    .card h2 {
      font-size: 14px;
      margin: 0 0 10px;
      color: var(--muted);
      font-weight: 600;
      text-transform: uppercase;
      letter-spacing: 0.06em;
    }
    input[type="file"] {
      width: 100%;
      padding: 8px;
      border-radius: 10px;
      border: 1px solid var(--border);
      background: var(--card2);
      color: var(--fg);
      font-size: 15px;
      font-family: inherit;
    }
    /* 写真選択用のドロップゾーン。タップでファイル選択 (内包の hidden input)、D&D でも選択可。 */
    .dropzone {
      display: block;
      margin-bottom: 12px;
      padding: 26px 16px;
      border: 2px dashed var(--border);
      border-radius: 12px;
      background: var(--card2);
      text-align: center;
      cursor: pointer;
      transition: border-color 0.15s, background 0.15s;
    }
    .dropzone:hover { border-color: var(--accent); }
    .dropzone.dragover { border-color: var(--accent); background: rgba(106, 169, 255, .08); }
    /* 同時実行ジョブ数の上限に達していて写真を選べない状態 (upload.js が付け外しする)。
       押せないことが見た目で分かるよう淡くし、hover の反応も止める。 */
    .dropzone.disabled {
      opacity: 0.45;
      cursor: not-allowed;
    }
    .dropzone.disabled:hover { border-color: var(--border); }
    .dropzone .dz-icon { line-height: 1; margin-bottom: 8px; }
    .dropzone .dz-city { display: block; width: 36px; height: 36px; margin: 0 auto; }
    .dropzone .dz-text { font-size: 14px; font-weight: 500; color: var(--fg); }
    .dropzone .dz-hint { font-size: 12px; color: var(--muted); margin-top: 4px; }
    button {
      width: 100%;
      padding: 12px 14px;
      border: 0;
      border-radius: 10px;
      background: var(--accent);
      color: #fff;
      font-size: 16px;
      font-weight: 600;
      cursor: pointer;
      appearance: none;
      /* ダブルタップ由来のページズームを抑止する (iOS Safari は viewport の
         user-scalable=no を無視するため、タップ可能要素側で明示的に止める)。
         通常のスクロール・ピンチズームは残す。 */
      touch-action: manipulation;
    }
    button:disabled { opacity: 0.5; cursor: not-allowed; }
    /* 開始ボタンは無効時に青ではっきりグレーアウトする (位置情報なし等)。 */
    #submit:disabled { background: var(--muted); color: #fff; opacity: 0.6; }
    button.secondary {
      background: transparent;
      border: 1px solid var(--border);
      color: var(--fg);
      font-weight: 500;
      font-size: 13px;
      padding: 6px 10px;
      width: auto;
    }
    .preview { display: flex; gap: 12px; align-items: center; margin-bottom: 10px; }
    .preview img {
      width: 64px; height: 64px;
      object-fit: cover;
      border-radius: 10px;
      background: var(--card2);
      border: 1px solid var(--border);
    }
    .preview .meta { font-size: 13px; color: var(--muted); overflow: hidden; }
    .preview .meta .name { color: var(--fg); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    /* 同時実行ジョブ数の上限に達している間、アップロードカード上部に出す注意書き。
       「今は投入できない」ことを見落とされないよう、写真の内容に対する警告
       (.photo-warning) と同じ警告色にする。 */
    .job-limit-notice {
      margin: 0 0 12px;
      padding: 10px 12px;
      font-size: 13px;
      line-height: 1.5;
      color: var(--bad);
      background: var(--bg);
      border: 1px solid var(--bad);
      border-radius: 8px;
      display: flex;
      align-items: flex-start;
      gap: 8px;
    }
    /* 不活化した写真選択エリアをタップしたときに、この注意書きを軽く 2 回だけ脈打たせて
       視線を上に誘導する (ボタン下に同じ文言を重複表示する代わり)。upload.js がタップ
       ごとに .flash を付け直す。点滅ではなく地色をふわっと明滅させるだけにしてあるので、
       文字は常に読める濃さのまま。 */
    .job-limit-notice.flash { animation: job-limit-flash 0.5s ease-in-out 2; }
    @keyframes job-limit-flash {
      0%, 100% { background: var(--bg); }
      50%      { background: rgba(255, 107, 107, .12); }
    }
    /* アニメーションを避ける設定の利用者には点滅させない (文言自体は常時見えている)。 */
    @media (prefers-reduced-motion: reduce) {
      .job-limit-notice.flash { animation: none; }
    }

    .photo-warning {
      margin: -2px 0 10px;
      font-size: 13px;
      color: var(--bad);
      display: flex;
      align-items: flex-start;
      gap: 8px;
    }
    .photo-warning #photo-warning-text { flex: 1; }
    /* 警告文中のリンク (例: 「対応エリア」→ モーダル)。警告色を継いだ下線付きの押下可能テキスト。 */
    .photo-warning .warn-link {
      color: inherit;
      text-decoration: underline;
      font-weight: 600;
      cursor: pointer;
    }

    /* 読み取れた EXIF の要約 (?debug=stats 限定)。値をそのまま読む用途なので等幅・折り返し可。 */
    .exif-debug {
      margin: -2px 0 10px;
      font-size: 11px;
      line-height: 1.5;
      color: var(--muted);
      font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
      overflow-wrap: anywhere;
    }

    .help-modal {
      position: fixed;
      inset: 0;
      background: rgba(0,0,0,0.5);
      display: none;
      align-items: center;
      justify-content: center;
      padding: 16px;
      z-index: 60;
    }
    .help-modal.open { display: flex; }
    .help-modal-card {
      background: var(--card);
      border: 1px solid var(--border);
      border-radius: 12px;
      max-width: 480px;
      width: 100%;
      max-height: 85vh;
      overflow: auto;
    }
    .help-modal-header {
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 12px 14px;
      border-bottom: 1px solid var(--border);
      position: sticky;
      top: 0;
      background: var(--card);
    }
    .help-modal-header .title { flex: 1; font-weight: 600; font-size: 15px; }
    .help-modal-header .close {
      background: transparent;
      border: 1px solid var(--border);
      color: var(--fg);
      border-radius: 8px;
      width: auto;
    }
    .help-modal-body { padding: 14px; font-size: 13px; line-height: 1.6; color: var(--fg); }
    .help-modal-body h4 { margin: 14px 0 6px; font-size: 13px; }
    .help-modal-body ol,
    .help-modal-body ul { margin: 0; padding-left: 1.3em; }
    .help-modal-body li { margin: 4px 0; }
    .help-modal-body .help-note { margin-top: 14px; color: var(--muted); font-size: 12px; }
    .help-modal-body .help-step { margin: 12px 0 4px; }
    .help-modal-body .help-sub { margin: 8px 0 0; font-size: 11px; color: var(--muted); line-height: 1.5; }
    /* 対応エリアモーダル: 都道府県見出し直下に、自治体名を中黒連結で 1 段落にまとめる。 */
    .help-modal-body .area-munis { margin: 0 0 4px; }
    /* 確認モーダルの二択ボタン。破壊的な側 (.danger) を赤系にして取り違えを防ぐ。 */
    .help-modal-body .confirm-actions {
      display: flex;
      justify-content: flex-end;
      gap: 8px;
      margin-top: 14px;
    }
    .help-modal-body .confirm-actions button { flex: 0 0 auto; }
    button.secondary.danger { color: var(--bad); border-color: var(--bad); }

    .empty { color: var(--muted); font-size: 13px; padding: 8px 2px; }
    /* 一覧のページャ (2 ページ以上あるときだけ出る)。件数が多いと番号が並びきらないので、
       現在ページの前後だけを出して離れたところは省略記号にする (中身は jobList が作る)。 */
    .pager {
      display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
      gap: 4px; padding: 12px 2px 2px;
    }
    .pager button {
      width: auto; min-width: 34px; padding: 5px 8px;
      background: transparent; border: 1px solid var(--border); color: var(--fg);
      font-size: 13px; font-weight: 500; font-variant-numeric: tabular-nums;
    }
    .pager button[aria-current="page"] {
      background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 700;
    }
    .pager .pager-gap { color: var(--muted); font-size: 13px; padding: 0 2px; }
    .pager .pager-status { color: var(--muted); font-size: 12px; width: 100%; text-align: center; }

    .job {
      display: grid;
      grid-template-columns: 56px 1fr auto;
      gap: 12px;
      align-items: center;
      padding: 10px;
      background: var(--card2);
      border-radius: 10px;
      margin-bottom: 8px;
      cursor: pointer;
    }
    /* 詳細部分はテキスト読みやすさのためデフォルトカーソルに戻す (ボタンは button 既定で pointer) */
    .job .detail { cursor: auto; }
    .job:last-child { margin-bottom: 0; }
    .job img {
      width: 56px; height: 56px;
      object-fit: cover;
      border-radius: 8px;
      background: var(--card);
      border: 1px solid var(--border);
    }
    .job .noimg {
      width: 56px; height: 56px;
      border-radius: 8px;
      background: var(--card);
      border: 1px solid var(--border);
      display: flex; align-items: center; justify-content: center;
      font-size: 11px; color: var(--muted);
    }
    .job .info { min-width: 0; }
    .job .name { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .job .sub { font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    /* 時刻 + ステータスバッジの行。バッジは省略させず、はみ出すなら時刻側を縮める。
       (縦持ちの狭い幅でバッジが "…" に切れていたのを防ぐ) */
    .job .sub.statusline {
      display: flex;
      align-items: center;
      gap: 6px;
      overflow: visible;
      text-overflow: clip;
      white-space: normal;
    }
    .job .sub.statusline .when {
      min-width: 0;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .job .sub.statusline .badge { flex: 0 0 auto; }
    /* 位置合わせ品質が低い done ジョブに出す警告アイコン (タップで注意ポップアップ)。 */
    .job .sub.statusline .warn-badge {
      flex: 0 0 auto;
      cursor: pointer;
      font-size: 13px;
      line-height: 1;
      /* クリック可能であることが分かるようリンク風の下線を添える。 */
      text-decoration: underline dotted;
      text-underline-offset: 3px;
    }
    .badge {
      display: inline-block;
      padding: 2px 8px;
      border-radius: 999px;
      font-size: 11px;
      font-weight: 600;
      letter-spacing: 0.03em;
      vertical-align: 1px;
    }
    .badge.queued  { background: rgba(251,191,36,.15); color: var(--warn); }
    .badge.running { background: rgba(106,169,255,.15); color: var(--accent); }
    .badge.done    { background: rgba(74,222,128,.15); color: var(--good); }
    .badge.failed  { background: rgba(248,113,113,.15); color: var(--bad); }
    .badge.unknown { background: rgba(152,160,173,.15); color: var(--muted); }
    /* 位置合わせ失敗 = 探索は最後まで走ったが、写真と CG が重ならなかった。結果を出せない
       という意味では「失敗」と同じ重さなので、--bad から色相だけを橙へ寄せた同系色
       (--bad-soft) を当てる。並んでも見分けはつくが、赤系の一味だとは伝わる濃さ。
       背景の淡い網はダーク基準の rgba 固定で、他のバッジと同じ流儀にしてある。 */
    .badge.alignfail { background: rgba(247,138,96,.15); color: var(--bad-soft); }
    /* running バッジには小さな回転スピナーを差し込む (計算中であることが一目で分かるように) */
    .badge.running::before {
      content: "";
      display: inline-block;
      width: 8px;
      height: 8px;
      border-radius: 50%;
      border: 1.5px solid currentColor;
      border-right-color: transparent;
      margin-right: 5px;
      vertical-align: -1px;
      animation: badge-spin 0.9s linear infinite;
    }
    @keyframes badge-spin {
      to { transform: rotate(360deg); }
    }

    .toggle {
      cursor: pointer;
      user-select: none;
      padding: 2px 6px;
      color: var(--muted);
      font-size: 12px;
    }
    /* 行の右端の縦並び: 上に統計パネルの開閉キャレット (▾/▴, debug=stats のみ)、下に削除ボタン。
       .job は align-items:center なのでセルを stretch させ、削除は margin-top:auto で
       下端に落とす (キャレットが無い通常モードでも右下に来る)。 */
    .job .row-actions {
      align-self: stretch;
      display: flex;
      flex-direction: column;
      align-items: flex-end;
      justify-content: flex-start;
      gap: 2px;
    }
    /* ⋯ (名前を変更 / 削除) は破壊的操作を含むので目立たせない。存在は分かるが視線を奪わない
       濃度にし、実際の確認はモーダルで取る。button の既定 (width:100%/青背景) を打ち消す。
       文字は小さくても **タップ領域は 28px 角**を確保する (指で押せる最小限)。 */
    .job .row-actions .job-menu-btn {
      margin-top: auto;
      width: auto;
      min-width: 28px;
      height: 28px;
      padding: 0 4px;
      border: 0;
      background: transparent;
      color: var(--muted);
      font-size: 16px;
      line-height: 1;
      opacity: 0.6;
      cursor: pointer;
    }
    .job .row-actions .job-menu-btn:hover,
    .job .row-actions .job-menu-btn:focus-visible { opacity: 0.9; }

    /* ⋯ メニュー本体。**body 直下**に置かれ (一覧の再描画で消えないため)、jobMenu.js が
       アンカーの位置に合わせて left/top をページ座標で指定する。トーストより手前に出す。 */
    .job-menu {
      position: absolute;
      min-width: 148px;
      padding: 4px;
      background: var(--card);
      border: 1px solid var(--border);
      border-radius: 10px;
      box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
      /* トーストと同じ「行に寄せて浮かべる body 直下のレイヤ」なので z-index も揃える。
         モーダル (60) より手前だが、メニューは項目を選んだ時点で必ず閉じてから
         モーダルを開く (jobMenu.js) ので、両方が同時に出ることはない。 */
      z-index: 1000;
    }
    .job-menu .job-menu-item {
      display: block;
      width: 100%;
      padding: 9px 12px;
      border: 0;
      border-radius: 7px;
      background: transparent;
      color: var(--fg);
      font-size: 13px;
      line-height: 1.2;
      text-align: left;
      cursor: pointer;
    }
    .job-menu .job-menu-item:hover,
    .job-menu .job-menu-item:focus-visible { background: var(--card2); }
    .job-menu .job-menu-item.danger { color: var(--bad); }

    /* 名前変更モーダルの入力欄。font-size は 16px 未満にしない — iOS Safari が
       フォーカス時にページを自動ズームしてしまう。 */
    .help-modal-body .rename-input {
      width: 100%;
      box-sizing: border-box;
      padding: 9px 11px;
      background: var(--card2);
      border: 1px solid var(--border);
      border-radius: 8px;
      color: var(--fg);
      font-size: 16px;
      line-height: 1.4;
    }
    .help-modal-body .rename-input:focus {
      outline: none;
      border-color: var(--accent);
    }
    .detail {
      grid-column: 1 / -1;
      padding: 8px 10px 0;
      font-size: 12px;
      color: var(--muted);
      border-top: 1px solid var(--border);
      margin-top: 8px;
    }
    .detail dl { display: grid; grid-template-columns: 110px 1fr; row-gap: 4px; column-gap: 8px; margin: 6px 0; }
    .detail dt { color: var(--muted); }
    .detail dd { margin: 0; color: var(--fg); word-break: break-all; font-variant-numeric: tabular-nums; }
    .detail dd a { color: var(--accent); text-decoration: none; }
    .detail dd a:hover { text-decoration: underline; }
    .detail .actions { display: flex; gap: 8px; margin: 10px 0 4px; justify-content: flex-end; }
    .upload-status { margin-top: 10px; font-size: 13px; color: var(--muted); }
    .upload-status.err { color: var(--bad); }
    .progress {
      height: 4px;
      background: var(--card2);
      border-radius: 2px;
      overflow: hidden;
      margin-top: 8px;
      display: none;
    }
    .progress > div {
      height: 100%;
      background: var(--accent);
      width: 0%;
      transition: width .1s linear;
    }
    .footer { text-align: center; color: var(--muted); font-size: 11px; margin-top: 24px; }
    /* デプロイ中フロントのコミットハッシュ (?debug=stats のときだけ表示)。一番目立たないよう
       極小・極薄のモノスペースでフッター末尾にひっそり置く。 */
    .footer .version-tag {
      display: block;
      margin-top: 8px;
      font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
      font-size: 9px;
      opacity: 0.3;
      letter-spacing: 0.02em;
    }
    /* ─── データ出典 (集約クレジット) ───────────────────────────────
       各ライセンス (PLATEAU=CC BY / OSM=ODbL / Wikidata=CC0 / 国土地理院) は
       レコード単位ではなく集約 1 箇所での表示を認めるため、データが見える各文脈
       (フッター / 詳細モーダル / プリント版) に同じ 1 行を常時出す。文言は
       js/credits.js が single source として流し込む。 */
    .credits {
      text-align: center;
      color: var(--muted);
      font-size: 10px;
      line-height: 1.6;
      margin: 20px auto 0;
      max-width: 640px;
      padding: 0 12px;
    }
    .credits a { color: inherit; text-decoration: underline; }
    .credits-info-btn {
      /* 基底の button{width:100%} を打ち消し、テキスト末尾に収まる小さな
         インライン要素にする (これを消すと ⓘ が全幅ブロック化して改行落ちする)。 */
      display: inline;
      width: auto;
      background: transparent;
      border: none;
      color: var(--accent);
      cursor: pointer;
      font-size: inherit;
      padding: 0 2px;
      line-height: 1;
      vertical-align: baseline;
      /* クリック可能であることが分かるようリンク風の点線下線を添える (警告マークと同様)。 */
      text-decoration: underline dotted;
      text-underline-offset: 2px;
    }
    /* 詳細モーダル内: 建物リストの下に、薄い区切り付きで常時表示。 */
    .credits-modal-line {
      /* 水平マージンは auto を保つ (`.credits` の `20px auto 0` を丸ごと上書きしない)。
         下の `.credits-print-line` とまったく同じ理由で、`margin: 0` にすると
         `max-width: 640px` の枠が親の左端に張り付き、枠内で中央寄せされたテキストが
         画面中央からずれる。`.modal` は `position: fixed; inset: 0` なのでこの器は
         ビューポート全幅になり、640px を超える横持ち / PC でだけ症状が出る
         (縦持ちは枠が全幅になるので水平マージンが効かず、気づけない)。 */
      margin: 0 auto;
      padding: 8px 12px;
      border-top: 1px solid var(--border);
      background: var(--card);
    }
    /* プリント版: 用紙に焼き込む (印刷成果物自体に出典を残す)。 */
    .credits-print-line {
      /* 水平マージンは auto を保つ (.credits の 20px auto 0 を丸ごと上書きしない)。
         上書きで auto を落とすと、シート幅が max-width:640px を超える横持ち/横長用紙で
         640px 上限のクレジット枠が左端に張り付き、中央寄せテキストがシート中央からずれる。 */
      margin: 8px auto 0;
      color: #555;
      /* 建物インデックス本文 (pi-list = 6 × --pi-fs 1.2 = 7.2u) と同径。固定 px だと幅追従の
         本文が縮む狭い画面で出典だけ相対的に大きく見えるため、同じ --u に載せて常に一致させる
         (フッター内なので --u は .print-footer から継承。単体使用時の保険で fallback を置く)。 */
      font-size: calc(7.2 * var(--u, 0.62px));
      line-height: 1.4;
    }
    /* ダイアログ内のライセンス箇条書き (.help-modal のスタイルを流用)。 */
    .help-modal-body ul.credits-list { margin: 8px 0 0; padding-left: 1.2em; }
    .help-modal-body ul.credits-list li { margin: 6px 0; }

    .debug-banner {
      background: #3a2a00; color: #ffd479; border: 1px solid #6a4a00;
      border-radius: 8px; padding: 8px 12px; margin: 0 0 12px;
      font-size: 12px; line-height: 1.5;
    }
    .debug-banner code { background: rgba(0,0,0,0.3); padding: 1px 5px; border-radius: 3px; }
    .job .src-tag { font-size: 10px; opacity: 0.6; margin-left: 4px; }

    /* ─── 詳細モーダル (写真ビュー + 建物ピック) ─────────────────────── */
    .modal {
      position: fixed;
      inset: 0;
      background: var(--bg);
      display: none;
      flex-direction: column;
      z-index: 50;
    }
    .modal.open { display: flex; }
    .modal-header {
      flex: 0 0 auto;
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 10px 12px;
      padding-top: max(10px, env(safe-area-inset-top));
      background: var(--card);
      border-bottom: 1px solid var(--border);
    }
    .modal-header .title {
      flex: 1;
      font-weight: 500;
      font-size: 14px;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .modal-header .close, .modal-header .reset {
      background: transparent;
      border: 1px solid var(--border);
      color: var(--fg);
      border-radius: 8px;
      font-weight: 500;
      width: auto;
    }
    /* モーダルの閉じるボタンは「閉じる」の文字ではなく × のアイコンボタンにする
       (読み上げ用の名前は aria-label が、ホバー時の説明は title が持つ)。ヘッダに並ぶ
       ↻ (.reset) も同じ寸法にして見た目を揃える。タップ標的は 32px 角を確保する。
       ※ 個別の .close / .reset 規則と同詳細度なので、必ずそれらより後に置くこと。 */
    .help-modal-header .close,
    .modal-header .close,
    .modal-header .reset {
      flex: 0 0 auto;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 32px;
      height: 32px;
      padding: 0;
      font-size: 20px;
      line-height: 1;
    }
    .modal-viewer {
      flex: 1 1 auto;
      overflow: hidden;                 /* pan / pinch は JS 側で transform 経由で行う */
      touch-action: none;               /* ブラウザの page-level pinch-zoom を抑止 */
      position: relative;
      background: #000;
      overscroll-behavior: contain;
    }
    .modal-viewer .photo-stage {
      position: absolute;               /* 左上原点 (overflow が無いので translate で動かす) */
      top: 0; left: 0;
      display: inline-block;
      transform-origin: 0 0;
      /* will-change は iOS でタブメモリ上限を超えやすいので、ジェスチャ中だけ JS で
         photoStageEl.style.willChange を切り替える (普段は auto)。 */
    }
    .modal-viewer img.photo {
      display: block;
      /* 版面は photo-stage (JS が natural を STAGE_LAYOUT_MAX_PX で頭打ちにした値を指定) に
         従わせる。natural のまま組むとジェスチャ中の合成レイヤの裏面が「版面 × デバイス倍率」
         で確保され iOS でタブが落ち、逆に幅 fit まで縮めると拡大時に粗くなる
         (detailModal.js の STAGE_LAYOUT_MAX_PX のコメント参照)。 */
      width: 100%;
      height: 100%;
      cursor: crosshair;
      user-select: none;
      -webkit-user-drag: none;
      pointer-events: auto;
    }
    .modal-viewer canvas.overlay {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      pointer-events: none;   /* タップは photo にスルーする */
      /* building map PNG (長辺 1024。BUILDING_MAP_LONG_EDGE を renderer の
         MAX_RENDER_WIDTH/HEIGHT=1280x720 でクランプするので 16:9 は 1024x576、
         4:3 は 960x720) を photo-stage の版面 (幅 fit) へストレッチするときの平滑。
         silhouette は柔らかい縁の方が見映えが良いので auto。pixelated にすると
         ピクセル境界がカクつく代わりに正確になる。 */
      image-rendering: auto;
    }
    .modal-viewer .pin {
      position: absolute;
      width: 10px; height: 10px;
      border-radius: 50%;
      border: 1px solid #fff;
      background: rgba(106, 169, 255, 0.7);
      transform: translate(-50%, -50%);
      box-shadow: 0 0 0 1px rgba(0,0,0,0.5);
      pointer-events: none;
      display: none;
    }
    .modal-viewer .pin.show { display: block; }
    /* マーカー移動直後の注意喚起アニメ。pin 本体の transform は JS (applyStageTransform)
       が毎フレーム上書きするので、独立した ::after 疑似要素に「拡がって消えるリング」を
       出す。pin の counter-scale を継承するので拡大率に依らず一定サイズで見える。
       .pulse はマーカーを置くたびに JS 側で付け直して再生する。 */
    .modal-viewer .pin::after {
      content: "";
      position: absolute;
      left: 50%; top: 50%;
      width: 10px; height: 10px;
      border-radius: 50%;
      border: 2px solid rgba(106, 169, 255, 0.95);
      transform: translate(-50%, -50%) scale(1);
      opacity: 0;
      pointer-events: none;
    }
    .modal-viewer .pin.pulse::after {
      animation: pin-ping 1.2s ease-out 2;
    }
    @keyframes pin-ping {
      0%   { transform: translate(-50%, -50%) scale(1);   opacity: 0.95; }
      100% { transform: translate(-50%, -50%) scale(4);   opacity: 0; }
    }
    @media (prefers-reduced-motion: reduce) {
      .modal-viewer .pin.pulse::after { animation: none; }
    }
    /* ─── 選択された建物のフキダシ (写真の上に乗る情報表示) ─────────────
       従来はビュー下部の .modal-popup に出していた「タップした建物の情報」を、
       その建物の近くに吹き出しとして重ねる。photo-stage の子にすると写真の
       transform で一緒に動く代わりに拡縮も食らう (= 毎フレーム counter-scale と
       再レイアウトが要る) ので、viewer 直下に置いて JS が viewer 座標で配置する。
       写真の pan/zoom 中はアンカーが動くので隠し、終わったら置き直して戻す。
       z-index は photo/overlay (無指定) より上、ヘッダ (10) と
       読み込みオーバーレイ (.modal-loading) より下。 */
    .modal-viewer .pick-balloon {
      position: absolute;
      left: 0; top: 0;
      z-index: 6;
      max-width: min(78%, 320px);
      box-sizing: border-box;
      /* 右は × ボタンのぶんだけ余分に空ける (本文と重ならないように)。 */
      padding: 8px 30px 8px 11px;
      border-radius: 10px;
      /* 色はテーマ変数 (--card / --fg) ではなく白地 + 黒文字に固定する。写真の上に
         乗る要素なので、ダークテーマでも暗くしてはいけない (ヘッダのコントロールや
         プリント版の紙面と同じ扱い)。 */
      background: rgba(255, 255, 255, 0.94);
      border: 1px solid rgba(0,0,0,0.12);
      backdrop-filter: blur(3px);
      -webkit-backdrop-filter: blur(3px);
      color: #111;
      font-size: 14px;
      line-height: 1.4;
      box-shadow: 0 4px 16px rgba(0,0,0,0.35);
      /* 戻すときだけフェードさせる (隠す側は .panning が transition を切る)。 */
      transition: opacity 140ms ease;
      opacity: 1;
      /* フキダシは写真のタップを遮らない。選択建物には被らない位置に出るが、その裏には
         別の建物が居るので、遮ると「フキダシの下の建物はタップできない」ことになる。
         操作が要る子要素 (× / コピーボタン / リンク) だけ auto に戻す。 */
      pointer-events: none;
    }
    .modal-viewer .pick-balloon .pb-close,
    .modal-viewer .pick-balloon button,
    .modal-viewer .pick-balloon a { pointer-events: auto; }
    .modal-viewer .pick-balloon[hidden] { display: none; }
    /* pan / pinch / ホイールズーム中、およびプログラム的パンアニメ中は隠す。
       アンカーが viewer の外に出たときも同じクラスで隠す (指す先が無いため)。 */
    .modal-viewer .pick-balloon.panning {
      opacity: 0;
      transition: none;
      pointer-events: none;
    }
    /* 右上の × (フキダシだけを閉じる)。文字サイズは小さいが、タップ標的は 24px 角。 */
    .modal-viewer .pick-balloon .pb-close {
      position: absolute;
      top: 0; right: 0;
      width: 24px; height: 24px;
      padding: 0;
      display: flex;
      align-items: center;
      justify-content: center;
      background: transparent;
      border: 0;
      border-radius: 0 10px 0 8px;
      color: #5b6371;
      font-size: 15px;
      line-height: 1;
      cursor: pointer;
    }
    .modal-viewer .pick-balloon .pb-close:hover { color: #111; background: rgba(0,0,0,0.05); }
    /* 中身が長い経路 (地図無効時の dl 表示) でも写真を覆い尽くさないよう頭打ちにする。 */
    .modal-viewer .pick-balloon .pb-body {
      max-height: 42vh;
      overflow-y: auto;
      overscroll-behavior: contain;
    }
    /* **実際に頭打ちに当たったときだけ** (JS が測って付ける) 本文を操作可能にする。
       フキダシ全体は pointer-events: none で「裏の建物をタップできる」性質を保って
       いるので、本文を常時 auto にするとその範囲の建物が選べなくなる。届かない中身が
       あるときに限って、その面積ぶんのタップ透過を手放してスクロールを通す。
       touch-action: 親の .modal-viewer は自前 pinch/pan のため none なので、この
       スクロール容器では縦パンだけ明示的に許可し直す (指でのスクロールに要る)。 */
    .modal-viewer .pick-balloon .pb-body.scrollable {
      pointer-events: auto;
      touch-action: pan-y;
    }
    /* 吹き出しの尻尾。クラス名は**尻尾が向く方向**で、フキダシはその反対側に居る
       (.tail-down = フキダシは指す先の上)。位置は JS が入れる CSS 変数で決める:
       縦向き (down/up) は --tail-x = フキダシ左端からの px、
       横向き (right/left) は --tail-y = フキダシ上端からの px。 */
    .modal-viewer .pick-balloon::after {
      content: "";
      position: absolute;
      width: 0; height: 0;
      border: 7px solid transparent;
    }
    .modal-viewer .pick-balloon.tail-down::after {
      top: 100%;
      left: var(--tail-x, 50%);
      transform: translateX(-50%);
      border-bottom-width: 0;
      border-top-color: rgba(255, 255, 255, 0.94);
    }
    .modal-viewer .pick-balloon.tail-up::after {
      bottom: 100%;
      left: var(--tail-x, 50%);
      transform: translateX(-50%);
      border-top-width: 0;
      border-bottom-color: rgba(255, 255, 255, 0.94);
    }
    .modal-viewer .pick-balloon.tail-right::after {
      left: 100%;
      top: var(--tail-y, 50%);
      transform: translateY(-50%);
      border-right-width: 0;
      border-left-color: rgba(255, 255, 255, 0.94);
    }
    .modal-viewer .pick-balloon.tail-left::after {
      right: 100%;
      top: var(--tail-y, 50%);
      transform: translateY(-50%);
      border-left-width: 0;
      border-right-color: rgba(255, 255, 255, 0.94);
    }
    /* 中身のスタイルは .modal-popup 配下の規則が当たらないので、ここで再定義する
       (マークアップは renderPickPopup が両方の器に対して同じものを出す)。器と同じく
       写真の上に乗るので、色はテーマ変数ではなく白地・黒文字向けに固定する。 */
    .modal-viewer .pick-balloon .pick-compact { word-break: break-word; }
    .modal-viewer .pick-balloon .pick-compact .h {
      margin-left: 8px;
      color: #5b6371;
      font-variant-numeric: tabular-nums;
    }
    .modal-viewer .pick-balloon .pick-compact .meta-copy {
      width: auto;
      margin-left: 8px;
      padding: 0 5px;
      background: rgba(0,0,0,0.04);
      border: 1px solid rgba(0,0,0,0.2);
      border-radius: 4px;
      color: #5b6371;
      font-size: 11px;
      font-weight: 500;
      line-height: 16px;
      height: 18px;
      vertical-align: text-bottom;
    }
    .modal-viewer .pick-balloon .empty { color: #5b6371; font-size: 13px; }
    .modal-viewer .pick-balloon .row {
      display: grid;
      grid-template-columns: 72px 1fr;
      gap: 4px 8px;
      font-size: 12px;
      margin: 0;
    }
    .modal-viewer .pick-balloon .row dt { color: #5b6371; }
    .modal-viewer .pick-balloon .row dd {
      margin: 0;
      word-break: break-all;
      font-variant-numeric: tabular-nums;
    }
    .modal-viewer .pick-balloon .uuid {
      font-family: ui-monospace, "SF Mono", Menlo, monospace;
      font-size: 11px;
    }
    .modal-viewer .pick-balloon a { color: #1565c0; }
    .modal-viewer .pick-balloon .actions {
      display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; margin-top: 8px;
    }
    .modal-viewer .pick-balloon .actions button.secondary {
      flex: 0 0 auto;
      width: auto;
      padding: 3px 8px;
      background: rgba(0,0,0,0.04);
      border: 1px solid rgba(0,0,0,0.2);
      color: #111;
      font-size: 11px;
    }
    /* デバッグ用: 建物 ID マップ全体を写真上に半透明オーバーレイする (色は id をハッシュ)。
       silhouette overlay より下、photo より上に重ねる。 */
    .modal-viewer canvas.debug-overlay {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      pointer-events: none;
      image-rendering: auto;
      display: none;
      opacity: 0.55;
    }
    .modal-viewer canvas.debug-overlay.show { display: block; }
    /* 結果ビューを開いた直後に、ビルマップを一度だけ点灯させてフェードアウトする演出
       (flashBuildingMap)。「写真のどこに建物が乗っているか」を一目で伝えるためのもので、
       🏙️ トグル (.show) とは独立に効く。display は animate できないので block に固定し、
       見え方は opacity だけで作る (山は既定値の 0.55 = トグル ON のときと同じ濃さ)。
       演出が終わったら JS のタイマーが .flash を外し、display:none に戻る
       (アニメーションの fill には頼らない)。 */
    .modal-viewer canvas.debug-overlay.flash {
      display: block;
      opacity: 0;
      animation: debug-overlay-flash 930ms ease-out;
    }
    /* 立ち上がり ~47ms → 山で ~149ms 保持 → 残り ~735ms でフェードアウト。保持を挟むのは
       「点いた」と認識させるため (無いと立ち上がりと下降が繋がって一瞬のちらつきに見える)。
       ease-out が各区間に効くので、下降は最初に大きく落ちて尾を引きながら消える。
       **立ち上がりは伸ばさない** (絶対時間で固定): ここを尺に比例させるとスナップが鈍って
       「点灯」ではなくフェードインになり、演出の性格が変わってしまう。延ばした尺は保持と
       下降に配る。 */
    @keyframes debug-overlay-flash {
      0%        { opacity: 0; }
      5%, 21%   { opacity: 0.55; }
      100%      { opacity: 0; }
    }
    /* アニメーションを避ける設定の利用者には演出を出さない。
       .flash 自体は JS のタイマーが外すので、ここで止めても取り残されない。 */
    @media (prefers-reduced-motion: reduce) {
      .modal-viewer canvas.debug-overlay.flash { animation: none; }
    }
    /* ヘッダの「ID マップ表示」トグル */
    .modal-header .debug-toggle {
      background: transparent;
      border: 1px solid var(--border);
      color: var(--muted);
      padding: 5px 9px;
      border-radius: 8px;
      font-size: 12px;
      font-weight: 500;
      width: auto;
      cursor: pointer;
    }
    .modal-header .debug-toggle.active {
      background: var(--accent);
      color: #fff;
      border-color: var(--accent);
    }
    /* ヘッダの「🖨️ プリント版」ボタン (debug-toggle と同じ見た目・正方形寄り) */
    .modal-header .print-toggle {
      background: transparent;
      border: 1px solid var(--border);
      color: var(--fg);
      padding: 5px 9px;
      border-radius: 8px;
      font-size: 14px;
      line-height: 1;
      width: auto;
      cursor: pointer;
    }
    /* 結果ビュー (#modal) のヘッダは写真の上に直接重ねる: card 背景の白をやめ、
       上端にだけ薄い暗グラデを敷いて (写真は透けたまま) コントロールの可読性を確保する。
       探索ログ (#searchlog-modal) のヘッダは従来どおり card 背景のまま。 */
    #modal .modal-header {
      position: absolute;
      top: 0; left: 0; right: 0;
      z-index: 10;
      background: linear-gradient(to bottom, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.22) 55%, rgba(0,0,0,0) 100%);
      border-bottom: 0;
    }
    /* オーバーレイ上のコントロールは写真の明暗に依らず読めるよう、明色 + 半透明グラス地に固定。 */
    #modal .modal-header .title { color: #fff; }
    #modal .modal-header .close,
    #modal .modal-header .debug-toggle,
    #modal .modal-header .print-toggle,
    #modal .modal-header .mode-toggle {
      background: rgba(0,0,0,0.38);
      border-color: rgba(255,255,255,0.35);
      color: #fff;
      backdrop-filter: blur(2px);
      -webkit-backdrop-filter: blur(2px);
    }
    #modal .modal-header .mode-toggle button { color: rgba(255,255,255,0.85); }
    #modal .modal-header .mode-toggle button.active { background: var(--accent); color: #fff; }
    #modal .modal-header .debug-toggle.active {
      background: var(--accent);
      border-color: var(--accent);
      color: #fff;
    }
    .modal-popup {
      flex: 0 0 auto;
      background: var(--card);
      border-top: 1px solid var(--border);
      padding: 12px 14px max(14px, env(safe-area-inset-bottom));
      max-height: 40vh;
      overflow-y: auto;
    }
    .modal-popup .empty { padding: 6px 0; color: var(--muted); font-size: 13px; }
    .modal-popup .row {
      display: grid;
      grid-template-columns: 90px 1fr;
      gap: 6px 10px;
      font-size: 13px;
    }
    .modal-popup .row dt { color: var(--muted); }
    .modal-popup .row dd { margin: 0; word-break: break-all; font-variant-numeric: tabular-nums; }
    .modal-popup .uuid {
      font-family: ui-monospace, "SF Mono", Menlo, monospace;
      font-size: 12px;
    }
    .modal-popup .actions {
      display: flex; gap: 8px; justify-content: flex-end; margin-top: 8px;
    }
    .modal-popup .actions button.secondary { flex: 0 0 auto; }
    /* ピックされた建物の位置を Google Maps で表示する小窓。
       建物が選ばれた時だけ .show が付いて出現。リストモードでは折りたたまれる。 */
    .modal-popup .gmap {
      display: none;
      margin-top: 10px;
      height: 180px;
      border: 1px solid var(--border);
      border-radius: 6px;
      overflow: hidden;
      background: var(--card2);
    }
    .modal-popup .gmap.show { display: block; }
    .modal-popup .gmap .gmap-canvas { width: 100%; height: 100%; }
    .modal-popup.list-mode > .gmap { display: none; }
    /* 地図が有効な場合は pick-info を 1 行に圧縮し、地図に縦領域を譲る。
       .compact クラスは Google Maps API キーが設定されているとき JS で付ける。 */
    .modal-popup.compact .gmap { height: max(240px, 30vh); margin-top: 6px; }
    .modal-popup.compact .pick-info { padding: 0; }
    .modal-popup .pick-compact {
      font-size: 14px;
      line-height: 1.4;
      padding: 2px 0;
      word-break: break-word;
    }
    .modal-popup .pick-compact .h {
      margin-left: 8px;
      opacity: 0.7;
      font-variant-numeric: tabular-nums;
    }
    /* 1 行表示の右端に置く「生メタをコピー」ミニボタン。
       行の line-height (≈20px) より低く抑え、情報エリアの高さを変えない。 */
    .modal-popup .pick-compact .meta-copy {
      width: auto;
      margin-left: 8px;
      padding: 0 5px;
      background: transparent;
      border: 1px solid var(--border);
      border-radius: 4px;
      color: var(--muted);
      font-size: 11px;
      font-weight: 500;
      line-height: 16px;
      height: 18px;
      vertical-align: text-bottom;
    }
    /* 読み込み中オーバーレイ: 写真全体を半透明で覆い、中央にスピナー + メッセージ。 */
    .modal-loading {
      position: absolute;
      inset: 0;
      display: none;
      align-items: center;
      justify-content: center;
      gap: 10px;
      background: rgba(0,0,0,0.5);
      color: #fff;
      font-size: 13px;
      z-index: 5;                 /* 写真の上・ヘッダ(z:10)の下。閉じる × は上に残る */
    }
    .modal-loading.show { display: flex; }
    /* 読み込み失敗時: スピナーを消して赤字メッセージにする (JS が .error を付与)。 */
    .modal-loading.error .ml-spinner { display: none; }
    .modal-loading.error .ml-text { color: var(--bad); text-align: center; padding: 0 16px; }
    /* くるくる回るスピナー。 */
    .modal-loading .ml-spinner {
      flex: 0 0 auto;
      width: 18px; height: 18px;
      border: 2px solid rgba(255,255,255,0.3);
      border-top-color: #fff;
      border-radius: 50%;
      animation: ml-spin 0.8s linear infinite;
    }
    @keyframes ml-spin { to { transform: rotate(360deg); } }
    @media (prefers-reduced-motion: reduce) {
      .modal-loading .ml-spinner { animation-duration: 2.4s; }
    }
    /* 読み込み中 (写真 + ビル ID マップ完了まで) は結果ビュー内の操作を無効化する。
       閉じる (×) ボタンだけは常に押せるよう対象外。 */
    #modal.loading .modal-viewer,
    #modal.loading .modal-popup { pointer-events: none; }
    #modal.loading .mode-toggle,
    #modal.loading .print-toggle,
    #modal.loading .debug-toggle { pointer-events: none; opacity: 0.45; }

    /* 一時表示トースト。ジョブ行から吹き出しのように出る (left/top は JS でページ座標に合わせる)。
       position: absolute なのでページスクロールに追従する。行の下に収まらなければ .above を
       付けて行の上に出し、矢印の向きを反転する。 */
    .toast {
      position: absolute;
      max-width: min(80vw, 320px);
      background: rgba(20, 20, 24, 0.95);
      color: #fff;
      padding: 9px 14px;
      border-radius: 10px;
      font-size: 13px;
      line-height: 1.45;
      box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
      opacity: 0;
      pointer-events: none;
      transition: opacity 0.18s ease, transform 0.18s ease;
      z-index: 1000;
      text-align: center;
      white-space: pre-wrap;
      word-break: break-word;
      transform: translate(-50%, -4px);
    }
    .toast.show { opacity: 1; transform: translate(-50%, 0); }
    /* 吹き出し内に添える情報アイコン (タップで詳細モーダルを開く)。トースト自体は
       pointer-events:none なので、このボタンだけ pointer-events:auto で受け取る。 */
    .toast .toast-info {
      /* クレジットの ⓘ と同じ「枠なしグリフ + 点線下線」方式に揃える。円枠の中に文字を
         入れると下線が枠と干渉して見えないため、グリフ自体に下線を引く。 */
      pointer-events: auto;
      display: inline;
      width: auto;
      margin-left: 5px;
      padding: 0 1px;
      border: none;
      background: transparent;
      color: inherit;
      font-size: inherit;
      line-height: 1;
      vertical-align: baseline;
      cursor: pointer;
      text-decoration: underline dotted;
      text-underline-offset: 2px;
    }
    /* 吹き出しの矢印。既定 (行の下に出る) は上向き、.above (行の上に出る) は下向き。 */
    .toast::before {
      content: "";
      position: absolute;
      left: 50%;
      bottom: 100%;
      margin-left: -7px;
      border: 7px solid transparent;
      border-bottom-color: rgba(20, 20, 24, 0.95);
    }
    .toast.above::before {
      bottom: auto;
      top: 100%;
      border-bottom-color: transparent;
      border-top-color: rgba(20, 20, 24, 0.95);
    }

    /* ─── 詳細モーダルのリストモード ─────────────────── */
    .mode-toggle {
      display: inline-flex;
      gap: 0;
      border: 1px solid var(--border);
      border-radius: 8px;
      overflow: hidden;
    }
    .mode-toggle button {
      width: auto;
      padding: 5px 9px;
      background: transparent;
      color: var(--muted);
      font-size: 12px;
      font-weight: 500;
      border: 0;
      border-radius: 0;
    }
    .mode-toggle button.active {
      background: var(--accent);
      color: #fff;
    }
    /* 「📋 リスト」への案内フキダシ。名前の出ない建物を続けてタップしている人にだけ出す
       (listModeHint.js)。ヘッダ (#modal .modal-header は position:absolute) を基準に
       その真下へ垂らし、尻尾で「📋 リスト」ボタンを指す。left と --tail-x は JS が実測
       して入れる (ヘッダの並びは題名の長さや ?debug=stats の有無で変わるため)。
       色は写真の上に乗る他のフキダシ (.pick-balloon) と同じく白地・黒文字に固定する。 */
    #modal .modal-header .mode-hint {
      position: absolute;
      top: 100%;
      left: 0;                      /* JS が実値に差し替える */
      margin-top: 6px;
      z-index: 1;                   /* ヘッダ内での重なり (ヘッダ自体が z-index:10) */
      max-width: min(78vw, 300px);
      box-sizing: border-box;
      padding: 8px 30px 8px 11px;   /* 右は × のぶんだけ空ける */
      border-radius: 10px;
      background: rgba(255, 255, 255, 0.96);
      border: 1px solid rgba(0,0,0,0.12);
      backdrop-filter: blur(3px);
      -webkit-backdrop-filter: blur(3px);
      color: #111;
      box-shadow: 0 4px 16px rgba(0,0,0,0.35);
    }
    #modal .modal-header .mode-hint[hidden] { display: none; }
    /* 尻尾 (上向き = フキダシは指す先の下)。 */
    #modal .modal-header .mode-hint::after {
      content: "";
      position: absolute;
      bottom: 100%;
      left: var(--tail-x, 50%);
      transform: translateX(-50%);
      width: 0; height: 0;
      border: 7px solid transparent;
      border-top-width: 0;
      border-bottom-color: rgba(255, 255, 255, 0.96);
    }
    /* 本文はボタン: タップでそのままリストへ切り替える (救済の最短経路)。見た目は文字のまま。 */
    #modal .modal-header .mode-hint .mh-body {
      width: 100%;
      padding: 0;
      background: transparent;
      border: 0;
      border-radius: 0;
      color: #111;
      font-size: 13px;
      font-weight: 400;
      line-height: 1.45;
      text-align: left;
      cursor: pointer;
    }
    #modal .modal-header .mode-hint .mh-body b { font-weight: 600; white-space: nowrap; }
    /* 文節の塊。inline-block にすると塊の内部では折り返さないので、境目でだけ改行する。 */
    #modal .modal-header .mode-hint .mh-body .mh-seg { display: inline-block; }
    /* 右上の × (フキダシを閉じるだけ)。文字は小さいがタップ標的は 24px 角。 */
    #modal .modal-header .mode-hint .mh-close {
      position: absolute;
      top: 0; right: 0;
      width: 24px; height: 24px;
      padding: 0;
      display: flex;
      align-items: center;
      justify-content: center;
      background: transparent;
      border: 0;
      border-radius: 0 10px 0 8px;
      color: #5b6371;
      font-size: 15px;
      line-height: 1;
      cursor: pointer;
    }
    #modal .modal-header .mode-hint .mh-close:hover { color: #111; background: rgba(0,0,0,0.05); }
    .building-list {
      display: none;
    }
    .building-list.show { display: block; }
    .building-list .summary {
      font-size: 12px;
      color: var(--muted);
      margin-bottom: 8px;
    }
    .building-list .summary .more {
      background: transparent;
      border: 1px solid var(--border);
      color: var(--fg);
      padding: 2px 8px;
      border-radius: 999px;
      font-size: 11px;
      width: auto;
      margin-left: 8px;
    }
    .building-list ul {
      list-style: none;
      padding: 0;
      margin: 0;
    }
    .building-list li {
      display: grid;
      grid-template-columns: 24px 1fr auto;
      gap: 8px;
      align-items: center;
      padding: 6px 4px;
      border-bottom: 1px solid var(--border);
      cursor: pointer;
      transition: background 0.1s;
    }
    .building-list li:last-child { border-bottom: 0; }
    .building-list li:hover { background: var(--card2); }
    .building-list li.selected {
      background: var(--card2);
      border-left: 3px solid var(--accent);
      padding-left: 1px;
    }
    .building-list li .swatch {
      width: 18px;
      height: 18px;
      border-radius: 4px;
      background: rgba(255, 165, 0, 0.45);
      border: 1px solid rgba(255, 165, 0, 0.9);
    }
    .building-list li .lbl {
      font-family: ui-monospace, "SF Mono", Menlo, monospace;
      font-size: 11px;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
      color: var(--fg);
    }
    .building-list li .lbl .sub { color: var(--muted); font-family: inherit; font-size: 10px; }
    /* sub 行の区切り。中黒は使わず margin だけで間隔をあける。 */
    .building-list li .lbl .sub .sub-sep { margin: 0 4px; }
    .building-list li .share {
      font-size: 11px;
      color: var(--muted);
      font-variant-numeric: tabular-nums;
      text-align: right;
    }
    /* 占有ピクセル数 (割合の下に小さく添える) */
    .building-list li .share .px { font-size: 9px; opacity: 0.7; }
    /* リストモードのとき、popup の "tapped building info" は折りたたむ */
    .modal-popup.list-mode > .pick-info { display: none; }
    .modal-popup .pick-info { display: block; }

    /* ─── 探索ログビューア ─────────────────────── */
    .sl-body {
      flex: 1 1 auto;
      overflow-y: auto;
      padding: 12px 12px max(16px, env(safe-area-inset-bottom));
      background: var(--bg);
    }
    .sl-ref { display: flex; gap: 10px; align-items: flex-start; margin-bottom: 14px; font-size: 12px; color: var(--muted); }
    .sl-ref img { width: 120px; height: auto; border-radius: 8px; border: 1px solid var(--border); background: #000; flex: 0 0 auto; }
    .sl-ref code { background: var(--card2); padding: 1px 5px; border-radius: 3px; font-size: 11px; }
    .sl-iter { margin-bottom: 20px; }
    .sl-iter h3 { font-size: 13px; margin: 0 0 8px; color: var(--fg); }
    .sl-iter h3 .stage { color: var(--muted); font-weight: 400; margin-left: 8px; font-size: 12px; }
    .sl-iter h3 .sl-csv-btn {
      width: auto; margin-left: 10px; padding: 3px 9px;
      font-size: 11px; font-weight: 500; vertical-align: 1px;
    }
    .sl-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; border: 1px solid var(--border); border-radius: 10px; }
    .sl-table { width: 100%; border-collapse: collapse; font-size: 12px; }
    .sl-table th, .sl-table td { border-bottom: 1px solid var(--border); padding: 7px 8px; text-align: left; vertical-align: top; }
    .sl-table tr:last-child td { border-bottom: 0; }
    .sl-table th {
      color: var(--muted); font-weight: 600; font-size: 11px;
      text-transform: uppercase; letter-spacing: 0.04em; white-space: nowrap;
      background: var(--card2); position: sticky; top: 0; z-index: 1;
    }
    .sl-table td.num { font-variant-numeric: tabular-nums; white-space: nowrap; }
    .sl-table img.cg {
      width: 130px; height: auto; border-radius: 6px;
      border: 1px solid var(--border); background: #000; display: block; cursor: zoom-in;
    }
    .sl-table .noimg {
      width: 130px; height: 74px; border-radius: 6px; border: 1px dashed var(--border);
      display: flex; align-items: center; justify-content: center; color: var(--muted); font-size: 10px;
    }
    .sl-table tr.is-best td { background: rgba(74, 222, 128, 0.10); }
    .sl-best { color: var(--good); font-weight: 600; }
    .sl-pose { font-variant-numeric: tabular-nums; line-height: 1.55; white-space: nowrap; font-size: 11px; }
    .sl-pose .k { color: var(--muted); }
    .sl-pose a { color: var(--accent); text-decoration: none; white-space: nowrap; }
    .sl-pose a:hover { text-decoration: underline; }
    .sl-err { color: var(--bad); white-space: normal; }
    .sl-cand { font-weight: 600; }
    .sl-cand .lbl { display: block; color: var(--muted); font-weight: 400; font-size: 10px; margin-top: 2px; }

    /* ─── プリント版 (建物名オーバーレイ) ───────────────────────────── */
    /* 結果ビューの「🖨️」から開く全画面オーバーレイ。写真の上に、名称が判明した
       建物の名称を引き出し線つきラベルで重ねる。印刷 (window.print) にも対応。 */
    .print-modal {
      display: none;
      position: fixed;
      inset: 0;
      flex-direction: column;
      z-index: 60;                 /* 詳細モーダル(50)より前面 */
      background: #fff;
      color: #111;
    }
    .print-modal.open { display: flex; }
    .print-toolbar {
      flex: 0 0 auto;
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 10px 12px;
      padding-top: max(10px, env(safe-area-inset-top));
      background: var(--card);
      color: var(--fg);
      border-bottom: 1px solid var(--border);
      /* シート (ズーム/パンする .print-scroll) より上に載せて、ツールバーが
         スクロール内容に潜らないようにする。 */
      position: relative;
      z-index: 10;
    }
    /* タイトルは余白を吸って伸びず、割く横幅を絞る (上限 30%、はみ出しは … で省略)。
       余白は下の mode-toggle の margin-left:auto が吸うので、他コントロールは従来どおり右寄せ。 */
    .print-toolbar .print-title {
      flex: 0 1 auto;
      min-width: 0;
      max-width: 30%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      font-weight: 600;
      font-size: 14px;
    }
    .print-toolbar .print-mode-toggle { margin-left: auto; }
    .print-toolbar .print-actions { display: flex; gap: 8px; }
    /* コントロール群は縮まない (= スペース不足はタイトルが先に … 省略で吸う)。
       これでボタン文字が 2 行に折り返すことがない。 */
    .print-toolbar .print-mode-toggle,
    .print-toolbar .print-group-toggle,
    .print-toolbar .print-actions { flex-shrink: 0; }
    .print-toolbar button {
      background: transparent;
      border: 1px solid var(--border);
      color: var(--fg);
      padding: 6px 12px;
      border-radius: 8px;
      font-size: 13px;
      font-weight: 500;
      width: auto;
      cursor: pointer;
      white-space: nowrap;   /* ボタン文字は常に 1 行。縮小はタイトル側に集約する */
    }
    .print-toolbar .print-do {
      background: var(--accent);
      border-color: var(--accent);
      color: #fff;
    }
    /* アイコンのみのアクションボタン (印刷 🖨️ / 閉じる ✕) は省スペースにコンパクト化。 */
    .print-toolbar .print-actions button {
      padding: 5px 9px;
      font-size: 15px;
      line-height: 1;
    }
    /* ズーム/パンの viewport。ネイティブのピンチ (iOS の visual-viewport ズーム) は
       大きな写真 + 多数の絶対配置ラベルと組み合わさるとピンチ中のリレイアウトで
       タブが落ちることがあるため、touch-action:none で OS デフォルトを止め、内側の
       .print-zoom を CSS transform で自前にズーム/パンする (合成レイヤ操作=リレイアウト無し)。 */
    .print-scroll {
      flex: 1 1 auto;
      position: relative;
      overflow: hidden;
      touch-action: none;
      overscroll-behavior: contain;
      background: #f3f4f6;
    }
    /* transform 対象。幅は JS が viewport 幅に合わせ、translate/scale でズーム/パンする。 */
    .print-zoom {
      position: absolute;
      top: 0; left: 0;
      transform-origin: 0 0;
      box-sizing: border-box;
      padding: 16px;
      display: flex;
      justify-content: center;
      align-items: flex-start;
    }
    .print-sheet {
      background: #fff;
      box-shadow: 0 1px 6px rgba(0,0,0,0.18);
      padding: 12px;
      max-width: 100%;
    }
    /* stage は写真と同じ寸法の relative box。ラベル/線/マーカーを px で重ねる。 */
    .print-stage {
      position: relative;
      display: inline-block;
      line-height: 0;
      max-width: 100%;
    }
    .print-stage img.print-photo {
      display: block;
      max-width: 100%;
      height: auto;
    }
    .print-stage svg.print-lines {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      pointer-events: none;
      overflow: visible;
    }
    .print-stage .print-labels {
      position: absolute;
      inset: 0;
      pointer-events: none;
      line-height: 1.2;
      /* ラベル/番号のフォントを cqw (= このボックス=写真の幅比) で指定できるようにする。
         写真サイズが変わる回転・印刷でも文字サイズが追従する (px だとズレる)。 */
      container-type: inline-size;
    }
    /* 個々の名称ラベル。重心の真上に、左 90° 回転 (下→上に読む) で置き、JS が縦方向の
       重なりを解消する。背景は持たず、白フチ (text-shadow) で写真の上でも読めるようにする。
       left/top には「回転後の見かけ box の中心」を置く前提 (translate(-50%,-50%))。 */
    .print-stage .print-label {
      position: absolute;
      transform: translate(-50%, -50%) rotate(-90deg);
      transform-origin: center center;
      color: #ff1744;
      padding: 0;
      /* 実サイズは JS (layoutNamesOverlay) が NAMES_FONT_FRAC × 写真幅 で上書きする。
         ここの値は JS 未実行時のフォールバックで、基準幅 (写真ボックス 334px) 相当。 */
      font-size: 3.6px;
      font-weight: 700;
      white-space: nowrap;
      /* 白フチは em 指定 (= 文字サイズ比)。px だと写真幅に応じて文字が拡縮したときに
         フチの太さだけ取り残される。値は基準幅の 0.25px / 0.5px を 3.6px で割った比。 */
      text-shadow:
        -0.069em -0.069em 0 #fff,  0.069em -0.069em 0 #fff,
        -0.069em  0.069em 0 #fff,  0.069em  0.069em 0 #fff,
         0 0 0.139em #fff;
    }
    .print-empty {
      margin-top: 12px;
      color: var(--muted);
      font-size: 13px;
      text-align: center;
    }
    /* ツールバーの表示モード切替 (名称 / 番号)。 */
    .print-toolbar .print-mode-toggle {
      display: flex;
      border: 1px solid var(--border);
      border-radius: 8px;
      overflow: hidden;
    }
    .print-toolbar .print-mode-toggle button {
      width: auto;
      background: var(--card2);
      color: var(--muted);
      border: 0;
      border-radius: 0;
      padding: 6px 12px;
      font-size: 13px;
      font-weight: 600;
      cursor: pointer;
    }
    .print-toolbar .print-mode-toggle button + button { border-left: 1px solid var(--border); }
    .print-toolbar .print-mode-toggle button.active { background: var(--accent); color: #fff; }
    /* ツールバーのトグルボタン (現在は「輪郭」のみ)。押下中は accent 反転。 */
    .print-toolbar .print-group-toggle {
      width: auto;
      background: var(--card2);
      color: var(--muted);
      border: 1px solid var(--border);
      border-radius: 8px;
      padding: 6px 12px;
      font-size: 13px;
      font-weight: 600;
      cursor: pointer;
    }
    .print-toolbar .print-group-toggle.active { background: var(--accent); color: #fff; border-color: var(--accent); }
    /* 番号モード: 建物中心に置く番号。文字サイズは JS が占有形状に応じて設定する。
       白フチ (em 基準なのでサイズに比例) で写真上でも読めるようにする。シートは白地
       固定なので色は固定値を使う。 */
    .print-stage .print-number {
      position: absolute;
      transform: translate(-50%, -50%);
      color: #ff1744;
      font-weight: 800;
      line-height: 1;
      white-space: nowrap;
      /* 2 桁以上で間延びしないよう字間を詰める (em 指定なのでサイズに比例)。対応フォント
         では少し縦長 (condensed) にもする。box 幅は JS が offsetWidth で測るので衝突判定にも反映される。 */
      letter-spacing: -0.1em;
      font-stretch: 80%;
      text-shadow:
        -0.03em -0.03em 0 #fff,  0.03em -0.03em 0 #fff,
        -0.03em  0.03em 0 #fff,  0.03em  0.03em 0 #fff,
         0 0 0.02em #fff;
    }
    /* 写真外のインデックスコーナー (番号→建物名)。シートは白地固定なので文字色は固定。 */
    .print-index {
      /* インデックスを幅追従 (cqw) のサイズコンテナにする。通常フローのブロックなので
         幅は用紙 (= シート) 幅いっぱいに定まり (内容非依存)、size containment を付けても潰れない
         (shrink-to-fit の .print-sheet に付けると 0 幅に潰れるため、コンテナはこの要素に置く)。
         子孫 (pi-title/pi-list/li/pi-num/section) の全サイズを、この幅に対する相対単位 --u で
         決めるので、用紙幅が何であっても列数 (4 固定) を保ったままインデックスが相似で拡縮し、
         比率・余白が崩れない。--pi-ref は「この幅 [px] のとき下の係数がそのまま px 相当になる」
         基準幅で A4 縦の印刷可能幅の目安。今の A4 での見た目を微調整したいときはこの 1 値だけ
         触ればよい (画面プレビューではシート幅 = viewport 幅なので、狭い端末ほど縮んだ原寸大
         サムネイルになる。ピンチズームで拡大して読める / 印刷結果とレイアウトが一致する)。
         自要素の margin/padding は cqw が祖先コンテナ基準で誤解決するため px 据え置き
         (ここは区切り上の余白なので幅追従不要)。 */
      container-type: inline-size;
      --pi-ref: 720;
      --u: calc(100cqw / var(--pi-ref));
      /* インデックス全体のフォント倍率 (余白・段組はそのままに文字だけ拡縮する 1 ノブ)。 */
      --pi-fs: 1.2;
      /* 写真との区切り線は置かず、余白 (padding) だけで間隔を確保する。 */
      margin-top: 12px;
      padding-top: 10px;
    }
    .print-index[hidden] { display: none; }
    /* インデックスのフォントは名称モードより小さめ (従来の半分) にして紙面を圧迫しない。 */
    .print-index .pi-title { font-weight: 700; font-size: calc(6.5 * var(--pi-fs) * var(--u)); margin-bottom: calc(5 * var(--u)); color: #222; }
    .print-index .pi-list {
      list-style: none;
      margin: 0;
      padding: 0;
      /* 段組は CSS grid で固定段数にする。CSS multicolumn (column-count) は WebKit/Safari で
         印刷時に 1 段へ潰れる既知の不具合があり、grid は画面・印刷・全ブラウザで指定どおりの
         段数になる。段数は用紙/画面の向きによらず 4 段固定とし、狭い紙・広い紙のどちらでも
         「4 段のまま相似で拡縮」させる (フォント/gap は --u で用紙幅に追従。§.print-index)。
         minmax(0,1fr) で長い建物名が列幅を押し広げてはみ出すのを防ぐ (列内で折り返す)。
         (multicolumn の縦流し順と異なり grid は行方向=左→右の順に番号が並ぶ。) */
      display: grid;
      grid-template-columns: repeat(4, minmax(0, 1fr));
      column-gap: calc(12 * var(--u));
      font-size: calc(6 * var(--pi-fs) * var(--u));
    }
    .print-index .pi-list li {
      break-inside: avoid;
      display: flex;
      gap: calc(4 * var(--u));
      align-items: baseline;
      padding: calc(1 * var(--u)) 0;
    }
    .print-index .pi-num {
      flex: 0 0 auto;
      min-width: 1.5em;
      text-align: center;
      background: #ff1744;
      color: #fff;
      border-radius: calc(2 * var(--u));
      font-weight: 700;
      font-size: calc(5.5 * var(--pi-fs) * var(--u));
      /* line-height は継承 (~1.45) のままにする。この余裕のある行ボックスが数字グリフを
         包むので、僅かな padding でも数字が赤い角丸 box を上下にはみ出さない (元からの挙動)。
         line-height:1 にすると行ボックスがグリフとほぼ同高に潰れ、行の絶対 Y のサブピクセル
         位相しだいで数字が上下にはみ出して見えたため、指定しない。 */
      padding: calc(0.5 * var(--u)) calc(2 * var(--u));
    }
    .print-index .pi-name { color: #222; }
    /* 高さ/距離 (🗼/🚶) は補助情報なので、建物名より小さく・薄く・細くして目立たせない。 */
    .print-index .pi-meta { color: #aaa; font-weight: 400; font-size: 0.8em; }
    /* 自治体セクション (「自治体で分ける」ON 時)。見出しは各段組の上に全幅で置く。 */
    .print-index .pi-section { break-inside: auto; }
    .print-index .pi-section + .pi-section { margin-top: calc(8 * var(--u)); }
    .print-index .pi-section-title {
      font-weight: 700;
      font-size: calc(6.5 * var(--pi-fs) * var(--u));
      color: #222;
      border-bottom: 0.5px solid #ccc;
      margin-bottom: calc(3 * var(--u));
      padding-bottom: calc(1 * var(--u));
      break-after: avoid;   /* 見出しだけ先に改ページ/改段されないように */
    }

    /* ─── 紙面フッター (ブランド署名 + データ出典) ───────────────────
       用紙下部に「ミハラシスコープ」ロゴ + 名称を発行元マークとして置き、その下に出典を出す。
       写真が主役の座を保ったまま、印刷物の定石であるフッター署名でブランドを添える。
       サイズは建物インデックスと同じ幅追従単位 --u に載せ、用紙幅に応じて相似で拡縮する。
       --u の cqw は「祖先のサイズコンテナ」基準で解くので、このフッター自身を container に
       する (.print-index と同じ理由。shrink-to-fit でない通常フローのブロックなので潰れない)。 */
    .print-footer {
      container-type: inline-size;
      --pi-ref: 720;
      --u: calc(100cqw / var(--pi-ref));
      /* インデックスとの区切り線は置かず、余白 (margin/padding) だけで間隔を確保する。 */
      margin-top: calc(12 * var(--u));
      padding-top: calc(8 * var(--u));
    }
    .print-brand {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: calc(9 * var(--u));
    }
    .print-brand-mark {
      flex: 0 0 auto;
      display: block;
      width: calc(36 * var(--u));
      height: calc(36 * var(--u));
    }
    .print-brand-ja {
      font-weight: 700;
      color: #234b78;              /* ロゴの藍色に合わせる */
      font-size: calc(16.5 * var(--u));
      line-height: 1.1;
      letter-spacing: 0.02em;
    }
    .print-brand-en {
      color: #7a869a;
      font-size: calc(8.25 * var(--u));
      letter-spacing: 0.08em;
    }
    /* フッター内の出典は、ブランドの下に区切り無しで続ける (フッター自身が border-top を持つ)。 */
    .print-footer .credits-print-line { margin-top: calc(5 * var(--u)); }

    /* 印刷時: ツールバーや背景クロームを消し、用紙にプリント版だけを出す。 */
    @media print {
      /* main を隠すのは「プリント版モーダルだけを刷る」ためなので、モーダルを持つアプリ本体
         (index.html) に限る。静的な文書ページ (faq / terms / privacy / tutorial) は本文
         そのものが印刷対象で、除外しないと用紙が白紙になる (body.doc-page で判別する)。
         :has() で「プリント版モーダルを持つ body」を条件にする書き方もあるが、未対応
         ブラウザではセレクタごと無効になり本体側で main が刷り込まれてしまうので採らない。 */
      body:not(.doc-page) > main,
      .modal,
      .help-modal,
      .toast { display: none !important; }
      /* 文書ページの紙面。紙は白いので、ダーク配色の端末でそのまま刷ると薄いグレーの文字が
         白地に乗って読めない。印刷時だけ配色トークンを紙用に固定する (画面表示は不変)。
         値はライト配色のものを踏襲し、地だけ card / card2 とも白に倒す。 */
      body.doc-page {
        --bg: #fff;
        --card: #fff;
        --card2: #fff;
        --fg: #1a1d24;
        --muted: #444;
        --border: #bbb;
        --accent: #2563eb;
      }
      /* 背景色は既定で印刷されないため、白抜き文字は紙の上で消える。ステップ番号は線画に
         置き換えて残し、紙では押せない CTA ボタンは省く (同じ導線はフッターにもある)。 */
      body.doc-page .tut-num {
        background: none;
        color: #1a1d24;
        border: 1px solid #1a1d24;
      }
      body.doc-page .tut-cta { display: none !important; }
      .print-modal { position: static; background: #fff; }
      .print-modal:not(.open) { display: none !important; }
      .print-toolbar { display: none !important; }
      .print-scroll { overflow: visible; background: #fff; padding: 0; display: block; }
      /* 画面用のズーム transform / 固定幅を打ち消し、用紙幅に素直に流す。 */
      .print-zoom {
        position: static;
        transform: none !important;
        width: auto !important;
        padding: 0;
        display: block;
      }
      .print-sheet { box-shadow: none; padding: 0; max-width: none; }
      .print-stage .print-label {
        /* 回転・白フチ・赤字はそのまま。色を確実に出すため color-adjust を exact に。 */
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
      }
      .print-stage .print-number,
      .print-index .pi-num,
      /* pi-meta (薄グレーの高さ/距離) は exact を付けないと WebKit の印刷 economy が
         「白に近い薄グレー」を白へ寄せて消してしまう。pi-name も含め indexの文字色を
         そのまま印刷させる (赤背景の番号と同じ扱い)。 */
      .print-index .pi-name,
      .print-index .pi-meta,
      .print-index .pi-section-title,
      /* フッターのブランド (藍色の名称 + 薄グレーの英字) も economy 補正で色が飛ばないよう exact に。
         ロゴは <img>(SVG) なので既定で色が出るが、テキスト 2 行はここで担保する。 */
      .print-brand-ja,
      .print-brand-en {
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
      }
      /* インデックスの段数 (4 固定) とサイズ (--u で用紙幅追従) は画面・印刷共通なので、
         ここでの上書きは不要 (§.print-index / .pi-list)。 */
      /* 用紙のクレジットは残す。ⓘ ボタンは紙では無意味なので消す。 */
      .credits-info-btn { display: none !important; }
      .credits-print-line {
        color: #444;
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
      }
    }

    /* ─── 認証バー (Google ログイン) ─────────────────────────────────── */
    .auth-bar {
      display: flex;
      justify-content: flex-end;
      align-items: center;
      margin: -4px 0 14px;
      min-height: 40px;
    }
    .auth-bar .auth-guest {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: 8px 12px;
      justify-content: flex-end;
    }
    .auth-bar .auth-note {
      color: var(--muted);
      font-size: 13px;
    }
    .auth-bar .auth-note-error { color: var(--bad); }
    /* ログイン中バッジ: アイコンのみ。押すとメニューを展開する。 */
    .auth-bar .auth-user { position: relative; display: flex; align-items: center; }
    .auth-bar .auth-avatar-btn {
      display: block;
      padding: 0;
      border: none;
      background: none;
      cursor: pointer;
      border-radius: 50%;
      line-height: 0;
    }
    .auth-bar .auth-avatar-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    .auth-bar .auth-avatar {
      width: 30px; height: 30px;
      border-radius: 50%;
      object-fit: cover;
      border: 1px solid var(--border);
    }
    .auth-bar .auth-avatar-fallback {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      background: var(--accent);
      color: #fff;
      font-size: 14px;
      font-weight: 700;
    }
    /* 未ログイン時のデフォルト人型アイコン。 */
    .auth-bar .auth-avatar-default {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      background: var(--card2);
      color: var(--muted);
    }
    /* アイコン下に開くコンテキストメニュー。 */
    .auth-bar .auth-menu {
      position: absolute;
      top: calc(100% + 6px);
      right: 0;
      min-width: 200px;
      max-width: 78vw;
      background: var(--card);
      border: 1px solid var(--border);
      border-radius: 10px;
      box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
      padding: 8px;
      z-index: 50;
    }
    .auth-bar .auth-menu-email {
      color: var(--muted);
      font-size: 12px;
      padding: 4px 8px 8px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      border-bottom: 1px solid var(--border);
      margin-bottom: 6px;
    }
    .auth-bar .auth-menu-item {
      display: block;
      width: 100%;
      text-align: left;
      background: none;
      color: var(--fg);
      border: none;
      border-radius: 6px;
      padding: 8px;
      font-size: 14px;
      cursor: pointer;
    }
    .auth-bar .auth-menu-item:hover { background: var(--card2); }
    /* 未ログインメニュー: Google ログインボタンを内包する。 */
    .auth-bar .auth-menu .auth-signin { display: flex; justify-content: center; padding: 2px; }
    .auth-bar .auth-menu-note { font-size: 12px; padding: 2px 4px 8px; }
    /* ログイン要求モーダル内の Google ログインボタンの間隔 */
    .login-required-signin { margin: 14px 0; display: flex; justify-content: center; }

    /* ─── よくあるご質問ページ (faq.html) ──────────────────────────
       メインページと同じトークン・.card を使い回す静的ページ専用の追加分。
       JS は credits.js (出典 1 行) だけで、開閉や取得の類は一切持たない。 */
    /* ロゴをトップへの戻り導線にする。faq / terms / privacy にしか無いセレクタなので、
       ここに置いた寸法はサブページのヘッダーだけに効く (トップの 52px は変えない)。
       サブページは本文を読ませる画面なので、ロゴは見出しに添える程度に小さくする。 */
    .brand-home { display: flex; flex: 0 0 auto; }
    .brand-home .brand-mark {
      width: 34px;
      height: 34px;
      border-radius: 8px;
      padding: 2px;
    }
    .faq-lead {
      color: var(--muted);
      font-size: 13px;
      line-height: 1.7;
      margin: 0 0 14px;
    }
    /* 目次。カテゴリ数が少ないので折り返し可のチップ列で置く。 */
    .faq-toc {
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
      margin-bottom: 14px;
    }
    .faq-toc a {
      background: var(--card2);
      border: 1px solid var(--border);
      border-radius: 999px;
      padding: 6px 12px;
      font-size: 12px;
      color: var(--fg);
      text-decoration: none;
    }
    .faq-toc a:hover { border-color: var(--accent); color: var(--accent); }
    /* 1 問 1 答。設問間は罫線で区切る (アコーディオンにはしない: 折りたたむと
       ブラウザのページ内検索から本文が漏れ、印刷でも答えが出ないため)。 */
    .faq-item + .faq-item {
      margin-top: 14px;
      padding-top: 14px;
      border-top: 1px solid var(--border);
    }
    .faq-q {
      display: flex;
      gap: 8px;
      align-items: baseline;
      font-size: 14px;
      font-weight: 700;
      line-height: 1.5;
      margin: 0 0 8px;
    }
    .faq-q .q-mark {
      flex: 0 0 auto;
      color: var(--accent);
      font-size: 13px;
      font-weight: 800;
    }
    .faq-a { font-size: 13px; line-height: 1.75; }
    .faq-a p { margin: 0 0 8px; }
    .faq-a p:last-child { margin-bottom: 0; }
    .faq-a ul { margin: 0 0 8px; padding-left: 1.3em; }
    .faq-a li { margin: 4px 0; }
    .faq-a .faq-note { color: var(--muted); font-size: 12px; line-height: 1.6; }
    /* FAQ に埋め込む対応エリア一覧 (js/coverageArea.js が流し込む)。都道府県見出し +
       自治体名の中黒連結という中身はモーダル版と同じなので、体裁もそちらに揃える
       (.help-modal-body の指定はこのページには効かないため、ここで同等のものを置く)。 */
    .faq-a .coverage-areas { margin: 0 0 8px; }
    .faq-a .coverage-areas h4 { margin: 10px 0 4px; font-size: 13px; }
    .faq-a .coverage-areas h4:first-child { margin-top: 0; }
    .faq-a .coverage-areas .area-munis { margin: 0 0 4px; }

    /* ─── 規約類の条文ページ (terms.html) ──────────────────────────
       faq.html と同じ .card / .faq-toc を使い回し、条文の体裁だけ足す。 */
    .doc-lead {
      color: var(--muted);
      font-size: 13px;
      line-height: 1.7;
      margin: 0 0 14px;
    }
    .doc-article + .doc-article {
      margin-top: 14px;
      padding-top: 14px;
      border-top: 1px solid var(--border);
    }
    .doc-article h3 {
      font-size: 14px;
      font-weight: 700;
      line-height: 1.5;
      margin: 0 0 8px;
    }
    .doc-article p { font-size: 13px; line-height: 1.75; margin: 0 0 8px; }
    .doc-article p:last-child { margin-bottom: 0; }
    /* 各号 = 1. 2. 3.、その下位 = 中黒。号番号は引用されるので必ず見えるようにする。 */
    .doc-items {
      font-size: 13px;
      line-height: 1.75;
      margin: 0;
      padding-left: 1.5em;
    }
    .doc-items > li { margin: 6px 0; }
    .doc-sub {
      margin: 4px 0 4px;
      padding-left: 1.1em;
      list-style: disc;
    }
    .doc-sub > li { margin: 3px 0; }
    /* 連絡先だけは行として独立させ、コピーしやすいように少し目立たせる。 */
    .doc-contact {
      margin: 6px 0 0;
      padding: 8px 10px;
      background: var(--card2);
      border: 1px solid var(--border);
      border-radius: 8px;
      font-size: 13px;
      word-break: break-all;
    }
    .doc-meta {
      margin-top: 16px;
      padding-top: 12px;
      border-top: 1px solid var(--border);
      color: var(--muted);
      font-size: 12px;
      text-align: right;
    }
    /* メインページからチュートリアルへの導線。アップロードカードの直前に置く 1 行。
       .card より薄い地 (card2) + アクセント色の左縁で「操作するカードではなく案内」
       だと分かる見た目にし、高さはカードを押し下げないよう 1 行ぶんに抑える。 */
    .tut-banner {
      display: flex;
      align-items: center;
      gap: 8px;
      margin-bottom: 12px;
      padding: 9px 12px;
      background: var(--card2);
      border: 1px solid var(--border);
      border-left: 3px solid var(--accent);
      border-radius: 10px;
      color: var(--fg);
      font-size: 13px;
      line-height: 1.4;
      text-decoration: none;
    }
    .tut-banner:hover { border-color: var(--accent); }
    .tut-banner .tb-icon { flex: 0 0 auto; font-size: 15px; line-height: 1; }
    /* 本文は伸ばして矢印を右端に寄せる。「はじめての方へ」の後ろだけ間隔を空ける。 */
    .tut-banner .tb-text { flex: 1 1 auto; min-width: 0; }
    .tut-banner .tb-text b { margin-right: 6px; }
    /* 2 つの句はそれぞれ塊で折り返す (nowrap)。CJK は任意の字間で折れるので、狭い端末
       (320px) では「使い方を 3 ステッ / プで見る」のように語中で割れてしまう。句の境界
       だけを改行位置に許せば、折り返しても「はじめての方へ / 使い方を 3 ステップで見る」
       の 2 行に収まる (どちらの句も 320px の残り幅に収まる長さにしてある)。 */
    .tut-banner .tb-text b,
    .tut-banner .tb-text .tb-sub { white-space: nowrap; }
    /* 説明句は「はじめての方へ」より一段小さく添える (主役はラベル側)。 */
    .tut-banner .tb-text .tb-sub { font-size: 11px; }
    .tut-banner .tb-arrow { flex: 0 0 auto; color: var(--accent); font-weight: 700; }

    /* ─── チュートリアル (tutorial.html) ───────────────────────────
       faq / terms が「読ませる」ページなのに対し、こちらは「3 歩で終わると一目で伝える」
       ページ。同じ .card を土台に、番号バッジと大きめの見出しだけを足す。本文は 1〜2 行に
       抑える前提の寸法にしてあるので、長文を入れるならこの余白から見直す。 */
    .tut-hero {
      font-size: 17px;
      line-height: 1.7;
      margin: 0 0 18px;
    }
    .tut-hero b { color: var(--accent); }
    .tut-steps { display: flex; flex-direction: column; }
    /* 各ステップ。カード自身の margin-bottom は矢印との間隔に置き換える。 */
    .tut-step { margin-bottom: 0; padding: 16px 14px; }
    .tut-head {
      display: flex;
      align-items: center;
      gap: 10px;
      margin-bottom: 8px;
    }
    /* 番号は装飾 (HTML 側で aria-hidden)。順序自体は 3 枚のカードの並びが表している。 */
    .tut-num {
      flex: 0 0 auto;
      display: grid;
      place-items: center;
      width: 30px;
      height: 30px;
      border-radius: 999px;
      background: var(--accent);
      color: #fff;
      font-size: 15px;
      font-weight: 800;
      line-height: 1;
    }
    /* .card h2 の uppercase / muted / 14px を、見出しとして読ませる寸法に上書きする。 */
    .tut-step h2 {
      display: flex;
      align-items: center;
      flex-wrap: wrap;
      gap: 8px;
      margin: 0;
      color: var(--fg);
      font-size: 18px;
      font-weight: 800;
      line-height: 1.3;
      letter-spacing: 0.02em;
      text-transform: none;
    }
    .tut-emoji { font-size: 20px; line-height: 1; }
    /* 所要時間バッジ。見出しの一部だが、数字だけ拾い読みできるよう別の地にする。 */
    .tut-time {
      background: var(--card2);
      border: 1px solid var(--border);
      border-radius: 999px;
      padding: 3px 9px;
      color: var(--muted);
      font-size: 12px;
      font-weight: 600;
      letter-spacing: 0;
    }
    .tut-lead { font-size: 14px; line-height: 1.7; margin: 0; }
    .tut-note {
      color: var(--muted);
      font-size: 12px;
      line-height: 1.65;
      margin: 8px 0 0;
    }
    .tut-note a { color: var(--accent); }
    /* ステップ間の下向き矢印。カード同士が「順を追う 3 歩」だと一目で分かるようにする。 */
    .tut-arrow {
      color: var(--border);
      font-size: 13px;
      line-height: 1;
      text-align: center;
      padding: 8px 0;
    }
    /* 結果 (ゴール) のカードだけアクセント色の縁で締める。 */
    .tut-step-goal { border-color: var(--accent); }
    .tut-ways {
      margin: 10px 0 0;
      padding: 0;
      list-style: none;
      font-size: 13px;
      line-height: 1.6;
    }
    .tut-ways li {
      background: var(--card2);
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: 8px 10px;
    }
    .tut-ways li + li { margin-top: 6px; }
    /* 「さっそく試す」導線。3 ステップを読み終えた直後に置く唯一のボタン。 */
    .tut-cta { margin: 16px 0 18px; text-align: center; }
    .tut-cta a {
      display: inline-block;
      padding: 12px 22px;
      border-radius: 999px;
      background: var(--accent);
      color: #fff;
      font-size: 15px;
      font-weight: 700;
      text-decoration: none;
    }
    .tut-tips {
      margin: 0;
      padding-left: 1.3em;
      font-size: 13px;
      line-height: 1.7;
    }
    .tut-tips li { margin: 5px 0; }

    .footer a { color: inherit; text-decoration: underline; }
    /* メインページのフッターから Q&A ページへの導線 (本文とは 1 行空ける)。 */
    .footer .footer-links { margin-top: 8px; }

    [hidden] { display: none !important; }
