/* 右上角常駐 chrome（2026-09-04）：旅程鈕 ＋ 音符，兩顆住在同一個外殼裡。
   markup 在 index.html 的 #app-chrome，顯示同步在 public/app/98-chrome.js。

   這一支是 2026-09-04 從 74-music.css **搬出來**的：那時右上角只有音符一顆，所以定位、
   熱區、灰階三層、標題讓位全寫在音樂那支裡。旅程鈕加進來之後那些規則有兩個使用者，
   留在 74 就會變成「音樂那支管著一顆跟音樂無關的鈕」。搬出來之後 74 只剩音樂與選單。
   ⚠️ 搬的是**規則不是判準**：每一條的理由（為什麼釘視窗不釘內容欄、為什麼紙色填底不畫框、
      為什麼 hover 要有無條件的 background 重設）都原封帶過來，別當成新寫的東西重新討論。

   🔴 **檔名編號是 73 不是 76**：X6d 釘死「最後一支樣式是 75-report.css」（順序就是行為）。
      排在 75 後面就得放寬那條鎖，而這裡完全不需要——外殼要蓋掉 .music-btn 的舊定位靠的是
      **特異性**（.app-chrome > .chrome-btn 是 0,2,0，舊的 .music-btn 是 0,1,0），跟載入
      順序無關。JS 那支反而必須排在音樂**後面**（98-chrome.js，它要呼叫 musicSetDucked），
      兩邊的編號因此刻意不成對，別為了「看起來整齊」把任何一邊搬家。

   契約對照：docs/contracts/icons.md（畫法、熱區、灰階、hover）、
             docs/contracts/visual-foundation.md ⑤（形狀白名單——桌機那個長圓框在裡面）。 */

/* ── 外殼的定位 ───────────────────────────────────────────────────────────────
   founder 2026-09-01 定稿（音符時代就是這樣，一字未改）：**釘在視窗右上角，不跟著內容欄跑。**
   理由：這是全域常駐的 app chrome——內容欄每一頁都在換（今天的任務／聊聊過程／照片），
   它不變。把不變的東西綁在會變的欄位上，語意本身就矛盾。
   ⚠️ 曾經對齊過內容欄右緣（用 max() ＋ --content-w 算），走查後推翻。留這段是避免下一個
      session 看到「桌機離內容很遠」就想改回去——那是刻意的。

   · 右距讀 `--body-pad-x`(20px)：跟內容欄同一個內距，不是挑出來的數字。
   · 手機上這條的結果**跟對齊輸入框右緣完全相同**——輸入列的水平內距就是 --body-pad-x，
     所以一條規則兩種尺寸都成立，不需要斷點。
   · env() 帶 fallback：非瀏海裝置與舊瀏覽器都退回 0px。

   🔴 **上距是 `--chrome-top`，視窗寬 ≤1000px 時比 --body-pad-x 少 12px（＝8px）**（founder 2026-10-06 選定，全站一起）。
      走查原話：「右上方圖示列可以再往上移…圖示的上方還有一些空白沒用到」。量到的是手機上圖示墨跡離頂邊 39、
      離底下的標題只有 21；往上 12 之後是 27 與 33（旅程頁標題另外再往下，見 70-journey.css〈頂部留白〉）。
      · **他看過「再往上 12、跟 ChatGPT 那排同高」的版本、這次不採**：「那裡的按鈕會有點壓迫，手指要點也比較遠…
        等未來要省空間時再來考慮」。別當成沒想過重推。
      · 桌機（>1000px、有外框那個形態）他說「可動可不動」，這次沒動，仍是 --body-pad-x。分界沿用本檔那個 1000。
      · ⚠️ **跟著這個值走的有三處，要動一起量**：音樂選單的 top（74-music.css 與本檔檔尾那條兩顆版）、
        首次音樂先行的置中算式（74-music.css `html.music-intro .app-chrome`）、紀錄兩頁的返回鍵（本檔下面那條）。
      · ⚠️ 各頁「第一個內容從 68 起」那條**沒有跟著動**（見下面〈共用頂部控制區〉）：移的只有這組圖示。 */
:root { --chrome-top: var(--body-pad-x); }
@media (max-width: 1000px) {
  :root { --chrome-top: calc(var(--body-pad-x) - 0.75rem); }
}
.app-chrome {
  position: fixed;
  right: calc(var(--body-pad-x) + env(safe-area-inset-right, 0px));
  top: calc(var(--chrome-top) + env(safe-area-inset-top, 0px));
  /* 30：在一般內容之上，但在 .copy-backdrop(40)／.copy-menu(45)／回報卡(50) 之下——
     那三個是 modal 性質，開著的時候這兩顆本來就不該還能點。 */
  z-index: 30;
  display: flex;
  align-items: center;
  /* transition 掛在外殼上，index.html 那條開場閘門（html:not(.splash-done) .app-chrome）
     就有東西可以淡。⚠️ 別把它搬回個別的鈕：閘門擋的是外殼，兩邊要對得上。 */
  /* border-color 0.4s（2026-09-25）：首頁音樂先行時桌機外框先藏著，音符滑回這裡之後才淡入
     （74-music.css〈首頁音樂先行〉）。平常框色不會變，這一項對其他畫面沒有作用。 */
  transition: opacity 0.2s ease, border-color 0.4s ease;
}
.app-chrome[hidden] { display: none; }

/* ── 等待頁在改任務文字、鍵盤開著的時候，這兩顆**藏起來**（2026-09-17 走查第 3 輪定案）──
   founder 原話：「**圖示消失是不是其實沒關係，只要標題和原本的卡片保持正常的距離就好？
   因為那時候也不會用到圖示，對吧？反而可以減少雜訊**」。

   🔴 **這是「不要跟 iOS 搶位置」那條路的終點，不是偷懶。** 上一版（第 2 輪）試過把
      `--chat-offset` 補回 `top` 上，實機結果是**補過頭**：圖示被往下推到標題下面，
      而標題自己根本沒動（鍵盤動畫期間那個值會抖，追著它跑就會這樣）。
      改成藏起來之後，這一頁在編輯狀態下不需要任何位移補償——**不要再把補償加回來**。
   ⚠️ 射程只到「等待頁正在編輯 ＋ 鍵盤真的開著 ＋ 觸控裝置」三個條件同時成立：
      桌機沒有軟鍵盤（`pointer: coarse` 擋掉），鍵盤一收起 `html.chat-short` 就消失、
      圖示自己回來，不需要任何 JS 收尾。`html.waiting-kb-edit` 由 `70-waiting.js` 的
      `setWaitingKbEdit()` 與畫面上那個 `is-kb-edit` 同進同出。
   ⚠️ **這不是把 `CHROME_JOURNEY_SCREENS` 的判定改掉**（98-chrome.js 一格沒動）：
      那邊管的是「哪幾頁有這兩顆」，這裡管的是「正在打字的那幾秒先讓開」。 */
@media (pointer: coarse) {
  html.chat-short.waiting-kb-edit .app-chrome { display: none; }
}

/* ── 兩顆共用的鈕 ─────────────────────────────────────────────────────────────
   熱區與視覺刻意重合：紙色圓底要夠大才遮得住底下的字，44px 正好，也剛好是 icons.md
   「可點的 icon 一律 44×44 熱區」那條的下限。 */
.chrome-btn {
  width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  padding: 0; cursor: pointer;
  /* 🔴 紙色**填色**＝遮蔽（功能），不是裝飾。外殼是 fixed、浮在內文上，沒有不透明底的話
     線條會跟中文密筆畫糊在一起（手機字小、行距密，尤其明顯）。實測過拿掉會怎樣：入口對話頁
     捲動時音符會落在使用者泡泡（2026-09-15 起底色 --surface）的圓角上，--text-faint 疊在那個底色上讀起來
     像泡泡的雜點、不像控制項。**填色不能省。**
     ⚠️ 這跟送出鈕深色圓底的職務（「這是全 app 唯一的主要動作」）不同，兩者不衝突——
        也因此這裡**不是**實心深色圓，是紙色。 */
  background: var(--bg);
  /* 🔴 靜止時**沒有可見的框**（founder 2026-09-01 定稿，也是 icons.md 的預設：
     「新 icon 鈕預設不畫圓底」）。圓在這裡分兩層看：
       · 紙色填色 ＝ 遮蔽（功能）。靜止時跟紙同色、看不見；內容滑過時擋住底下的字。
       · 1px 框   ＝ 視覺物件（裝飾）。它會在大片留白裡多出一塊形狀，搶走注意力。
     所以留填色、拿掉框：靜止時畫面上只有裸露的圖示，互動時（hover／active／選單展開）
     才由 --surface 底把圓浮出來——「靜止裸露、互動才給容器」不必靠 hover 實作，手機也成立。
     border-color 仍宣告 transparent（不是 border:none）：狀態要換框色時才有東西可換。 */
  border: 1px solid transparent; border-radius: 50%;
  color: var(--text-faint);
  transition: background 0.15s ease, color 0.2s ease, border-color 0.15s ease;
}
.chrome-btn[hidden] { display: none; }

.chrome-btn svg {
  width: 20px; height: 20px;
  fill: none; stroke: currentColor;
  /* 20px 顯示用 1.7（icons.md〈畫法〉的字面規格） */
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}

/* 🔴 關掉手機的預設點擊高光。founder 2026-09-01 走查：手機切曲時「點擊瞬間出現黑色底色」。
   判準是**瞬間**——hover 洩漏會持續黑著（那條另外由下面的防黑閘擋），閃一下的是 iOS／
   Android 的 `-webkit-tap-highlight-color` 預設半透明黑，蓋的是元素外接矩形。
   這兩顆是圓的，方形高光套上去很突兀（同 70-journey.css:18 旅程節點那條）。
   ⚠️ 關掉之後手機唯一的按壓回饋就是 `:active`（下面那條），改動它之前先想清楚手機上
      還剩什麼——不是「桌機還有 hover」就沒事。
   ⚠️ 對桌機**零影響**：這個屬性只作用在觸控互動，滑鼠與鍵盤走的是 :hover/:focus-visible。 */
.chrome-btn { -webkit-tap-highlight-color: transparent; }

/* ── 狀態 ──────────────────────────────────────────────────────────────────────
   | 狀態                | 圓底      | 圖示         |
   |---------------------|-----------|--------------|
   | 靜止                | 紙色      | --text-faint |
   | hover / focus       | --surface | --text-muted |
   | :active（觸控按壓） | --surface | --text-muted |

   🔴 **每個 :hover 都要有無條件的 background 重設**，不能只寫在 @media (hover: hover) 裡：
      全域 `button:hover { background:#4a443d }`（10-base.css:122）特異性 0,1,1，
      而 `.chrome-btn` 的基礎規則是 0,1,0——**hover 時全域那條贏**。
      手機會黏住 :hover，於是點完就黑在那裡（founder 走查在音樂選單實拍到整列變黑）。
      媒體查詢裡的修正救不了不支援 hover 的裝置，因為那條根本不會被套用。 */
.chrome-btn:hover { background: var(--bg); }

@media (hover: hover) {
  .chrome-btn:hover { background: var(--surface); color: var(--text-muted); }
}
/* 手機沒有 hover，用短暫的 :active 當觸控回饋 */
.chrome-btn:active { background: var(--surface); color: var(--text-muted); }

/* focus-visible 而不是 focus：滑鼠點一下不該留下 focus 樣式（會被誤讀成「還開著」）。
   ⚠️ outline-offset 是負的：桌機那個長圓框只比鈕大 1px，正的 offset 會被框切掉。 */
.chrome-btn:focus-visible {
  outline: none;
  background: var(--surface); color: var(--text-muted);
}

/* ── 桌機：一個長圓框把兩顆框起來（founder 2026-09-04 提案）────────────────────
   走查原話：「手機版距離沒問題，但桌機版兩個圓圈會連在一起」。成因是舊的「桌機才畫框」
   ——兩顆 44px 熱區本來就相鄰，各畫一個圓框就變成兩個相切的圈。
   改成框外殼、兩顆的框轉透明，hover 時才由 --surface 底指出焦點落在哪一顆。

   🔴 **圓角 23px 是算出來的**：外殼 border-box 高 ＝ 44（鈕）＋ 上下各 1px 框 ＝ 46，
      一半正好 23，兩端才是**正圓**。hover 那顆 44px 圓形高光的半徑是 22，比外框內縮一個
      框寬、兩者同心貼齊。寫 22 會讓框的兩端各留一小段直邊，圓形高光落上去就露出一圈月牙。
   🔴 這在 visual-foundation ⑤ 的**形狀白名單**多一位（判準：半徑 ≥ 高度/2 就是形狀、
      不是圓角）。founder 2026-09-04 明知放行，已寫進契約——別當成可以自由複製的圓角級距。
   ⚠️ 只有一顆在場時（旅程頁上旅程鈕會收起來，只剩音符）外殼寬度剛好 46，長圓框自動退化
      成正圓，看起來跟 2026-09-04 之前一模一樣——不必為那個狀態另寫規則。

   為什麼手機不畫框（founder 2026-09-01 定稿，沿用）：
     · 桌機：這組鈕孤立在視窗角落，旁邊沒有東西可以參照，裸露的線條不容易被認出「可以點」。
     · 手機：它就在內容旁邊，紙色圓滑過內容時本來就會提供分離；再加框反而多一塊形狀搶焦點。 */
@media (min-width: 1001px) {
  .app-chrome {
    border: 1px solid var(--border);
    border-radius: 23px;
    background: var(--bg);
  }
  /* 圈中不能有圈 */
  .chrome-btn { border-color: transparent; }
}

/* ── 共用頂部控制區 68px；桌機入口的額外留白見下方專屬規則 ───────────────────
   founder 2026-09-01 定稿：留白**兩個尺寸都留**（原本只給窄螢幕，走查後推翻）。理由不是
   音符需要它，是「先把高度留出來，未來上方要加導覽列時位置已經在那」。
   ⚠️ 2026-10-06 起 ≤1000px 的圖示往上移了 12px（--chrome-top），**這裡的 68 沒有跟著改**：
      它是各頁第一個內容的共同起點（visual-foundation ⑥），改它＝每一頁的內容一起上移，沒有人核准過。
      旅程頁有路時另外再往下一段，寫在 70-journey.css〈頂部留白〉（ID 特異性蓋過下面這條）。
   ⚠️ 這一組的數字**不隨鈕的顆數變**：外殼高度仍是 44，多一顆只會變寬。2026-09-04 加旅程鈕
      時逐條確認過，一個數字都沒動。

   🔴 **不能每頁都寫 68**。實測（cmux 2026-09-01，390 寬）發現各頁起始高度不一致：
        紀錄頁／入口對話 68px　vs　等待／照片／走完一步／旅程／回饋 116px
      根因：非對話頁的 `body` 還有 `margin: 3rem auto`（10-base.css:72）給的 48px，
      68 疊在它上面 → 116。對話頁的 body margin 被 `html.chat-fullscreen` 歸零
      （30-layout.css），所以那兩頁只有 68。founder 走查講的「走完一步頁留白太多」就是這 48px。
      解法是**扣掉已經有的**，不是每頁重寫一個數字。
   ⚠️ **選項頁 `#s-options` 是第三種算法**（2026-09-14 落地，20-screens.css）：它跟選擇頁
      （`#s-record-mode`；原節奏選擇頁 `#s-pace` 2026-10-03 下架）一樣把 `body` 上下 margin 歸零、整組垂直置中，但它**有**頂部控制區，
      所以 `#s-options.active` 直接寫 `padding-top: 4.25rem`（＝68px）取代下面這條 calc
      fallback（ID 特異性蓋得過）——68 這個數字守住了，只是換一種方式量出來。 */

/* 非對話頁：body 已經給了 3rem，這裡只補到 68 */
.has-app-chrome .screen.active { padding-top: calc(68px - 3rem); }

/* 直接寫下來頁（2026-10-04）：這一頁原本沒有右上角那一組，版面是 founder 對著目標圖走查過的
   （docs/design-references/2026-09-14-record-write-target.png）。「寫給我們」加入後它第一次掛上
   .has-app-chrome——上面那條會把整頁往下推 20px（實測返回鍵 36→56、標題 48→68）。
   這一頁維持原樣：不吃那 20px。⚠️ 紀錄選擇頁與照片頁實測無位移（各自的樣式本來就蓋過上面那條）。 */
.has-app-chrome #s-record-write.active { padding-top: 0; }

/* 這兩頁（紀錄選擇頁、直接寫下來頁）左上的返回鍵跟右上那顆對齊（2026-10-04）。
   以前這兩頁右上角是空的，返回鍵的高度沒有東西可以對；「寫給我們」進來之後，手機上量到返回鍵
   比它低 16px（中線 58 對 42），兩顆在同一列卻一高一低。
   · 用 `position: relative` 往上提：只動它自己，**底下的標題與內容一格都不動**（這兩頁的版面 founder 走查過）。
   · 只在右上角那組在場時才提（`html.has-app-chrome`）：還沒有第二顆節點的人，這兩頁維持原樣。
   · 桌機滑鼠環境不適用：那裡返回鍵是 fixed 貼視窗左上角，本來就跟右上那組同一條中線（20-screens.css）。
   ⚠️ 第一圈回顧頁那顆返回（.record-chat-back）不在這條裡：那一頁只有第一圈才有返回鍵，而這顆要第二顆節點之後才出現。 */
@media (max-width: 1000px), (pointer: coarse) {
  html.has-app-chrome #s-record-write .record-write-back,
  html.has-app-chrome #s-record-mode .record-mode-back { position: relative; top: -1rem; }
}
/* 2026-10-06：≤1000px 時右上那組往上 12px（--chrome-top），返回鍵跟著多提 12，兩顆才仍在同一條中線。
   寬螢幕的觸控裝置（>1000px）右上那組沒動，維持上面那條的 -1rem。 */
@media (max-width: 1000px) {
  html.has-app-chrome #s-record-write .record-write-back,
  html.has-app-chrome #s-record-mode .record-mode-back { top: -1.75rem; }
}

/* 🔴 #s-input 要單獨寫，而且要寫滿 68：一來它的 body margin 是 0（chat-fullscreen），
   二來 30-layout.css 的 `#s-input.active { padding-top: 1.5rem }` 特異性是 1,1,0、
   壓過上面那條的 0,3,0——曾經實測入口對話頁只有 24px，**使用者第一個看到的畫面反而沒留白**。
   這條 1,2,0 才蓋得過去。這頁沒有 sticky 標題，padding 會跟著訊息一起捲走，正是要的行為。 */
.has-app-chrome #s-input.active { padding-top: 68px; }

/* 桌機入口第一行 116：68 容器＋16 對話內距＋最多 32。
   2026-09-22 founder 選 148（最多 64），2026-10-08 用了兩週後說「又覺得太下面了一些」，看對照改選 116。
   矮視窗逐步收回新增留白；padding 隨內容捲走，不縮減後續閱讀高度。 */
@media (min-width: 1024px) and (pointer: fine) {
  .has-app-chrome #s-input.active {
    padding-top: calc(68px + clamp(0px, calc(32vh - 208px), 32px));
  }
}

/* 對話上緣：≤1000px 112px、桌機 68px 過渡區，越靠上模糊越重。
   2026-09-28 回顧頁所有寬度統一；2026-10-05 入口對話也比照（founder：桌機入口「該有的模糊淡出」
   不見了——其實從沒做過，那裡原本只有 ≤480px）。⇒ 兩個對話頁（`chat-fullscreen`）在所有寬度同一套。
   各濾鏡是同層的真實元素，外殼不放 filter／mask／opacity，以免建立 backdrop root
   後讓子層只模糊到外殼。未捲動時透明，避免擴大的範圍遮住第一句。
   位於 chrome(30) 與 modal(40+) 下方，不擋觸控；不支援背景模糊時仍有紙色淡出。 */
.chat-top-fade {
  display: none;
  position: fixed; top: 0; left: 0; right: 0;
  height: calc(112px + env(safe-area-inset-top, 0px));
  z-index: 29; pointer-events: none;
}
html.splash-done.chat-fullscreen.has-app-chrome .chat-top-fade { display: block; }
.chat-top-fade > span {
  position: absolute; inset: 0;
  opacity: var(--chat-edge-progress, 0);
}
.chat-top-fade-soft {
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  -webkit-mask-image: linear-gradient(#000 60%, transparent);
  mask-image: linear-gradient(#000 60%, transparent);
}
.chat-top-fade-medium {
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  -webkit-mask-image: linear-gradient(#000 35%, transparent 78%);
  mask-image: linear-gradient(#000 35%, transparent 78%);
}
.chat-top-fade-strong {
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  -webkit-mask-image: linear-gradient(#000 14%, transparent 58%);
  mask-image: linear-gradient(#000 14%, transparent 58%);
}
.chat-top-fade-paper {
  background: linear-gradient(var(--bg), transparent);
  background: linear-gradient(var(--bg), color-mix(in srgb, var(--bg) 85%, transparent) 38%, transparent);
}
/* 短視窗減少遮蔽高度；捲動進度仍從 0 起，不改初始內容位置。 */
html.splash-done.chat-short.chat-fullscreen.has-app-chrome .chat-top-fade {
  height: calc(68px + env(safe-area-inset-top, 0px));
}
/* 桌機縮短霧化，讓文字更靠近頂端才模糊（founder 2026-09-28 定在回顧；2026-10-05 入口比照）。
   沿用 chrome 的桌機寬度分界；手機／平板與各層 mask、blur、漸入不變。 */
@media (min-width: 1001px) {
  html.splash-done.chat-fullscreen.has-app-chrome .chat-top-fade {
    height: calc(68px + env(safe-area-inset-top, 0px));
  }
}
/* 桌機回顧的返回鍵是 fixed、z-index:30；拿掉已退役 sticky 標題的 stacking context，
   讓返回鍵留在霧化(29)之上。只動與返回鍵釘位相同的桌機滑鼠條件。 */
@media (min-width: 1001px) and (pointer: fine) {
  #s-record .record-header { z-index: auto; }
}

/* ── 狀態列維持紙色（2026-10-13，founder 實機對照五個版本後選定）──────────────────
   **看到的現象**（founder 的 iPhone、加到主畫面的 web app；錄影逐格量過，數字與來源在
   docs/sessions/2026-10-13-statusbar-dim-sync.md）：`.report-backdrop` 那一族的全螢幕遮罩一出現，頂部那一條
   （時間、電量）就一格跳暗；收起來時等到遮罩 display:none 才亮回來——跟畫面的淡入淡出對不上。
   長按複製的遮罩不會動到那一條。`.device-help-backdrop` 那兩張寫法相同、沒有實機看過。
   **我們對成因的理解（讀 WebKit 的討論與別人的實測文章推出來的，不是自己驗過的規則）**：那一條不是頁面畫的，
   系統拿「最上層、貼著視窗上緣、幾乎整寬的 fixed 元素」算出來的底色去上色，不理會它的 opacity 淡入淡出。
   **做法**：放一條底色是紙色的細條，用最大的 z-index 讓它排在遮罩前面 ⇒ 預期那一條一直是紙色，遮罩開關都不動它。
   實機驗過的是測試版（JS 加進 body、`top: 0`、沒有 @supports）；跟這裡的差別見下面三個「未驗」。

   每一個值都有用途，改之前先讀：
   · `background-clip: text` ＋沒有字 ⇒ 預期一個像素都不畫（測試版實機上看不到任何東西）。
   · 高 11px：文章說兩邊都要大於 10px 系統才直接採用它的底色；測試版用的就是 11，沒試過別的值。
   · `top: var(--chat-offset)`：跟 `.report-backdrop` 的上緣同一個來源（`visualViewport.offsetTop`，鍵盤讓可視範圍
     位移時不是 0）。想法是「遮罩在哪裡會被拿去上色，這一條就在同一個位置、排在它前面」。**未驗**：鍵盤開著時
     實機上是不是這樣；不對的話另一個選擇是測試版的 `top: 0`。
   · 不加 border／box-shadow／transition／opacity，也不包進有 transform 的容器（`.screen`）：測試版沒有這些，
     加了會不會失效沒試過，所以別加。
   · 想只在 iOS 開：用「認不認得 `-webkit-touch-callout`」來猜是不是 iOS 的 WebKit（是猜的方法、不是保證），
     別的瀏覽器就不必多一個疊在最上層的元素。
     **未驗**：這個條件在主畫面 web app、Safari 分頁、其他 app 內建瀏覽器是不是都成立；不成立＝這條安靜地沒作用。
   · **未驗**：寫在 index.html 裡（測試版是 JS 後加的）有沒有差。
   ⚠️ 桌機與自動檢查看不到效果，只有 iPhone 實機算數。系統哪天換了做法，可能回到「那一條跟著遮罩變暗」，
      也可能讓這條自己露出來（頂部多一道 11px 的紙色蓋在暗掉的畫面上）——兩種都要回這裡處理。
   ⚠️ 想讓那一條「跟畫面一起暗」，試過的方向是讓頁面自己畫到那一條底下（`viewport-fit=cover`）：2026-10-13
      在同一台手機上看起來是同時暗，但底部露出一條沒被遮罩蓋到的紙色（高度等於那一條），其他頁沒看過；
      visual-foundation.md〈底部空間〉也寫了別為別的理由加它。 */
.statusbar-paper { display: none; }
@supports (-webkit-touch-callout: none) {
  .statusbar-paper {
    display: block;
    position: fixed; left: 0; right: 0; top: var(--chat-offset, 0px);
    height: 11px; z-index: 2147483647;
    pointer-events: none;
    background-color: var(--bg);
    -webkit-background-clip: text; background-clip: text;
  }
}

/* 兩顆手機控制合成一個紙色藥丸；只剩一顆時保留既有圓底。 */
@media (max-width: 1000px) {
  .app-chrome:has(> .chrome-btn:not([hidden]) ~ .chrome-btn:not([hidden])) {
    padding: 4px; border-radius: 999px; background: var(--bg);
  }
  .app-chrome:has(> .chrome-btn:not([hidden]) ~ .chrome-btn:not([hidden])) ~ .music-menu {
    top: calc(var(--chrome-top) + 52px + 0.375rem + env(safe-area-inset-top, 0px));
  }
}

/* 🔴 #s-record 不能用 scroll port 的 padding-top：sticky 的定位基準是 scrollport 的
   **內容盒**，padding 會把標題整個往下釘（實測釘在 68 而不是 0）＝每次捲動都永久少掉
   68px 對話高度（founder 選 B：只保留初始留白，捲動後收掉）。改成把留白掛在 sticky 元素
   自己的 margin-top 上：初始撐開、捲動時 margin 跟著捲走、標題最後仍釘回 top:0。
   ⚠️ `.record-header` 的 `max-height: max(3rem, calc(100% - ...))` 讀的是 `100%`
   （解析自 #s-record.active 的 --chat-vh），margin 不進那條算式，高度保證不受影響。 */
.has-app-chrome #s-record.active { padding-top: 0; }
/* 44 不是 68：.record-header 自己還有 padding-top（3rem 桌機／1.5rem 手機／1rem 鍵盤開），
   兩者相加才是標題到頂端的距離。手機 44+24=68，跟其他畫面對齊。 */
.has-app-chrome #s-record.active .record-header { margin-top: 44px; }

/* ⚠️ 選擇器是 `html.chat-short.has-app-chrome`（**沒有空格**）：兩個 class 都掛在 <html>
   自己身上，寫成 `html.chat-short .has-app-chrome` 會變成「找 chat-short 的後代」，永遠不匹配。
   2026-09-01 第一版就是這樣寫的，CSSOM 裡看得到規則、實際卻沒生效，很容易誤判成快取。
🔴 鍵盤開啟時整段留白收掉。`html.chat-short` 由 syncChatShort 依 visualViewport 掛
   （40-chat.css:174-176 同一套機制）。理由是實測出來的：390×350（iPhone SE 開鍵盤的量級）
   留著 44px 會讓 .record-header 底下的問題區只剩 68px，低於 40-chat.css 檔頭承諾的 5rem；
   收掉之後回到 112px。那個狀態下使用者正在打字，頂部控制區沒有價值。 */
/* ⚠️ 旅程頁有路時另有一條帶 ID 的 padding-top（70-journey.css〈頂部留白〉），特異性比下面這條高——
   那邊自己把 chat-short 明寫回 0。之後再有哪一頁用 ID 寫自己的頂距，也要記得補矮視窗那一條。 */
html.chat-short.has-app-chrome .screen.active { padding-top: 0; }
html.chat-short.has-app-chrome #s-input.active { padding-top: 1.5rem; }  /* 回到 30-layout.css 的原值 */
html.chat-short.has-app-chrome #s-record.active .record-header { margin-top: 0; }

/* ── 標題讓位：只在這組鈕**真的會壓到內容欄**的寬度才需要 ─────────────────────
   幾何門檻是算出來的、不是挑的。內容欄右緣在 viewport/2 + --content-w/2 = viewport/2 + 384，
   鈕組佔 viewport 右緣的 [20, 20 + 44×顆數]。會重疊的條件是
       viewport/2 + 384 > viewport - (20 + 44×n)  ⟺  viewport < 808 + 88×n
   🔴 **n 從 1 變成 2，門檻就從 896 變成 984**（2026-09-04 旅程鈕加入時重算）。
      舊的斷點是 900；沿用會讓 900–984 這一段的標題被圓底蓋掉，而那正是「文字被遮掉」
      這個比糊更糟的症狀。取 1000 留一點餘裕。
   ⚠️ 桌機那個長圓框的斷點**跟這條共用同一個門檻**（min-width: 1001px）：框的理由是
      「孤立在視窗角落」，而在 900–1000 這一段它根本還壓在內容上，不算孤立。
      兩條同進退，不另開第二個數字。
   讓位值 96 ＝ 52（音符時代那個值）＋ 44（多一顆）。 */
@media (max-width: 1000px) {
  .has-app-chrome .screen.active h1 { padding-right: 96px; }

  /* 🔴 **置中的標題不能吃這個 padding**：padding-right 會把置中的文字往左推半個值。
     founder 2026-09-01 在真機首頁（空路的「你的路，從這裡開始」）抓到，實測偏移 -26px，
     正好是當時 52 的一半。app 內置中的 h1 就是那一顆（70-journey.css:560
     `#s-journey.is-empty h1 { text-align: center }`），而它又短又置中、根本碰不到右上角
     的鈕，所以直接歸零。⚠️ 以後若再有置中的標題，要一起加進這條，否則它也會偏。
     （以下這段講的節奏選擇頁 #s-pace 已於 2026-10-03 下架，留著當這個坑的紀錄。）
     （節奏選擇頁 #s-pace 的 h1 也置中。⚠️ **2026-09-17 起它也有頂部控制區了**——founder 要求
     那一頁也放這兩顆鈕方便返回，所以「那頁永遠不掛 .has-app-chrome」這個舊前提**已經不成立**；
     它跟選項頁一樣在 20-screens.css 自己寫 `padding-right: 0`（不加就會被推左 48px、
     標題在 390 寬的手機上擠成兩行，當天實測）。
     選項頁 #s-options 的 h1 也置中、而且**有**頂部控制區，理論上該加進這條——2026-09-14 落地時
     改成不掛在這裡：20-screens.css 直接寫 `#s-options h1 { padding-right: 0 }`，ID 特異性
     蓋得過這條 class 規則、也不受這個 `max-width: 1000px` 媒體查詢限制，效果相同，只是
     選項頁自己的樣式段落自己負責。） */
  .has-app-chrome #s-journey.is-empty h1 { padding-right: 0; }
}

/* ── 三顆同時在場（2026-10-04，「寫給我們」加入）────────────────────────────────
   `html.chrome-3` 由 98-chrome.js 依**真的在場**的顆數掛上。
   · 讓位值 140 ＝ 96 ＋ 44（多一顆）。
   · 門檻照上面那條公式重算：n=3 ⇒ viewport < 808 + 88×3 = 1072，取 1080 留一點餘裕。
   🔴 **只多開這一條，不動上面那個 1000 的斷點**：那個數字同時是桌機長圓框與返回鍵釘位的門檻，
      兩顆在場的頁面（多數人多數時候）不該因為別頁多了一顆就換樣子。1001–1080 之間三顆在場時，
      外殼是桌機那個長圓框、標題照樣讓位——兩件事各管各的。
   ⚠️ 置中的標題照舊不吃讓位：那幾條（上面的 #s-journey.is-empty、20-screens.css 的 #s-options）
      都是 ID 選擇器，特異性蓋得過這條。 */
@media (max-width: 1080px) {
  html.has-app-chrome.chrome-3 .screen.active h1 { padding-right: 140px; }
}

/* ── 「寫給我們」第一次出現的提示（93-report.js syncFeedbackNudge）──────────────
   founder 2026-10-04：不把整頁暗下來，外圈輕輕動一下就好。只在第一次出現的那次主頁停留動，
   他點了或離開主頁就停，之後不再動（會自己停＝不是催促；那段邏輯在 JS）。
   畫法照音符第一次那一圈（74-music.css 的 .music-btn.is-intro）：icon 本身不動，動的是外面那一圈；
   用 `::before` 的 scale＋opacity，不用 box-shadow（全 app 零陰影）。
   ⚠️ 主頁路上那顆圓點也在呼吸，這是「整頁只有一個東西在動」的例外（founder 明知放行，記在 icons.md）。
   founder 2026-10-04 從三種輕重選 B：跟音符那次**同樣的輕重**（同尺寸、同節奏、icon 提一階到 --text-muted）。
   提示一停（class 拿掉）顏色就回到平常的 --text-faint。 */
.feedback-btn.is-nudging { position: relative; color: var(--text-muted); }
.feedback-btn.is-nudging::before {
  content: ''; position: absolute; top: 50%; left: 50%;
  width: 20px; height: 20px; margin: -10px 0 0 -10px;
  border: 1px solid var(--text-muted); border-radius: 50%;
  pointer-events: none;
  animation: feedbackNudgePulse 2s ease-out infinite;
}
@keyframes feedbackNudgePulse { 0% { transform: scale(1); opacity: 0.45; } 100% { transform: scale(2); opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .app-chrome { transition: none; }
  .chrome-btn { transition: none; }
  /* 提示是裝飾：停下來、留一圈淡的靜態外圈（同音符那次的做法）。 */
  .feedback-btn.is-nudging::before { animation: none; transform: scale(1.5); opacity: 0.35; }
}
