/* 背景音樂控制（2026-09-01）
   職務：讓使用者自己開一段安靜的底，幫助沉澱。**預設關閉**——瀏覽器本來就擋自動播放，
   而且有人在辦公室／捷運上開網頁，突然出聲是很強的負面第一印象。

   契約對照（動這支之前先讀 docs/contracts/icons.md 與 visual-foundation.md）：
   · icon 判準過的是第 ③ 條「通用到不需要學」——音符＝音樂是共通詞彙。
   · **狀態不用斜線／打叉表示，用灰階＋底色層級**（四態表見下方〈狀態〉）。
   · **零 box-shadow**。founder 2026-09-01 提過「圓底加淡陰影做立體感」，擋掉了：
     全 app 至今 box-shadow 宣告數為 0，開這個口不是「加一個陰影」，是「這個 app 從此有
     陰影了」，下一個 session 會拿它當先例。浮起來要的分離感靠**不透明**做，不靠陰影。 */

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

   · 值讀 `--body-pad-x`(20px)：四邊同一個內距，不是挑出來的數字。
   · 手機上這條的結果**跟對齊輸入框右緣完全相同**——輸入列的水平內距就是 --body-pad-x，
     所以一條規則兩種尺寸都成立，不需要斷點。
   · env() 帶 fallback：非瀏海裝置與舊瀏覽器都退回 0px。 */
.music-btn,
.music-menu { right: calc(var(--body-pad-x) + env(safe-area-inset-right, 0px)); }

.music-btn {
  position: fixed;
  /* 垂直內距同樣讀 --body-pad-x（20px），不是挑一個數字：鈕離視窗上緣的距離＝內容離兩側的
     距離，四個方向同一個內距。原本的 10px 會讓它看起來「卡在瀏覽器邊緣」（founder 走查）。
     env() 帶 fallback：非瀏海裝置與舊瀏覽器都退回 0px。 */
  top: calc(var(--body-pad-x) + env(safe-area-inset-top, 0px));
  /* 30：在一般內容之上，但在 .copy-backdrop(40)／.copy-menu(45)／回報卡(50) 之下——
     那三個是 modal 性質，開著的時候音樂鈕本來就不該還能點。 */
  z-index: 30;
  width: 44px; height: 44px;      /* icons.md：可點的 icon 一律 44×44 熱區 */
  display: flex; align-items: center; justify-content: center;
  padding: 0; cursor: pointer;
  background: var(--bg);
  /* ⑤ 形狀白名單新增一位（2026-09-01，founder 拍板）。
     這裡 50% 的職務是**遮蔽**：按鈕 fixed 浮在內文上，沒有不透明底的話音符線條會跟中文
     密筆畫糊在一起（手機字小、行距密，尤其明顯）。這跟送出鈕深色圓底的職務（「這是全 app
     唯一的主要動作」）不同，兩者不衝突——也因此這裡**不是實心深色圓**，是紙色。
     ⚠️ 刻意不開 token（⑤ 原話：「token 會邀請別處引用……沒有名字，就抄不走」）。

     🔴 **沒有可見的框**（founder 2026-09-01 定稿，也是 icons.md 的預設：「新 icon 鈕預設
     不畫圓底」）。圓在這裡分兩層看：
       · 紙色**填色** ＝ 遮蔽（功能）。靜止時它跟紙同色、看不見；內容滑過時擋住底下的字。
       · 1px **框** ＝ 視覺物件（裝飾）。它會在大片留白裡多出一塊形狀，搶走注意力。
     所以留填色、拿掉框：靜止時畫面上只有一個裸露的音符，互動時（hover／active／選單展開）
     才由 --surface 底把圓浮出來——「靜止裸露、互動才給容器」不必靠 hover 實作，手機也成立。
     ⚠️ 實測過拿掉填色會怎樣：入口對話頁捲動時，音符會落在使用者泡泡（--bubble-user）的
        圓角上，--text-faint 疊在那個底色上讀起來像泡泡的雜點、不像控制項。填色不能省。
     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, opacity 0.2s ease;
  /* 熱區與視覺在這顆刻意重合：圓底要夠大才遮得住底下的字，44px 正好。 */
}
.music-btn[hidden] { display: none; }

/* 🔴 關掉手機的預設點擊高光。founder 2026-09-01 走查：手機切曲時「點擊瞬間出現黑色底色」。
   判準是**瞬間**——hover 洩漏會持續黑著（那條另外由下面的防黑閘擋），閃一下的是 iOS／
   Android 的 `-webkit-tap-highlight-color` 預設半透明黑，蓋的是元素外接矩形。
   ⚠️ 這個知識 repo 裡本來就有（70-journey.css:18，founder 2026-08-28 在旅程節點踩過），
      當時的射程判斷是「一般矩形按鈕的高光形狀本來就對，不必跟著關」——那條對節點成立，
      但選單列是**淺色的大橫條**，半透明黑蓋上去在這裡很明顯，所以這裡要關。
   ⚠️ 關掉之後手機唯一的按壓回饋就是 `:active`（下面兩條），改動它們之前先想清楚
      手機上還剩什麼——不是「桌機還有 hover」就沒事（同 70-journey.css 那條警告）。
   ⚠️ 對桌機**零影響**：這個屬性只作用在觸控互動，滑鼠與鍵盤走的是 :hover/:focus-visible。 */
.music-btn,
.music-menu-item { -webkit-tap-highlight-color: transparent; }

/* ── 狀態 ──────────────────────────────────────────────────────────────────────
   founder 2026-09-01 定稿：**播放中不鋪灰底、不做任何持續動態。**
   理由（走查原話）：持續運動會一直吸引餘光，跟「幫助使用者專注自己」相反；音樂本身已經
   是陪伴，不需要畫面再持續提醒它存在。轉動另外還會被讀成載入中，而且音符轉到倒立很怪。
   （icons.md〈不准做〉本來就寫著「不准 icon 自己動」，這裡是同一條的具體落地。）

   | 狀態                  | 圓底      | 框（見註）  | 音符         |
   |-----------------------|-----------|-------------|--------------|
   | 靜止（**含播放中**）  | 紙色      | 桌機 --border ／ 手機 無 | --text-faint |
   | hover / focus         | --surface | --border    | --text-muted |
   | :active（觸控按壓）   | --surface | --border    | --text-muted |
   | 選單展開              | --surface | --border    | --text       |

   🔴 **播放中與關閉長得一模一樣**，這是刻意的（founder 2026-09-01）：有沒有在放音樂用聽的
   就知道，畫面不需要再講一次；一變色它就會在大片留白裡吸走視覺焦點。
   所以「靜止」只有一種樣子，圓只在**正在互動**的三個狀態浮出來。

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

/* 無條件的防黑閘：任何裝置、任何情況都不准讓深底漏進來 */
.music-btn:hover { background: var(--bg); }

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

/* focus-visible 而不是 focus：滑鼠點一下不該留下 focus 樣式（會被誤讀成「還開著」）。 */
.music-btn:focus-visible {
  outline: none;
  background: var(--surface); border-color: var(--border); color: var(--text-muted);
}

/* 🔴 **正在播放刻意沒有任何視覺狀態**（founder 2026-09-01 定稿）。
   理由：音符是配角，有沒有在放音樂**用聽的就知道**，不需要畫面再講一次；一變色它就會
   在大片留白裡吸走視覺焦點，跟「幫助使用者專注自己」相反。
   ⚠️ `.music-btn.is-on` 這個 class 仍然由 97-music.js 掛著——它配的是 aria-label 那半
      （螢幕閱讀器仍聽得到「背景音樂：安靜」），只是**沒有對應的視覺規則**。
      這是刻意的空白，不是漏寫：想加回視覺狀態之前，先看上面那段理由。
   ⚠️ 唯一的取捨：裝置靜音時，看畫面無法判斷音樂是不是開著。選單打開就看得到（目前那首
      是 --text ＋ 加粗），所以狀態沒有丟失，只是需要多一步。 */

/* 選單展開：唯一會鋪灰底的常駐狀態，收起來就恢復。 */
.music-btn[aria-expanded="true"] {
  background: var(--surface); border-color: var(--border); color: var(--text);
}

.music-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;
}

/* ── 選單 ──────────────────────────────────────────────────────────────────── */
.music-menu {
  position: fixed;
  /* ⚠️ 跟著鈕的 top 走：--body-pad-x + 鈕高 44 + 6px 間隙。改鈕的 top 要一起改這裡。 */
  top: calc(var(--body-pad-x) + 44px + 0.375rem + env(safe-area-inset-top, 0px));
  z-index: 31;
  width: 170px;
  background: var(--bg); border: 1px solid var(--border-input);
  border-radius: var(--radius);
  /* padding:0 讓每一列滿版可點（觸控目標才不會被內距切掉）；
     ⚠️ 配 overflow:hidden 是**必要**的，不是保險：列的底色是方角，沒有裁切就會從外框的
     14px 圓角戳出去（2026-09-01 截圖可見）。 */
  padding: 0; overflow: hidden;
  opacity: 0; transform: translateY(-10px) scale(0.96);
  transform-origin: top right;
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.music-menu[hidden] { display: none; }
.music-menu.is-open { opacity: 1; transform: translateY(0) scale(1); }


.music-menu-item {
  width: 100%; text-align: left;
  background: none; border: none; cursor: pointer;
  color: var(--text-muted); font-size: 0.9375rem;
  /* 每一列至少 44px：手機觸控目標的下限（同 icons.md 對可點 icon 的要求）。 */
  min-height: 44px; padding: 0.5rem 1rem; border-radius: 0;
  display: flex; align-items: center;
  white-space: nowrap; transition: background 0.15s ease;
}
/* 同一道防黑閘（理由見上面那條 🔴）：founder 走查在手機上拍到「安靜」整列變黑，
   就是這裡少了無條件重設、全域 button:hover 的深底漏進來。 */
.music-menu-item:hover { background: none; }
@media (hover: hover) {
  .music-menu-item:hover { background: var(--bubble-user); }
}
.music-menu-item:active { background: var(--bubble-user); }
/* ⚠️ **`:focus-visible` 擋不掉「程式呼叫的 .focus()」**（WebKit 上仍會匹配）。
   所以「什麼時候把焦點移進選單」是在 JS 那層決定的，不是靠這條 CSS——
   97-music.js 的 musicOpenMenu(fromKeyboard) 只在鍵盤開啟時才 focus()。
   2026-09-02 之前無條件 focus()，手機上第一項會被打上這塊底色，founder 實拍後讀成
   「安靜被選中／被反灰」，但其實什麼都沒在放。**改這條之前先看那支的守門。** */
.music-menu-item:focus-visible { outline: none; background: var(--bubble-user); }
/* 「現在正在放哪一首」用灰階層級＋字重表示，不加打勾 icon——✓ 在 icons.md 的職務表上是
   「已複製」狀態，一顆 icon 一個職務。語意給 aria-checked，視覺給重量差。 */
.music-menu-item[aria-checked="true"] { color: var(--text); font-weight: 500; }

/* 「關閉音樂」自成一區。⚠️ 分隔線只准有**一條**：上一版同時放了 .music-menu-divider
   元素**和** .music-menu-close 的 border-top，computed 確認兩條都在。留元素這條。 */
.music-menu-divider { height: 1px; background: var(--border); margin: 0.25rem 0; }
.music-menu-divider[hidden] { display: none; }

/* 「停止播放」比三首曲目小一號、淡一階：它是次要動作，不該跟選擇搶層級（founder 定稿）。
   熱區仍維持 40px 以上——它只在已經播放時出現，誤觸成本低於三首曲目，可以比 44 略小。 */
.music-menu-stop {
  font-size: 0.875rem; color: var(--text-faint); min-height: 40px;
}
.music-menu-stop[hidden] { display: none; }

/* ── 頂部控制區：所有畫面的內容都從**距頂 68px** 開始 ─────────────────────────
   founder 2026-09-01 定稿：留白**兩個尺寸都留**（原本只給窄螢幕，走查後推翻）。理由不是
   音符需要它，是「先把高度留出來，未來上方要加導覽列時位置已經在那」。

   🔴 **不能每頁都寫 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。
      解法是**扣掉已經有的**，不是每頁重寫一個數字。 */

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

/* 🔴 #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-music-btn #s-input.active { padding-top: 68px; }

/* 🔴 #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-music-btn #s-record.active { padding-top: 0; }
/* 44 不是 68：.record-header 自己還有 padding-top（3rem 桌機／1.5rem 手機／1rem 鍵盤開），
   兩者相加才是標題到頂端的距離。手機 44+24=68，跟其他畫面對齊。 */
.has-music-btn #s-record.active .record-header { margin-top: 44px; }

/* ⚠️ 選擇器是 `html.chat-short.has-music-btn`（**沒有空格**）：兩個 class 都掛在 <html>
   自己身上，寫成 `html.chat-short .has-music-btn` 會變成「找 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。那個狀態下使用者正在打字，頂部控制區沒有價值。 */
html.chat-short.has-music-btn .screen.active { padding-top: 0; }
html.chat-short.has-music-btn #s-input.active { padding-top: 1.5rem; }  /* 回到 30-layout.css 的原值 */
html.chat-short.has-music-btn #s-record.active .record-header { margin-top: 0; }

/* ── 標題讓位：只在音符**真的會壓到內容欄**的寬度才需要 ───────────────────────
   幾何門檻是算出來的、不是挑的：鈕佔 viewport 右緣的 [20, 64]，內容欄右緣在
   viewport/2 + --content-w/2 = viewport/2 + 384。會重疊的條件是
       viewport/2 + 384 > viewport - 64  ⟺  viewport < 896
   取 900 留一點餘裕。桌機上音符釘在視窗角落、離內容欄很遠，讓位純屬浪費寬度。
   實測（390 寬）：最長的標題「有些答案，要回頭看才會看見」在 320px 時會被圓底蓋掉 32px。
   ⚠️ 圓底不透明，症狀是**文字被遮掉**、不是糊在一起，那比糊更糟。 */
@media (max-width: 900px) {
  .has-music-btn .screen.active h1 { padding-right: 52px; }

  /* 🔴 **置中的標題不能吃這個 padding**：padding-right 會把置中的文字往左推半個值。
     founder 2026-09-01 在真機首頁（空路的「你的路，從這裡開始」）抓到，實測偏移 -26px，
     正好是 52 的一半。app 內唯一置中的 h1 就是那一顆（70-journey.css:560
     `#s-journey.is-empty h1 { text-align: center }`），而它又短又置中、根本碰不到右上角
     的鈕，所以直接歸零。⚠️ 以後若再有置中的標題，要一起加進這條，否則它也會偏。 */
  .has-music-btn #s-journey.is-empty h1 { padding-right: 0; }
}

/* ── 桌機才畫框 ───────────────────────────────────────────────────────────────
   founder 2026-09-01 定稿。理由是兩個尺寸的處境不同，不是不一致：
     · 桌機：音符孤立在視窗角落，旁邊沒有任何東西可以參照，一個裸露的線條不容易被認出
       「這是可以點的」。一圈淡框把它標成控制項。
     · 手機：它就在內容旁邊，而且紙色圓在滑過內容時本來就會提供分離；再加框反而多出一塊
       形狀在搶焦點——那正是 founder 要拿掉框的原因。
   斷點沿用上面標題讓位那條的 900（同一個幾何門檻，不另開數字）。 */
@media (min-width: 901px) {
  .music-btn { border-color: var(--border); }
}

@media (prefers-reduced-motion: reduce) {
  .music-menu { transition: none; }
  .music-menu.is-open { transform: none; }
  .music-btn { transition: none; }
}
