/*
* このファイルは application.css にまとめてコンパイルされるCSSの本体
* Propshaft（Railsの新しいアセット管理）で配信される
* 下に書いたCSSほど優先度が高くなる（上書き）
*/

/* =========================================
box-sizing の全体リセット
width/height に padding と border を含める。これが無いと
`width:100%` + padding の要素が親より padding ぶん太って横溢れする。
（auth.css / signup.css / password.css が各ページで個別にやっていたのを統合）

注意: このリセットを前提に、固定px の width/height は
「border を含んだ値」を書くこと。

overflow-x: hidden は入れない。入れると溢れが見えなくなって永久に残るし、
profiles/show は body スクロール（min-height:100svh、内側スクローラ無し）なので
将来の position:sticky を壊す。デグレは見えるままにしておく。
========================================= */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  height: 100%;
  -webkit-text-size-adjust: 100%; /* iOS の横向き時の勝手な文字拡大を止める */

  /* =========================================
     画面全体の背景ウォッシュ

     なぜ html に置くか:
       ルート要素の背景は「キャンバス背景」として、どの要素よりも先（下）に描かれる仕様。
       body::before で敷くと z-index の層争いになり、#map-permanent(1)・.search-overlay(10)・
       .bottom-nav(1000)・各モーダル(9000〜) と干渉する（z-index:0 だと本文テキストの上に乗り、
       z-index:-1 だと body 自身の背景の下に沈む）。html なら stacking context も
       containing block も作らないので重なり順は一切変わらない。
       html は height:100% でスクロールしない（スクローラは body）ため、
       background-attachment:fixed なしで画面に貼り付いたまま動かない
       （iOS Safari は attachment:fixed を正しく扱わないので使わないこと）。
       body の background は必ず transparent にする（body の箱＝ほぼ画面サイズなので塗ると隠れる）。

     html / body に filter・backdrop-filter・transform・isolation・opacity<1・contain・z-index を
     追加してはいけない。position:fixed の containing block になって .bottom-nav と
     全モーダル（9000〜10000）が壊れる。

     背景は専用画像 app/assets/images/icons/blush_wall.png の1枚もの。
     以前は radial-gradient を10層重ねて同じ絵を作っていたが、画像に差し替えた。
     色・構図を変えたいときは CSS ではなく画像そのものを差し替える。

     構図（画像側の性質。CSS では制御できない）:
       ピンク = 左上 → 右中 → 右下 ／ イエロー = 右上 → 左中 → 左下 が斜めに交差し、
       その間を乳白色の帯が通る。純白の場所は無く、いちばん淡い所でも色味が残る。

     画像を差し替えるときに守ること:
       ・縦長で用意する。cover なので、画面より横長の画像だと上下が大きく切れる
       ・いちばん淡い部分の色を background-color にも反映する
         （読み込み前の下地と、ラバーバンドで画面外が見えたときに出る色）
       ・文字が直接乗る画面（notes / profiles）があるので、暗い色を入れない。
         現状の最暗部は左上の約 #FDC1D4 で、#111 の文字に対して十分なコントラストがある
  ========================================= */
  /* 画像が読み込まれるまでの下地と、ラバーバンドで画面外が見えたときの色。
     画像のいちばん淡い部分に合わせてある。画像が 404 でもここだけは出る */
  background-color: #fdf8f1;

  /* Propshaft が CSS 内の url() を digest 付きパスへ書き換える。
     実体は app/assets/images/icons/blush_wall.png。
     app/assets/images がロードパスのルートなので、ここでは images/ を含めず
     "icons/blush_wall.png" と論理パスで書く。ファイルを移動したらここも直すこと */
  background-image: url("/assets/icons/blush_wall-798f47e8.png");

  /* cover + center。画像は 941x1672（比 0.563）でモバイル実機（390x844 = 0.462）より
     横長なので、高さを合わせて左右が 9% ずつトリミングされる。
     背景だけのスクロールは起きない（html は height:100% でスクローラは body）ので
     background-attachment は既定の scroll のままでよい。
     fixed にしてはいけない — iOS Safari が正しく扱わず、画像が文書全体のサイズに伸びる。 */
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
}

body {
margin: 0;                /* 画面の外側の余白をすべて消す */
font-family: sans-serif; /* 文字のフォントをゴシック体に統一 */

/* 画面の背景は html（キャンバス背景）が持つ。body の箱は height:100dvh で
   ほぼ画面サイズなので、ここを塗ると html のウォッシュが完全に隠れる。
   transparent のままにすること。

   ここに feTurbulence の粒ノイズを敷いていた時期があるが、
   「紙の繊維・砂粒」に見えて狙っている柔らかいにじみから遠ざかるため撤去した。
   質感は html 側のぼかした色面の重なりだけで作る。復活させないこと。 */
background: transparent;

height: 100vh;           /* 画面の高さを「表示領域ちょうど100%」にする（dvh非対応ブラウザ向けフォールバック） */
height: 100dvh;          /* モバイルSafariのアドレスバー変動に追従する実表示高さ */
overflow: auto;        /* xxx body（画面全体）はスクロールさせない */
}

/* =========================================
ポップアップ共通
========================================= */

/* 一覧・閲覧系のポップアップを画面上部に出すときの、上端からの距離。
   dvh ではなく vh にして、モバイルのアドレスバー伸縮で位置が跳ねないようにする。
   高さ上限側は dvh を使い、実表示領域からはみ出させない。 */
:root {
  --popup-top-offset: 10vh;

  /* 下部ナビは画面下端に浮くピル（フッター固定の全幅バーではない）。
     サイズは2つに分けて持つ:
       --bottom-nav-pill-h … ピル自体の高さ
       --bottom-nav-gap    … 画面下端（セーフエリアの上）とピルの隙間
     --bottom-nav-h は「ナビが占める縦の領域」= ピル高 + 隙間。
     env(safe-area-inset-bottom) は含まない（ピル側の bottom に別途足している）。

     ページ側の下端クリアランスは必ず
       calc(var(--bottom-nav-h) + env(safe-area-inset-bottom, 0px) + 任意の余白)
     で書き、数値を直接埋め込まないこと（ナビの高さを変えたらここだけ直せば全画面に効く）。
     ピル化で占有領域が 56px → 68px に増えたが、全画面がこの変数を見ているので
     クリアランスは自動で追従する。
     低height端末では下の @media (max-height:700px) が内訳ごと差し替える。 */
  --bottom-nav-pill-h: 56px;
  --bottom-nav-gap: 12px;
  --bottom-nav-h: calc(var(--bottom-nav-pill-h) + var(--bottom-nav-gap));
}

/* =========================================
   共通ページシェル
   home.css にしか無かったため、同じマークアップを使う
   notifications#index / announcements#show / post_comments#index /
   user_lists#list で scroll コンテナも下ナビ分の余白も効いていなかった。
   （feed.css は .page の background だけを後勝ちで上書きしている）
========================================= */
.page {
  position: relative;
  min-height: 100vh;   /* dvh非対応ブラウザ向けフォールバック */
  min-height: 100dvh;
  background: transparent;   /* html のウォッシュを透かす。塗ると画面背景が消える */
}

/* スクローラは画面いっぱいに取り、下ナビ分は **padding で** 空ける。

   高さから `- var(--bottom-nav-h)` を引いていた頃は、スクローラの下端が
   ピルの上端とぴたり一致していた。つまり本文がナビの下を一切通らず、
   Home / Feed だけピルの背後が素のウォッシュのまま＝ガラスが何も透かさず、
   最終行の下に何も無い帯が残っていた。
   Note / Profile（body スクロール）や Search（マップ）は元から本文が下を流れるので、
   この2画面だけ見た目が違っていた。

   padding に移すと:
     ・スクロール中は本文がピルの下を通る（ガラス越しにカードが透ける＝他画面と同じ）
     ・スクロール末尾では padding のぶん最終行がナビの上に残る（潜らない）
   余白は必ず `calc(var(--bottom-nav-h) + env(safe-area-inset-bottom, 0px) + 余白)` で書く
   （この式の意味は :root の --bottom-nav-h の節）。 */
.page-scroll {
  height: 100vh;    /* dvh非対応ブラウザ向けフォールバック */
  height: 100dvh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  padding-bottom: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom, 0px) + 16px);
}

/* =========================================
   ヘッダーを固定する縦2段レイアウト（.page--fixed-header）

   ヘッダー（.app-header）を .page-scroll の **外**（.page の直下）に置き、.page を
   縦2段の flex にして「ヘッダー＝固定 ／ .page-scroll＝残り全部」にする。
   .app-header を描く4画面すべてに付けてある
   （Home / Feed / 投稿ビューア / 掲載停止ページ。ERB 側で class に足す）。

   ヘッダーを .page-scroll の中に入れるとスクローラの1行目になり、本文と一緒に上へ
   流れていく（4画面とも元はそうなっていた）。**壊れても DOM には何も出ない**
   （ヘッダーは今日も画面の上に在る）ので、検知は
   test/integration/fixed_header_markup_test.rb と test/system/fixed_header_test.rb だけ。

   **position:fixed / sticky で浮かせない。** どちらも本文がヘッダーの背後を通る作りで、
   そうすると次の2つが付いてくる:
     ・Home の山（.header-curve__fill）は塗りではなく「白を抜いた穴」なので、
       スクロール中のカードが穴から覗く
     ・投稿ビューアのヘッダーは塗りを持たない（ウォッシュがそのまま見える）ので、
       背後を本文が通ると文字が重なって読めなくなる
   スクロール領域ごと分ければどちらも起こらず、「ヘッダーの実測高さを本文の
   padding-top に写す」という同期も要らない。

   セレクタを .page.page--fixed-header（詳細度 0-2-0）にしてあるのは、ページ別CSSの
   `.page`（feed.css / trend.css。0-1-0）に必ず勝たせるため。head への到着順は訪問順で
   変わるのでカスケード順には頼れない（#map-permanent.is-detail-map と同じ事情）。

   overflow:hidden は付けない。縦は flex が収めるので要らず、付けると横溢れまで隠れて
   mobile_layout_test の検知が効かなくなる（冒頭の overflow-x の注記と同じ理由）。
   上端のセーフエリア（iPhone のステータスバー）は各ヘッダーの padding-top が足す
   （白い塗りをステータスバーの下まで伸ばすため、container 側では足さない）。
========================================= */
.page.page--fixed-header {
  display: flex;
  flex-direction: column;
  height: 100vh;    /* dvh非対応ブラウザ向けフォールバック */
  height: 100dvh;
}

.page.page--fixed-header > .app-header {
  flex: 0 0 auto;
}

/* 残りの高さを全部もらう。min-height:0 が無いと中身の高さまで膨らんでスクロールしない。
   height:auto は上の .page-scroll が持つ 100dvh の打ち消し（残すとヘッダーのぶんだけ
   ページが下へ溢れて body 側がスクロールし、ヘッダーが一緒に動く＝直したはずの症状が
   そのまま戻る）。下ナビ分の padding-bottom は上の .page-scroll のものがそのまま効く。 */
.page.page--fixed-header > .page-scroll {
  height: auto;
  flex: 1 1 auto;
  min-height: 0;
}

/* ==========================================
共通カード
   cards.css は Propshaft では @import できない（no-op になる）ため、
   各ビュー・パーシャル側で stylesheet_link_tag "cards" して読み込んでいる。
   ここに @import を書き戻さないこと。
========================================= */

/* =========================================
   評価モーダル（旧スタイル - visit_modal.css に移行済み）
========================================= */


.rating-modal {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: rgba(0, 0, 0, 0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.rating-modal.is-hidden {
  display: none;
}

.rating-sheet {
  width: min(92vw, 420px);
  background: #fff;
  border-radius: 24px;
  padding: 24px 20px 20px;
  box-shadow: 0 14px 40px rgba(0, 0, 0, 0.18);
}

.rating-sheet__header {
  margin-bottom: 20px;
}

.rating-sheet__title {
  margin: 0 0 10px;
  font-size: 22px;
  font-weight: 700;
  line-height: 1.4;
}

.rating-sheet__text {
  margin: 0;
  color: #666;
  font-size: 14px;
  line-height: 1.7;
}

.rating-fields {
  display: grid;
  gap: 18px;
}

.rating-field__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
  font-size: 15px;
  font-weight: 600;
}

.rating-field input[type="range"] {
  width: 100%;
}

.rating-sheet__actions {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-top: 24px;
}

.rating-sheet__cancel,
.rating-sheet__submit {
  border: 0;
  border-radius: 999px;
  padding: 10px 18px;
  cursor: pointer;
  font-size: 14px;
}

.rating-sheet__cancel {
  background: #f1f1f1;
}

.rating-sheet__submit {
  background: #e95b6b;
  color: #fff;
}

.rating-btn {
  min-width: 42px;
  height: 42px;
  border: none;
  background: transparent;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}

/* ★はそのまま */
.rating-star {
  font-size: 20px;
  color: #f5b301; /* ゴールドっぽく */
}

/* =========================================
   評価済み（自分が付けた点数）の塗り

   点数が出るホストは3つだけで、どれも同じ色で塗り直す必要があるので、
   色はここ1箇所で持つ:
     .rate-pill            … カード右下のピル（検索結果・Saved・フォルダ・友人MAP）
     .rd-rate .rating-btn  … 店舗詳細の店名の隣のCTA
     .quick-actions__circle … Home のおすすめ/発見カードの「…」の丸ボタン

   **:root には置かない。** ページ別CSSの :root 禁止（reviewed_restaurants.css /
   trend.css / rank.css の冒頭コメント）と同じ考えで、色トークンはそれを使う
   コンポーネントにスコープする。custom property は下へ継承するので、
   各ホストの子である .rating-value がそのまま引ける。

   .rating-btn を素で書かないこと。`.place-side .rating-btn`（common/_card が
   rate_pill local 無しで描く休眠のホスト）まで巻き込まないよう、店舗詳細側は
   .rd-rate で絞ってある。

   地色は店舗詳細の集計★（detail.css の .rd-score__star = #ffb020）と同系の淡い
   オレンジ。--reviewed-score-ink はその地の上でコントラスト比 3.47:1 なので、
   **数字は必ず 14px / font-weight 700 以上で出すこと**（WCAG の「大きな文字」＝
   14px太字 の 3:1 基準に乗せている。12px のままだと通常文字扱いで 4.5:1 に届かない）。
   通常文字サイズでも AA にしたいなら #ad5500（4.70:1）へ差し替える。 */
.rate-pill,
.rd-rate .rating-btn,
.quick-actions__circle {
  --reviewed-score-bg: #fff3e0;
  --reviewed-score-border: #ffdb9b;
  --reviewed-score-ink: #c86a00;
}

/* 評価済みの点数。アイコン（オレンジの★＋白い縁の緑丸＋白いチェック）は ::before で足す
   —— rating_modal_controller の _updateReviewDisplays が保存後に起点ボタンの innerHTML を
   丸ごと差し替えるので、中に実要素で置くと保存した瞬間に消える
   （cards.css / detail.css の同趣旨のコメントと同じ理由）。

   色は .rating-value への直接の宣言で決めること。親の color の「継承」では、
   要素に直接マッチするこの宣言自身に負ける（詳細度に関係なく）。
   アイコンの実寸は3つのホスト側で width/height を上書きして決める。 */
.rating-value {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  color: var(--reviewed-score-ink, #c86a00);
}

/* 1枚のSVGに★・白い縁・緑丸・チェックを全部入れてある。画像ファイルを足さないのは、
   Propshaft の load path が起動時に固定されるため新しい app/assets/images/<dir> を
   作れないから（app/views/rank/_icon.html.erb の冒頭コメント）。
   data: で始まる url() は Propshaft::Compiler::CssAssetUrls が書き換えないので、
   ダイジェストも付かない（cards.css が白★で同じことをしていた）。

   寸法の根拠（18px で描いたときの実寸）:
     ★   … 既存の polygon を scale(0.88) ＋ 下げて、右上にチェック丸の居場所を作る
     白縁 … r=5.9 の白い円。緑より 1.3 大きい ＝ 約1px の細い縁
     緑丸 … r=4.6 ＝ 直径 6.9px
     チェック … 横 3.9px（丸端を入れて 5.2px）・線幅 1.275px。
              4点すべて緑の円の内側（最遠点＋線幅の半分で 3.87 < 4.6）
   小さすぎたら SVG の緑丸を大きくするのではなく、ホスト側の width/height を上げること
   （緑丸を大きくすると★が削られて星に見えなくなる）。 */
.rating-value::before {
  content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpolygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2' fill='%23ffb020' transform='translate%28-0.4 2.6%29 scale%280.88%29'/%3E%3Ccircle cx='17.4' cy='6.6' r='5.9' fill='%23ffffff'/%3E%3Ccircle cx='17.4' cy='6.6' r='4.6' fill='%2317a34a'/%3E%3Cpolyline points='14.7 6.75 16.6 8.6 19.9 4.9' fill='none' stroke='%23ffffff' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  width: 16px;
  height: 16px;
  flex: none;
}






/* ==========================================
店舗詳細の全画面地図（常駐 Google Map の見え方の切り替え）

   #map-permanent（layouts/application.html.erb の <body> 直下・data-turbo-permanent）は
   検索画面と店舗詳細で共有する唯一の Map インスタンス。詳細で地図を開いているあいだだけ
   JS（search/search_places.js の openDetailMap）が .is-detail-map を付けて全画面に伸ばす。

   なぜ detail.css ではなく、ここ（全画面で読む application.css）に置くか:
     ・共有リンクから詳細画面に直接着地した場合、search.css は読み込まれていない。
       それでも地図は全画面で出る必要がある
     ・search.css と detail.css の head への到着順は訪問順で変わる
       （search→detail では detail.css が後、detail→search では search.css が後）。
       だからカスケード順では勝敗が決まらない

   search.css の `#map-permanent`（結果シート上端に下端を合わせる height:calc）に必ず
   勝たせる必要があるので、id+class（詳細度 1-1-0）で書いている。
   このセレクタを `#map-permanent` だけに書き換えないこと（1-0-0 で引き分けになり、
   訪問順によって地図が全画面にならなくなる）。

   --map-bottom-offset は意図的に読まない。あれは検索の結果シート用に JS が実測して
   <html> に書く値で、詳細画面に遷移しても残っている。参照すると直前の検索のシート高さ
   のぶんだけ地図が縮む。

   display は showMap() がインラインスタイルで持つので、ここで !important を使わないこと。

   重なり順: .rd-panel(1) / .rd-actionbar(50) / .lightbox(999)
             < 地図(1100) < 地図のバー .rd-map(1101) < 各モーダル(9000〜)
========================================= */

/* 常駐 Map の上のジェスチャは全部 Google に渡す（ブラウザ自身のパンを断つ）。

   Google が地図の下端に描く帰属表示 —— 左下の Google ロゴ（maps.google.com へのリンク）と
   右下の「キーボード ショートカット」「地図データ ©… Google」「利用規約」（.gm-style-cc）——
   は、Google の操作レイヤー（.gm-style の最初の子）の**外**に居る兄弟なので、そこから
   始めた縦スワイプは Google のリスナーに一切届かない。そして Google はコンテナに
   touch-action を書かず、地図本体のドラッグを touchmove の preventDefault だけで
   止めているため、**帯の上のジェスチャはそのままブラウザのパンになる**。

   文書がスクロールできない /search では、iOS Safari がそれをページのラバーバンドにする。
   position:fixed の #map-permanent と .search-overlay が一緒に動くので、画面の症状は
   「地図の下端の帯をなぞると地図が動く」。search.css の overscroll-behavior では
   止まらない（あれは連鎖を止める指定で、ブラウザ自身のパン／バウンスは断れない。
   ハンドル行・タグバーを touch-action で塞いでいるのと同じ事情）。

   実測（ヘッドレス Chrome ＋ touch エミュレーション。文書をスクロール可能にして
   「ブラウザがパンしようとしたか」を scrollY で可視化した）:
     帯から 150px 上へスワイプ → scrollY 0 → 135。地図の中心は動かない
       （＝Google は受け取っていない。JS 側のガードの有無でも変わらない）
     #map-permanent に touch-action:none → scrollY 0 のまま。地図本体のパンは従来どおり効く
     「利用規約」のタップも従来どおりリンクが発火する（止まるのはパンだけ）

   **帯の要素側に書いても直らない。** .gm-style-cc（div）には効くが、ロゴは
   非置換インライン要素（<a>）なので touch-action は仕様どおり無視される
   （computed が none でも scrollY 0 → 135 のまま）。Google 生成の DOM の display を
   上書きするより、コンテナ1枚で塞ぐほうが構造の変化に強い。

   ここ（全画面で読む application.css）に置く理由は #map-permanent.is-detail-map と同じ ——
   4画面（検索・店舗詳細の全画面地図・友人MAP・フォルダMap）が同じ Map を共有していて、
   ページCSS側に書くと「どの画面から来たか」で読み込みの有無が変わる。地図を出していない
   画面では showMap() の hidden ＝ display:none なのでヒットテストに出ない。

   地図の上ではブラウザのピンチズームも断られるが、gestureHandling:"greedy" なので
   2本指は元から Google が取っている（実質の差は帯の 14px 分だけ）。

   JS 側のガード（search_places.js の wireMapAttributionGuard）とは別経路。あちらは
   「Google のリスナーへ届かせない」ガードで、こちらは「ブラウザ既定のパンを断つ」。
   画面の症状を作っていたのはこちら側。
   検知は test/system/search_sheet_overscroll_test.rb（計算値を直接見る。
   **消しても DOM にも見た目にも何も出ない**）。 */
#map-permanent {
  touch-action: none;
}

#map-permanent.is-detail-map {
  /* !important はサードパーティのインラインスタイルに勝つためで、ここだけの例外。
     Google Maps は new google.maps.Map(el) のときコンテナに
     style="position: relative; overflow: hidden" をインラインで書き込む。
     インラインはスタイルシートに勝つので、素の position:fixed では負けて
     inset が効かず height:auto ＝ 高さ0 になり、地図が見えなくなる。

     検索画面がこれで壊れていないのは、search.css が height を明示していて
     「position:relative のまま通常フローで全幅・全高の箱になる」ため
     （fixed に依存していない）。詳細の全画面表示は inset に依存するので勝たせる必要がある。

     display は showMap() がインラインで持つ（こちらは自分で書いているので
     !important を付けないこと。付けると showMap(false) で隠せなくなる）。 */
  position: fixed !important;
  inset: 0;
  height: auto;   /* search.css の height:calc(100dvh - var(--map-bottom-offset)) を打ち消す */
  z-index: 1100;
}

/* ------------------------------------------
   北へ戻すコンパス（#map-compass）

   要素は layout が描き、配線は search_places.js（showMap が hidden、
   syncCompass が針の角度）。ここ（application.css）に置くのは
   #map-permanent.is-detail-map と同じ理由 —— 4画面が同じ Map を共有していて、
   ページCSS側に書くと「どの画面から来たか」で読み込みの有無が変わる。

   見た目は現在地ボタン（search.css の .geo-btn）と対。**ただし素の .geo-btn を
   写してはいけない** —— あちらは同じファイルの後ろ2箇所で上書きされていて、
   実際の見た目はそこで決まっている:
     search.css:1471  @media (max-width:400px) で 42px に縮む
     search.css:2198  body.searches.index .geo-btn がフロストガラスに差し替える
                      （rgba(255,255,255,0.32) ＋ backdrop-filter。白の不透明ではない）
   冒頭の宣言（48px / #ffffff）だけを写すと「大きさも透明度も違う」になる（実際になった）。
------------------------------------------ */
#map-compass {
  position: fixed;
  /* 「このエリアで検索」（search.css の #area-search-btn-wrap）と同じ高さ。
     あちらと同じ式にしてあるので、検索バーの高さが変わっても2つ同時に追従する。

     --searchbar-bottom-offset … search_places.js が documentElement に書く検索バーの実測下端
     --searchbar-bottom-fallback … search.css の :root にしか無い（JS実行前用）

     **3段目のリテラルを外さないこと。** 店舗詳細の全画面地図や友人MAPは search.css を
     読まないので2段目も未定義で、そこで止めると calc ごと不正になって top の宣言が
     丸ごと落ちる（位置が auto ＝ 画面左上に出る）。値は2段目と同じ式（12 + 50 + safe area）。 */
  top: calc(
    var(--searchbar-bottom-offset,
      var(--searchbar-bottom-fallback, calc(env(safe-area-inset-top, 0px) + 62px))
    ) + 8px
  );
  right: 16px;
  z-index: 20;               /* map(1) < overlay(10) < compass(20) = geo(20) < advanced(100) */

  /* ここから下は現在地ボタンの**実際に効いている値**（＝ body.searches.index .geo-btn の
     フロストガラス）と同じ。片方を触るときは必ず両方そろえること
     （.geo-icon ↔ .map-compass__needle も対）。

     **position: relative を書かないこと。** .geo-btn のガラス指定には入っているが、
     あちらは外側の #geo-btn-wrapper が fixed なので relative で足りる。こちらは
     自分自身が fixed なので、上書きすると通常フローへ戻って画面から消える。

     padding は指定しない —— .geo-btn も指定しておらず、どちらも <button> で
     UA 既定の padding が同じだけ乗っているため。片方だけ 0 にすると中身の寸法がずれる。 */
  width: 48px;
  height: 48px;
  border: none;
  border-radius: 50%;
  isolation: isolate;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.32);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.3),
    0 4px 14px rgba(0, 0, 0, 0.1);
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: background 0.15s ease;
}

/* 狭い画面では現在地ボタンと同じ 42px に縮む（search.css:1471 と同じ境界）。
   セレクタを id で書き、#map-compass の宣言より**後ろ**に置くこと
   （同詳細度は後勝ち。前に置くと縮まない）。 */
@media (max-width: 400px) {
  #map-compass { width: 42px; height: 42px; }
}

/* **このブロックのセレクタはすべて id（#map-compass）で書くこと。**
   位置指定を id に移した結果 display / opacity 等も 1-0-0 になっているので、
   状態側を .map-compass.is-visible（0-2-0）や .map-compass[hidden]（0-1-1）で
   書くと**詳細度で負けて一切効かない**。実際に opacity:0 を id 側に置いたまま
   .is-visible で戻そうとして、コンパスが永久に透明になっていた。

   UA 既定の [hidden]{display:none} も同じ理由で負けるので、ここで明示する
   （これが無いと showMap(false) で消えない）。 */
#map-compass[hidden] { display: none; }

/* 店舗詳細の全画面地図では地図自身が z-index:1100（バーが1101）まで上がるので、
   既定の 20 のままだと地図の下に潜って見えなくなる。#map-compass は layout で
   #map-permanent の直後に置いてあるので隣接兄弟で引ける。 */
#map-permanent.is-detail-map + #map-compass { z-index: 1102; }

/* 押したときは**ガラスを少し明るくするだけ**。#f0f4ff のような不透明な色を入れないこと
   （一瞬だけ白い丸になってガラスが消える）。現在地ボタン側の .geo-btn:active の #f0f4ff は
   search.css:2198 のガラス指定に詳細度で負けていて実際には効いておらず、あちらの押し心地は
   tastyGlassPulse のアニメーション。あの keyframes は search.css にしか無いので、
   4画面で出るコンパスから参照すると検索画面だけで効く＝画面ごとに挙動が変わるため写さない。 */
#map-compass:active { background: rgba(255, 255, 255, 0.46); }

#map-compass .map-compass__needle {
  /* 現在地ボタンの中身（search.css の .geo-icon）と同じ 22px */
  width: 22px;
  height: 22px;
  /* --compass-rot は syncCompass が heading の逆向きで入れる。
     transition を付けないこと —— 指の回転に追従させる値なので、
     補間すると実際の向きから遅れて針だけが滑る。 */
  transform: rotate(var(--compass-rot, 0deg));
}

/* 検索画面だけはコンパスを**現在地ボタンの上**に積む（使い方ガイドの入口と位置を
   入れ替えた。入口側は search.css の #geo-btn-wrapper .guide-icon。片方だけ動かすと
   2つが同じ場所に重なるので、必ず両方そろえること）。
   他の3画面（店舗詳細の全画面地図・友人MAP・フォルダMap）は検索バーの下のまま
   ＝上の #map-compass の top がそのまま効く。
   セレクタは id を含むので上の @media (max-width:400px) の 42px には影響しない
   （あちらは width/height だけ）。 */
body.searches.index #map-compass {
  /* top を auto に戻すのが必須。高さが決まっている要素で top と bottom を両方
     持つと over-constrained で bottom が無視され、入れ替え前の位置に出たままになる。 */
  top: auto;
  /* --geo-btn-top-offset ＝ 画面下端から現在地ボタン上端までの距離。
     search_places.js の syncGeoBtnPosition が結果シートの実測位置から書く
     （コンパスは #geo-btn-wrapper の子ではないので、あのラッパーの bottom には乗れない）。
     +10px は現在地ボタンとの間隔で、入れ替え前に入口が使っていた値と同じ。
     fallback は JS 実行前の1フレーム用で、#geo-btn-wrapper の bottom:44vh
     ＋ 現在地ボタン 48px ＝ ボタン上端の位置。 */
  bottom: calc(var(--geo-btn-top-offset, calc(44vh + 48px)) + 10px);
}

/* 狭い画面では現在地ボタンのラッパーが right:12px に寄る（search.css の
   @media (max-width:400px)）。同じ列に縦に並ぶので、この画面のコンパスも合わせないと
   丸2つの右端が 4px ずれて見える（他の画面は現在地ボタンが無いので 16px のまま）。
   上の #map-compass { right:16px } より後ろに置くこと。 */
@media (max-width: 400px) {
  body.searches.index #map-compass { right: 12px; }
}

/* 現在地ボタンと同じ条件で隠す。あちらは #geo-btn-wrapper の display を JS が
   書き換えているが、コンパスはそのラッパーの外（layout）に居るので自分で引く。
   隠さないと、結果シートが大表示のときに**シートの上に浮いたまま残る**
   （シートは z-index:10、コンパスは 20）。
     .is-sheet-tall    … setSheetState とシートのドラッグ中が body に付ける
     .is-advanced-open … openAdvanced / closeAdvanced が body に付ける（既存）
   body のクラスなので Turbo 遷移では残らない（他の画面のコンパスを消さない）。 */
body.searches.index.is-sheet-tall #map-compass,
body.searches.index.is-advanced-open #map-compass {
  display: none;
}

/* ------------------------------------------
   友人MAP（/feed の「友人MAP」タブ）

   ここに置く理由は .is-detail-map と同じ。/feed は search.css を読まないので
   #map-permanent にサイズ指定が1つも無く（素の div ＝高さ0）、ページCSS側に
   書くと「どの画面から来たか」で勝敗が変わる。
   id+class（詳細度 1-1-0）も同じ理由で必須。

   --map-bottom-offset は読まない（検索の結果シート用の実測値が <html> に残っている）。

   z-index は 0。詳細地図（1100）と違ってこの画面では地図が「背面」で、上に Feed の
   ヘッダー・ボトムシート・下ナビが乗る。

   0 でなければならない理由（1 にしてはいけない）: feed.css の `.page` は
   `isolation: isolate` ＋ `position: relative` を持つスタッキングコンテキストで、
   z-index は auto。#map-permanent は <body> 直下でその .page の「前」の兄弟なので、
   z-index:0（= auto と同じ描画ステップ）ならツリー順で .page が後＝上に描かれる。
   1 にすると地図が正のレイヤーへ上がり、Feed の中身を丸ごと覆う。

   地図にタップを通すのは CSS ではなくこちら側の責務ではない。友人MAPのあいだ
   .page-scroll を pointer-events:none にして、ヘッダーとオーバーレイだけ auto に
   戻す指定が feed.css にある（.search-overlay / .rd-map と同じ考え方）。
------------------------------------------ */
#map-permanent.is-friend-map {
  /* !important の理由は .is-detail-map と同じ（Maps が生成時にコンテナへ
     style="position: relative; overflow: hidden" をインラインで書き込む）。
     display には付けない（showMap() がインラインで持っているため）。 */
  position: fixed !important;
  inset: 0;
  /* 下端はボトムシートの上端。friend_map_controller 経由で search_places.js の
     syncFriendMapHeight() が .fmap-sheet の実測値を書く（JS が動く前は 0）。

     シートに隠れる領域を地図コンテナに含めないのが要点で、これで
     panTo / fitBounds / getBounds のすべてが「見えている範囲」に対して働く
     ＝検索画面（--map-bottom-offset で同じことをしている）と同じ動きになる。
     その変数は**読まない**（検索の結果シート用の実測値が <html> に残っている）。 */
  bottom: var(--fmap-map-bottom, 0px);
  height: auto;
  z-index: 0;
}

/* ==========================================
下のナビゲーション（浮いているピル / Liquid Glass）

   全幅のフッター固定バーではなく、画面下端から --bottom-nav-gap だけ浮かせた
   角丸ピル。背景（html のウォッシュ・マップ・カード）が透けて見えるのが狙いなので
   不透明な白は敷かない（backdrop-filter 非対応ブラウザだけ下の @supports で白に落とす）。

   幅は min(100% - 左右24px, 420px)。left/right:0 ＋ margin:0 auto で中央に寄せる。
   left:50% + transform:translateX(-50%) にしないこと —— transform を持つと
   position:fixed の containing block になり、将来ピルの中に fixed 要素
   （バッジ・ポップオーバー等）を足したときに静かに壊れる。
   overflow:hidden があるのでタブのラベルが溢れてもページの横スクロールにはならない。

   占有する縦の領域は上の --bottom-nav-h（= ピル高 + 隙間）。各画面の下端
   クリアランスはそれを見ているので、ここの数値を直接コピーしないこと。
========================================= */

.bottom-nav {
  position: fixed;
  /* 画面下端から浮かせる。セーフエリア（iPhone のホームインジケータ）はここで足す */
  bottom: calc(var(--bottom-nav-gap) + env(safe-area-inset-bottom, 0px));
  left: 0;
  right: 0;
  margin: 0 auto;
  width: min(calc(100% - 24px), 420px);
  height: var(--bottom-nav-pill-h);
  display: flex;
  align-items: stretch;
  gap: 2px;
  padding: 5px 6px;
  border-radius: 999px;
  z-index: 1000;

  /* ピルの上で始まった縦スワイプをブラウザのパンにしない。
     ナビはスクロールコンテナでもドラッグの起点でもないので、素のままだと
     ジェスチャが素通りして**文書がパンする**。全画面に常駐する position:fixed の
     #map-permanent も一緒に動くため、/search では「ナビを触るとマップがずれる」
     （overscroll-behavior は連鎖を止める指定なので、スクロールしない要素では効かない。
     search.css の .sheet-handle-wrapper と同じ理由で touch-action を使う）。
     none でよいのはピルが横にも縦にもスクロールしないため（タブは flex で等幅に
     収まり、ラベルが溢れても overflow:hidden で切れる）。タップは touch-action の
     影響を受けないので、リンクも :active の演出もそのまま動く。 */
  touch-action: none;

  /* Liquid Glass: 半透明の板 ＋ 背面ぼかし ＋ 縁の光 ＋ 浮遊影。
     不透明な白地（検索結果シート等）の上でもピルの輪郭が読めるように、
     border と外側の影は薄くしすぎない。 */
  background: rgba(255, 255, 255, 0.62);
  backdrop-filter: blur(18px) saturate(180%);
  -webkit-backdrop-filter: blur(18px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.7);
  box-shadow:
    /* 1本目は輪郭のリング。白い border は白地（検索結果シート等）の上で消えるので、
       うっすら暗いリングを重ねて輪郭が残るようにする。外すと白地でピルが溶ける */
    0 0 0 1px rgba(45, 32, 78, 0.07),
    0 10px 30px rgba(45, 32, 78, 0.14),
    0 2px 8px rgba(45, 32, 78, 0.06),
    inset 0 1px 1px rgba(255, 255, 255, 0.65),
    inset 0 -1px 2px rgba(0, 0, 0, 0.03);
  overflow: hidden;
}

/* 上半分の光沢。position:absolute なので非配置の在フロー要素（タブ）より後ろに
   描かれるよう、.bottom-nav a 側に position:relative を付けてある（下） */
.bottom-nav::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 52%;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0));
  pointer-events: none;
}

/* backdrop-filter が無いブラウザでは背面がぼけず、半透明のままだと文字が読めない */
@supports not ((backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px))) {
  .bottom-nav {
    background: rgba(255, 255, 255, 0.95);
  }
}

/* 画面ごとに板を外さないこと。ナビは全画面で同じピルに見えるのが前提で、
   Home / Feed だけ板を外す指定を一度入れたが、タブを移動するたびにナビの見た目が
   変わる（＝ナビの同一性が切れる）ため戻した。
   「Home / Feed は .page-scroll がピルの上端で止まるので背後がウォッシュだけ」なのは
   事実だが、板を敷かない理由としては採らない。 */

/* タブの箱（ERB 側の <div class="active?">）。等幅にして6タブ（管理者）でも収まるようにする */
.bottom-nav > div {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
}

/* 下ナビ内のリンクの下線を消す＆色を揃える */
.bottom-nav a {
  position: relative;            /* ::before の光沢より前面に描くため */
  flex: 1;
  text-decoration: none;         /* アンダーバーを消す */
  color: inherit;                /* 親要素の文字色をそのまま使う */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  white-space: nowrap;
  border-radius: 999px;          /* 選択中の塗りがピルの形に見えるように */
}

/* ---------- 選択中の下タブ ---------- */
/* 文字色（#ff6b6b）とブランド色の薄い塗りが対。塗りだけ外すと白地に埋もれるので、
   打ち消すときは両方まとめて指定し直すこと（下の【Glass 化の対象にしない要素】参照） */
.bottom-nav .active {
  color: #ff6b6b;   /* 選択中は赤くする */
}

.bottom-nav .active a {
  background: rgba(255, 107, 107, 0.14);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.5);
}

.nav-icon {
width: 22px;
height: 22px;
display: block;
margin: 0 auto 1px;
}

/* 低height端末（実機iPhoneの表示領域383×662px相当を想定）では
   下部ナビゲーションの高さも必要最小限まで詰める */
@media (max-height: 700px) {
  :root {
    /* ピル高と隙間だけ差し替える。--bottom-nav-h は calc なので自動で 52px になる
       （ここで --bottom-nav-h を直接書くと内訳とずれる） */
    --bottom-nav-pill-h: 44px;
    --bottom-nav-gap: 8px;
  }

  .bottom-nav {
    padding: 3px 5px;
  }

  .bottom-nav a {
    font-size: 9px;
  }

  .nav-icon {
    width: 18px;
    height: 18px;
    margin: 0 auto 1px;
  }
}

/* =========================================
   Tasty共通Glass UI（うるうる＋ぷっくり＋ぷるぷる）
   application.css は全画面で読み込まれるため、ここに付与したものは
   Home/Search/Note/Feed/Profile すべてに効く。
   下部ナビは**ナビ本体（.bottom-nav）がピル型の Glass** になっている（上の節）。
   タブのリンク（.bottom-nav a）側には板を敷かず、選択中だけブランド色の薄い塗りと
   タップ演出を持たせてある（下のコメント参照）。
   レシピは home.css で最初に作ったものと同じ値を再利用する（新規考案しない）。
   keyframe もここに集約し、他ファイル（cards.css・google_attribution.css・
   各画面専用CSS）からはこの tastyGlassPulse を参照するだけにする。
========================================= */
@keyframes tastyGlassPulse {
  0%   { transform: scale(1); }
  30%  { transform: scale(0.95); }
  60%  { transform: scale(1.04); }
  100% { transform: scale(1); }
}

/* 【Glass 化の対象にしない要素】— 各画面のCSSからもこの節を参照する
   素の見た目が `background: transparent`（または `none`）かつ **border-radius なし**の
   要素には、半透明白の板
   （`background: rgba(255,255,255,.22)` ＋ `::before` の光沢 ＋
    `inset 0 1px 1px rgba(255,255,255,.45)` / `inset 0 -1px 2px rgba(0,0,0,.03)` /
    `0 1px 4px rgba(0,0,0,.05)`）を敷かない。

   不透明な白地（白いバー・白いカード・白いダイアログ）の上では、板の四角い輪郭が
   そのまま枠や線として見える。角丸が無いので輪郭はぼけず、要素が横幅いっぱいの行なら
   行間の区切り線、小さなアイコンなら×やアイコンを囲う四角い枠になる。
   実際に下部ナビのタブ（.bottom-nav a）と検索ボックスのクリアボタン（.search-button）が
   これで線・枠として見えていた。

   対象のままでよいもの: 角丸のあるピル・円（板が「ボタンの形」として読める）、
   元から色や境界線を持つもの、写真やマップの上に乗るもの（透けるのが狙い）。
   下部ナビ本体（.bottom-nav）は後者2つに該当するので Glass 化している
   （角丸999px のピル ＋ マップやカードの上に浮く）。タブのリンクは角丸を持つが
   非選択時は塗らない — 5〜6個の板が横に並ぶとピル内が区画に割れて見えるため。

   外すときは打ち消し（`background: transparent` の上書き）ではなく、一括指定の
   セレクタから外すこと。打ち消すと塗りだけ消えて `color` が基底ルールのまま残り、
   塗りと文字色が対で成立していた要素の配色が壊れる（CLAUDE.md の /note の
   選択中タブの例）。タップ演出（tastyGlassPulse）は transform だけなので残してよい。 */
.bottom-nav a:active {
  animation: tastyGlassPulse 260ms cubic-bezier(0.34, 1.56, 0.64, 1);
}

@media (prefers-reduced-motion: reduce) {
  .bottom-nav a:active {
    animation: none;
  }
}

/* おともだち追加アイコン（Feedヘッダー／プロフィールのフォローダイアログで共有）。
   素の見た目が background: transparent・角丸なしなので、半透明白の板は敷かない
   （→ 上の【Glass 化の対象にしない要素】）。タップ演出だけ残す。 */
.user-search-icon-btn:active {
  animation: tastyGlassPulse 260ms cubic-bezier(0.34, 1.56, 0.64, 1);
}

@media (prefers-reduced-motion: reduce) {
  .user-search-icon-btn:active {
    animation: none;
  }
}

/* ---------- おともだち追加への導線アイコン ----------
   Feedヘッダーとプロフィールのフォローダイアログの両方で使う共通トリガー。
   （以前 user_search_modal.css にあったものを、モーダル廃止に伴いここへ移設） */
.user-search-icon-btn {
  flex-shrink: 0;
  appearance: none;
  border: none;
  background: transparent;
  padding: 0;
  cursor: pointer;
  display: grid;
  place-items: center;
  color: inherit;
  text-decoration: none;
}

.user-search-icon-img {
  width: 28px;
  height: 28px;
  display: block;
}

/* ==========================================
入力欄の既定フォントサイズ
========================================= */

/* iOS Safari は font-size が 16px 未満の入力欄にフォーカスすると画面を自動ズームする。
   クラス側でも 16px 未満を指定しないこと。ここは font-size 未指定の入力欄
   （素の Devise 画面など。UA 既定の約13px でズームしてしまう）向けの保険。
   クラスセレクタの方が詳細度が高いため、既存のデザイン指定は上書きしない。 */
input,
textarea,
select {
  font-size: 16px;
}