﻿/* ============================================================
   material-notice.css — 教材版本提醒（四個顯示點共用）
   顯示點一 商品總覽卡片 plan.aspx ／ 顯示點二 商品詳細頁 feature.aspx
   顯示點三 購物車 shopping.aspx  ／ 顯示點四 訂單明細
   ⚠️ 由**各顯示點的頁面自行載入**（file-ticks 自動 cache-bust），不掛 main.master：
      掛 master 對 27 個用不到的公開頁是多餘的 render-blocking 往返，
      而顯示點四 buyrecord.aspx 走的是 member.master，掛了也蓋不到 ⟹ 過寬又不完整。
      🔴 這份「已載入」清單就是後續 Task 的檢查依據，改動載入點時**必須同步這裡**：
         漏掉不會報錯，只會讓琥珀標籤靜默退化成無樣式純文字。
      目前已載入：
        · web/plan.aspx:18      （顯示點一／商品總覽卡片，Task 5）
        · web/feature.aspx:20   （顯示點二／商品詳細頁，Task 6）
      尚待補上：
        · Task 7 → web/shopping.aspx（顯示點三／購物車品項）
        · Task 8 → web/buyrecord.aspx（顯示點四／訂單明細）
          ⚠️ buyrecord.aspx 走的是 member.master 不是 main.master，
             掛在 main.master 上蓋不到它——這正是本檔改成逐頁掛的原因。
   視覺沿用站上既有的琥珀色提醒語彙（「規範改版提醒」「教材新版提醒」兩個先例）。

   🔴 rem 的基準是 **10px，不是瀏覽器預設的 16px**（2026-08-14 複審抓出，本檔全數換算）：
        css/bootstrap.css:1079-1080  html { font-size: 10px; }   ← 不在任何 @media 內
        main.master:41               <link … href="/css/bootstrap.min.css…">（feature.aspx／plan.aspx 都吃得到）
      全站沒有第二處樣式表把它改回來；本專案自己的 css/web/buyrecord-v2.css:5 檔頭也早已把
      「站台 html{font-size:10px}」寫成已知事實，全站 4 支 iv2 CSS 的 rem 字級值域（1.2～5.8rem）
      也都是按 10px 基準寫的 —— 本檔是跟上同一慣例，不是另立一套。
      ⟹ 本檔所有 font-size 的 rem 值一律以 10px 換算：**1.68rem = 16.8px（內文下限）**、
         **1.84rem = 18.4px（按鈕下限）**、1.76rem = 17.6px。
      ⛔ 不得再寫 1.05／1.1／1.15rem 這類「照 16px 基準想出來」的值 —— 它們實際只渲染出
         10.5／11／11.5px，比旁邊的內文小約 40%，而且**不會有任何錯誤提示**。
   ============================================================ */

/* 區塊：總覽卡片與詳細頁用（.mn-line 保留給購物車／訂單的單行版） */
.mn-block {
  margin: 0.6em 0 0;
}

/* 琥珀標籤 */
.mn-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  max-width: 100%;
  background: #FEF3C7;
  color: #92400E;
  border: 1px solid #FCD34D;
  border-radius: 999px;
  padding: 0.25em 0.8em;
  font-size: 1.68rem;
  font-weight: 600;
  line-height: 1.4;
}

/* 一句摘要 */
.mn-summary {
  font-size: 1.68rem;
  color: #78350F;
  line-height: 1.6;
  margin: 0.4em 0 0;
  word-break: break-word;
  overflow-wrap: break-word;
}

/* 🔴 全站 css/style.css:143 的 p{font-size:15px} 會直接命中 <p> 並蓋過容器繼承字級。
   沒有這一條，.mn-summary 以外的任何 <p>（例如詳細頁的完整說明段落）都會被壓回 15px。 */
.mn-block p,
.mn-line p {
  font-size: inherit;
}

/* 🔴 上面那條 `.mn-block p`(0,1,1) 的特異度高於 `.mn-summary`(0,1,0)，**與原始碼順序無關**，
   會讓 :55 的 font-size 變成死宣告，摘要一律吃容器的環境字級。
   顯示點一僥倖沒事（.iv2-root 有 17px 基準，見 css/web/plan-v2.css:53），
   但顯示點三（購物車）／四（訂單明細）沒有那個基準，摘要會**靜默**縮到 13px。
   ⚠️ 13px 是實查值不是估值（2026-08-14 複審 N-6）：main.master 上**最後一條** body 規則是
      css/style.css:51-59（`font-size: 13px` 在 :53、`line-height: 21px` 在 :54），
      它以相同特異度 (0,0,1) 蓋過 css/bootstrap.css:1086 的 `font-size: 14px`（同一個 `body{}`
      區塊起於 :1084）——決勝的是載入順序（main.master:41 bootstrap.min.css → main.master:55 style.css）。
   ⛔ 不用 !important（要留覆寫空間給後續三個顯示點）；
   ⛔ 也不把上面那條改成 :not(.mn-summary)（會變成每新增一個 class 就要維護的排除清單）。 */
/* 🔴 同理，css/web/plan-v2.css 的 `.iv2-root p{margin:0}`(0,1,1) 與全站 style.css 的
   `p{font-size:15px}` 都會以更高特異度壓過單一 class 的 `.mn-summary`(0,1,0)：
   :58 的 margin 在顯示點一（.iv2-root 底下）會被歸零，標籤與摘要間距失效。
   把 margin 併進這條 (0,2,0) 規則才追得上，理由與上面 font-size 那條完全對稱。 */
.mn-block .mn-summary,
.mn-line .mn-summary {
  font-size: 1.68rem;
  margin: 0.4em 0 0;
}

/* 設定錯誤：同一組版型但降低彩度，避免看起來像一則「真的有提醒」。
   ⛔ 不得改成隱藏——那等於在該提醒的科目上靜默失去揭露。 */
.mn-error .mn-tag {
  background: #F3F4F6;
  color: #4B5563;
  border-color: #D1D5DB;
}

.mn-error .mn-summary {
  color: #4B5563;
}

/* 顯示點一：.iv2-prod-body 本身是 flex column 且已有 gap:12px，
   若再吃 .mn-block 的上外距會多出一段空白，故在卡片情境歸零。 */
.iv2-prod-body .mn-block {
  margin-top: 0;
}

/* ============================================================
   顯示點二（商品詳細頁 feature.aspx）：常駐完整說明
   ------------------------------------------------------------
   🔴 特異度策略（Task 5 為此吃了兩次虧，這裡改成從根本迴避）：
      本站有兩條後代選擇器會壓過任何單一 class (0,1,0)——
        css/style.css:143            p { color: #666; font-size: 15px }
        css/web/feature-v2.css:80-84 .iv2-root h1,h2,h3,h4,p { margin: 0 }  (0,1,1)
      而顯示點二的區塊是插進 #planContents（.iv2-rich-content）內，那裡還有更狠的：
        feature-v2.css:553-555  .iv2-root .iv2-rich-content:not(.iv2-hero-infos) p { font-size: 1em }   (0,3,1)
        feature-v2.css:567      .iv2-root .iv2-rich-content p { margin-bottom: … }                      (0,2,1)
      要贏過 (0,3,1) 得寫到四層 class，那是在跟樣式表打軍備競賽。
      ⟹ **本區塊的新標記刻意一個 <p> 都不用**：說明文字用 <div class="mn-content">，
         .iv2-rich-content 底下沒有任何針對 div 的元素選擇器（style.css 亦無 `div {}`／`span {}`），
         單一 class 就能定案，不必也不會被靜默壓掉。
      仍需要提高特異度的只有 <h4 class="mn-title">：
        css/style.css:128        h4 { font-size: 16px; line-height: 22px }   (0,0,1)
        css/style.css:101-111    h1..h6 { margin: 0; color: #444 }           (0,0,1)
        css/web/feature-v2.css:80-84  .iv2-root h4 { margin: 0 }（h4 在 :83）(0,1,1)  ← 這條要贏
      故一律寫成 .mn-block .mn-title (0,2,0)。⛔ 不用 !important。
   ============================================================ */

/* 科目名稱：一個方案可能含多個科目、而只有其中幾個受影響，提醒必須點名科目
   ——否則學生看到「本科目上課教材將出新版」卻不知道是哪一科。 */
.mn-subject {
  display: inline-block;
  margin-left: 0.5em;
  font-size: 1.68rem;
  font-weight: 600;
  color: #92400E;
  word-break: break-word;
}

/* 提醒標題（後台 title 欄，一句話） */
.mn-block .mn-title {
  font-size: 1.84rem;
  font-weight: 700;
  line-height: 1.5;
  color: #78350F;
  margin: 0.55em 0 0.3em;
  word-break: break-word;
}

/* 完整說明（後台 content 欄，長文；換行已由 JS 轉成 <br>）

   🔴 white-space: pre-wrap 必須有，否則**後台所見 ≠ 前台所得**：
      後台預覽區用的就是 pre-wrap（Admin/html/material-notice.html:60），
      作者在 textarea 打的連續空白／縮排（例如條列對齊）在前台會被 HTML 預設的
      white-space:normal 摺疊成一個空白，而且不會有任何錯誤提示。
   ⚠️ 與 JS 的「換行轉 <br>」不衝突：mnTextToHtml 是把 \r\n／\n **取代**成 <br>，
      換行字元已經消失，pre-wrap 不會再多算一次而變成雙倍行距。 */
.mn-block .mn-content {
  font-size: 1.76rem;
  line-height: 1.8;
  color: #78350F;
  margin: 0;
  white-space: pre-wrap;
  word-break: break-word;
  overflow-wrap: break-word;
}

/* 詳細頁版型：沿用站上既有「規範改版提醒」的琥珀 callout 語彙
   （blockquote.iv2-warn，見 _migrations/2026-07-02_add_regulation_notice_steel_rc_books.sql），
   讓兩種提醒在同一個位置看起來是同一件事。
   ⚠️ 只在 .iv2-rich-content 情境套用，總覽卡片（顯示點一）維持無框的輕量版。 */
.iv2-rich-content .mn-block {
  background: #FFFBEB;
  border: 1px solid #FDE68A;
  border-left: 4px solid #F59E0B;
  border-radius: 8px;
  padding: 0.9em 1.1em;
  margin: 1.2em 0;
}

/* 設定錯誤：降彩度但不隱藏（⛔ 隱藏＝在該科目上靜默失去揭露）。
   ⚠️ 這兩條必須排在上面兩條之後：.mn-error .mn-content 與 .mn-block .mn-content
      同為 (0,2,0)，靠原始碼順序決勝。 */
.mn-error .mn-content {
  color: #4B5563;
}

.iv2-rich-content .mn-block.mn-error {
  background: #F9FAFB;
  border-color: #E5E7EB;
  border-left-color: #9CA3AF;
}

/* ============================================================
   顯示點二 位置①（購買面板 .iv2-buy-panel，feature.aspx:73）：摘要 ＋ 可展開
   ------------------------------------------------------------
   🔴 為什麼要另寫一段作用域規則：.iv2-buy-panel **不是** .iv2-rich-content，
      上面那條 `.iv2-rich-content .mn-block` 的 callout 版型在這裡完全不會套用，
      不補這一段就會退化成沒有底色／沒有邊框的裸文字，混在深藍價格區與白色 CTA 之間看不出是提醒。
   🔴 字級環境與位置②不同（審查已點名的坑）：
        位置②在 .iv2-rich-content:not(.iv2-hero-infos)（feature-v2.css:552）底下 → 1.05em 放大；
        位置①只有 .iv2-root 的 17px（feature-v2.css:54；≤600px 為 16px），**沒有那層放大**。
      ⟹ .mn-tag／.mn-subject／.mn-title／.mn-content 一律用 **rem 絕對單位**，環境換了字級也不會跟著縮。
      ⛔ 這個「不用 em」的理由本身沒變、仍然成立：顯示點三（購物車）／四（訂單明細）**沒有**
         .iv2-root 的 17px 基準，用 em 會讓字級跟著各自容器**靜默**縮到 **13px**
         ——依據是 css/style.css:53 的 `body { font-size: 13px }`（區塊 :51-59），它以相同特異度
         (0,0,1) 但更晚的載入順序（main.master:41 → main.master:55）蓋過 css/bootstrap.css:1086
         的 14px。這是實查值，不是估值（2026-08-14 複審 N-6）。
      ⚠️ 但 rem 的基準是 **10px 不是 16px**（依據見本檔開頭：css/bootstrap.css:1079-1080，
         由 main.master:41 載入）。2026-08-14 前這裡寫的 1.05／1.15／1.1rem 是照 16px 想的，
         實際只渲染出 10.5／11.5／11px，已全數換算成 1.68／1.84／1.76rem。
         ⛔ 改任何 .mn-* 字級前，先回頭讀本檔開頭那段基準說明，不要再憑 16px 心算。
      🔴 **顯示點四（web/buyrecord.aspx:1）走的是 member.master，那裡沒有 bootstrap**：
         member.master:5-12 只掛 Google Fonts ＋ css/web/member-shell-v2.css，
         **不載 bootstrap.min.css**——而 `html{font-size:10px}` 全站只有 bootstrap 那一處。
         ⟹ 在該頁 rem 基準會退回瀏覽器預設 16px，本檔的 1.68rem 會渲染成 26.9px 而非 16.8px，
         一樣**沒有任何錯誤提示**。⛔ Task 8 接上顯示點四前必須先處理，本輪只記錄不動。
   ⚠️ .iv2-buy-panel 是 padding:0 ＋ overflow:hidden（feature-v2.css:373-381），
      區塊內縮距離由本規則自己給：對齊 .iv2-actions 的 padding-inline
      （桌機 24px，feature-v2.css:452；≤980px 為 18px，:521），取 20px 折中，不另開 media query。
   ⚠️ ≤980px 時 .iv2-buy-panel 會變 position:static 並單欄堆疊（feature-v2.css:513-514），
      本區塊跟著走版不需要另外處理；至於懸浮條 .iv2-buy-fixed 這個「hero 之外的第三個購買入口」，
      已由本檔最下方的**位置③**（.iv2-buy-fixed .mn-strip-tag）另行涵蓋，不再是已知缺口。
   ============================================================ */
.iv2-buy-panel .mn-block {
  background: #FFFBEB;
  border: 1px solid #FDE68A;
  border-left: 4px solid #F59E0B;
  border-radius: 8px;
  padding: 0.85em 1em;
  margin: 16px 20px 0;
}

.iv2-buy-panel .mn-block.mn-error {
  background: #F9FAFB;
  border-color: #E5E7EB;
  border-left-color: #9CA3AF;
}

/* 展開按鈕。
   🔴 特異度必須到 (0,2,0)：feature-v2.css:85 的 `.iv2-root button { font: inherit; border: none;
      background: none; color: inherit }` 是 (0,1,1)，會壓過任何單一 class 的 font-size／border／
      background／color——單寫 `.mn-toggle` 這四個宣告全部是死宣告，按鈕會長得跟一段純文字一樣。
   ⚠️ font-size 1.84rem ＝ 專案規則的按鈕下限 18.4px（rem 基準 10px，見本檔開頭）。 */
.mn-block .mn-toggle {
  display: inline-block;
  margin-top: 0.5em;
  padding: 0.3em 0.9em;
  border: 1px solid #FCD34D;
  border-radius: 999px;
  background: #FFFFFF;
  color: #92400E;
  font-size: 1.84rem;
  font-weight: 600;
  line-height: 1.4;
  cursor: pointer;
}

.mn-block .mn-toggle:hover,
.mn-block .mn-toggle:focus {
  background: #FEF3C7;
  border-color: #F59E0B;
}

/* 收合態。
   🔴 不能只靠 hidden 屬性：舊瀏覽器（含部分 Android WebView）不套 UA 的 [hidden]{display:none}，
      沒有這一條的話完整長文會直接攤在購買面板裡，把加入購物車／直接結帳擠出首屏——
      那正是位置①改成摘要版要避免的失敗模式。
   特異度 (0,3,0)，勝過 normalize/bootstrap 的 `[hidden]`(0,1,0)（css/bootstrap.css:41-43）。
   ⚠️ 這裡不需要跟同檔 .mn-block .mn-content(0,2,0) 比 —— 那條**沒有宣告 display**，沒有東西要勝過
      （2026-08-14 複審 M-4：原註解多寫了這個不成立的理由，已刪）。 */
.mn-block .mn-detail[hidden] {
  display: none;
}

/* 展開後的說明與標題之間留一點距離（.mn-content 的 margin 為 0，此處只在展開版加回） */
.mn-block .mn-detail {
  margin-top: 0.5em;
}

/* ============================================================
   顯示點二 位置③（懸浮條 .iv2-buy-fixed，web/feature.aspx:174-182）：極簡揭露標籤
   ------------------------------------------------------------
   🔴 為什麼位置①還不夠（2026-08-14 使用者裁決「補」）：
      懸浮條在 ≤980px 顯示（css/web/feature-v2.css:895-896）、window.scrollY > 400 就出現
      （js/web/feature.js:61-64），內含可直接加入購物車的 #bfBuyBtn（feature.aspx:180）；
      而 ≤980px 時 hero grid 單欄堆疊（feature-v2.css:286-288），購買面板（<aside>）排在
      hero banner ＋ 完整海報 ＋ plan_infos 之後 ⟹ 捲 400px 時使用者大機率**還沒看到位置①**。
      這條標籤補的就是那條路徑上缺席的揭露；不擋購買動線，只多一個可點的入口。
   🔴 高度必須連同 body 的預留一起改（這一段是本區塊最容易靜默出事的地方）：
      feature-v2.css:897 的 `body { padding-bottom: 72px }` 是**寫死**的懸浮條預留高度，
      註解自己寫著「預留底部 strip 空間，避免遮住內容」。懸浮條多一列而預留沒跟著加大，
      頁尾內容會被遮住而且**不會有任何錯誤**。
      ⟹ 本檔另給 body.mn-has-strip-tag 的加大版（class 由 feature.js 在**真的插入標籤時**才加）。
      ⛔ 不直接把 feature-v2.css:897 的 72px 改大：那會讓**沒有提醒**的商品也多出一段頁尾空白。
   ⚠️ 波及範圍已查證：`.iv2-buy-fixed`／`#buyFixed` 全站只出現在 web/feature.aspx:174，
      feature-v2.css 也只有 web/feature.aspx:12 一處載入 ⟹ 這裡動 body padding 不會波及其他頁；
      顯示點一 plan.aspx 沒有這個節點，本段規則在該頁一條都不會命中。
   🔴 **凡是會改變懸浮條版型的規則，一律收進 `body.mn-has-strip-tag` 作用域**
      （2026-08-14 複審 N-1）：本檔由 web/feature.aspx:20 **無條件載入**（Task 5 拍板的既有代價），
      所以沒有作用域的 `.iv2-buy-fixed{flex-wrap:wrap}` 在**旗標關閉／本商品沒有提醒**時照樣生效，
      320px 級裝置上會讓一個完全沒有標籤的懸浮條裂成兩列，而 feature-v2.css:897 的 72px 預留
      不會跟著變 ⟹ 頁尾被遮，且**沒有任何錯誤訊息**。
      `mn-has-strip-tag` 由 js/web/feature.js 在**真的插入標籤時**才掛（見該檔位置③段落），
      沒有標籤就一條都不命中 ＝「旗標關閉時與現況完全一致」在 CSS 這一側也成立。
      ✅ 這個不變式可機械複查：本檔**每一個**選擇器都必須含 `mn-` 字樣（只有 at-rule 例外），
         把註解剝掉後 `grep "{" | grep -v "mn-"` 只該回 @media／@supports 這幾行，
         回到任何一個真正的選擇器就代表又出現一條無條件生效的副作用。
   ⚠️ 特異度：feature-v2.css:871-882 的 `.iv2-root .iv2-buy-fixed`(0,2,0) **沒有宣告 flex-wrap**，
      本檔 `body.mn-has-strip-tag .iv2-buy-fixed`(0,2,1) 本來就贏得過；
      `body.mn-has-strip-tag`(0,1,1) 也勝過 :897 的 `body`(0,0,1)。
      按鈕本體則必須寫到 (0,2,0)：feature-v2.css:85 的 `.iv2-root button`(0,1,1) 會壓過任何單一 class。
   ============================================================ */
body.mn-has-strip-tag .iv2-buy-fixed {
  flex-wrap: wrap;
}

/* 🔴 讓既有的「價格｜按鈕」恆為同一列 —— 懸浮條只准兩列（2026-08-14 複審 N-2 的根本解）。
   問題：flex-wrap 一開，320px 上會裂成**三列**（標籤／價格／按鈕），下方的預留式子要再加一列，
   而漏加的失敗方式一樣是靜默遮住頁尾。改成「不讓既有列裂開」，預留式子才穩得住。
   🔴 斷行是用 hypothetical main size（flex-basis 解析後的值）決定的，發生在 flex-shrink **之前**：
      · .iv2-bf-prices 目前是預設 flex:0 1 auto ⟹ basis = auto = max-content
      · .iv2-bf-actions 有 flex-shrink:0（feature-v2.css:887），內含 height:44px／padding:0 22px
        的 .iv2-btn（feature-v2.css:888）≈120px，不會被壓縮
      · 320px 視窗可用內容寬 = 320 − 16×2（feature-v2.css:876 的左右 padding）= 288px
      ⟹ 只要價格的 max-content > 288 − 12（feature-v2.css:879 的 gap）− 120 = 156px 就必定斷行。
   ⛔ 寫 `flex:1 1 auto` **沒有用**：basis 仍是 auto ＝ max-content，斷行判定完全不變。
      必須把 basis 設成 **0**，hypothetical main size 歸零 ⟹ 一定擠得進同一列，再靠 grow 撐開剩餘空間。
   ⚠️ min-width:0 是必要的搭配：flex item 的 min-width 預設 auto ＝ 自動最小尺寸（內容寬），
      沒有它，basis 0 依然會被內容最小寬撐回去。
   ⚠️ 特異度已查證無競爭：全站唯一命中 .iv2-bf-prices 的規則是 feature-v2.css:884
      `.iv2-root .iv2-buy-fixed .iv2-bf-prices`(0,3,0)，宣告的是 display／align-items／gap／flex-wrap，
      **沒有** flex／flex-basis／min-width（已 grep 全站 *.css 確認只有這一條）；
      本條寫成 (0,3,1) 另外還贏得過它。
   ⚠️ 連帶效應：.iv2-bf-prices 自己也是 flex 容器且 flex-wrap:wrap，basis 改 0 之後它拿到的是
      「剩餘空間」而非 max-content，內部可能摺成兩行 —— 已併進下方預留值的最壞情況推算，
      ⛔ 不得為了避免摺行去動 .iv2-bf-now／.iv2-bf-was 的字級（那會動到既有商品的價格顯示）。 */
body.mn-has-strip-tag .iv2-buy-fixed .iv2-bf-prices {
  flex: 1 1 0;
  min-width: 0;
}

/* 標籤自成一列（flex-basis:100%），排在既有的價格／按鈕那一列之前。
   ⚠️ 刻意**不**塞進既有那一列：懸浮條在 ≤980px 才變成 display:flex（feature-v2.css:896；
      feature-v2.css:878 寫的是 `display:none`，那是它的預設態，別再引錯），
      320px 寬的手機上價格 ＋「加入購物車」已幾乎填滿（實算見上一條：可用 288px，光按鈕就 ≈120px），
      再擠一個標籤會把按鈕壓到換行或截字。
   ⚠️ 字級 1.84rem ＝ **18.4px ＝ 專案的按鈕下限**（rem 基準 10px，見本檔開頭）。
      這是一顆 <button>，不能用內文下限 16.8px（2026-08-14 複審 N-5）；
      實算放得下：「教材版本提醒｜看說明」10 個全形字 ≈ 18.4×10 = 184px
      ＋ 左右 padding 0.9em×2 ≈ 33px ＋ 框線 2px ＝ 219px ≤ 288px，不必為此破例縮小。 */
.iv2-buy-fixed .mn-strip-tag {
  flex: 0 0 100%;
  width: 100%;
  display: block;
  text-align: left;
  padding: 0.3em 0.9em;
  border: 1px solid #FCD34D;
  border-radius: 999px;
  background: #FEF3C7;
  color: #92400E;
  font-size: 1.84rem;
  font-weight: 600;
  line-height: 1.4;
  cursor: pointer;
}

.iv2-buy-fixed .mn-strip-tag:hover,
.iv2-buy-fixed .mn-strip-tag:focus {
  background: #FDE68A;
  border-color: #F59E0B;
}

/* 只命中設定錯誤時降彩度（與 .mn-error 同一套語彙）。⛔ 一樣不得隱藏。 */
.iv2-buy-fixed .mn-strip-tag.mn-error {
  background: #F3F4F6;
  border-color: #D1D5DB;
  color: #4B5563;
}

/* 懸浮條加高後的頁尾預留 —— 2026-08-14 複審 N-2／N-5／N-7 後**整條重算**，⛔ 不是沿用舊的 124px。
   ⚠️ 誠實標註：下面三個數字（標籤列高、既有列高、總預留）**都是靜態推算**，
      最終仍需在 ≤360px 的 DevTools／真機目視驗收。每一項都標了來源行號，可直接複算並就地調整。

   量測環境：≤980px 懸浮條才 display:flex（feature-v2.css:895-896）；
             ≤600px 時 .iv2-root 字級為 16px（feature-v2.css:99-100），以下 em 一律以 16px 換算；
             以最窄的 320px 視窗計，可用內容寬 = 320 − 16×2 = 288px（feature-v2.css:876 的 padding）。

   (a) 懸浮條外框
       border-top 1px                          （feature-v2.css:875）
       padding-top 10px ＋ padding-bottom 10px  （feature-v2.css:876；bottom 另含 env(safe-area)，
                                                 故先不計入，最後用 calc 加回）        = 21px
   (b) 標籤列（本檔 .iv2-buy-fixed .mn-strip-tag）
       18.4px（font-size 1.84rem，N-5 從 1.68rem 拉到按鈕下限）× line-height 1.4 = 25.76
       ＋ padding 0.3em×2 = 11.04 ＋ border 1px×2 = 2                              = 38.8px
   (c) 兩列之間的 row-gap 12px（feature-v2.css:879 的 gap，wrap 後同時作用於 row）      = 12px
   (d) 既有的「價格｜按鈕」列 —— 取**最壞情況**（因為漏算是靜默遮住頁尾，多算只是多一點空白）：
       · 按鈕側：.iv2-bf-actions 內 .iv2-btn 的 height:44px（feature-v2.css:888）
       · 價格側：上面那條把 .iv2-bf-prices 的 basis 改成 0 之後，它拿到的是「剩餘空間」
         ≈ 288 − 120（按鈕，feature-v2.css:888 的 padding 0 22px ＋ 5 個 0.95em 全形字）
           − 12（gap） = 156px；
         而它自己是 flex 容器且 flex-wrap:wrap（feature-v2.css:884），現價＋刪除線原價的
         max-content 在六位數價格時會逼近 156px ⟹ **可能**摺成兩行。摺行時：
           現價 20px（.iv2-bf-now 1.25em×16，line-height:1，feature-v2.css:885）
         ＋ row-gap 8px（feature-v2.css:884 的 gap）
         ＋ 原價 21px（.iv2-bf-was 0.85em×16 = 13.6px，行高繼承 css/style.css:54 的
                       line-height:21px —— 絕對長度會原值繼承，不隨字級縮）
         = 49px（> 按鈕側的 44px，故此列由價格側決定）                              = 49px
       ⟹ 不摺行時此列只有 44px，多預留的 5px 不會出事；反過來漏算就會靜默遮住頁尾。

   合計 = 21 + 38.8 + 12 + 49 = **120.8px** ＝ 下面的 `--mn-strip-h`（懸浮條實高，本檔唯一來源）。

   🔴 這個實高有**兩個**消費端，⛔ 不得各寫各的魔術數字（改一個忘另一個是必然）：
      (1) body 的 padding-bottom ＝ 實高 ＋ **7.2px 餘裕** ＝ 128px
          餘裕口徑沿用既有值：feature-v2.css:897 的 72px 對應實高 1+10+44+10 = 65px，餘裕也是 7px。
      (2) `--fab-base`（右側浮動鈕堆疊的底部錨點）＝ 實高 ＋ **21px 間距**（2026-08-14 複審 N-10）
          ⚠️ 這 21px 同樣不是新發明：feature-v2.css:864 的 `--fab-base: 86px` 就是為舊懸浮條校準的，
             86 − 65（舊實高）= 21px ＝ 既有的視覺間距，本檔只是把同一口徑套到新實高上。
          🔴 不改它會怎樣：token 定義在 css/style.css:4725-4731（`:root`，`--fab-base:24px`），
             ≤980px 由 feature-v2.css:864 覆寫成 86px；懸浮條從 65px 長到 120.8px 而 86px 沒動，
             最底部的置頂鈕（css/style.css:4765 `bottom: var(--fab-base)`）會插進懸浮條約 35px，
             LINE（css/style.css:4737）與購物車（feature-v2.css:818）跟著整組偏低。**一樣是靜默**。
          ⚠️ 繼承方向已查證可行：既有兩處定義都在 `:root`（＝ html），我們定義在 `body`；
             三個消費端 `.back-to-top`／`#pop_social_icon`／`.iv2-root .iv2-float-cart` 都是 body 的
             子孫（position:fixed 不影響繼承，繼承走 DOM 樹），且全站沒有更近的祖先重新定義它。
          ⚠️ 顯示點一 plan.aspx 也消費 `--fab-base`（css/web/plan-v2.css:702），但該頁沒有懸浮條、
             `mn-has-strip-tag` 也只在 js/web/feature.js 內 addClass ⟹ 完全不受影響。

   🔴 env(safe-area-inset-bottom) 必須加回來（2026-08-14 複審 N-7）：懸浮條自己的 padding-bottom
      含它（feature-v2.css:876），我們的預留不含就會在有家用鍵區的 iPhone 上短少一截，
      失敗方式一樣是**靜默**遮住頁尾。
   ⚠️ safe-area 只能加**一次**：`--fab-base` 是**取代**掉 feature-v2.css:864 那個已含 env() 的值，
      不是疊加，所以新值自己含一次即可，⛔ 不要在消費端再加。
   ⛔ 不直接改 feature-v2.css:864／:897：那是「沒有提醒」時的既有行為，不在本案範圍，
      而且該檔不在版控內，改了 git 也看不到。
   🔴 ≤360px 的 DevTools／真機驗收時，三個鈕（置頂／LINE／購物車）都必須**不碰**懸浮條；
      旗標關閉或無提醒時 `--fab-base` 必須維持 86px、FAB 位置完全不變。 */
@media (max-width: 980px) {
  body.mn-has-strip-tag {
    /* 懸浮條實高（推算式見上方註解）——本檔唯一來源，兩個消費端都由它推導 */
    --mn-strip-h: 120.8px;
    /* safe-area 的保守預設；支援 env() 的瀏覽器由下方 @supports 換成實際值 */
    --mn-safe-b: 0px;

    /* 頁尾預留 ＝ 實高 ＋ 7.2px 餘裕 ＝ 128px（＋ safe-area） */
    padding-bottom: calc(var(--mn-strip-h) + 7.2px + var(--mn-safe-b));

    /* 右側浮動鈕堆疊 ＝ 實高 ＋ 21px 既有間距口徑（86 − 65 = 21，見 feature-v2.css:864） */
    --fab-base: calc(var(--mn-strip-h) + 21px + var(--mn-safe-b));
  }
}

/* 🔴 為什麼 env() 要繞這一層 `--mn-safe-b`，而不是直接寫進上面兩條 calc()：
   含 `var()` 的宣告在**解析階段一律視為有效**，要到 computed-value 階段才會因為 env() 不被支援而失效；
   而「computed-value 階段失效」對非繼承屬性 padding-bottom 的結果是退回**初始值 0**
   ——不是退回同區塊上一行的字面值，也不是退回 feature-v2.css:897 的 72px。
   ⟹ 「同一屬性多寫一行字面值當退路」這招一旦混用 var() 就**不成立**，
      只會把預留靜默變成 0 並遮住頁尾，比不修還糟。
   改用 @supports 做特徵偵測：不支援 env() 的瀏覽器（有 var() 沒 env() 的世代確實存在）
   拿到 0px，兩條 calc() 都算得出數值；支援的則拿到真實 inset。
   ⚠️ 這一段的作用域仍然是 body.mn-has-strip-tag，沒有提醒時一條都不命中（同 N-1 的紅線）。

   🔴 2026-08-14 複審 Minor：**@supports 只能驗證宣告語法，不保證那個環境變數真的存在。**
      `env()` 對「未知變數名」在語法上完全合法 ⟹ @supports 這一關會通過，
      但到了 computed-value 階段才發現變數不存在。此時自訂屬性 `--mn-safe-b` 會取得
      **guaranteed-invalid** 值 ⟹ 上面兩條 var(--mn-safe-b) 的消費端一起失效
      ⟹ padding-bottom 退回初始值 0 ——**正是本段一開始要避免的那個失敗**，@supports 擋不住。
      故改用規格提供的第二引數 `env(<name>, <fallback>)`（CSS Environment Variables Module Level 1）：
      變數不存在時直接取 0px，不會產生 guaranteed-invalid，這一層洞才真正補起來。
      ⛔ 兩處都要帶 fallback：@supports 條件裡帶是為了與宣告寫法一致（避免日後有人只改一邊），
         真正起作用的是宣告那一行。 */
@supports (padding: env(safe-area-inset-bottom, 0px)) {
  @media (max-width: 980px) {
    body.mn-has-strip-tag {
      --mn-safe-b: env(safe-area-inset-bottom, 0px);
    }
  }
}
