/* ==========================================================================
   首頁專屬版面
   --------------------------------------------------------------------------
   🔴 視覺與排版來源＝舊版 demo。2026-08-13 R1c 整份重寫。
      ⚠️ `Demo網站/` 已於 2026-08-17 刪除（T6）：`git show 07596d6:Demo網站/index.css`
      客戶看過多頁後表示比較喜歡 demo 的風格，PM 定案「首頁排版全部照 demo」。

   ⚠️ 這裡只放首頁的**版面**。共用元件（按鈕、標題群、卡片、頁籤、輪播控制、
      家庭樹卡）一律在 style.css §11 —— 複製到這裡會產生隱形分叉。
   ⚠️ 不照抄 demo 的兩件事：
        ① clamp() 流體字級 → 字級放大按鈕會對它失效且不報錯（CLAUDE.md 9.11）
        ② 珊瑚粉底白字 → 1.78:1，長輩讀不到（CLAUDE.md 3.1）
   ========================================================================== */

/* ==========================================================================
   ① 主視覺 .hero
   demo：min-height 75vh、底圖 opacity+scale 交叉淡入、左側漸層 overlay、
        兩側細線大箭頭釘在垂直中央。
   ========================================================================== */
.hero {
  position: relative;
  /* 🔴 2026-08-14 R2m⑤：75vh → 82vh（PM：高度增加一點）。
     ⚠️ 9.4 的坑：`aspect-ratio` 不可與 `max-height` 併用（會反過來縮寬度）。
        這裡本來就是直接給高度，所以調這個值即可，不要改寫成 aspect-ratio。

     🔴 **這一項會同時改變兩件事，實測過才定案**：
     ① **圖片裁切**——容器變高＝更接近圖片比例，桌機裁得**更少**：
        1920×1080 下三張從「裁 24／36／36%」變成「裁 17／30／30%」。
     ② **手機不跟著加高**（見下方 767 斷點維持 60vh）：
        直向螢幕加高會裁得**更多**（375×812 從 67% → 70%），
        而且會把下方內容推出首屏——高度佔比在手機最吃緊。 */
  min-height: 82vh;
  display: flex;
  align-items: center;
  overflow: hidden;
  background-color: var(--c-maroon);   /* 圖還沒到位時是深底，白字仍然讀得到 */
}

.hero__slides { position: absolute; inset: 0; z-index: 0; }

.hero__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transform: scale(1.08);
  transition: opacity 0.9s ease-out, transform 1.4s ease-out;
}
.hero__slide.is-active { opacity: 1; transform: scale(1); }

.hero__img { width: 100%; height: 100%; }

/* 🔴 濃度不是從 demo 抄的，是回推的：白字要 ≥4.5:1，最亮的照片也得撐得住。
   文字在左側，所以左濃右淡——右邊留給照片本身。 */
.hero__overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--overlay-hero);
}

/* 🔴 R7-14（2026-08-18）：主視覺文字被左右箭頭擋到。
   **實測臨界點是 1520px，不是 PM 說的 1440**——1520 以下左箭頭的右緣就開始壓到文字
   （1500px 時：文字左緣 63、箭頭右緣 72，重疊 9px）。所以**整個區間都要處理，不是只改 1440**。

   **算法**：箭頭佔 `left: 8px` ＋ `min-width: 56px` ＝ 到 64px，hover 再位移 4px → **文字左緣至少要 72px**。
   而 `.wrap` 的內容左緣 ＝ `max(0, (100vw − 1400px) / 2) + padding`。
   → 需要的 padding ＝ 72px 減掉 wrap 自己已經讓出的那一段，且不小於原本的 `--container-pad`。

   ⚠️ **用 `max()` 讓它自己收斂，不要寫死斷點**：
      1920 → 已讓出 260px，取 `--container-pad`（20），**寬螢幕的版面完全不變**；
      1440 → 已讓出 20px，補到 52px；1200 → 沒讓出，補滿 72px。
   ⚠️ 899px 以下箭頭已經移到下方置中（見響應式），那裡要還原成一般內距，否則文字白白內縮。
   ⚠️ `CLAUDE.md` 6.4 記過手機版的同一件事（「箭頭留在兩側要給文字讓出左右各 56px」），
      **桌機這一段當時沒處理**——這次補上。 */
.hero__content {
  position: relative;
  z-index: 2;
  width: 100%;
  /* 🔴 用 `100%` 不是 `100vw`——**`100vw` 含捲軸寬度**，1440px 的視窗實際版面只有 1425，
     用 vw 會少算 7.5px，實測 1440／1500 仍然重疊。
     `padding-inline` 的百分比是相對「containing block（`.hero`）的寬度」＝版面寬，不含捲軸。

     🔴 **R10-03／04（PM 2026-08-24）：左緣再往內縮、右緣放寬到容器邊。**
     ~~兩側共用一個 `padding-inline`~~ 已作廢，改成**兩側各自負責**：

     ・**左＝閃避左箭頭（R10-03）。4.5rem(72px) → 7.5rem(120px)。**
       🔴 **R7-14 取 72px 是算錯的**：那次的註解寫「箭頭 `left: 8px` ＋ `min-width: 56px` ＝
          到 64px，所以文字左緣至少 72px」，**但 `min-width` 沒有生效**——
          箭頭的實際寬度是 64px（svg `2.5rem` ＋ 左右 `--sp-3` 內距 ＝ 40+24），
          右緣落在 8+64 ＝ **72px**。於是「讓出 72px」實際上是**貼著箭頭、間距 0**。
          1440 實測：文字左緣 72、左箭頭右緣 72 → **PM 說「太近」是準確的，不是感覺問題。**
          改成 120px 之後間距 48px。
       ⚠️ prev 的 hover 是 `translate(-0.25rem)`＝往**左**離開文字，不必再預留。

     ・**右＝只留一般容器內距（R10-04 要的加寬）。**
       原本 `padding-inline` 是兩側同值，1440 時右邊也被塞了 52px，白白吃掉內容寬度。

     ⚠️ 用 `padding-inline-start/end` 不用 `padding-left/right`——767 以下那條還原規則寫的是
        `padding-inline` 簡寫，**同為邏輯屬性才蓋得乾淨**（物理與邏輯混用時勝負只看宣告順序，
        看起來會像「有寫卻沒生效」）。 */
  padding-inline-start: max(
    var(--container-pad),
    calc(7.5rem - max(0px, (100% - var(--container-max)) / 2))
  );
  padding-inline-end: var(--container-pad);
}
/* 🔴 R10-04：~~`max-width: 42rem`（672px）~~ 移除。
   **`rem` 不會跟著字級三檔變**（rem 綁的是瀏覽器根字級，三檔換的是 `--fs-*`），
   所以 672px 這個框只有「預設」檔夠用。實測內文最長一句 **28 個字**：
     22px 檔要 616px ✅／25px 檔要 700px ❌／28px 檔要 784px ❌
   → 「中」「大」檔提前斷行（實測第三張在「中」檔就變 3 行、「大」檔 4 行）。
   改成由 `.hero__lead` 自己用 `em` 限寬——em 綁的是它自己的 `--fs-lead`，
   **三檔的每行字數因此完全一樣**（與 `.cta__lead` 是同一個做法與同一個理由）。 */
.hero__body { max-width: none; }

.hero__title {
  font-size: var(--fs-hero);
  font-weight: var(--fw-bold);
  color: var(--c-white);
  text-shadow: 0 2px 16px rgba(46, 25, 25, 0.35);
}
/* 三張各一顆按鈕（R2f）。
   ⚠️ 要一致的只有**對齊方式**（三張都靠左）——位置因文字量上下浮動是可接受的，
      不要為了強制固定而多寫程式碼（PM 2026-08-14）。 */
.hero__actions {
  margin-top: var(--sp-8);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
}

.hero__lead {
  margin-top: var(--sp-6);
  /* 🔴 R10-04：限寬移到這裡，且用 `em` 不用 `rem`／`px`。
     內文最長一句 28 字，29em 留一個字的餘裕 → **三檔都是一句一行、每行最多 29 字**
     （中文舒適區 30–36 字，29 在區間內，不會變成又長又難讀的一行）。 */
  max-width: 29em;
  font-size: var(--fs-lead);
  line-height: var(--lh-lead);
  color: var(--c-white);
  text-shadow: 0 1px 8px rgba(46, 25, 25, 0.3);
}

/* 🔴 R11-06：箭頭與 dots 的外層容器。
   **桌機用 `display: contents` ＝ 這個 div 不生成任何盒子**，三個子元素照舊
   各自對 `.hero` 絕對定位（箭頭兩側垂直置中、dots 下方置中），桌機一個 px 都不變。
   ⚠️ **不可改成「不寫任何樣式」**：那樣它會變成 `.hero` 這個 flex 容器的一個 flex item，
      雖然子元素都是絕對定位、量起來是 0×0，但它仍會參與 flex 的分配，
      **而那種影響小到不會有人發現，卻是真的存在**。手機那一段見 767 斷點。 */
.hero__ctrl { display: contents; }

/* ==========================================================================
   輪播控制列（PM 2026-09-01 統一規格）
   --------------------------------------------------------------------------
   **左箭頭 ● dots ● 右箭頭，三者一列**，首頁三個輪播（家庭天地／最新消息／活動相簿）
   與 hero 共用同一種排法。PM 原話：「否則現在手機版首頁就有好幾種箭頭和 dots 樣式」。
   🔴 **手法與 `.hero__ctrl` 相同**：桌機 `display: contents`，這一層在版面上等於不存在，
      箭頭照舊絕對定位在兩側、圓點照舊是流內元素留在原位
      → **桌機一個 px 都不會動**（PM 指定只改手機版）。
   ⚠️ 斷點沿用本檔既有的 899（不是全站的 767）：那正是這三個輪播原本就切成
      「箭頭移到下方」的位置，改用別的值會讓 768–899 這一段跟著變。
   ========================================================================== */
.carousel-ctrl { display: contents; }

/* 🔴 **`.carousel-group` 在桌機是 `display: contents`——在版面上完全不存在**，
   因此包上這一層之後桌機與改動前一模一樣（PM 指定其他尺寸不可變動）。
   它存在的理由只有一個：**家庭天地的圓點與箭頭分屬兩層**
   （箭頭在 `.carousel` 內、圓點在外），不攤平就排不進同一列。
   ⚠️ 曾試過「把圓點移進 `.carousel` 再用 margin 補回空間」——箭頭的 `top` 是百分比，
      容器一被撐高就跑掉，補償值也對不準（實測圓點差 16px、下一區上移 56px）。
      **凡是「桌機一個 px 都不能動」的重排，用 `display: contents` 包一層才是零風險的作法。** */
.carousel-group { display: contents; }

/* 🔴 PM 2026-09-01：**手機版的箭頭一律用 hero 那一支圖示**。
   兩支圖示形狀相同但比例與線寬不同（`icon-arrow-long-*` 是 96×24／線寬 2，
   `icon-hero-*` 是 40×16／線寬 1.5），**單純把前者縮到 40×16 會變形**
   （`preserveAspectRatio` 會以寬為準等比縮成 40×10，線也跟著細成 0.83）。
   所以按鈕裡放兩支 svg 由 CSS 切換——`<use href>` 是 HTML，換不掉。
   ⚠️ 桌機顯示 `--wide`、手機顯示 `--slim`，桌機因此一個 px 都不會動。 */
.carousel-arrow__slim { display: none; }

/* 最新消息的圓點是這次新增的，**桌機維持原本的「只有箭頭」**。 */
.news__dots { display: none; }

/* 左右箭頭：demo 是細線長箭頭、貼在畫面兩側
   ⚠️ 點擊區靠 padding 撐到 ≥44×44（08-04 長輩友善準則②），不是只有那條線。 */
.hero__arrow {
  position: absolute;
  top: 50%;
  z-index: 3;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 3.5rem;
  min-height: 3.5rem;
  padding: var(--sp-3);
  color: var(--c-white);
  opacity: 0.85;
  transition: opacity var(--dur-base) var(--ease), transform var(--dur-base) var(--ease);
}
.hero__arrow:hover { opacity: 1; }
.hero__arrow svg { width: 2.5rem; height: 1rem; }
.hero__arrow--prev { left: var(--sp-2); }
.hero__arrow--next { right: var(--sp-2); }
.hero__arrow--prev:hover { transform: translate(-0.25rem, -50%); }
.hero__arrow--next:hover { transform: translate(0.25rem, -50%); }

/* 🔴 圓點指示 demo 沒有，我方加上——08-04 長輩友善準則①：
   左右箭頭與圓點指示必須明顯可見，不可只靠滑動手勢（長輩不會滑）。 */
.hero__dots {
  position: absolute;
  z-index: 3;
  left: 50%;
  bottom: var(--sp-6);
  transform: translateX(-50%);
}
.hero__dots .carousel-dot { background-color: rgba(255, 255, 255, 0.55); }
.hero__dots .carousel-dot[aria-current="true"] { background-color: var(--c-white); }

/* --------------------------------------------------------------------------
   主視覺的進場動畫（2026-09-01）
   --------------------------------------------------------------------------
   🔴 **不是 AOS，是載入即播的 CSS animation** —— `.hero` 身上的 `.no-anim` 留著，
      它擋的是「捲動觸發的出場動畫」（style.css 第 8 節鐵則②），那條約束沒有放寬：
      捲動觸發＝一進站先看到空白再淡入，而這裡是**載入當下就開始播**，
      使用者不會有任何一刻在等內容出現。與 style.css 8.6.4 的 `.page-head` 同一個做法。
   🔴 一樣掛在 `.anim-on` 底下（鐵則③）：JS 沒跑就完全不播，文字直接是最終狀態。

   ⚠️ 為什麼原本「沒有進場動畫」：第一張 slide 是伺服器端就帶著 `is-active` 印出來的，
      而 `home.js` 的 `go(0)` 只是 `classList.toggle('is-active', true)` ——
      class 本來就在，**沒有狀態變化就不會觸發 transition**。
      `.hero__slide` 那組 0.9s／1.4s 的 transition 只在「換張」時才看得到。
   -------------------------------------------------------------------------- */

/* 第一張的緩慢推近。⚠️ 掛在裡層的 `<img>`，不是 `.hero__slide` ——
   slide 自己的 `transform` 是換張動畫在用的，動它會打架。
   ⚠️ 只給 `:first-child`：第 2、3 張由 JS 插入，它們的進場是換張的 transition，
      不需要也不應該再播一次載入動畫。 */
@keyframes lin-hero-zoom {
  from { transform: scale(1.1); }
  to   { transform: scale(1); }
}
.anim-on .hero__slide:first-child .hero__img {
  animation: lin-hero-zoom 2.4s var(--ease) both;
}

/* 文字：標題 → 內文 → 按鈕依序浮上來。
   ⚠️ `lin-head-in` 定義在 style.css 8.6.4（keyframes 是全域的，不必再寫一份）。
      內頁頁首用的是同一支，兩處的節奏刻意一致。 */
.anim-on .hero__title,
.anim-on .hero__lead,
.anim-on .hero__actions {
  animation: lin-head-in 0.7s var(--ease) both;
}
.anim-on .hero__title   { animation-delay: 0.10s; }
.anim-on .hero__lead    { animation-delay: 0.26s; }
.anim-on .hero__actions { animation-delay: 0.42s; }

/* 箭頭與圓點最後才浮現 —— 它們是操作元件，比內容晚一步出現才不會搶戲。
   🔴 **只淡入、不可碰 transform**：`.hero__arrow` 用 `translateY(-50%)`、
      `.hero__dots` 用 `translateX(-50%)` **定位**，動畫一旦寫 transform 就會把定位
      整條蓋掉，箭頭會從別的位置飛進來（同 style.css 8.6.2 ④ 記的那個坑）。
   🔴 `@keyframes` 刻意**只寫 `from`**：沒有 `to` 時終點就是元素自己的計算值，
      於是 `.hero__arrow` 的 `opacity: 0.85` 會被保留。寫死 `to { opacity: 1 }`
      會讓箭頭永遠停在全不透明，比正常狀態亮一階。
   ⚠️ 不掛在 `.hero__ctrl` 上：它在桌機是 `display: contents`，**不生成盒子**，
      opacity／animation 對它一律無效（同 `.gallery > li` 的坑）。 */
@keyframes lin-hero-fade { from { opacity: 0; } }
.anim-on .hero__arrow,
.anim-on .hero__dots {
  animation: lin-hero-fade 0.6s var(--ease) 0.6s both;
}

/* 換張時文字重播一次淡入（由 `home.js` 的 `go()` 加 `.is-changing` 觸發）。
   🔴 **這一組刻意不寫 `animation-fill-mode`**：沒有 fill-mode 時，
      動畫沒播／播完之後元素都是它原本的樣子。
      如果寫了 `both`，一旦動畫因為任何理由沒真的跑起來（例如減少動態效果把
      `animation` 設成 none 之外的中間狀態），文字就會卡在 `from` 的 opacity: 0
      —— **主視覺標題整個消失**，那是這一頁最不能出的事。
   ⚠️ 這一組的權重要壓過上面那組載入動畫（多一個 class 就夠），
      否則加了 `.is-changing` 也還是播載入那一支（它有 `both`，會從頭再演一次延遲）。 */
@keyframes lin-hero-swap {
  from { opacity: 0; transform: translate3d(0, 0.75rem, 0); }
  to   { opacity: 1; transform: none; }
}
.anim-on .hero__body.is-changing .hero__title,
.anim-on .hero__body.is-changing .hero__lead,
.anim-on .hero__body.is-changing .hero__actions {
  animation: lin-hero-swap 0.5s var(--ease);
}
.anim-on .hero__body.is-changing .hero__lead    { animation-delay: 0.07s; }
.anim-on .hero__body.is-changing .hero__actions { animation-delay: 0.14s; }

/* 🔴 逃生門：減少動態效果時全部不播。
   style.css 8.6.7 那一段只管 `[data-aos]`，**管不到這裡**（本段是 animation 不是 AOS），
   所以必須在這裡自己補一份。漏了的話，開了「減少動態效果」的訪客
   會看到主視覺照樣推近、文字照樣飛進來。 */
@media (prefers-reduced-motion: reduce) {
  .anim-on .hero__slide:first-child .hero__img,
  .anim-on .hero__title,
  .anim-on .hero__lead,
  .anim-on .hero__actions,
  .anim-on .hero__arrow,
  .anim-on .hero__dots,
  .anim-on .hero__body.is-changing .hero__title,
  .anim-on .hero__body.is-changing .hero__lead,
  .anim-on .hero__body.is-changing .hero__actions {
    animation: none;
  }
}

/* ==========================================================================
   ② 簡介 .about（左圖右文）
   demo：grid 1fr 1fr、gap 64、圖後方墊一塊往左下偏移的奶油色塊、右下角葉子出血。
   ========================================================================== */
/* 額外的下方留白，容納往下移的葉子裝飾（見 .about__decor）。
   ⚠️ 不夠的話葉子會被下一區蓋掉半截——那看起來像圖破了，不像設計。 */
.about { padding-bottom: calc(var(--section-pad) + 3.5rem); }

.about__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-16);
  align-items: center;
}

/* 偏移色塊：demo 的 top 40 / left -40。
   🔴 R9-10（PM 2026-08-19）：**不再貼齊視窗，要有間距。**
      ✅ **這推翻了 R7-稽核的「不要修」結論**——那份報告把它歸進 C 類「刻意的出血」，
         **但 PM 看過實際畫面後判定要改。設計判斷以 PM 為準。**
   **根因**：原本寫死 `left: -40px`，而 `.wrap` 的留白 **1920 有 273px、1440 只剩 33px**
      → 40 > 33 就超出去、被 `main { overflow-x: clip }` 裁掉，而且不會報錯。

   🔴 **R10-01（PM 2026-08-24）：「色塊在中間寬度會跑位」——R9-10 的收斂留下的後遺症。**
   ~~top 固定 `--sp-10`、left 用 `min(--sp-10, --decor-bleed)`~~ 已作廢。
   **實測根因＝兩軸的收斂行為不同步**（`--fs` 預設檔，量 `.about__backdrop` 對 `.about__img`）：

     | 視窗 | 左偏移 | 下偏移 | 看起來 |
     |------|-------|-------|--------|
     | 1920／1600／1520 | 40 | 40 | 對稱 45° ✅ |
     | 1440 | **8** | 40 | 歪斜 |
     | 1280／1024 | **0** | 40 | 完全不往左，只剩下緣一條 |

   **改法：斜角量統一由 `--backdrop-gap` 提供，兩軸同一個值 → 任何寬度都是 45°。**
   收斂沒有取消（R9-10 的定案仍在），只是**收斂的對象換成「往容器外浮出的那一段」**
   （`--backdrop-bleed`）；浮不出去的部分改由**照片自己往內讓**（`--backdrop-inset`），
   **斜角本身不縮**。代價是 1400px 以下照片會窄 40px（1280 實測 580→540，約 7%）。

   🔴 **被否決：兩軸都寫 `min(--sp-10, --decor-bleed)`**（＝最直覺、也是 PM 提的那一版）。
      `--decor-bleed` 在 1400px 以下**恆為 0** → 位移 0 → **色塊整個躲到照片後面消失**，
      而 1440／1280 正是最多人用的筆電寬度。斜角是一致了，但東西不見了。
   🔴 **被否決：改寫成 `.about__media::before` ＋ `inset` 負值**（PM 提的另一版）。
      `::before` 的定位基準一樣是 `.about__media` 的 padding box，**與現在完全相同**，
      解不掉「往外浮出多少」這件事；卻要動 `index.html` 拿掉那個 div，換不到任何東西。 */
.about__media {
  position: relative;
  --backdrop-gap: var(--sp-10);                                         /* 色塊相對照片的位移＝斜角 */
  --backdrop-bleed: min(var(--backdrop-gap), var(--decor-bleed));       /* 其中可以浮到容器外的部分 */
  --backdrop-inset: calc(var(--backdrop-gap) - var(--backdrop-bleed));  /* 浮不出去、由照片讓出的部分 */
  padding-left: var(--backdrop-inset);
}
.about__backdrop {
  position: absolute;
  /* ⚠️ 絕對定位的基準是 `.about__media` 的 **padding box**（＝grid 欄位外緣），
     照片則在 content box（被 `padding-left` 往右推）。所以：
        色塊左緣 ＝ 欄位外緣 − bleed　／　照片左緣 ＝ 欄位外緣 + inset
     → 兩者恆差 `bleed + inset` ＝ `--backdrop-gap`，與視窗寬無關。（判斷基準見 CLAUDE.md 9.22） */
  top: var(--backdrop-gap);
  left: calc(var(--backdrop-bleed) * -1);
  width: calc(100% - var(--backdrop-inset));   /* 與照片同寬（100% 是 padding box 寬，不是照片寬） */
  height: 100%;
  z-index: 0;
  background-color: var(--c-sand);
}
.about__img {
  position: relative;
  z-index: 1;
  width: 100%;
  aspect-ratio: 3 / 2.2;
  object-fit: cover;
  /* 🔴 直角，不加圓角：demo 的照片一律直角，那是這套視覺的特徵。 */
}

.about__text { position: relative; }
.about__cta { margin-top: var(--sp-8); }

/* 手繪葉子出血：溫度感的主要來源之一。
   🔴 2026-08-14 R2l④：往下移（PM：現在和內文太靠近）。
      實測原本葉子頂端比按鈕底端還高 62px（是重疊，不是靠近），水平也壓進文字區 30px。

   ⚠️ **用 `top: 100%` 而不是調 `bottom` 的負值**——
      調 bottom 要算「葉子高 ＋ 想留的間距」，那個值綁在圖片的實際高度上，
      換一張比例不同的裝飾圖就會重新壓到文字，而且不會報錯。
      `top: 100%` 是「從文字區底端開始」，與圖片高度無關。
   ⚠️ 連帶要給 .about 額外的下方留白容納它，否則葉子會伸進下一區、
      被 .services 的 z-index 蓋掉半截（demo 也是這樣做的：.about-section 有
      比一般區塊更大的 padding-bottom）。 */
/* 🔴 R9-11（PM 2026-08-19）：「有插圖地方用筆電大小瀏覽時，會被視窗切掉一半。」
   實測就是這樣：右緣離視窗 **1920 有 212.5px、1440 是 −27.5px（裁掉 27.5px）**。
   **與 R9-10 同一個技術根因**（固定負值 ＋ 留白會隨視窗變化），因此**用同一套收斂寫法**，
   不各寫一份。實測改後：1920／1600 維持 60px 不變；1440 收斂成 8px，離視窗 24.5px。
   ⚠️ 基準是 `.about__text`（grid 子欄）→ 同樣直接用 `var(--decor-bleed)`。 */
.about__decor {
  position: absolute;
  right: calc(min(var(--sp-15), var(--decor-bleed)) * -1);
  top: 100%;
  margin-top: var(--sp-5);
  width: 5.625rem;
  height: auto;
  opacity: 0.9;
  pointer-events: none;
}

/* ==========================================================================
   ③ 三項介紹卡 .services
   demo：三欄、圓形淡底內置手繪線稿插圖、水墨筆刷從左下出血並蓋過下一區交界。
   ========================================================================== */
.services {
  position: relative;
  z-index: 1;   /* 自成堆疊環境：整區（含裝飾）蓋在下一區之上 */
}

.services__decor {
  position: absolute;
  left: -7.5rem;
  bottom: -6.875rem;
  width: 20rem;
  height: auto;
  z-index: 1;
  opacity: 0.9;
  pointer-events: none;
}

.services__grid {
  position: relative;
  z-index: 2;   /* 卡片內容蓋在裝飾之上 */
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-12);
}

.service-card { text-align: center; }

/* 🔴 圓底用純白不是奶油：本區的區塊底已經是奶油（.section--alt），
   再填奶油就看不出圓形。要換區塊底色時這裡要一起看。 */
.service-card__icon {
  width: 10rem;
  height: 10rem;
  margin-inline: auto;
  border-radius: var(--r-round);
  background-color: var(--bg-card);
  display: flex;
  align-items: center;
  justify-content: center;
}
/* ⚠️ 這是「插圖」不是「圖示」，不適用「圖示只准 Lucide」的鐵則——
   手繪線稿感正是客戶喜歡的那個「感覺」，不要換成 Lucide。 */
.service-card__icon img { width: 6.25rem; height: 6.25rem; object-fit: contain; }

.service-card__title {
  margin-top: var(--sp-7);
  font-size: var(--fs-lead);
  color: var(--text-heading);
}
.service-card__body {
  margin-top: var(--sp-4);
  font-size: var(--fs-sm);
  line-height: var(--lh-prose);
  color: var(--text-main);
}
/* 「了解更多 →」：hover 時箭頭往右推開，是 demo 的小動作 */
.service-card__link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 2.75rem;          /* 點擊區 ≥44px */
  margin-top: var(--sp-4);
  color: var(--c-berry);        /* demo 用 #fc938e（2.08:1）→ 改莓紅 4.56:1 on 奶油 */
  font-size: var(--fs-sm);
  transition: gap var(--dur-base) var(--ease);
}
.service-card__link:hover { gap: var(--sp-3); }
/* R2l⑤：與 .btn__arrow 同——原本是 16×2px 的線，改用 Lucide 後要方形比例 */
.service-card__link .btn__arrow { width: 1.125em; height: 1.125em; }

/* ==========================================================================
   ④⑦ 輪播的共用外殼 .carousel
   ⚠️ 家庭樹卡與最新消息用同一組——demo 的 .family-carousel 與 .events-carousel
      也是同一套，只是它複製了兩份 CSS。我方合成一份。
   箭頭一半疊在卡片上、一半露出在區塊邊界外，這是 demo 辨識度最高的細節之一。
   ========================================================================== */
.carousel {
  position: relative;
  margin-top: var(--sp-14);
}
.carousel__viewport { overflow: hidden; }
.carousel__track {
  display: flex;
  gap: var(--sp-8);
  transition: transform 0.55s cubic-bezier(0.65, 0, 0.35, 1);
  will-change: transform;
}
.carousel__track > * { flex: 0 0 calc((100% - var(--sp-8) * 2) / 3); }

.carousel-arrow--prev,
.carousel-arrow--next {
  position: absolute;
  top: 36%;
  z-index: 1;
  /* 🔴 R10-02（PM 2026-08-24）：「1440px 時輪播箭頭太貼邊」。
     **實測不是「快被切到」，是已經切掉了**（量箭頭外緣離視窗左／右緣，負數＝在視窗外、
     被 `main { overflow-x: clip }` 裁掉）：

       | 視窗 | 1920 | 1600 | 1520 | 1440 | 1280 | 1024 |
       |------|------|------|------|------|------|------|
       | 外緣 | 216.5 | 56.5 | 16.5 | **−23.5** | **−36** | **−32** |

     **根因與 R9-10／11 完全相同**：`translate(∓50%)` ＝永遠往外凸 56px（箭頭寬 112 的一半），
     但容器外的留白會隨視窗變化（1920 有 252.5px、1440 只剩 12.5px）。
     **因此用同一個 token 收斂**（`--decor-bleed`，定義與三個實作細節見 style.css :root）——
     視窗夠寬時取滿 56px（**1600 以上外觀完全不變**），窄了就自己收回容器內。
     ⚠️ 再扣掉 hover 的 0.25rem：hover 是往**外**位移，不預留的話滑過去的瞬間又會貼到邊。
     ⚠️ 基準是 `.carousel`（`.wrap` 的子元素）→ 直接用 `--decor-bleed`，不扣 `--container-pad`。
     ⚠️ **家庭天地與最新消息兩區共用這一組**（6.2），改一次兩區都會動——兩區都要驗。
     ⚠️ 3.5rem ＝ 箭頭寬 112px 的一半（svg 6rem ＋ 左右 --sp-2 內距），
        不是隨手取的數字；`.carousel-arrow` 的尺寸改了，這裡要跟著改。 */
  --arrow-bleed: min(3.5rem, calc(var(--decor-bleed) - 0.25rem));
}
.carousel-arrow--prev { left: 0;  transform: translate(calc(var(--arrow-bleed) * -1), -50%); }
.carousel-arrow--next { right: 0; transform: translate(var(--arrow-bleed), -50%); }
.carousel-arrow--prev:hover:not(:disabled) { transform: translate(calc(var(--arrow-bleed) * -1 - 0.25rem), -50%); }
.carousel-arrow--next:hover:not(:disabled) { transform: translate(calc(var(--arrow-bleed) + 0.25rem), -50%); }

/* 區塊底部置中的按鈕（demo 的 .events-cta-wrap） */
/* 🔴 `.section-foot` 已移到 `style.css`（共用層）——R7-21 的根因就是它原本只在這裡：
   `album.html` 用了這個 class 但**沒有載 index.css**，於是「回到所有相簿」那顆按鈕
   **完全沒有上距、緊貼照片牆**。與 `.news-card`（§11.21）是同一類。
   （原本這裡還舉了 `.family__note` 當例子，**該 class 已於 R10-11 整個移除**。）
   **判準：只要不只首頁會用到，就屬共用元件層。** */

/* ==========================================================================
   ④ 家庭天地
   ========================================================================== */
.family__dots {
  justify-content: center;
  margin-top: var(--sp-8);
}
/* 🔴 `.family__note` 這個 class 現在**全站已不存在**，分兩次移除：
   ① R6d（2026-08-18）刪掉 `index.css` 這一份——首頁那一句被 PM 拿掉了。
      ✅ 順帶收掉一個隱形分叉：同一個 class 在 `style.css` 也有一份、**值還不一樣**
      （這裡 `--sp-8` ＋ `--text-muted`／那裡 `--sp-12`、沒有顏色）。首頁載 `index.css`
      吃到這一份、其他頁沒載它吃到另一份 → **同一個 class 在不同頁面長得不一樣，
      而且不會有人發現**。
   ② R10-11（2026-08-24）PM 指定移除 `family.html` 最下方那一句 →
      `style.css` 那一份也失去最後一個使用者，已一併刪除。
   ⚠️ 當時這段結尾寫著「那三處仍在用 style.css 的定義，**不可連那一份一起刪**」——
      **那句話現在不成立了**，因為使用者已經歸零。留著會讓下一個人不敢動。
      🔴 這就是 9.21：註解不會跟著程式碼一起失效，得有人回來改它。 */

/* ==========================================================================
   ⑤ 活動相簿（借 demo 家庭樹頁的照片格線）
   ========================================================================== */
/* 🔴 R6e②：`.albums__grid`／`.albums__item` 整組移除——它們是 `.gallery` 的複製品
   （格線三欄、`aspect-ratio: 4/3`、hover 放大、899／599 兩個斷點，**連值都一樣**），
   而共用元件那份還多了燈箱。改用 `.gallery` 之後這裡只需要交代「排列位置」。
   ⚠️ **排列方式各自負責、卡片內部共用**——與 `.news-card` 那次是同一個判準。 */
/* 🔴 PM 2026-09-01：格線包進 `.albums__carousel` 之後，**上方間距改由 `.carousel` 提供**
   （它已經有 `margin-top: var(--sp-14)`）。這一行若留著就是同一段間距寫兩份，
   實際會疊成 112px——而且沒有人會發現那是兩條規則相加的結果。 */
.albums__carousel { margin-top: var(--sp-14); }

/* --- 手機版輪播（PM 2026-09-01）---------------------------------------------
   原本 ≤599 是單欄格線，8 張照片一路往下排、整頁被拉得很長。
   🔴 **桌機一行覆寫都不必寫**：控制列在 ≥768 直接不顯示，`.gallery` 維持共用元件的
      三欄格線，軌道在格線狀態下量到的位移量恆為 0（見 home.js 的說明）。
   ⚠️ 只在 `.albums__carousel` 之下改 `.gallery` 的**排列方式**，
      卡片內部樣式仍然共用（同 R6e② 立下的判準：排列各自負責、卡片內部共用）。 */
/* 相簿的輪播只在 ≤767 啟用（768–899 仍是兩欄格線，不會太長），
   所以控制列在那一段要收起來——其餘外觀一律由共用的 `.carousel-ctrl` 提供。 */
@media (min-width: 768px) and (max-width: 899px) {
  .albums__ctrl { display: none; }
}

/* 🔴 **選擇器要疊成 `.carousel.albums__carousel`（0,2,0），不可只寫一個 class。**
   下方 `@media (max-width: 899px)` 有一組 `.carousel { display: grid; grid-template-areas:
   "viewport viewport" "prev next" }`——那是**給最新消息輪播**的（把箭頭移到卡片下方），
   但選擇器是通用的 `.carousel`，相簿這一組同樣吃得到。
   ⚠️ 實測後果：`.carousel__viewport` 變成沒有 `grid-area` 的 grid item，
      **被內容撐開到 476px、而父層只有 331px**——照片右半截被裁掉。
      而 `overflow: hidden` 讓它看起來只是「照片有點大」，頁面也不會橫向捲，
      **不會有人聯想到是另一個區塊的規則跑過來了。**
   ✅ 相簿的控制列是獨立的 `.albums__ctrl`，不靠 `grid-area` 定位，所以維持 block 即可。
   🔴 提權重是正解，不是把這段搬到 899 那組後面、也不是 `!important`
      （搬順序的話，下一個人在中間插一條規則就又壞了）。 */
.carousel.albums__carousel { display: block; }
/* grid/flex 子項的 `min-width: auto` 會被內容撐開——這一條是上面那個坑的第二道保險。 */
.albums__carousel .carousel__viewport { min-width: 0; }

@media (max-width: 767px) {
  .albums__carousel .gallery {
    display: flex;
    gap: var(--sp-5);
    /* 軌道的位移動畫。共用的 `.carousel__track` 有這一條，而這裡的軌道是 `.gallery`，
       所以要自己給——秒數與 `.carousel__track` 一致，兩個輪播的手感才一樣。 */
    transition: transform 0.55s cubic-bezier(0.65, 0, 0.35, 1);
    will-change: transform;
  }
  /* 一次一張，與最新消息的手機版一致（`flex: 0 0 100%`）。
     🔴 **`display: block` 不可省**：共用元件有一條 `.gallery > li { display: contents }`
        （style.css §11.19），讓格線直接排列裡面的 `.gallery__item`。
        `display: contents` 的元素**不產生框**——於是 `flex` 宣告雖然吃得到卻毫無作用，
        `li` 量出來的寬高是 0。
        ⚠️ **而它不會報錯，畫面上也不是空的**（照片仍照常顯示，只是排成一直條），
           真正壞掉的是 `makeCarousel` 依 `track.firstElementChild` 算的每一步位移量：
           量到 0 → 圓點數算不出來、箭頭永遠 disabled。**輪播看起來像沒做。**
     ⚠️ `min-width: 0`：flex 子項預設 `min-width: auto` 會被圖片的內在寬度撐開，
        每一張都比 100% 寬一點點，位移一張之後會露出前一張的邊。 */
  .albums__carousel .gallery > li {
    display: block;
    flex: 0 0 100%;
    min-width: 0;
  }

  /* ⚠️ 控制列的外觀不在這裡——它與另外兩個輪播共用 `.carousel-ctrl`（見上方）。
     這裡曾經有一份自己的 flex 宣告，是統一規格前的產物，已移除以免變成隱形分叉。 */
}

/* ==========================================================================
   ⑥ 行動呼籲 .cta（全幅背景橫幅）
   demo：min-height 520、置中文字、水墨筆刷從右下出血並蓋過下一區。
   ⚠️ demo 的 .cta-banner-overlay 沒有給背景色（空的 div），白字直接壓在照片上——
      在亮照片上會讀不到。我方補上遮罩，濃度回推自「白字 ≥4.5:1」。
   ========================================================================== */
.cta {
  position: relative;
  z-index: 1;
  min-height: 32.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  overflow: visible;
  /* 🔴 底色留著當「圖還沒到／抓不到」時的保底——這一區是白字，
     沒有底色的話載入瞬間會是白字壓在頁面底色上，看不見。 */
  background-color: var(--c-maroon);

  /* 🔴 R4f：背景改成 CSS（原本是兩個 <img class="cta__bg">）。
     `background-attachment` 對 <img> 無效，要做 fixed 視差只能走 CSS 背景。
     ⚠️ `background-size: cover` ／ `no-repeat` ／ `attachment` 由 `.bg-parallax`
        提供（style.css §11.22），這裡只指定「是哪一張」。 */
  background-image: url("../img/1480055376.jpg");   /* 未登入 */
  background-position: center;
}

/* 🔴 依登入狀態換背景，**沿用全站既有的 `html[data-auth]` 前綴**，不另外發明狀態機制
   （style.css 的 `html[data-auth="guest"] [data-auth-only="member"]` 就是同一套）。
   🔴 **絕對不可改成 JS 換 background-image**——與原本禁止「JS 換 src」是同一個問題：
      使用者會先看到一張、再跳成另一張。切換必須由 CSS 依屬性選擇器完成。 */
html[data-auth="member"] .cta { background-image: url("../img/2213684634.jpg"); }
.cta__overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  background-color: var(--overlay-cta);
}
.cta__decor {
  position: absolute;
  /* 🔴 R10-05（PM 2026-08-24）：桌機的右下插圖縮小。PM 沒有給數字，取 **−20%**：
     280 → 224px。理由是這張筆刷是「出血裝飾」不是內容，縮 15% 幾乎看不出改過、
     縮 25% 又會變成一小撮墨點失去筆刷感；20% 是量過後看起來仍是一筆、但不再搶戲。
     ⚠️ **出血量跟著等比縮**（右 −60→−48、下 −90→−72）：只縮寬度不縮位移的話，
        被視窗裁掉的比例會從 21% 變成 27%，看起來像「破得更多」而不是「變小」。
     ⚠️ 手機（≤899）那一組維持原值，PM 只說桌機。 */
  right: -3rem;
  bottom: -4.5rem;
  width: 14rem;
  height: auto;
  z-index: 2;
  pointer-events: none;
}

.cta__content { position: relative; z-index: 2; }
/* 🔴 R6a②（2026-08-18）：40rem → 56rem，讓 CTA 內文的最後一行不會只剩一兩個字。
   ⚠️ **孤字是「內容長度 × 容器寬 × 字級」三者的巧合**，調到「剛好不孤」下次改文案或
      切字級檔就會再犯。所以這裡不是把寬度調到剛好，而是：
      ① 容器給足空間（56rem）② **內文本身用 `em` 限寬**（見 .cta__lead），
      `em` 會跟著字級檔一起放大 → **三檔都是同樣的每行字數**，不會只在預設檔對。 */
.cta__content > div { max-width: 56rem; margin-inline: auto; }

/* 🔴 eyebrow 在深底上一律用奶油白，不用珊瑚粉。
   實測珊瑚粉 on「遮罩疊在最亮處」只有 3.22:1，14px 小字過不了 AA；
   奶油白是 6.37:1。與標題同色但字級（14 vs 40）、字重與字距已經分得出層次。
   ⚠️ 這與 3.1「珊瑚粉不得承載任何文字」是同一條規則，深底上也不例外。 */
.eyebrow--on-dark { color: var(--text-inverse); }

.cta__title {
  margin-top: var(--sp-3);
  font-size: var(--fs-h2);
  font-weight: var(--fw-bold);
  color: var(--c-white);
  text-shadow: 0 2px 16px rgba(46, 25, 25, 0.35);
}
.cta__lead {
  /* 🔴 用 `em` 不用 `rem`：`em` 綁在自己的 `--fs-body` 上，字級檔切到「中」「大」時
     容器跟著變寬，每行字數不變（約 40 字）；用 `rem` 的話字變大、寬度不變 → 又會孤字。
     ⚠️ 最長那一行是 37 字，40em 留了 3 字餘裕。 */
  max-width: 40em;
  margin-inline: auto;
  margin-top: var(--sp-4);
  line-height: var(--lh-prose);
  color: var(--c-white);
  text-shadow: 0 1px 8px rgba(46, 25, 25, 0.3);
}
.cta .btn { margin-top: var(--sp-8); }
.cta__note {
  margin-top: var(--sp-4);
  font-size: var(--fs-xs);
  color: var(--text-inverse-soft);
}

/* ==========================================================================
   ⑦ 最新消息
   ========================================================================== */
.news { position: relative; }
.news__decor {
  position: absolute;
  left: 8.75rem;
  bottom: 5rem;
  width: 5rem;
  height: auto;
  opacity: 0.9;
  pointer-events: none;
}
.news__tabs { margin-top: var(--sp-8); justify-content: center; }

/* ==========================================================================
   響應式
   ⚠️ 斷點沿用 demo 的 900px（不是全站的 767）——它的三欄在 900 以下就擠了。
   ========================================================================== */
@media (max-width: 1199px) {
  .about__decor,
  .news__decor { display: none; }   /* 裝飾在窄版面會壓到文字 */
  /* 🔴 R14-02（PM 2026-08-28：「修。」）：**補上 `left`。**
     這一條原本只縮 `width`（`20rem` → `13.75rem`）卻**沒有跟著縮 `left`**
     （基準規則 `.services__decor` 的 `left: -7.5rem` ＝ 120px 照樣生效）
     → 被視窗裁掉的比例從 ≥1200 的 **120/320 ＝ 37.5%** 變成 **120/220 ＝ 54.5%**，
     **看起來像「破得更多」而不是「變小」。**
     ✅ **這不是新判斷，是同一條原則漏套一處**——`.cta__decor`（R10-05，見上方 ⑤）
        已經寫過一次：「**出血量跟著等比縮**：只縮寬度不縮位移的話，被裁掉的比例會從
        21% 變成 27%。」**兩處互相指認，下一個人才不會第三次踩到。**
     **算法**：`13.75rem × 0.375 = 5.156rem` → `-5.15rem`（實測裁切 37.4%，與 ≥1200 同級）。
     ⚠️ **它在全幅 section 裡**（不是 `.wrap` 的子元素），**出血是 demo 的刻意設計、不是跑位**
        ——不要順手把它拉回容器內。 */
  .services__decor { width: 13.75rem; left: -5.15rem; bottom: -4.375rem; }
}

/* 🔴 R14-03（PM 2026-08-28：「修。」）：**`.cta__decor` 在 900–1023px 與 CTA 內文只差 0.5px。**
   ✅ R10-05 已把「重疊 43.5px」改善到「差 0.5px」，但**0.5px 的餘裕等於沒有餘裕**——
      字體 fallback 換上來、或使用者切字級，就會再撞上。
   🔴 **待決 25 原本寫的修法「把 ≤899 那組斷點上移到 1023」不可照做**：
      下面那個 `@media (max-width: 899px)` 裡有 **14 條規則**——三欄改單欄、
      **整組輪播改成上下排列**（`.carousel` 換 `grid-area`、箭頭移位）……
      **整組上移等於在 900–1023px 把首頁版面整個換掉**，而 PM 交辦的只有一張裝飾圖。
   **正解：只把 `.cta__decor` 那一行單獨拉出來**放進這個 `@media (max-width: 1023px)`，
   **≤899 的其餘 13 條一律不動。**
   ⚠️ **位置必須在 899 那個 block 之前**：兩條的權重相同，後寫的贏。
      放到後面的話 900–1023 的值會把 ≤899 蓋掉——**而手機上看起來只是「裝飾大了一點」，
      不會有人聯想到是規則順序。**（本項已實測 375／899 兩個寬度：改前＝改後。）
   ✅ **值就是原本 ≤899 那一組**（不是另外調一組新的）：這張圖在窄版面本來就該用小的那一套，
      900–1023 只是先前漏掉的一段區間。 */
@media (max-width: 1023px) {
  /* ⚠️ R10-05 把桌機的 `right` 從 -3.75rem 改成 -3rem，這裡**刻意把舊值寫回來**：
     PM 只要求改桌機，這一組要維持原樣。不寫回去的話窄版面會跟著變
     （出血 60→48px，被裁比例 33.3%→26.7%）——**改了也不會有人發現**。
     🔴 這不是多餘的重複宣告：桌機那個值改了，這一行就是 ≤1023 的唯一來源。 */
  /* PM 2026-09-01：手機版這張水彩太大 → 11.25rem(180) 縮到 10rem(160)。
     🔴 **`right` 要跟著等比縮**（R10-05／R14-02 已經寫過兩次的同一條原則）：
        只縮寬度不縮位移的話，被視窗裁掉的比例會從 60/180＝33.3% 變成 60/160＝37.5%，
        看起來像「破得更多」而不是「變小」。
        算法：160 × 0.333 = 53.3px → -3.33rem，維持 33.3%。 */
  .cta__decor { width: 10rem; right: -3.33rem; bottom: -3.125rem; }
}

@media (max-width: 899px) {
  .about__grid { grid-template-columns: 1fr; gap: var(--sp-10); }
  /* 🔴 R9-11：~~`left: calc(var(--sp-5) * -1)`~~ 已移除——**留著會把 R9-10 的收斂蓋掉**，
     手機又變回貼齊視窗（原本實測就是 0）。
     ⚠️ 而且它現在是 no-op：`--decor-bleed` 在 1400px 以下恆為 0，
        基準規則的 `min()` 本來就會取到 0，這裡再寫一次只會製造一個「看起來有設定」的分叉。 */
  /* 🔴 R10-01：~~`.about__backdrop { top: var(--sp-5) }`~~ 改成調 `--backdrop-gap`。
     手機把斜角縮到 20px 的意圖不變，但**只調 top 會讓兩軸又不同步**（左 0／下 20）——
     那正是 R10-01 這一項要修掉的病。改調位移量，左右上下一起變。 */
  .about__media { --backdrop-gap: var(--sp-5); }
  .services__grid { grid-template-columns: 1fr; gap: var(--sp-14); }

  /* 🔴 手機一次一張，且箭頭改放到卡片下方——
     露在區塊邊界外的箭頭在窄螢幕會被裁掉，而 08-04 準則①要求箭頭必須明顯可見。
     demo 的做法是直接 display:none，那違反該準則，不照抄。 */
  .carousel__track > * { flex: 0 0 100%; }
  /* 🔴 PM 2026-09-01：**控制列統一規格**——原本這裡是
     `grid-template-areas: "viewport viewport" / "prev next"`，兩顆箭頭各佔一半、
     中間沒有圓點的位置；家庭天地的圓點又另外掛在輪播外面，最新消息則完全沒有圓點。
     **手機版首頁於是有三種排法，看起來像三個不同的元件。**
     改成與 hero 一致的「左箭頭 ● dots ● 右箭頭」一列（見 `.carousel-ctrl`）。
     ⚠️ `.carousel` 不再需要 grid：viewport 是它唯一的流內子元素，控制列接在後面。
        **這一組移除之後，相簿那條提權重的 `.carousel.albums__carousel` 也就沒有
        對手了**——但刻意留著，它擋的是「日後有人又在 `.carousel` 上加版面規則」。 */
  /* ⚠️ 這裡原本有 `.carousel-arrow svg { width: 4rem }`（舊箭頭在手機放大用）。
     **已移除**：手機版現在改用 `--slim` 那一支，而該條的權重 (0,1,1) 會壓過
     `.carousel-arrow__slim` (0,1,0)，把 40×16 撐成 64×24——箭頭於是又與 hero 不一樣。
     🔴 桌機沒有這條規則（它本來就只寫在 ≤899 裡），所以移除不影響桌機。 */

  .carousel-ctrl {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-5);
    margin-top: var(--sp-5);
  }
  /* 交還定位權：`.carousel-arrow--prev/next` 的基準是 `position: absolute`。 */
  .carousel-ctrl .carousel-arrow { position: static; transform: none; }

  /* 🔴 **hover 的位移一定要一起關掉，而且要涵蓋全部三個輪播。**
     基準區有兩條 `.carousel-arrow--prev/next:hover { transform: translate(…, -50%) }`
     ——那個 `-50%` 是**垂直**位移，桌機箭頭靠它配合 `top` 做垂直置中；
     手機箭頭已改成 `position: static`，那段位移就變成「hover 時整顆往上跳一半」。
     ⚠️ **觸控裝置的 hover 會黏著**：點完不會自己解除，要點別處才恢復
        ——症狀就是 PM 說的「按完停在比較高的位置，點空白處才會回來」。
     🔴 先前只寫了 `.carousel-ctrl .carousel-arrow:hover`，而**家庭天地的箭頭不在
        `.carousel-ctrl` 裡**（它走 `.family__group` 那條路）→ 只有它中招，
        最新消息與相簿沒事。**這裡改成對兩個 modifier 直接寫，三個輪播一起涵蓋。**
     ⚠️ 權重與基準那兩條相同 (0,3,0)，靠「同權重後寫者勝」——本區段在檔案後方。
        若日後有人把這段搬到前面就會失效，要搬請改成提權重。 */
  .carousel-arrow--prev:hover:not(:disabled),
  .carousel-arrow--next:hover:not(:disabled) { transform: none; }
  /* `.family__dots` 有 `margin-top: var(--sp-8)`（桌機用來與卡片拉開距離）——
     進到一列之後那段外距會把圓點推離箭頭的水平線。間距改由 `.carousel-ctrl` 的
     `gap` 統一提供（全域守則：間距由容器給，欄位不自帶 margin）。 */
  .carousel-ctrl .carousel-dots { margin-top: 0; }

  /* 手機版的箭頭換成 hero 那一支，尺寸也照 hero（`.hero__arrow svg` 是 2.5rem × 1rem）。 */
  .carousel-arrow__wide { display: none; }
  /* 🔴 **選擇器要疊成 `.carousel-arrow .carousel-arrow__slim`（0,2,0）**：
     `style.css` §11 有一條 `.carousel-arrow svg { width: 6rem; height: 1.5rem }`，
     權重 (0,1,1) 壓得過單一 class 的 (0,1,0) → 尺寸會被撐成 96×24，與 hero 不一樣。
     ⚠️ 這一條藏在**另一個檔案**裡。第一次修時只掃了 index.css，
        以為移除本檔那條 `width: 4rem` 就好，結果箭頭反而從 64 變成 96——
        **「某條規則沒生效」要掃的是全部 CSS 裡同一個選擇器，不是只掃手邊這一支。**
     ⚠️ `flex: none` 不可省：控制列是 flex、圓點那欄是 `1fr`，
        箭頭在窄螢幕會被壓縮（實測相簿那顆被壓成 28px 寬）。 */
  .carousel-arrow .carousel-arrow__slim {
    display: block;
    flex: none;
    width: 2.5rem;
    height: 1rem;
  }

  /* 🔴 PM 2026-09-01：**家庭天地與最新消息在手機版不顯示圓點。**
     這兩個輪播的筆數多（十房 10 顆、消息 8 顆以上），圓點會把一列撐開，
     箭頭因此被擠到看不見或貼邊。**活動相簿維持顯示**（只有 6 顆，放得下）。
     ⚠️ 中間那一欄刻意保留（grid 的 `1fr` 仍在），箭頭因此仍分居兩側，
        與 hero 的箭頭位置一致——不要順手把它改成兩顆並排置中。 */
  .family__dots,
  .news__dots,
  .albums__dots { display: none; }

  /* 家庭天地：箭頭與圓點分屬兩層，把 `.carousel` 也攤平，讓四者成為同一個格線的項目。
     ⚠️ `.carousel` 攤平後它的 `margin-top: var(--sp-14)` 會失效（`display: contents`
        的元素不產生框，外距無效）——**改由 group 提供**，否則輪播會貼上一段標題。 */
  /* 🔴 PM 2026-09-02：**兩顆箭頭並排置中**，以最新消息那一組為準。
     ~~原本是 `auto 1fr auto` ＋ 中間留給圓點~~——圓點在手機已不顯示，
     那個 `1fr` 只是把兩顆箭頭推到左右兩端（實測相距 223px，最新消息是 20px）。
     ✅ 改成兩等分欄、prev 靠右 next 靠左，中間就是 `gap` 的 20px，與最新消息同值。
     ⚠️ 圓點不再有自己的欄（`grid-area: dots` 一併移除）——它 `display: none`，
        留著只會讓下一個人以為那一欄還在用。 */
  .family__group {
    display: grid;
    grid-template-areas: "viewport viewport" "prev next";
    grid-template-columns: 1fr 1fr;
    align-items: center;
    gap: var(--sp-5);
    margin-top: var(--sp-14);
  }
  .family__carousel { display: contents; }
  .family__carousel .carousel__viewport { grid-area: viewport; width: 100%; }
  .family__carousel .carousel-arrow--prev { grid-area: prev; justify-self: end; position: static; transform: none; }
  .family__carousel .carousel-arrow--next { grid-area: next; justify-self: start; position: static; transform: none; }

  /* 🔴 R14-03：`.cta__decor` 那一行**已搬到上面的 `@media (max-width: 1023px)`**——
     手機（≤899）自然也吃得到，值完全沒變（實測 375／899 改前＝改後）。
     **不要在這裡再寫一份**：兩份同值宣告只會讓下一個人改了其中一份就以為改完了。 */
}

@media (max-width: 767px) {
  /* 🔴 **R11-05 的連帶問題：hero 一滿版，右下角的浮動工具就壓在輪播控制列上。**
     --------------------------------------------------------------------------
     `.floating-tools`（回到頂端 ＋ 字級放大）是 `position: fixed`、`z-index: 90`，
     永遠停在視窗右下角。改成滿版之前，hero 的下緣離視窗底部還有 155px（375×812），
     控制列在它上方、互不相干；**滿版之後 hero 下緣就是視窗下緣**，
     於是控制列與那顆膠囊**實測重疊 36px 高**——「下一張」整顆被蓋住、圓點蓋掉 48px。

     🔴 **這種蓋法完全不報錯**：z-index 90 遠大於 3，畫面上只是「箭頭不見了」，
        而且**只在捲動位置 0 時發生**（往下捲一點控制列就跟著走了）——
        那正好是使用者第一眼看到的畫面。
     ⚠️ **這是量不出來的那一類**：我先量的是 hero 內部各元素的位置，全部正常；
        **是實際截圖看畫面才發現的**（動到畫面就要真的開頁看）。

     **修法：控制列往上讓開那條帶狀區，hero 的下內距跟著加大同樣的量。**
     只加大 `bottom` 而不動 `padding-bottom` 的話，控制列會往上撞到主視覺的文字
     （橫向手機實測文字底部離控制列只有 24px）。 */

  /* 浮動工具在視窗底部佔掉的高度：`--sp-2`（它自己的 bottom）＋ 3.25rem（膠囊高度）。
     🔴 **3.25rem 與 `style.css` 的 `.floating-tools` 是兩地一值**，改一邊要回頭改另一邊。
     ⚠️ 捲動之後 `.to-top` 會出現、那一組變高，**但那時控制列早就捲上去了**，不必算進來。 */
  /* ⚠️ 自訂屬性必須寫在某條規則裡，不可直接放在 `@media` 的第一層——
     那樣整段會被當成語法錯誤丟掉，**而且不報錯，只是三個變數都不存在**。 */
  :root {
    --hero-ctrl-clear: calc(var(--sp-2) + 3.25rem);
    /* 控制列離 hero 底部的距離：讓開那條帶狀區，再留 `--sp-2` 的呼吸。 */
    --hero-ctrl-bottom: calc(var(--hero-ctrl-clear) + var(--sp-2));
    /* hero 的下內距要放得下「控制列的位置 ＋ 它自己的高度 ＋ 與文字的間距」。
       ⚠️ 原本寫死 5rem（80px），現在由上面兩個值推導——**控制列一移動它就自動跟上**。 */
    /* 🔴 PM 2026-09-01：文字改靠下（見下方 `.hero` 的 `align-items`），
       原本 `--sp-3`(12) 的間距在靠下之後會讓文字幾乎貼著圓點與箭頭
       → 改 `--sp-8`(32)。**改的是這個推導式的最後一項**（「與文字的間距」），
       控制列自己的位置與高度不動。 */
    --hero-pad-bottom: calc(var(--hero-ctrl-bottom) + 2.75rem + var(--sp-8));
  }

  /* 🔴 手機**不跟著加高**（R2m⑤）：直向螢幕的 hero 是「裁左右」，
     加高只會裁更多、又把下方內容推出首屏。實測 375×812：60vh 裁 67%、
     若跟著加到 82vh 會變 72%。 */
  /* 🔴 R10-19②（PM 2026-08-24）：hero 加高——「現在太擠、背景被文字蓋過」。
     **60vh → 72vh（推翻 R2m⑤「手機不跟著加高」的那一半，桌機 82vh 不動）。**
     ⚠️ R2m⑤ 當初不加高的理由**沒有消失，是被 PM 接受了**：直向螢幕加高＝裁得更多。
        實測 375×812：60vh 圖只看得到 48.9% 的寬度、72vh 剩 40.8%（多裁 8.1 個百分點）。
     ⚠️ 沒有一路加到 82vh（＝桌機值）：那會讓 hero ＋ header 佔掉 810/812，
        **下一區在首屏完全看不到**，長輩不會知道下面還有東西。72vh 留了約 155px 的預告。
     ⚠️ 9.4 的坑：不可改寫成 `aspect-ratio`＋`max-height`，這裡直接給高度是對的。 */
  /* 🔴 R11-05（PM 2026-08-25）：**hero 高度改成跟著裝置高度**（72vh → 一整個螢幕）。
     **PM 原話**：「首頁 hero 區高度改成 100vh，讓他跟著手機裝置高度去自然伸縮高度。」

     🔴 **三個實作決定，每一個都不是照字面做**：

     ① **`dvh` 不是 `vh`。** iOS Safari 的 `100vh` 是「網址列收起來時」的高度＝**比實際可視區大**，
        於是**下方的箭頭與 dots 會被網址列蓋住**，而且捲動時網址列收合，整個 hero 會抽動一下。
        `100dvh` 跟著實際可視區走。**`vh` 那一行要留著當 fallback**（不支援 dvh 的舊瀏覽器
        會丟掉整條宣告、退回前一行，那是 CSS 的正常行為，不是相容性技巧）。

     ② 🔴 **要扣掉 `--header-h`，不可以直接寫 `100dvh`。**
        `.site-header` 是 `position: sticky` ＝ **佔流排空間**，hero 從 72px 才開始。
        直接寫 100dvh 的話 hero 的下緣會落在螢幕外 72px →
        **箭頭與 dots（距 hero 底 24～68px）整組看不到**，直接違反 08-04 準則①
        「箭頭與圓點必須明顯可見」。扣掉之後「header ＋ hero ＝ 剛好一個螢幕」，
        才是 PM 說的「跟著手機裝置高度」。
        ⚠️ 實測 375×812：直接寫 100dvh → 控制列在 816～860px，**首屏完全看不到**。

     ③ **仍然用 `min-height` 不是 `height`。** 橫向手機只有 375px 高，
        內容若比它高就讓 hero 自己長高，不要裁掉文字（實測沒有發生，但這是保險）。

     ⚠️ **R2m⑤／R10-19② 的既有理由沒有消失，是被 PM 接受了**：
        直向螢幕加高＝**圖裁得更多**，而且**下一區被推出首屏**。
        實測 375×812：72vh 時第一張圖看得到 34.6% 的寬度、下一區露出 155px；
        改成滿版之後分別是 30.4% 與 0px。 */
  /* 🔴 **fallback 必須用 `@supports`，不可以寫成「上下兩行 min-height」。**
     那個「後面那行不支援就退回前面那行」的技巧**只在不含 `var()` 時成立**：
     一旦式子裡有 `var()`，瀏覽器在解析階段無法判定它有效與否 → 照收，
     等到計算值階段才發現 `dvh` 不認得 → 該宣告變成 **invalid at computed-value time**，
     結果是 `min-height` 掉回**初始值 `auto`**，**不是退回上一行**。
     → 那會讓 hero 在舊瀏覽器上縮成內容高度，**而且完全不報錯**。 */
  /* 🔴 PM 2026-09-01：文字區靠下，不要停在照片正中央擋掉主體。
     基準規則是 `align-items: center`（桌機維持），手機改成 `flex-end`；
     與控制列的距離由 `--hero-pad-bottom` 的最後一項提供（見上方註解）。 */
  .hero {
    min-height: calc(100vh - var(--header-h));
    padding-bottom: var(--hero-pad-bottom);
    align-items: flex-end;
  }
  @supports (height: 100dvh) {
    .hero { min-height: calc(100dvh - var(--header-h)); }
  }

  /* 🔴 R10-19①：標題不夠像標題 → 拉開與內文的字級差距。
     **原本主標 `--fs-hero` 在手機是 24／25／26，內文 `--fs-lead` 是 19／21／23**
     → 比值只有 1.26／1.19／**1.13**，「大」檔幾乎看不出誰是標題。
     根因是 `--fs-hero` 被 9.7 綁死不能平移（三張輪播主標行數必須一致），
     **但那個上限是「箭頭還在兩側、可用寬只有 233px」時算的**——箭頭早就移到下方了。

     🔴 **改法是換一階 token，不是寫死 px**（字級放大按鈕才不會失效，見 9.11）：
        主標改吃 `--fs-h2`（30／33／36），內文維持 `--fs-lead`。比值變成 1.58／1.57／1.57。
     ⚠️ **9.7 的約束仍然成立，只是重新量過**。用二分搜尋量「三張主標都維持一行的最大字級」
        （非作用中的 slide 是 visibility:hidden，仍有版面尺寸，量得到）：

          | 視窗 | 320 | 360 | 375 | 390 | 414 | 599 |
          |------|-----|-----|-----|-----|-----|-----|
          | 上限 | 29.4 | 33.9 | 35.3 | 37.0 | 39.7 | 59.7 |

        → 36px 要 390 以上、33px 要 360 以上、30px 要 325 以上，所以下面兩個斷點是**算出來的**。
     ⚠️ 上限來自「最長的標題是 9 個字」（三張是 7／9／9）。**改文案要重量這張表。** */
  .hero__title { font-size: var(--fs-h2); }

  /* 🔴 手機把左右箭頭從兩側移到底部，與圓點併成一列控制區。
     原因是量出來的：箭頭留在兩側，就得給 .hero__content 左右各 56px 的閃避空間，
     320px 只剩 208px 可用 → 9 字主標在 24px 下變兩行，而第一張 7 字仍是一行，
     **三張行數不一致**（D1 8.7 的硬約束：切換時下方內容會上下跳動）。
     移到底部後可用寬回到 280px，三張都是一行。
     ⚠️ 附帶好處：底部的大箭頭比貼邊的細線好按，符合 08-04 的長輩友善準則。
     ⚠️ 不可改成「手機隱藏箭頭、只能滑」——demo 是那樣做的，
        但 08-04 準則①明訂箭頭必須明顯可見，長輩不會滑。 */
  /* 🔴 R11-06（PM 2026-08-25）：**箭頭與 dots 的點擊區實測重疊 18px（兩側都是）。**
     改成一列 flex：`gap` 直接給出間距，不必再回推偏移量。

     **改前的算式（＝為什麼會疊）**：箭頭用 `left/right: calc(50% - 7.5rem)`
     → 內緣落在 50%±64px；而 dots 是 3 顆 × 44 的點擊區 ＋ 2 個 8px 間隔 ＝ **148px**、
     置中後外緣在 50%±74px → **兩邊各疊 10px**（再加上箭頭盒 64 比 56 寬的那 8px，實測 18px）。
     🔴 **那組偏移量的正確值取決於「dots 有幾顆」**——多一張輪播圖就會再疊回去，
        而且**不會報錯**，只會變成「箭頭又壓到圓點了」。用 `gap` 就沒有這個問題。

     🔴 **不是靠縮小箭頭或 dots 換來的間距**：兩者仍各自 ≥44×44
        （箭頭 64×44、每顆圓點的 `.carousel-dot-hit` 44×44），08-04 準則② 不變。
     🔴 **也沒有把箭頭移回兩側**：6.4 記著「手機箭頭刻意移到下方置中」（demo 是
        `display: none` 只能滑，我方不跟進，理由是長輩不會滑）。

     ⚠️ **`gap` 取 `--sp-2`（8px）是量出來的，不是隨手取的**：
        一列的總寬 ＝ 64 ＋ gap ＋ 148 ＋ gap ＋ 64 ＝ **276 ＋ 2×gap**。
        最窄的 320px 螢幕要放得下 → gap ≤ 22。取 8 之後總寬 292，左右各留 14px。
        **視覺上的間距遠大於 8**：箭頭的線離盒緣 12px、第一顆圓點離 dots 盒緣 17px
        → 看得到的間距是 12＋8＋17 ＝ **37px**（改前只有 11px）。
        🔴 **輪播圖從 3 張變成 4 張的話這個算式要重算**（dots 會變 200px，總寬 344 > 320）。

     ✅ **R10-19③「箭頭與 dots 中線對齊」由 `align-items: center` 結構性地保證**，
        不再依賴「兩者同高 ＋ 同 bottom」這兩個要一起維護的條件。 */
  .hero__ctrl {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    position: absolute;
    z-index: 3;
    left: 0;
    right: 0;
    bottom: var(--hero-ctrl-bottom);
  }
  /* 兩者交還定位權給上面那一列——`position: static` 之後 `left`／`right`／`top`／
     `bottom` 全部失效，不必逐條覆寫成 `auto`。 */
  .hero__arrow,
  .hero__dots {
    position: static;
    transform: none;
  }
  .hero__arrow { min-height: 2.75rem; }

  /* R7-14：箭頭移到下方之後，文字不必再為它讓位，還原成一般內距。
     ⚠️ R10-03 把桌機那條拆成 `padding-inline-start/end` 兩個邏輯屬性，
        這裡的簡寫仍然蓋得過去（同為邏輯屬性，且宣告在後）。 */
  .hero__content { padding-inline: var(--container-pad); }
  /* hover 位移：`transform: none` 在上面，這兩條在後面所以蓋得過去。 */
  .hero__arrow--prev:hover { transform: translateX(-0.25rem); }
  .hero__arrow--next:hover { transform: translateX(0.25rem); }

  .service-card__icon { width: 8rem; height: 8rem; }
  .service-card__icon img { width: 5rem; height: 5rem; }
}

/* 🔴 R10-19① 的兩個窄螢幕退階（值全部來自上方那張實測上限表，不是憑感覺取的）。
   ⚠️ **不是「放大按鈕在窄螢幕失效」**——退的只有主標，內文三檔照常放大。
      這與 style.css 既有的 `@media (max-width: 359px) { :root[data-fs="large"] { --fs-hero } }`
      是同一個手法、同一個理由，只是那裡管 `--fs-hero`、這裡管換階之後的 `.hero__title`。 */
@media (max-width: 389px) {
  /* 「大」檔 36px 需要 390px 以上（實測 375 的上限是 35.3）→ 退回「中」檔的 33px。 */
  :root[data-fs="large"] .hero__title { font-size: 2.0625rem; }
}
@media (max-width: 359px) {
  /* 320px 的上限只有 29.4px，`--fs-h2` 連預設檔的 30px 都放不下
     → 整組退到 `--fs-h3`（25／28／30），「大」檔再退一階到 28px。
     ⚠️ `:root[data-fs="large"] .hero__title` 的權重高於 `.hero__title`，
        所以「大」檔那一條要寫在後面，不能只留上面 389 那條（33px 一樣放不下）。 */
  .hero__title { font-size: var(--fs-h3); }
  :root[data-fs="large"] .hero__title { font-size: 1.75rem; }
}
