@charset "UTF-8";
/* ==========================================================================
   這是產物，不要手改。

   由 ivory-wp/腳本/make_base.py 合併產生：
     1. ivory-homepage/assets/css/style.css   版面樣式
     2. ivory-wp/共用/wp-adapt.css            WordPress 與 Blocksy 適配

   要改版面請改 style.css，要改 WordPress 適配請改 wp-adapt.css。
   同一支腳本也會把 main.js 複製成 共用/page.js。
   ========================================================================== */


/* ===== 1. 版面樣式（來源：ivory-homepage/assets/css/style.css）===== */

/* ==========================================================================
   象牙設計 形象官網｜首頁樣式
   --------------------------------------------------------------------------
   基準：設計稿畫布 1440px、內容區 1290px 置中（Blocksy 預設）
   原始設計稿是 1920px 畫布，所有數值已乘 0.75 換算到 1440 基準。

   斷點只有兩個，全檔不得出現第三個數字：
     平板 @media (max-width: 999.98px)
     手機 @media (max-width: 689.98px)

   色彩取用一律走 var(--theme-palette-color-N, fallback)，
   上稿到 WordPress 後會吃 Blocksy 色盤，本機預覽吃 fallback。
   色階語意（不可錯置）：
     1 主色＝按鈕實色底（#C40068，唯一通過 4.5:1 配白字的粉）
     2 hover 深階   3 內文   4 標題   5 邊框
     6 點綴＝設計主色 #FF63BA（永遠不承載文字）
     7 頁面淺粉底   8 純白
   ========================================================================== */

/* ── 1. 變數 ────────────────────────────────────────────────────────── */
:root {
  /* 色票（hex 一律以設計稿色票表的「標示文字」為準）
     色塊像素不可當權威：styleguide_01.jpg 是 JPEG，同一格 Hover 色塊
     可以讀到 #E7017F / #E9007F / #EA0080 三種值。
     唯一的無損樣本 00_設計稿/局部/01_hero左_文字.png，h1 填色實測
     (255,99,185) = #FF63B9 出現 1300 次，與標示一致，
     所以五個差 1 的值一律改回標示值。
     五個值的 ΔE00 是 0.202 / 0.133 / 0.218 / 1.232 / 0.809，
     對比度變化 ≤ 0.009，沒有一個跨門檻，純粹是把單一來源對齊設計稿。

     【重要】這裡改的是 fallback。線上吃的是 Blocksy 色盤，
     改 CSS 不會讓線上變色，色盤 5 與 6 要一起改（腳本 20_色盤校正.php）。 */
  --ivo-btn:        var(--theme-palette-color-1, #C40068);
  --ivo-btn-hover:  var(--theme-palette-color-2, #A30056);
  --ivo-text:       var(--theme-palette-color-3, #292A2F);
  --ivo-title:      var(--theme-palette-color-4, #1A1B1F);
  --ivo-line:       var(--theme-palette-color-5, #EBEBEE);   /* 原 #EBEAEF */
  --ivo-primary:    var(--theme-palette-color-6, #FF63B9);   /* 原 #FF63BA。設計主色，永遠不承載文字 */
  --ivo-paper:      var(--theme-palette-color-7, #FFF7F9);
  --ivo-white:      var(--theme-palette-color-8, #FFFFFF);

  --ivo-deep:       #EA007E;   /* 原 #EA007F。白底 4.375:1，僅面與記號 */
  --ivo-pink-lt:    #FFD4ED;   /* 淺粉，底色帶 */
  --ivo-accent:     #BA4BFE;   /* 原 #BA4BFF。輔助紫，裝飾用，白底 3.801:1 */
  --ivo-text-warm:  #4D443F;
  --ivo-off:        #C3C3C7;   /* 原 #C4C3C8。停用態，1.4.3 豁免 */
  --ivo-lilac:      #FFF0FF;   /* 理念區漸層起點 */

  /* 舊名，值不動。唯一使用端是 preview-chrome.css 的 .ivo-footer__bar，
     那條是「承載 13px 白字」的色帶不是裝飾漸層，重指向會出事。 */
  --ivo-pink-3:     #F76DB8;

  /* ── 輔助色3：色票表那一格是漸層色塊，不是單色 ──
     來源一（無損）01_hero左_文字.png 的主按鈕：純水平 90deg，
       對 y=655 逐欄取樣後線性外插到按鈕兩緣 → (239,45,149) / (255,175,219)
     來源二（JPEG）styleguide_01.jpg「輔助色3」色塊橫掃 → #EC3198 / #FFACD7，
       與上面 ΔE00 = 0.72 / 1.01，在 JPEG 誤差內。兩個獨立來源指向同一條漸層。
     標示的 #F1A7CF 落在這條 ramp 約 95% 處，是取樣點不是端點。 */
  /* 品牌漸層的兩個端點，客戶 2026-09-12 指定：左 #EF2D96、右 #FFAFDB。
     這組值與設計稿實測一致（無損 PNG 沿按鈕橫掃再外插到兩緣）。
     ⚠ 對比度實況（客戶已知悉並要求照設計稿實作）：
        #EF2D96 配白字 3.83:1（過大字 3:1，不過小字 4.5:1）
        #FFAFDB 配白字 1.69:1
     設計稿的按鈕白字橫跨 19.1%~78.7%，右緣底色實測 #FC94CC = 2.05:1。
     也就是說這個落差來自設計稿本身，不是實作偏差。 */
  --ivo-pink-3a:    #EF2D96;   /* 品牌漸層 深端 */
  --ivo-pink-3b:    #FFAFDB;   /* 品牌漸層 淺端 */
  --ivo-grad-brand: linear-gradient(90deg, var(--ivo-pink-3a) 0%, var(--ivo-pink-3b) 100%);
                    /* 設計稿原樣的官方定義，只能鋪在沒有文字的面上 */

  /* 主 CTA 漸層的最亮端。只用在按鈕「自己的面」上，
     底色就是這條漸層本身，不受區段背景影響，白字 3.322:1 成立。
     不可拿去當文字色：它在 .ivo-philo 的實際合成底（Y 0.7863）上只有 2.646:1。 */
  --ivo-pink-btn-hi: #ED50A9;

  /* 小字粉（14~22px，門檻 4.5:1）。設計稿是 #FF63B9 但只有 2.714:1，不能照抄。
     以「≥4.5:1 且對 #FF63B9 的 ΔE00 最小」窮舉得 #CB2B8B：
     白 4.932:1、paper 4.680:1、lilac 4.500:1，色相 347.7 與品牌粉完全一致
     （#C40068 是 359.3，偏正紅）。ΔE00 16.19，#C40068 是 22.37。 */
  --ivo-pink-md:    #CB2B8B;

  /* 品牌漸層（唯一定義處，全站承載白字的漸層面共用）
     原本是 linear-gradient(100deg, #C40068 0%, #C40068 55%, #EA007F 100%)：
     前 55% 是同一個顏色、亮度跨度只有 1.49 倍，在一顆 170~250px 的按鈕上
     根本看不出是漸層，客戶說的「按鈕的處理有落差」就是這個。
     這裡把暗端往下開到 --ivo-btn-hover，跨度拉到 2.25 倍且全幅連續。

     停駐點取 60% 不是 50%，是為了把 4.5:1 的邊界推到文字區之外。
     以最長的「預約 30 分鐘免費諮詢」（按鈕約 251px、文字佔 13.1%~86.9%）實算：
       0%    #A30056  7.80:1
       60%   #C40068  5.92:1
       86.9% #DE0077  4.80:1  ← 文字右緣，過 AA 小字
       100%  #EA007F  4.37:1  ← 只落在右側 padding，不承載文字 */
  /* 主 CTA 漸層 = 設計稿原樣的兩端，客戶指定（2026-09-12）。
     先前為了讓白字全程合規，改成三段式 #C40068 → #EF2E96 → #ED50A9，
     客戶看了覺得「主視覺色明顯偏深」，要求照設計稿。
     ⚠ 已知後果：白字在漸層右半部的對比不足
        0%   #EF2D96  3.83:1（過大字 3:1）
        50%  約 #F76EB9  2.60:1
        100% #FFAFDB  1.69:1
     設計稿本身就是這樣（實測文字右緣底色 2.05:1）。
     .ivo-btn 維持 19px/700 的大字，讓左半段至少站得住。 */
  --ivo-grad-cta:
    linear-gradient(90deg,
      var(--ivo-pink-3a)   0%,
      var(--ivo-pink-3b) 100%);

  /* hover 整條往深的方向移一階，順便把對比拉回來一些。 */
  --ivo-grad-cta-hv:
    linear-gradient(90deg,
      var(--ivo-btn)       0%,
      var(--ivo-pink-3a) 100%);

  /* 以下兩條保留不動，現在只服務 .ivo-form__submit（16px/500，門檻 4.5:1）。 */
  --ivo-grad:
    linear-gradient(90deg,
      var(--ivo-btn-hover) 0%,
      var(--ivo-btn)       60%,
      var(--ivo-deep)     100%);

  /* hover 不用 filter: brightness()。那會連白字一起壓暗（白 → #E6E6E6），
     實算只剩 4.51:1，反而是最不利的狀態。改成整條換暗色階。 */
  --ivo-grad-hv:
    linear-gradient(90deg,
      var(--ivo-btn-hover) 0%,
      var(--ivo-btn)      100%);

  --ivo-grad-glow:    0 6px 20px rgba(234, 0, 126, .22);
  --ivo-grad-glow-hv: 0 10px 28px rgba(234, 0, 126, .32);

  /* 字級階梯（量測設計稿後收斂） */
  --ivo-fs-h1:      48px;
  --ivo-fs-h2:      32px;
  --ivo-fs-h3:      18px;
  --ivo-fs-lead:    18px;
  --ivo-fs-body:    16px;
  --ivo-fs-btn:     19px;   /* 按鈕專用。≥18.66px 且 bold 才算 WCAG 大字，
                               門檻由 4.5:1 降到 3:1，--ivo-grad-cta 的
                               3.322:1 靠這個成立。取 19 不取 18.66 是避開
                               sub-pixel 取整。改小按鈕最亮端就會掉到門檻下。 */
  --ivo-fs-sm:      14px;
  --ivo-fs-xs:      13px;

  /* 版面 */
  --ivo-maxw:       1290px;
  /* 版心寬度的唯一來源。CTA 的左側出血要反推
     「溝寬 =（區段內容寬 − 版心寬）/ 2」，如果那邊自己再抄一次
     min(90vw, 1290px)，日後改版心就會有一邊沒跟上，而且不會有錯誤訊息。
     跟 .ivo-hint 曾經寫死 max-width: 850px 是同一種坑。 */
  --ivo-wrap-w:     min(90vw, var(--ivo-maxw));
  --ivo-gap:        30px;
  --ivo-radius:     16px;
  --ivo-radius-sm:  8px;
  --ivo-radius-pill: 999px;

  /* 陰影 */
  --ivo-shadow:     0 4px 24px rgba(41, 42, 47, .06);
  --ivo-shadow-lg:  0 10px 40px rgba(196, 0, 104, .10);
  /* 評論卡專用：與 --ivo-shadow 同色相，加重到交疊時分得出上下層 */
  --ivo-shadow-card: 0 8px 22px rgba(41, 42, 47, .10);
  /* hover 的抬起感只加 y 位移與模糊半徑，alpha 維持 .10 不動。
     理由是對比度，而且計算模型要對：box-shadow 是被 hover 那張畫在
     鄰卡「整片」之上，鄰卡的底色、本文、署名、★ 會被同一層半透明黑
     一起壓暗，不是只壓底色（只壓底色會低估 ★ 的存活率兩倍以上）。
     用 WCAG 相對亮度重算（先在 sRGB 空間做 alpha 合成，再算 L）：
       ★ #BA4BFE 對淺灰／白卡 #EBEBEE   3.194:1
           α .10 → #AC48E9 對 #D8D8DB = 3.04:1   仍過非文字的 3:1
           α .12 → 3.008:1  臨界　　α .14 → 2.98:1  跌破
       ★ 對紫卡 #FFF0FF                3.468:1，α .10 → 3.278:1
       本文 #292A2F  α .10 下 10.07:1
       署名（opacity .82 合成為 #4C4D51） α .10 下 6.24:1
     .10 本來就已經接近上限，這次不動它，鄰卡「最多會被壓多暗」
     與改動前完全相同，對比度是零回歸。抬起感改由偏移 8→20、
     模糊 22→46 提供，這兩個都不影響最深處的 alpha。 */
  --ivo-shadow-card-hv: 0 20px 46px rgba(41, 42, 47, .10);

  /* 區段垂直間距。
     原本 96px，但相鄰兩段疊起來就是 192px 的連續空白，
     整頁有三成以上的高度是沒有內容的區域。收到 80px。 */
  --ivo-sec-y:      80px;
  --ivo-sec-y-sm:   64px;
}

/* ── 2. Reset ──────────────────────────────────────────────────────── */
.ivo-page,
.ivo-page * { box-sizing: border-box; }

.ivo-page {
  margin: 0;
  font-family: "Noto Sans TC", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: var(--ivo-fs-body);
  line-height: 1.7;
  color: var(--ivo-text);
  background: var(--ivo-white);
  -webkit-font-smoothing: antialiased;
}

/* 圖片 reset 只作用在自己的區段裡，不能寫成 .ivo-page img。
   主題靠 .site-logo-container img { height: inherit } 控制頁首 logo 高度，
   兩者特異性相同（都是 0,1,1），而 base.css 後載入會贏，
   結果 height:auto 蓋掉 inherit，logo 以原始 400px 高度渲染，
   撐出頁首、疊在主視覺上。畫面很明顯但完全沒有錯誤訊息。 */
.ivo-sec img,
.ivo-bfooter img,
.ivo-modal img { max-width: 100%; height: auto; display: block; }
/* reset 一律包在 :where() 裡。
   :where() 的特異性是 0，整條規則只算 .ivo-page 的 (0,1,0)，
   與後面 .ivo-hero__title 這類 class 規則同級，後定義的會贏。

   先前寫成 .ivo-page h1, .ivo-page p { margin: 0 } 是 (0,1,1)，
   比所有 class 規則都高，結果整份 CSS 裡每一條 margin-top
   都被這一行默默吃掉：標題與內文貼在一起、清單與上方標題沒有間隔，
   而且怎麼調 margin 都沒反應。這是本案最花時間找的一個坑。 */
.ivo-page :where(h1, h2, h3, h4, h5, h6, p) { margin: 0; }

/* blockquote 的瀏覽器預設是 margin: 1em 40px，不重設的話
   評論卡的內文會被推出卡片的左內距，看起來像整段右對齊。
   figure 同理，預設左右各有 40px。
   另外 WordPress 與 Blocksy 會給 blockquote 一條左邊框與內距，
   上稿後評論卡左側會多出一條深色直線，這裡一併歸零。 */
.ivo-page :where(blockquote, figure) { margin: 0; }
.ivo-page :where(blockquote) {
  border: 0;
  padding: 0;
  font-style: normal;
  background: none;
}

/* WordPress 與 Blocksy 會把 figcaption 置中並縮小字級（那是圖說的預設樣式）。
   評論卡的署名用的是 figcaption，不重設的話會變成置中的小灰字，
   看起來像排版跑掉。 */
.ivo-page :where(figure) { text-align: left; }
.ivo-page :where(figcaption) {
  margin: 0;
  padding: 0;
  /* 不能寫 inherit：Blocksy 給 figure 套了 text-align: center，
     inherit 會把置中一起繼承下來，評論卡的署名就會飄到卡片中間。 */
  text-align: left;
  font-size: inherit;
  color: inherit;
}

.ivo-page :where(ul, ol) { margin: 0; padding: 0; list-style: none; }
.ivo-page :where(a) { color: inherit; text-decoration: none; }

.ivo-page :focus-visible {
  /* 鍵盤焦點環，整站的通用規則（2026-09-13 客戶回饋第 3 項）。

     ⚠ 這條規則要能生效，「外觀 → 自訂 → 額外 CSS」輸出的
     「*:focus-visible{outline: none !important;}」就必須先被濾掉。
     那一行的特異性只有 (0,1,0)，卻因為帶 !important 而蓋過這裡的 (0,2,0)。
     實測（CDP 送真實 Tab 鍵走十個頁面）在濾掉它之前，每一個聚焦站的
     outline-style 都是 none，整站看不到任何焦點框。
     負責濾掉它的是 functions.php 裡的 wp_get_custom_css filter。

     色：從 --ivo-btn 換成 --ivo-title。#C40068 落在頁尾品牌漸層帶上時
     只有 1.55:1（最暗端 #EF2D96）到 3.50:1（最亮端 #FFAFDB），
     達不到焦點指示器要求的 3:1。#1A1B1F 在實測到的四種外圈底色上
     分別是 白 17.21:1、米白 16.32:1、淺粉 13.02:1、漸層帶最暗端 4.49:1。
     寬：2px 加到 3px。

     不設 border-radius：外框本來就會跟著元素自己的圓角走。
     原本寫死的 border-radius: 3px 特異性 (0,2,0) 壓過 .ivo-dot 的 999px
     與 .ivo-arrow 的 50%，會把那兩顆圓的外框變成圓角方塊。
     實測拿掉之後：圓點 999px、圓形箭頭 50%、回頂端 100%、實心鈕 8px、
     MORE 0px，全部跟著元件自己走。

     只寫 :focus-visible 不寫 :focus：實測用滑鼠點實心鈕、線框鈕、MORE、
     FAQ 問題列、輪播圓點、圓形箭頭、作品卡、單選鈕，:focus-visible
     全是 false，滑鼠使用者不會看到框。 */
  outline: 3px solid var(--ivo-title);
  outline-offset: 3px;
}

/* ── 通用焦點環的三條例外 ────────────────────────────────────────────
   三條都是實測出來的問題，不是預防性寫法。 */

/* 例外一：FAQ 問題列。
   .ivo-faq__item 是 overflow: hidden（圓角要裁背景），.ivo-faq__q 又剛好
   填滿它，outline-offset: 3px 的環整圈落在父層外面，
   實測 1440 / 1000 / 768 / 375 / 320 五個寬度都被裁到一條線都看不到。
   環改畫在 .ivo-faq__item 上，就落在卡片外側，並自動跟著卡片的圓角。 */
.ivo-faq__item:has(.ivo-faq__q:focus-visible) {
  outline: 3px solid var(--ivo-title);
  outline-offset: 3px;
}

/* 例外二：服務頁的十張作品卡。
   成因與例外一相同，.ivo-work 是 overflow: hidden，裡面的 <a> 填滿它。
   環改畫在 figure 上，落在卡片外側的白底（17.21:1），
   同時避開「環壓在照片上、對比度隨圖片而變」的問題。 */
.ivo-work:has(a:focus-visible) {
  outline: 3px solid var(--ivo-title);
  outline-offset: 3px;
}

/* 例外三：先生／小姐單選鈕。
   下面 .ivo-form input:focus 的 outline: none 也吃到 radio，
   而勾選之後 .ivo-form__radio input:checked 又把 border-color、background、
   box-shadow 三個屬性都設成跟聚焦態一樣的值。實測「已勾選且聚焦」與
   「已勾選但沒聚焦」的 computed style 逐項完全相同，等於鍵盤使用者
   看不出焦點停在哪裡，是 WCAG 2.4.7 的實質違反。
   radio 用滑鼠點擊時 :focus-visible 是 false（實測），補外框不影響滑鼠使用者。
   選擇器前面多帶一個 .ivo-page 是為了把特異性拉到 (0,3,1)，
   確實蓋得過 .ivo-form input:focus 的 (0,2,1)，不必依賴誰寫在檔案後面。 */
.ivo-page .ivo-form__radio input:focus-visible {
  outline: 3px solid var(--ivo-title);
  outline-offset: 3px;
}

/* 只給螢幕閱讀器 */
.ivo-sr {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── 3. 容器與格線 ─────────────────────────────────────────────────── */
.ivo-wrap {
  width: var(--ivo-wrap-w);
  margin-inline: auto;
}

.ivo-sec { padding-block: var(--ivo-sec-y); position: relative; }
.ivo-sec--tight { padding-block: 64px; }

/* 相鄰且底色相同的兩個區段，中間會疊出兩份內距（80 + 80 = 160px）
   的連續空白，因為沒有底色變化，視覺上就是一整片空的。
   這裡把後一段的上內距收掉一半，讓兩段之間只保留一份的量。
   底色會變化的組合（白接粉、粉接白）不在此列，那是設計稿的節奏。 */
.ivo-service + .ivo-process,
.ivo-clients + .ivo-cta,
.ivo-cta + .ivo-price,
.ivo-service + .ivo-works { padding-top: 16px; }

/* 首頁的 .ivo-voice 原本也在上面那一組，但它一樣不符合「同底色」的前提。
   逐像素實測（1440，x = 100 / 720 / 1340 三處同值）：
   首頁 voice 底緣最後一列是 rgb(255,251,252)，clients 第一列是 rgb(255,255,255)，
   ΔRGB (0,+4,+3)。數值很小，但它是一條橫貫整個視窗寬、零過渡的硬邊，
   在大面積平塗上看得很清楚。收成 16px 之後，
   「服務產業類別多元　不受限的設計風格」等於貼著那條邊在跑，
   客戶回報的就是這個。首頁改吃 .ivo-sec--tight 自己的 64px。
   同一組裡另外四筆（service→process、clients→cta、cta→price、service→works）
   同樣實測過，交界上下都是 rgb(255,255,255)，ΔRGB 全為 0，分類正確，一律不動。

   關於頁不能一起拿掉：那裡的 voice 被下面的 .ivo-dual + .ivo-voice 改成白底，
   底緣實測就是 rgb(255,255,255)，與 logo 牆完全同色，才是這條規則真正的對象。
   所以用三段相鄰選擇器（特異性 0,3,0，壓得過 .ivo-sec--tight 的 0,1,0）
   把 16px 單獨留給關於頁。前提是關於頁的區塊順序維持 dual → voice → clients，
   中間插進別的區段這條就會失效，clients 會落回 64px（只是變鬆，不會壞版）。 */
.ivo-dual + .ivo-voice + .ivo-clients { padding-top: 16px; }

/* .ivo-dual + .ivo-voice 原本也在上面那一組，但它不符合這條規則的前提。
   關於頁的 .ivo-dual 底部是 --ivo-lilac（#FFF0FF，看得出來的淺紫粉），
   而 .ivo-dual + .ivo-voice 又把 voice 改成白底，
   所以這是「粉接白」的底色變化組合，正是上面註解說不該收的那一種。
   收成 16px 之後，dual 的色帶幾乎貼著「客戶感受到了什麼不同？」那顆 pill，
   客戶回報的就是這個。改回與其他區段一致的 80px。 */

/* 精選作品接 CTA 時底色會從白變粉，交界看得出來，
   不需要收得像同色那麼緊 */
.ivo-works + .ivo-ready { padding-top: 48px; }

/* 12 欄格線：單欄 80px、gap 30px，跨欄寬度公式 110n − 30 */
.ivo-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--ivo-gap);
}
.ivo-col-3  { grid-column: span 3; }
.ivo-col-4  { grid-column: span 4; }
.ivo-col-5  { grid-column: span 5; }
.ivo-col-6  { grid-column: span 6; }
.ivo-col-7  { grid-column: span 7; }
.ivo-col-8  { grid-column: span 8; }
.ivo-col-12 { grid-column: span 12; }

/* ── 4. 字級與標題 ─────────────────────────────────────────────────── */
.ivo-h1 {
  font-size: var(--ivo-fs-h1);
  line-height: 1.2;
  font-weight: 700;
  color: var(--ivo-title);
  letter-spacing: .01em;
}
.ivo-h2 {
  font-size: var(--ivo-fs-h2);
  line-height: 1.4;
  font-weight: 700;
  color: var(--ivo-title);
  letter-spacing: .01em;
}
.ivo-h3 {
  font-size: var(--ivo-fs-h3);
  line-height: 1.5;
  font-weight: 700;
  color: var(--ivo-title);
}
.ivo-lead {
  font-size: var(--ivo-fs-lead);
  line-height: 1.8;
  color: var(--ivo-text);
}
.ivo-body { font-size: var(--ivo-fs-body); line-height: 1.7; }
.ivo-sm   { font-size: var(--ivo-fs-sm); line-height: 1.6; }

/* 標題裡的粉紅強調字。
   同一個 .ivo-em 出現在兩種字級，不能只給一個顏色：
     (1) .ivo-h1 / .ivo-h2 內：48/38/30px 與 32/28/24px，皆 700，
         三個斷點最小是 24px bold，全部 ≥18.66px bold → 大字，門檻 3:1
     (2) .ivo-hero__quote：18px（手機 16px）/500 → 不是大字，門檻 4.5:1
   .ivo-h3 是 18px/700，18 < 18.66，刻意不納入。

   【2026-09-12 客戶指定改回 #FF63B9】
   客戶要求標題強調字就用色票表的主視覺色。
   ⚠ #FF63B9 對白底 2.71:1，對理念區的實際合成底（Y 0.7863）2.35:1，
     連 WCAG 大字門檻 3:1 都沒過。客戶已知悉並要求照設計稿。
   以下是先前為了合規所做的推導，保留備查：

   為什麼曾經取 #EF2E96 而不是更亮的粉：
   .ivo-em 最不利的底不是 token 值，是 .ivo-philo 的「實際合成底」。
   那一區除了漸層底色，還壓了 .ivo-philo__deco（deco-lines.png，
   opacity .55，inset:0），而標題直接坐在上面沒有白卡墊底。
   實測（墨跡遮罩膨脹 9px 後取 15202 個背景像素）最暗 Y = 0.7863。
     #EF2E96（Y 0.22507）在其上 3.040:1、白底 3.817:1 → 全過
     #ED50A9（Y 0.26606）在其上只有 2.646:1 → 不過
   ΔE00 vs 設計稿 #FF63B9：#EF2E96 是 9.52，原本的 #C40068 是 22.37，
   色相也從 359.3（偏正紅）校回 352.7（品牌是 347.7）。 */
.ivo-em { color: var(--ivo-btn); font-weight: 700; }

.ivo-h1 .ivo-em,
.ivo-h2 .ivo-em,
.ivo-page .ivo-h1 .ivo-em,
.ivo-page .ivo-h2 .ivo-em,
.entry-content .ivo-h1 .ivo-em,
.entry-content .ivo-h2 .ivo-em {
  color: var(--ivo-primary);   /* #FF63B9，設計稿的主視覺色 */
}

.ivo-center { text-align: center; }

/* ── 5. 元件：膠囊標籤 ─────────────────────────────────────────────── */
/* 設計稿的粉紅細框膠囊。
   框線用主色 #FF63BA（不承載文字，允許低對比），
   文字用 #C40068 才過 4.5:1。 */
.ivo-pill {
  display: inline-flex;
  align-items: center;
  min-height: 30px;
  padding: 4px 18px;
  /* 外框維持 --ivo-primary。設計稿那條 1px 框在無損 PNG 上被縮放攤成三列
     （y55 (255,233,245)、y56 (255,105,187)、y57 (255,175,219)），
     中間那列才是本色，換算就是 #FF63B9 = --ivo-primary，現行值本來就對。 */
  border: 1px solid var(--ivo-primary);
  border-radius: var(--ivo-radius-pill);
  font-size: var(--ivo-fs-sm);
  line-height: 1.5;
  /* 設計稿 pill 文字實測是 #FF63B9，客戶指定照用。
     ⚠ 14px 對白底只有 2.71:1，未達小字門檻 4.5:1（客戶已知悉）。 */
  color: var(--ivo-primary);
  background: var(--ivo-white);
  letter-spacing: .05em;
}

/* ── 6. 元件：按鈕 ─────────────────────────────────────────────────── */
/* 觸控目標 ≥ 44×44，設計稿量到 45px，這裡取 46px。
   主按鈕走「路線 A」：實色底加深到 #C40068 配白字（5.92:1）。
   設計稿的粉紅漸層保留，但暗端壓在文字正下方，
   亮端 #FF63BA 只出現在按鈕右緣不承載文字的位置。 */
.ivo-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;    /* 給下面的 ::after 觸控保險用 */
  /* min-height 已移除。專案規範：撐觸控目標不能用 padding 或 min-height
     （會被畫進 ::selection 的反白背景，本案踩過三次）。
     高度現在由行盒自己撐：19 × 1.5 + 8 × 2 + 2 = 46.5px，已 ≥ 44。
     min-width 保留，那是寬度下限不是觸控手段。 */
  min-width: 168px;
  padding: 8px 28px;
  border: 1px solid transparent;
  /* 這顆透明邊框是為了讓 --solid 與 --ghost 等高（--ghost 有 1px 實邊框），
     但它同時製造了客戶說的「按鈕右邊有一點點深紅色」：
     background-origin 不寫時預設為 padding-box，漸層的鋪圖區域因此比按鈕
     左右各少 1px；background-clip 不寫時預設為 border-box，漸層卻要一路
     畫到邊框外緣。中間差出來的兩條 1px 邊框帶，由 background-repeat 的
     預設值 repeat 拿「下一次平鋪」補滿：
       右緣那 1px 補的是漸層最左端 #EF2D96（深），緊貼內側的 #FFAFDB（淺），
       單通道差 130，所以一眼就看得到；
       左緣那 1px 補的是漸層最右端 #FFAFDB（淺），襯在淺粉頁底上不明顯。
     改成 border-box 讓鋪圖區域與繪製區域一致，平鋪接不到邊，兩條接縫同時消失。
     版面尺寸完全不動（實測 181.125 × 46.5 前後相同）。

     為什麼不用其他做法（都實測過）：
       background-repeat: no-repeat：邊框帶改露 background-color #C40068，
         四邊變成一圈深紅，比現況更糟。
       拿掉 border：按鈕從 46.5px 矮到 44.5px，上面那份觸控餘裕只剩 0.5px，
         而且 --ghost 的實邊框還在，並排時會差 2px 高。
       background-clip: padding-box：邊框帶變全透明，露出頁底與 box-shadow
         的光暈，圓角處會把光暈跟按鈕本體切開。

     ⚠ background-origin 會被 background 簡寫重設回 padding-box。
       下面 --solid / --ghost 的 hover 規則上方那段註解要求「hover 只換
       background-image，不要動 background 簡寫」，現在多了一個理由：
       只要有人在按鈕任一變體上寫 background:，右緣深紅就會原地復發。 */
  background-origin: border-box;
  border-radius: var(--ivo-radius-sm);
  /* 19px/700 是為了讓按鈕文字進入 WCAG 大字（≥18.66px bold），
     門檻從 4.5:1 降到 3:1，--ivo-grad-cta 的 3.322:1 才成立。
     設計稿按鈕文字換算約 16.7px，這裡刻意放大並加粗，
     換來整條漸層可以往設計稿的亮粉推一大階。這是需要客戶知情的取捨。 */
  font-size: var(--ivo-fs-btn);
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: .08em;
  cursor: pointer;
  /* 監聽 box-shadow，否則光暈會在 transform 平滑上移時瞬間閃出。
     filter 拿掉：hover 不再用 brightness()（那會連白字一起壓暗）。 */
  transition: background-color .25s ease, color .25s ease,
              border-color .25s ease, transform .25s ease,
              box-shadow .25s ease;
}
/* 觸控目標保險：若日後 --ivo-fs-btn 被改小、行盒高度掉到 44px 以下，
   由這層絕對定位的透明區塊補足。不會被畫進 ::selection，也不影響版面。 */
.ivo-btn::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  top: 50%;
  transform: translateY(-50%);
  height: 44px;
}

/* hover 只換 background-image，不要動 background 簡寫：
   寫成 background: var(--ivo-btn) 會把 background-image 整個移除，
   而 transition 只監聽 background-color，結果是瞬間露出底色再跳回來。 */
.ivo-btn--solid,
.ivo-page .ivo-btn--solid,
.entry-content .ivo-btn--solid {
  color: #FFFFFF;
  /* 純色底要保留：一是 background-image 生效前的底色，
     二是 Lighthouse 與 axe 只讀 background-color，會判 5.92:1 通過。 */
  background-color: var(--ivo-btn);
  background-image: var(--ivo-grad-cta);
  box-shadow: var(--ivo-grad-glow);
}
.ivo-btn--solid:hover,
.ivo-page .ivo-btn--solid:hover,
.entry-content .ivo-btn--solid:hover {
  color: #FFFFFF;
  background-color: var(--ivo-btn-hover);
  background-image: var(--ivo-grad-cta-hv);
  transform: translateY(-1px);
  box-shadow: var(--ivo-grad-glow-hv);
}

.ivo-btn--ghost,
.ivo-page .ivo-btn--ghost,
.entry-content .ivo-btn--ghost {
  color: var(--ivo-btn);
  background-color: var(--ivo-white);
  background-image: none;
  border-color: var(--ivo-primary);
}
.ivo-btn--ghost:hover,
.ivo-page .ivo-btn--ghost:hover,
.entry-content .ivo-btn--ghost:hover {
  background-color: var(--ivo-btn);
  background-image: var(--ivo-grad-cta);
  border-color: transparent;
  box-shadow: var(--ivo-grad-glow);
  color: #FFFFFF;
}

/* 文字型的 MORE 連結 */
.ivo-more {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: var(--ivo-fs-sm);
  font-weight: 500;
  letter-spacing: .1em;
  color: var(--ivo-btn);
}
/* 設計稿用的是兩個大於符號，不是橫線 */
.ivo-more::after {
  content: ">>";
  margin-left: 8px;
  letter-spacing: -.04em;
  transition: transform .25s ease;
  display: inline-block;
}
.ivo-more:hover::after { transform: translateX(4px); }

/* ── 7. 元件：卡片 ─────────────────────────────────────────────────── */
.ivo-card {
  background: var(--ivo-white);
  border-radius: var(--ivo-radius);
  box-shadow: var(--ivo-shadow);
}

/* ── 8. 捲動淡入（styleguide 指定的唯一進場動效）───────────────────── */
.ivo-reveal {
  opacity: 0;
  transform: translateY(18px);

  /* 進場轉場的三個參數抽成自訂屬性。
     唯一理由是單一來源：Hero 評論卡要在同一條 transition 簡寫裡
     再掛 scale / box-shadow / z-index，而 CSS 沒有「附加一項」的寫法，
     那條規則（0,2,0）會把這裡整條取代掉。抽成變數之後，
     評論卡用 var() 引用即可，不必把 .7s ease 與五個延遲各抄一份。
     兩份複本不同步而且沒有錯誤訊息，這個專案已經踩過（見 make_base.py 的抬頭）。 */
  --ivo-rv-dur:   .7s;
  --ivo-rv-ease:  ease;
  --ivo-rv-delay: 0s;
  transition:
    opacity   var(--ivo-rv-dur) var(--ivo-rv-ease) var(--ivo-rv-delay),
    transform var(--ivo-rv-dur) var(--ivo-rv-ease) var(--ivo-rv-delay);

  will-change: opacity, transform;
}
/* 進場動畫跑完就把合成層釋放掉。will-change 常駐會留下一個合成層，
   而 WebKit 在合成層裡畫 background-clip: text（成果數字的漸層字）
   有閃爍或整個不顯示的已知問題。 */
.ivo-reveal.is-in { opacity: 1; transform: none; will-change: auto; }
/* 逐項延遲，最多排到第 6 項，再多就同時進場。
   只設變數，不直接設 transition-delay：直接設的話是單值，
   會把延遲一起套到日後任何加進同一條 transition 的屬性上。 */
.ivo-reveal[data-d="1"] { --ivo-rv-delay: .08s; }
.ivo-reveal[data-d="2"] { --ivo-rv-delay: .16s; }
.ivo-reveal[data-d="3"] { --ivo-rv-delay: .24s; }
.ivo-reveal[data-d="4"] { --ivo-rv-delay: .32s; }
.ivo-reveal[data-d="5"] { --ivo-rv-delay: .40s; }

/* ==========================================================================
   區段 01｜Hero
   設計稿 y 101-944（原稿）→ 1440 基準高約 633px
   ========================================================================== */
.ivo-hero { padding-block: 72px 96px; background: var(--ivo-white); }

.ivo-hero__inner { align-items: center; }
.ivo-hero__copy  { grid-column: span 7; }
.ivo-hero__visual{ grid-column: span 5; position: relative; }

.ivo-hero__title { margin-top: 24px; }
.ivo-hero__lead  { margin-top: 28px; color: var(--ivo-text); }
.ivo-hero__lead p + p { margin-top: 6px; }
.ivo-hero__quote { font-weight: 500; }

.ivo-hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-top: 36px;
}

.ivo-hero__proof {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-top: 32px;
  flex-wrap: wrap;
}
.ivo-hero__avatars { display: flex; }
.ivo-hero__avatar {
  width: 34px; height: 34px;
  margin-right: -8px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 2px solid var(--ivo-white);
  background: var(--ivo-pink-lt);
  color: var(--ivo-btn);
  font-size: var(--ivo-fs-xs);
  font-weight: 500;
}
.ivo-hero__proof-text { font-size: var(--ivo-fs-sm); color: var(--ivo-text); }
.ivo-hero__proof-num  { font-weight: 700; color: var(--ivo-title); letter-spacing: .04em; }

/* Hero 右側散落的評論卡。
   設計稿是五張旋轉錯位的卡片，這裡用絕對定位重現。
   關鍵是重疊只准發生在卡片的留白邊緣，不可壓到文字：
   卡片寬固定 195px，五張在 520px 的欄寬內錯開，
   並用 line-clamp 把內文鎖在 4 行，卡片高度才不會因文案長短而互撞。
   手機改成單欄堆疊（見手機斷點）。 */
/* Hero 右側散落的評論卡。設計稿是五張互相交疊、各自傾斜的便條紙。
   卡片群 436px 寬（左欄 x=0、右欄 x=220，單卡 216px），
   卡高固定 152px 並配 overflow: hidden，文案被改長時只會在卡內截斷，
   不會溢出到白色 hero 底上（深色卡的白字一旦溢出就等於消失，
   固定高度與 overflow 是必須配對的兩行）。 */
.ivo-hero__cards {
  position: relative;
  isolation: isolate;   /* 把 z-index 1-5 關在自己的堆疊脈絡，不外洩到頁首與 Popup */

  /* 右欄實寬。用跨欄公式精確展開，不寫死 px：
     版心 min(90vw, --ivo-maxw)，12 欄 gap 30 → span 5 = 版心×5/12 − gap×7/12 */
  --ivo-hc-col: calc(min(90vw, var(--ivo-maxw)) * 5 / 12 - var(--ivo-gap) * 7 / 12);

  /* 允許往兩側借的量。左邊借左欄，右邊借版心外的頁面留白。
     右邊只借 20px：留白是 5vw 再「整條」扣掉捲軸寬（捲軸全在右側），
     1000px 配 17px 傳統捲軸時留白只剩 33px，借 20 還有 13px 餘裕。
     先前版本借 33.6px，在 1000~1012px 會溢出 0.6px 長出水平捲軸。 */
  --ivo-hc-bleed-l: 70px;
  --ivo-hc-bleed-r: 20px;

  /* 旋轉造成的外框溢出，用外接矩形 w·|cosθ| + h·|sinθ| 實算，不是估計值 */
  --ivo-hc-rot-l: 7.4px;
  --ivo-hc-rot-r: 12.6px;

  /* 卡寬由「旋轉後外框 = 欄寬 + 左右外溢」反解。未旋轉外框恆為 2.94 × 卡寬 */
  --ivo-hc-w: min(216px, calc(
      (var(--ivo-hc-col) + var(--ivo-hc-bleed-l) + var(--ivo-hc-bleed-r)
       - var(--ivo-hc-rot-l) - var(--ivo-hc-rot-r)) / 2.94));

  width:  calc(var(--ivo-hc-w) * 2.94);
  /* 164（最低一張的 top）+ 152（卡高）。不要為了看起來剛好壓到 300 以下，
     下排兩張會掉出容器。旋轉的上下溢出落在 .ivo-hero 的 72/96px 內距裡。 */
  height: 316px;
  left: calc(var(--ivo-hc-rot-l) - var(--ivo-hc-bleed-l));

  /* ── 待機動效的四顆旋鈕。整組只有這裡要改，三檔一鍵切換 ──────────
     保守　amp 1.8deg / rate .85 / drift 3px / dim .955
     建議　amp 2.6deg / rate .72 / drift 5px / dim .935　← 目前
     激進　amp 3.4deg / rate .60 / drift 7px / dim .915
     整個關掉：--ivo-hc-amp: 0deg 加 --ivo-hc-drift: 0px，其餘規則都不用動。

     ⚠ amp 與 dim 一定要成對調，dim 是拿來把淨空還回來的。
       量法是分離軸，用「上層卡外框」對「下層卡逐行可見字框」，
       窮舉 ±amp 全部相位（3／5／7／9／15／25 格結果相同，端點即最差）：
       只把 amp 從 1.2 加到 2.6 而 dim 留在 .97，hover 時 1000px 的
       粉卡會壓進淺灰卡署名 −1.70px；配 dim .935 之後回到 +0.82px。

     ⚠ 換檔時連下面 hover 的 --ivo-hc-zd 一起檢查。z-index 翻轉的那一瞬間
       兄弟還沒縮到位，是整組動效最緊的一格，dim 拉得越低這一格越緊。
       zd .18s 之下 1000px 分別是 +0.65（保守）、+0.63（建議）、+0.67px（激進）；
       若把 zd 改回 .12s，建議檔掉到 +0.09px、激進檔變成 −0.04px 直接破。 */
  --ivo-hc-amp:   2.6deg;   /* 每張卡繞自己中心的晃動振幅 */
  --ivo-hc-rate:  .72;      /* 週期倍率，越小越快。只買速度，不動任何幾何 */
  --ivo-hc-drift: 5px;      /* 整叢一起上下浮動的振幅 */
  --ivo-hc-dim:   .935;     /* hover 時「其他四張」縮到多少 */

  /* 手機的振幅由上面那個值換算，不另外寫一個要手動同步的數字。
     ⚠ 不能在手機斷點直接寫 --ivo-hc-amp: calc(var(--ivo-hc-amp) * .7)：
       自訂屬性引用自己算循環，整條會變成 guaranteed-invalid，
       keyframes 裡的 calc(var(--ivo-hc-amp) * -1) 跟著失效、動畫完全不跑。
       所以在這裡先算好第二份給手機取用。線上實測手機端 computed 值是
       calc(2.6deg * .7)、rotate 讀得到 −1.80deg，確認沒有觸發循環。
     手機卡片寬 280~606px，角半徑是桌機的 1.4~2.4 倍，
     同樣角度換出來的線位移大得多，取 0.7 倍兩邊的觀感才對齊。 */
  --ivo-hc-amp-m: calc(var(--ivo-hc-amp) * .7);

  /* 整叢一起上下浮動。這一條是本輪「看得見」的主力，而且幾何上是免費的：
     translate 掛在容器上，五張卡的相對位置一格都沒動，
     共同平移在相對幾何裡直接相消，交疊與 hover 的淨空完全不受影響
     （1440 待機最差淨空，掛與不掛同為 +5.25px）。
     對照組：若把浮動拆成每張卡各自 translate，相鄰兩張反相時相對位移是
     兩倍，1px 浮動就吃掉約 2px 淨空。
     另一個好處是旋轉在卡片中心的位移趨近於零（原註解不加 translateY
     的理由），整叢平移則是連文字中心一起動，是「察覺得到在動」的來源。

     容器沒有掛 .ivo-reveal，所以這個 translate 不會和
     .ivo-reveal.is-in { transform: none } 打架，也不必把任何角度抄進
     keyframes。9.4s 與五張卡的 5.5／7.9／6.3／7.1／8.7 沒有公因數，不會鎖相。 */
  animation: ivo-hcards-float calc(9.4s * var(--ivo-hc-rate))
             ease-in-out calc(-3.1s * var(--ivo-hc-rate)) infinite;
}

@keyframes ivo-hcards-float {
  from, to { translate: 0 calc(var(--ivo-hc-drift) * -1); }
  50%      { translate: 0 var(--ivo-hc-drift); }
}
.ivo-hcard {
  position: absolute;
  width: var(--ivo-hc-w);
  height: 152px;        /* 固定高度，交疊幾何才可預測 */
  overflow: hidden;     /* 與固定高度成對出現，缺一不可 */
  padding: 14px 16px 16px;
  border-radius: 14px;
  box-shadow: var(--ivo-shadow-card);
  font-size: var(--ivo-fs-xs);
  line-height: 1.7;
}
/* base 值用 --ivo-accent 不用 --ivo-primary：#FF63BA 對白底只有 2.71:1、
   對 #EBEAEF 只有 2.27:1，連非文字元件的 3:1 都不到。 */
.ivo-hcard__stars { color: var(--ivo-accent); font-size: 11px; letter-spacing: 2px; }
/* 內文鎖 3 行，卡片高度才會一致，錯位排列不會因文案長短而互撞 */
.ivo-hcard__text  {
  margin-top: 7px;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.ivo-hcard__who   { text-align: left; margin-top: 10px; font-size: 11px; opacity: .82; }

/* 上三下二，照設計稿的構圖。left 是 --ivo-hc-w 的倍數、top 用固定 px。
   先前是「左欄 2 張、右欄 3 張」的直排（left 只有 0 與 220 兩個值，
   而單卡 216px，220 > 216 所以根本沒有真交疊），又細又貼著欄位右緣，
   客戶說「不是那個構圖、太偏右」講的就是這個。

   角度取自設計稿實測（黑 −6.1、粉 −4.6、右淺灰 +11.8、左淺灰 −3.7、紫 +10.3），
   四捨五入成 −6 / −5 / +11 / −4 / +10。

   這組數字是逐對做分離軸檢查解出來的：比較「上層卡的外框」對
   「下層卡的文字框」（內縮 左右 16、上 14、下 17.3）。
   在卡寬 140 到 220px 每 0.5px 掃描：沒有任何一張壓到別張的星號、
   內文或署名，最窄處仍留 2.3px；同時每張都與別張有 11px 以上的真實交疊。
   要改任何一個數字，請重跑這個檢查。

   補充（加待機動效時重掃）：上面用的掃描範圍 140~220px 比實際寬。
   這個版面真正會出現的卡寬是 145.4~216px
   （桌機 ≥1000px 是 145.4~200.7、平板 690~999.98px 是 197.6~216，
   750px 起就吃到 216 的上限；140 與 220 都不會發生）。
   在真實範圍內重掃，最窄淨空是 2.70px，出現在 W=145（視窗約 1000px），
   紫卡外框對淺灰卡文字框。待機晃動的 ±0.6° 與 hover 的兄弟 .97
   都是用 2.70px 這個預算反解出來的，動到任何一個座標都要一起重跑。 */
.ivo-hcard--dark   { background: #292A2F;            color: #FFFFFF;         left: 0;                            top:  30px; z-index: 1; --ivo-rot:  -6deg; }
.ivo-hcard--pink   { background: var(--ivo-pink-lt); color: var(--ivo-text); left: calc(var(--ivo-hc-w) * 0.94); top:   0;   z-index: 2; --ivo-rot:  -5deg; }
.ivo-hcard--gray   { background: var(--ivo-line);    color: var(--ivo-text); left: calc(var(--ivo-hc-w) * 1.94); top:  26px; z-index: 3; --ivo-rot:  11deg; }
.ivo-hcard--white  { background: var(--ivo-line);    color: var(--ivo-text); left: calc(var(--ivo-hc-w) * 0.44); top: 164px; z-index: 4; --ivo-rot:  -4deg; }
/* 紫卡改用 --ivo-lilac 當底、--ivo-text 當字（13.07:1）。
   --ivo-accent #BA4BFF 在色票註解就寫明「輔助紫，裝飾用」：
   它配白字 3.79:1、配深字 3.78:1，兩邊都過不了 AA，不能當承載小字的面。
   紫的存在感改由左緣色條與星號表達。 */
.ivo-hcard--violet { background: var(--ivo-lilac);   color: var(--ivo-text); left: calc(var(--ivo-hc-w) * 1.47); top: 154px; z-index: 5; --ivo-rot:  10deg;
                     box-shadow: var(--ivo-shadow-card), inset 3px 0 0 var(--ivo-accent); }

/* 星星分卡上色（★ 是 aria-hidden 的裝飾記號，門檻是非文字的 3:1）。 */
.ivo-hcard--dark  .ivo-hcard__stars { color: #FFFFFF; }
.ivo-hcard--pink  .ivo-hcard__stars { color: var(--ivo-text); }

/* 關鍵：把旋轉寫進 reveal 的三個狀態，權重壓過 .ivo-reveal.is-in
   { transform: none }（0,2,0）。先前卡片明明寫了 rotate 卻看不到傾斜，
   就是被那條在動畫結束時清掉了。沒有新增第四種動效，淡入還是原本那一種。
   rotate 放在 translateY 前面，兩個 transform list 才有共同前綴，
   轉場會逐函式內插，不會退化成 matrix 分解。 */
.ivo-hcard:not(.ivo-reveal) { transform: rotate(var(--ivo-rot, 0deg)); }
.ivo-hcard.ivo-reveal       { transform: rotate(var(--ivo-rot, 0deg)) translateY(18px); }
.ivo-hcard.ivo-reveal.is-in { transform: rotate(var(--ivo-rot, 0deg)); }

/* ── Hero 評論卡｜待機微晃與 hover 強調 ─────────────────────────────
   兩種動效都刻意不碰 transform。上面那三行已經被
   .ivo-reveal.is-in { transform: none }（0,2,0）綁死，任何新的 transform
   宣告或 keyframes 都會整條蓋掉旋轉或進場位移；把 rotate 再抄進 keyframes
   只是讓「傾斜角」這份資料多出第四、第五份，下次改角度必漏改。

   改走 rotate 與 scale 這兩個獨立變換屬性。瀏覽器的合成順序固定是
   translate → rotate → scale → transform，最終矩陣為
       R(晃動) · S(hover) · R(--ivo-rot) · T(進場位移)
   兩個旋轉共用同一個 transform-origin，相乘等於角度相加；等比縮放與旋轉
   可交換。幾何上就是「基準角 ± 晃動角，再等比縮放」，與那三行零衝突，
   進場的 translateY(18px) 原封不動，晃動也不必等進場跑完才啟動。

   ⚠ 代價要講清楚，不要誤以為繞過了坑 3：
   animation-iteration-count: infinite 會讓這五張卡「永久」保有合成層，
   效果與宣告 will-change 幾乎等價，正是坑 3 要避免的常駐圖層。
   這裡可以接受的真正理由是卡片內沒有 background-clip: text
   （漸層字只出現在成果數字那一段），踩不到 WebKit 在合成層裡畫漸層字
   會閃的那個已知問題。附帶代價是文字從「把旋轉烘進點陣」變成
   「軸對齊光柵化後由合成器旋轉」，小字會略軟（1× 螢幕比較看得出來）。
   要整個退場只需一行：--ivo-hc-amp: 0deg，結構全留、hover 不受影響。 */

.ivo-hcard {
  /* 振幅已經移到 .ivo-hero__cards 宣告，靠繼承下來。
     ⚠ 這裡刻意不再留一份：直接寫在 .ivo-hcard 上的值會贏過繼承值，
       容器那顆旋鈕就會變成裝飾品。實測若兩邊都留，桌機 computed
       --ivo-hc-amp 會停在 1.2deg，新旋鈕完全失效。
       手機斷點仍然是在 .ivo-hcard 上覆寫，那是「直接宣告贏過繼承」，
       不受這一條影響。 */

  /* z-index 延後翻轉的秒數，預設 0s。
     ⚠ 這一條刻意宣告在 .ivo-hcard（0,1,0）而不是
     .ivo-hero__cards .ivo-hcard（0,2,0）：覆寫它的 .ivo-hcard:hover
     也是 (0,2,0)，兩條同特異性就只能靠原始碼順序分勝負，
     日後有人搬動規則位置，hover 的延遲會靜默失效，而畫面上只是
     一閃即逝的重疊，驗收時抓不到。降一層之後是靠特異性勝出。 */
  --ivo-hc-zd: 0s;

  /* 兩個旋轉必須繞同一點，否則上面那個「角度相加」不成立。
     預設值就是 50% 50%，這裡明寫是為了不讓日後有人在別處改掉。 */
  transform-origin: 50% 50%;

  animation: ivo-hcard-sway var(--ivo-hc-per, 7s) ease-in-out
             var(--ivo-hc-off, 0s) infinite;
}

/* 振幅 ±1.2° 的依據，以及一個要記下來的教訓。

   ⚠ 原方案寫的是 ±0.6°，理由是「現況最窄淨空 2.70px，±0.6° 還剩 1.66px」。
     那個數字是錯的，線上實測推翻了它。錯在兩件事：
     (a) 它把「文字框」當成卡片框內縮固定內距，但 .ivo-hcard__text /
         __who 是 block，框橫跨整個內容寬，右邊一大片是空白；
         真正會被遮住的是 Range.getClientRects() 逐行回傳的字框。
     (b) 量測時只把獨立的 rotate 歸零，忘了基準傾斜是寫在 transform 上
         （而且卡片有 transition: transform .7s，直接設 transform:none
         只是啟動一段轉場，當下讀到的還是舊值）。等於基準角被算了兩遍。

   ⚠⚠ 上面那次重掃修好了 (a)(b)，卻換踩第三個坑，結論一樣是錯的。
     它當時報「靜態基準已經重疊：1440 −4.60px、1200 −8.57px、1000 −14.57px，
     每次都是 violet 壓到 gray 內文行尾」。那些行根本不存在於畫面上：
     .ivo-hcard__text 是 -webkit-line-clamp: 3 加 overflow: hidden，
     而 Range.getClientRects() 照樣回傳第 4、5、6 行（被夾掉、看不到的那幾行）。
     字框除了要被卡片框裁，還要再被「文字元素自己的框」裁一次。
     補上這一刀之後重掃，兩位工程師各自寫程式獨立量到同一組數字：
         靜態基準 1440 +10.57px　1280 +10.01　1200 +9.72　1100 +9.36
                  1000 +8.66　999~760 +6.79　690 +10.48px
         現況 ±1.2° 窮舉相位 1440 +8.07　1200 +7.57　1000 +6.34px
     也就是「可見文字之間從來沒有重疊過」，而且還有 6~10px 的真實預算。
     前一輪以為沒有預算才只敢加到 ±1.2°，那個保守是建立在假數字上的。
     相位取樣密度 3／5／7／9／15／25 格結果完全相同，±amp 端點即最差。

   有了真預算，改用「看得見」當標準挑值。
   ±1.2° 配原週期時，卡角峰值線速度只有 2.1~3.3 px/s
   （角半徑 125.9px、ease-in-out 的峰值斜率 1.7241、
   峰值角速度 = 1.7241 × 4 × amp ÷ 週期）。在 60cm 視距、
   1 CSS px 約 0.24mm 之下換算成視角速度約 0.05~0.08 °/s，
   屬於「要盯著看才看得出來」的量級，客戶說看不太清楚是合理的。
   ⚠ 不要把這句寫成「低於人眼閾值」：慢速運動的察覺閾值高度依賴有沒有
     靜止參考物，文獻值散佈在 0.003~0.3 °/s，而這個構圖旁邊全是靜止參考。
     現況不是「物理上看不到」，是「弱到讀不出意圖」。
     挑值的依據是觀感加幾何預算，不是某一條閾值。
   ±2.6° 配 rate .72 之後卡角是 9.9 px/s，再加上整叢浮動的 5.1 px/s，
   峰值合計約 15 px/s，是現況的四倍多，掃一眼就讀得到。
   淨空代價：1440 從 +8.07 掉到 +5.25px，1000 從 +6.34 掉到 +3.70px，都還是正的。

   仍然刻意不在「每張卡」上疊 translate：純繞中心旋轉時文字區中心的位移
   趨近於零，各自垂直平移卻是整塊基線上下爬，而且相鄰兩張反相時相對位移
   是兩倍，1px 就要吃掉約 2px 淨空。要的整體浮動感改掛在容器上
   （見上面的 ivo-hcards-float），那一份對相對幾何是零成本。

   ⚠ 另外記一筆既有缺陷（不是這次改出來的，也還沒動）：
     視窗 1000~1100px 時 .ivo-hcard__who 會斷行，落單的最後一個字
     （深色卡的「口」、粉卡的「人」）正好被下方卡片蓋住。
     補 white-space: nowrap + text-overflow: ellipsis 可解，只多約 1px 淨空，
     但會把署名改成截斷加刪節號，屬於文案呈現的決定，等客戶指示。 */
@keyframes ivo-hcard-sway {
  from, to { rotate: calc(var(--ivo-hc-amp) * -1); }
  50%      { rotate: var(--ivo-hc-amp); }
}

/* 週期刻意讓空間上相鄰、交疊最多的兩張拿差距最大的值。
   最緊的一對是淺灰與紫（它們決定了 2.70px 那個淨空），
   拍頻 1/(1/6.3 − 1/8.7) = 22.8s，相對相位每 22.8 秒走完一輪，
   永遠不會鎖在一起。五張真正同步要 LCM(55,63,71,79,87) 個 0.1s，
   71 與 79 是質數，實務上不會發生。
   負的 delay 只用來錯開起始相位；因為 rotate 不碰 transform，
   不需要留正的延遲去等 .ivo-reveal 的進場跑完。 */
/* 週期與起始相位都乘上 --ivo-hc-rate。縮短週期是唯一「只買速度、
   不付幾何代價」的維度：振幅一點沒變，交疊淨空完全相同，
   但峰值速度與 rate 成反比。起始相位也要一起乘，
   上面那段拍頻推導（1／(1／6.3 − 1／8.7) = 22.8s）才會等比縮成
   22.8 × rate 秒，五張永遠不會鎖在一起這個結論才繼續成立。
   線上實測 computed：--ivo-hc-per 是 calc(5.5s * .72)、
   animation-duration 3.96s、animation-delay −1.008s，calc 確實生效。 */
.ivo-hcard--dark   { --ivo-hc-per: calc(5.5s * var(--ivo-hc-rate)); --ivo-hc-off: calc(-1.4s * var(--ivo-hc-rate)); }
.ivo-hcard--pink   { --ivo-hc-per: calc(7.9s * var(--ivo-hc-rate)); --ivo-hc-off: calc(-4.7s * var(--ivo-hc-rate)); }
.ivo-hcard--gray   { --ivo-hc-per: calc(6.3s * var(--ivo-hc-rate)); --ivo-hc-off: calc(-0.3s * var(--ivo-hc-rate)); }
.ivo-hcard--white  { --ivo-hc-per: calc(7.1s * var(--ivo-hc-rate)); --ivo-hc-off: calc(-5.5s * var(--ivo-hc-rate)); }
.ivo-hcard--violet { --ivo-hc-per: calc(8.7s * var(--ivo-hc-rate)); --ivo-hc-off: calc(-3.7s * var(--ivo-hc-rate)); }

/* 轉場清單必須一次寫完：.ivo-reveal 的 transition 是簡寫（0,1,0），
   本規則（0,2,0）會整條取代它，所以進場那兩項要帶回來。
   但不抄數字：時長、緩動、逐項延遲全部 var() 引用 .ivo-reveal 抽出來的
   --ivo-rv-* 三個變數，改那邊這裡自動跟上，不會有兩份不同步的複本。

   z-index 那一項是 duration 0s、只有延遲的翻轉，見下面 hover 區塊。 */
.ivo-hero__cards .ivo-hcard {
  transition:
    opacity    var(--ivo-rv-dur, .7s) var(--ivo-rv-ease, ease) var(--ivo-rv-delay, 0s),
    transform  var(--ivo-rv-dur, .7s) var(--ivo-rv-ease, ease) var(--ivo-rv-delay, 0s),
    scale      .26s cubic-bezier(.22, .61, .36, 1)             0s,
    box-shadow .3s  ease                                       0s,
    z-index    0s   linear                                     var(--ivo-hc-zd);
}

/* hover 的放大是「相對放大」，被 hover 那張一個 px 都不長。

   原因是分離軸掃描的結果：hover 要把卡提到最上層，等於啟用了一批
   原註解從來沒驗過的配對方向（原本只驗現有 z 序）。光是提層、
   完全不放大，淨空就已經見底（不含晃動）：
       黑提到最上層   → 對白卡文字    0.00px
       粉提到最上層   → 對淺灰卡文字 +0.16px
       淺灰提到最上層 → 對紫卡文字   −0.32px
   含 ±0.6° 晃動時更差（−0.61 / −0.97 / −1.03px）。
   換算成最大安全 scale：黑 1.0000、粉 1.0000、淺灰 1.0000、
   白 1.0227、紫 1.0211，放大 1.04~1.06 在這個構圖裡沒有空間。
   而且壓到的不是空白：scale 1.03 時粉卡會侵入淺灰卡文字框
   x[−84, −80.9] y[+49.6, +60.4]，也就是署名「灼見文創　執行長」
   最左邊「灼」字的左半邊，3.1px 寬。CJK 字面貼滿字身框，沒有側邊留白可吃。

   改成兄弟縮小。兄弟縮小會把它們的文字框往自己中心收，
   方向與放大 hovered 那張相反，是在釋放淨空而不是消耗：
   ⚠ 這一段的數字與上面的振幅推導同源，一樣被「line-clamp 夾掉的行
     也被算成字框」污染過。用修正後的量法（字框再被文字元素自己的框裁一次），
     並且讓被 hover 那張自由停在 ±amp 任一相位（它是 paused，不是歸零），
     重掃五張各自被 hover 的情況，取最差的一格（穩定態，兄弟已縮到位）：
       amp 1.2° ／兄弟 .97   → 1440 +2.68　1200 +1.67　1050 +1.01　1000 +0.79px（現況）
       amp 2.6° ／兄弟 .97   → 1440 +0.48　1200 −0.69　1050 −1.45　1000 −1.70px　← 破
       amp 2.6° ／兄弟 .935  → 1440 +3.23　1200 +2.24　1050 +1.17　1000 +0.82px　← 採用
       amp 1.8° ／兄弟 .955  → 1440 +2.91　1200 +1.89　1050 +1.05　1000 +0.78px（保守檔）
       amp 3.4° ／兄弟 .915  → 1440 +3.39　1200 +2.65　1050 +1.34　1000 +0.91px（激進檔）
     最緊的一格永遠是「粉卡外框壓到淺灰卡的 .ivo-hcard__who」，
     也就是 1000~1100px 會斷行的那個署名。
     結論：兄弟縮放不是「順便加強」，是加大振幅的必要對價。
     .935 同時把相對放大從 3.1% 提到 6.9%，hover 的層次也更明顯。

   ⚠⚠ 穩定態不是最緊的一格，轉場窗才是。z-index 在 --ivo-hc-zd 翻轉，
     而兄弟要 .26s 才縮到位，翻轉那一瞬間兄弟只縮到中途，
     是「已經提層、兄弟還沒讓位」的最差格。
     緩動 cubic-bezier(.22, .61, .36, 1) 在 t=.12s 走到 84.5%、
     t=.18s 走到 96.1%，1000px 實測：
       zd .12s　現況 1.2／.97 → +0.46px　　新設定 2.6／.935 → +0.09px（退步五倍）
       zd .18s　現況 1.2／.97 → +0.71px　　新設定 2.6／.935 → +0.63px（比現況寬）
       zd .12s 的激進檔 3.4／.915 是 −0.04px 直接破，zd .18s 才回到 +0.67px。
     所以 dim 調低的同時 zd 必須從 .12s 拉到 .18s，兩者是一組的。
     離開 hover 仍然不延遲（base 的 --ivo-hc-zd 留 0s）：那一側兄弟只會變大、
     幾何只會變寬，不需要補償。

   ⚠ 評估過但否決：hover 時把被 hover 那張 rotate 回正（0 度）。
     除了要動 transform（.ivo-reveal.is-in 那三行綁死的東西）之外，
     幾何上直接不成立：淺灰卡 +11° 轉回 0° 時角點掃過約 24px，
     必定壓到紫卡的星號。窮舉「殘留角度比例 × 兄弟縮放」，
     兄弟要縮到 .86（等於整列在跳動）都還是負的，這條路沒有解。
     改用零幾何成本的訊號：被 hover 那張 animation-play-state: paused，
     其餘四張繼續晃、縮到 --ivo-hc-dim。「只有它停下來」本身就是強訊號。

   觸控裝置用 hover/pointer 查詢擋掉，避免 iOS 點一下之後 hover 黏住。
   這個 media query 不含 max-width，不會觸發 make_base.py 的斷點守門
   （那支只比對 @media 裡的 max-width 數字）。
   但它也不含寬度條件，窄視窗的桌機瀏覽器一樣會命中，
   所以手機斷點裡另有一段還原。 */
@media (hover: hover) and (pointer: fine) {

  /* :has 讓「游標在卡片群的空白處」不會誤觸。
     不用 .ivo-hero__cards:hover 是因為容器 316px 高，
     五張絕對定位之間有大片空隙，滑過空隙會變成五張一起縮、沒有一張被強調。 */
  .ivo-hero__cards:has(.ivo-hcard:hover) .ivo-hcard:not(:hover) {
    scale: var(--ivo-hc-dim, .935);
  }

  /* z-index 瞬間翻、scale 要 .26s，所以進入的第一幀會有
     「已在最上層、兄弟還沒縮」的空窗，實測最差 −1.03px。
     把提層延後 0.12s 補掉：t<0.12s 用的還是原本驗過的幾何（淨空 ≥1.66px），
     t=0.12s 時兄弟已緩動到約 0.975，淨空 ≈ +0.47px，之後單調變好。

     --ivo-hc-zd 只設在 :hover，base 在 .ivo-hcard 留 0s，這個不對稱是刻意的：
     轉場參數取自「切換後的狀態」，所以進入延後、離開立刻還原。
     若離開也延後，會出現「兄弟長回 0.99、卡還在最上層」的 −0.55px。 */
  .ivo-hcard:hover {
    /* .12s 改成 .18s。dim 從 .97 調到 .935 之後，兄弟要走的距離變成兩倍多，
       原本的 .12s 會在兄弟只縮到 84.5% 進度時就把卡提到最上層，
       1000px 那一格從現況的 +0.46px 掉到 +0.09px。
       改 .18s（緩動進度 96.1%）之後是 +0.63px，比現況還寬。
       換 amp 或 dim 檔位時要一起回頭看這個數字。 */
    --ivo-hc-zd: .18s;
    z-index: 6;
    box-shadow: var(--ivo-shadow-card-hv);

    /* 被 hover 的那張停住待機晃動，其餘四張繼續晃。
       這是 rotate 回正被否決之後找到的替代訊號：
       幾何成本是零（暫停只是把它鎖在 ±amp 之間的某一格，
       而上面的 hover 淨空表已經讓它自由停在任一相位，那就是上界），
       離開 hover 時從暫停的那一格接著跑，不像 animation: none 那樣
       會在重新掛上動畫時跳回起始相位。
       容器的 ivo-hcards-float 不暫停，整叢還是活的。

       ⚠ 這一條在手機斷點一定要還原成 running，理由見那邊的註解：
         本查詢只問指標種類、不問寬度，窄視窗的桌機一樣會命中，
         而手機斷點的三張卡是靠「同相位」才把振幅的幾何成本歸零的，
         只要暫停其中一張就會脫鉤。 */
    animation-play-state: paused;
  }

  /* 紫卡的左緣色條是它的識別，hover 不能弄丟。
     .ivo-hcard:hover 是 (0,2,0)，會壓過 .ivo-hcard--violet 的 (0,1,0)，
     所以這一條必須存在，而且要排在上面那條之後。
     順帶讓兩端都是「兩層陰影」，box-shadow 才逐層內插，不會跳一下。 */
  .ivo-hcard--violet:hover {
    box-shadow: var(--ivo-shadow-card-hv), inset 3px 0 0 var(--ivo-accent);
  }
}

/* ==========================================================================
   區段 02｜理念
   設計稿 y 945-2167 → 淡粉漸層 #FFF0FF → #FFFEFF，有機曲線裝飾
   ========================================================================== */
/* 設計稿這一段的上下留白比其他區段大得多（916px 對我們原本的 613px），
   那片留白是給背景有機曲線呼吸用的，不是多餘的空白。
   這裡單獨把垂直內距拉大，向設計稿的節奏靠攏。

   【第四輪修正】原本的 padding-block: 132px 把上下綁在一起，
   但「上下都 132」只有上緣站得住腳。下緣接的是白底服務區（上內距 80），
   132 + 80 = 212px，是首頁除了 Hero→理念以外最長的一段連續空白，
   而且落在「白色圓角卡片底部 → 粉色 pill」之間，前後都是高對比元素，
   空白被襯得更明顯，客戶回報的就是這一段。
   所以上緣維持 132（曲線起手式、由白轉粉的呼吸），
   下緣收回全站標準 var(--ivo-sec-y)，交界變成 80 + 80 = 160px，
   與 results→hold、dual→voice 這些交界的房規值一致。

   為什麼不會切壞裝飾曲線：.ivo-philo__deco 是 background-size: cover、
   定位 center top，圖 1600×1079（比例 1.483）。桌機 1440 時區段比例
   1440 / 735 = 1.96 比圖更扁，cover 的縮放倍率由「寬度」決定，
   所以區段變矮不會讓曲線縮小或變形，只是可見範圍從圖的 75.7% 收到 70.3%，
   多裁掉底部 52px。實拍對照過，那 52px 裡沒有任何造型端點，
   而且曲線本來就被 overflow: hidden 硬切在區段下緣，132 並沒有在保護它。
   手機是另一種情形：區段比圖瘦長，cover 改由高度決定，
   倍率 1.377 → 1.266，曲線會略微縮小、左右少裁一點，實拍確認沒有變形或空掉。

   下緣寫成變數，平板 72、手機 56 會自動跟著縮；上緣的 132 是寫死的，
   平板與手機在各自的斷點另外覆寫（原本整條規則完全沒有斷點覆寫，
   手機鄰段只有 56px，132 是它的 2.36 倍，比桌機還離譜）。 */
.ivo-philo {
  padding-top: 132px;
  padding-bottom: var(--ivo-sec-y);
  background: linear-gradient(180deg, var(--ivo-lilac) 0%, #FFFBFF 55%, #FFFEFF 100%);
  overflow: hidden;
}
.ivo-philo__deco {
  position: absolute;
  inset: 0;
  background: url("../img/deco-lines.png") no-repeat center top / cover;
  opacity: .55;
  pointer-events: none;
}
/* DOM 順序是「標題（h2）在前、卡片（h3）在後」，標題階層才不會跳級。
   桌機的視覺順序相反：卡片在左、標題在右，用 order 對調。 */
.ivo-philo__inner { position: relative; align-items: center; }
.ivo-philo__list  { grid-column: span 6; order: 1; display: grid; gap: 24px; }
.ivo-philo__copy  { grid-column: span 6; order: 2; padding-left: 30px; }

.ivo-pcard {
  display: flex;
  gap: 24px;              /* 設計稿換算 24px */
  align-items: flex-start;
  padding: 28px 30px;
  background: rgba(255, 255, 255, .82);
  border-radius: var(--ivo-radius);
  box-shadow: var(--ivo-shadow);
  backdrop-filter: blur(2px);
}
/* 圖示放大到設計稿的比例（設計稿換算約 88px 寬）。
   88 / 64 × 56 = 77，整除，不會有半像素。
   color 保留 hex fallback，避免 var() 解析不到時變成繼承的深灰。 */
/* 設計端提供的三個原始 SVG，長寬比各不相同
   （燈泡 70×97 = 0.72、對話框 111×96 = 1.16、愛心 91×83 = 1.10），
   不能共用一組固定寬高，否則會被拉變形。
   改成「固定高度、寬度自動」，三個圖示的視覺高度才會一致。
   欄寬取最寬的對話框（80 × 1.16 = 93px），留 112px 含左右呼吸。
   SVG 自帶 fill="#FF63B9" 與 opacity 分層，不吃 currentColor。 */
.ivo-pcard__icon {
  flex: 0 0 112px;
  display: grid;
  place-items: center;
}
.ivo-pcard__icon svg { height: 80px; width: auto; display: block; }
.ivo-pcard__text { margin-top: 14px; color: var(--ivo-text); }

.ivo-philo__title { margin-top: 20px; }
.ivo-philo__para  { margin-top: 24px; }
.ivo-philo__para p + p { margin-top: 20px; }

/* ==========================================================================
   區段 03｜服務六卡
   設計稿三欄，每欄 480/1494 ≈ 31.8%，與 1290 格線跨 4 欄（410px）吻合
   ========================================================================== */
.ivo-service { background: var(--ivo-white); }
.ivo-service__head { max-width: 780px; margin-inline: auto; }
.ivo-service__title { margin-top: 20px; }
.ivo-service__sub   { margin-top: 24px; color: var(--ivo-text); }

.ivo-service__grid { margin-top: 56px; row-gap: 48px; }
.ivo-scard { grid-column: span 4; }
.ivo-scard__fig {
  margin: 0;
  border-radius: var(--ivo-radius-sm);
  overflow: hidden;
  aspect-ratio: 410 / 262;
  background: var(--ivo-line);
}
.ivo-scard__fig img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; }
.ivo-scard:hover .ivo-scard__fig img { transform: scale(1.03); }
.ivo-scard__title { margin-top: 22px; }
.ivo-scard__text  { margin-top: 14px; color: var(--ivo-text); font-size: var(--ivo-fs-body); }
.ivo-scard__more  { margin-top: 8px; }

/* ==========================================================================
   區段 04｜六步驟
   ========================================================================== */
.ivo-process { background: var(--ivo-white); overflow: hidden; }
.ivo-process__inner { position: relative; }
/* 文字一定要在同心圓之上。同心圓是裝飾層，
   即使因為視窗寬度而與步驟欄重疊，也不可以壓到字。 */
.ivo-process__copy { grid-column: span 8; position: relative; z-index: 1; }
.ivo-process__viz  { grid-column: span 4; position: relative; z-index: 0; }

.ivo-process__title { margin-top: 20px; }

.ivo-steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 40px 30px;
  margin-top: 48px;
}
.ivo-step__head {
  position: relative;
  display: flex;
  align-items: baseline;
  /* 修正：設計稿量到的 12 設計px 是「墨跡間距」，不是 flex gap。
     「1」的右側邊距 + .08em 尾隨字距本身就佔掉 8~9px，
     gap 設 14px 會做出約 22px 的墨跡間距，比設計稿寬一倍。 */
  gap: 8px;
  padding-bottom: 10px;   /* 修正：維持 10px，數字放大後字身框已經變高 */
}
/* 設計稿的橫線是固定長度、不是滿欄（實測線長 139–140 設計px、欄距 201px，
   佔欄寬約 69%）。奇數步驟粉、偶數步驟紫：六條底線最深像素 R−B 依序為
   +44 / −41 / +40 / −24 / +30 / −29，兩個家族界線清楚，不是 JPEG 雜訊。
   修正：用 min(180px, 69%) 而不是死的 180px，版心在 1000–1434px 之間
   會縮到 90vw，固定 px 會讓線相對變短。 */
.ivo-step__head::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: min(180px, 69%);
  height: 2px;
  background: var(--ivo-primary);
}
.ivo-step:nth-child(even) .ivo-step__head::after { background: var(--ivo-accent); }

.ivo-step__num {
  font-size: 34px;                /* 設計稿墨跡 19 設計px 反推，原本 30px */
  font-weight: 300;
  line-height: 1;
  color: var(--ivo-title);
  letter-spacing: .08em;
}
.ivo-step__name { font-size: var(--ivo-fs-body); font-weight: 500; color: var(--ivo-title); }
.ivo-step__text { margin-top: 16px; font-size: var(--ivo-fs-sm); line-height: 1.85; color: var(--ivo-text); }

/* 同心圓視覺：設計稿是「同一圓心的四圈環」，每圈內緣帶粉、往外淡到透明，
   最內圈核心是 #FF63BA 實色。我沿三個方向（上 / 下 / 左）取樣過設計稿的
   徑向剖面，三組的環界半徑一致（r ≈ 0 / 78 / 137 / 222，外徑 290），
   確認是單一圓心，用一個元素 + 一條 radial-gradient 畫完即可，不吃圖片。
   圓心刻意落在版心右緣外側，由 .ivo-process 既有的 overflow:hidden 裁掉。 */
.ivo-rings {
  --ivo-rings-d: min(760px, 53vw);   /* 外徑。設計稿 580 ÷ 1100 畫布 = 52.7vw */
  position: absolute;
  top: 87%;                          /* 虛線落在第二排步驟橫線下方約 15px。
                                        .ivo-grid 沒設 align-items，viz 這格
                                        預設 stretch，高度＝列高＝左欄高度，
                                        所以百分比有東西可以換算。
                                        步驟文案增減行數時只需微調這個數字。 */
  /* 修正：原方案寫 +8px，圓心會跑到版心右緣「內側」，距畫布右緣 83px，
     和它自己量的 67px 對不上（註解也寫成外側）。
     −8px 才是圓心在版心右緣外側 8px ＝ 距畫布右緣 67px。 */
  right: calc(var(--ivo-rings-d) / -2 - 8px);
  width: var(--ivo-rings-d);
  height: var(--ivo-rings-d);
  /* 不能用 transform 置中：.ivo-reveal.is-in 會把 transform 歸零 */
  margin-top: calc(var(--ivo-rings-d) / -2);
}
/* 修正：原方案的 12 個 stop 用了 7 個不在色票裡的 hex
   （#FFC3E5 / #FFC7E3 / #FFE2F2 / #FFCAE7 / #FFE3F2 / #FBCDE7 / #FFDFF0），
   違反「色票只有這些，不要自己新增」。這些值本來就全是 #FF63BA 疊白底的
   不同透明度，逐個反算成 alpha 後改寫（最大通道誤差 4/255），
   與 style.css 既有的 rgba(255, 99, 185, …) 寫法一致，不新增任何色票；
   改成半透明後外緣是淡出到透明而非硬邊白圓，疊在白底上結果相同。 */
.ivo-rings__disc {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  /* closest-side 是必做的修正，不是選配：原本漏了它，radial-gradient 預設
     farthest-corner，在正方形裡 = R×1.414，整條四圈環的剖面被放大 1.414 倍，
     75.9% 那圈（第四環）整圈落在 border-radius 之外看不到，可見範圍只到
     剖面的 70.7%，邊緣還留一圈 α≈.05 的軟邊。
     同一個坑 .ivo-flow__ring::before 的註解早就寫過（「一定要寫 closest-side」）。
     設計稿量到的環界 r ≈ 0/78/137/222、外徑 290，換算 26.9 / 47.2 / 76.6%，
     與這 12 個 stop 的 24.8 / 45.5 / 75.9% 對得上，證實 stop 本來就是
     以「半徑」為單位寫的，closest-side 才是原意。
     無障礙後果（自算，sRGB 相對亮度，底 #FFFFFF）：
     .ivo-rings__label--3（#A30056）最近 ink 在離圓心 0.0495D，
       原本 farthest-corner：α .641 → 4.04:1（未過 AA 4.5）
       補上 closest-side：  α .493 → 4.71:1（過）
     這是既有缺陷不是動效造成的，但動效與它同一次上線，一起修掉。 */
  background: radial-gradient(circle closest-side at 50% 50%,
              rgba(255, 99, 185, 1)      0%,
              rgba(255, 99, 185, .385)  12%,
              rgba(255, 99, 185, 0)   24.1%,
              rgba(255, 99, 185, .359) 24.8%,
              rgba(255, 99, 185, .186) 34.5%,
              rgba(255, 99, 185, 0)   44.5%,
              rgba(255, 99, 185, .340) 45.5%,
              rgba(255, 99, 185, .180)  60%,
              rgba(255, 99, 185, 0)   74.5%,
              rgba(255, 99, 185, .321) 75.9%,
              rgba(255, 99, 185, .205)  88%,
              rgba(255, 99, 185, 0)    100%);

  /* ── 持續動效：本體微呼吸 + 波前（動效配額的例外，客戶指定）──────────
     例外範圍在這一版擴大了，要照實記：原本是
     .ivo-rings__disc 與 .ivo-flow__ring::before 兩個 selector，
     現在是這兩個再加 .ivo-rings__disc::before / ::after 與
     .ivo-flow__ring::after 共四個，keyframes 四組
     （ivo-breathe-disc / ivo-wave-disc / ivo-breathe-ring / ivo-wave-ring）。
     不是規範改了。

     為什麼本體幅度反而更小（scale 1 → .958，不是把 .982 無限放大）：
     三個 label 在視覺上釘在特定環上（label--3 核心、label--2 第二環、
     label--1 第三環）。本體只要縮到 .93，第三環邊界就會在文字底下滑動 27px，
     label 會脫離它該標的那圈，設計語意就壞了。動態主體交給下面的波前。

     維持「只往內縮 + opacity ≤ 1」的單向原則，理由不變：
     (1) 對比度。字底實際 α = 漸層 α × 層 opacity，opacity 上限 1。
         不可以改用「圓縮小所以變淡」當理由：這條漸層是鋸齒剖面
         （24.8 / 45.5 / 75.9 各一個上升緣），單點的 α 縮放時可能跳高。
         實算的保證是：每段文字的 ink 範圍本來就跨過它那一圈的峰值，
         縮放只會把峰值在 ink 範圍內左右移，不會製造出更高的 α。
         全週期逐格掃描後的最低對比度（自算，closest-side 之後）：
           .ivo-rings__q--r      6.89:1（最低點就是靜止值）
           .ivo-rings__q--l      9.77:1（波前全程掃過它）
           .ivo-rings__label--2  5.44:1
           .ivo-rings__label--1  5.67:1（波前經過時的最低點）
           .ivo-rings__label--3  4.71:1（靜止值，波前掃不到它）
         ≤689.98px 三個 label 改座標後最低 5.33:1。
     (2) 效能。本體最大縮放值仍是 1，光柵化倍率 1，不會為了放大重畫 760px。

     時間軸：0% / 50% 是最收縮，對齊下面兩道波的誕生點，
     讀起來是「核心一縮、吐出一圈」。本體實際呼吸週期 4.5s。
     0% 已經不是靜止外觀，減少動態偏好那邊改成靠 !important 歸位。
     一樣不寫 will-change：CSS 動畫本來就會升合成層。 */
  transform-origin: 50% 50%;
  animation: ivo-breathe-disc 9s ease-in-out infinite;
}

@keyframes ivo-breathe-disc {
  0%, 50%, 100% { transform: scale(.958); opacity: .90; }
  25%, 75%      { transform: scale(1);    opacity: 1;   }
}

/* 向外擴散的波前，兩道，相差半個週期（每 4.5s 出一道）。
   「明顯」靠的是一條邊界穿過靜止的場（相對運動），不是靠亮度，
   所以峰值 α 只有 .16 也讀得到，而且不會搶走旁邊步驟文案的注意力。

   ★ 重要：這兩個假元素是 .ivo-rings__disc 的「子節點」，不是兄弟。
     波會連帶吃到本體的 transform 與 opacity。實際畫面上：
       波的 scale = 自身 scale × 本體 scale（.958 ~ 1）
       波的 α    = 自身 α × 本體 opacity（.90 ~ 1）
     這是安全方向（波更小、更淡），但下面所有半徑都必須用複合值計算：
       內緣最小半徑 .58 × .5 × .52 × .958 = 0.1445D
       峰值半徑掃過 0.204D → 0.41D，起步約 34.6px/s（760px 時）
     這一層不能改掛到 .ivo-rings 上：.ivo-rings::after 是五段文字之後的
     最後一個子節點，會畫在文字上面。掛在 disc 才會被壓在文字底下
     （虛線、兩個端點、五段文字都是 disc 的兄弟，仍在一般繪製路徑上，
     文字本身完全沒有被 transform，不會糊也不會抖）。

   內緣 58% 與最小 scale .52 是算出來的，不是手感：
     .ivo-rings__label--3 最外側 ink 在 0.096D（字寬再寬 20% 也只到 0.107D），
     波的複合最小內緣 0.1445D，兩者留 0.037D（760px 時 28px）的間隙。
     label--3 是全圖唯一沒有對比度餘裕的位置（#A30056 的 4.5:1 天花板是
     α .535，補了 closest-side 之後靜止已經用掉 .493），波永遠不准走進去。
     所以 58% 與 .52 只能往上調，不能往下調；要加大幅度請調最大 scale 那一端。
   另外兩段提問文字（.ivo-rings__q）不在「掃不到」的名單裡：
     q--l 的 ink 落在 0.29D~0.47D，全程被波掃過，實算最低 9.77:1；
     q--r 的外角約在 0.145D，剛好貼著波的複合最小內緣，α 幾乎為 0，最低 6.89:1。
     兩者都是深色字（#292A2F），餘裕夠，但不要再把它們往外移。

   timing 用 linear，減速寫在 keyframe 的間距裡（峰值半徑約 35 → 25 → 13 → 8 px/s），
   模擬水波：出生快、外擴慢、在外緣化掉。逐段 ease-out 會在每個 keyframe
   把速度歸零，反而看得出一頓一頓。
   最大 scale 是 1（複合後仍是 1），光柵化倍率維持 1。 */
.ivo-rings__disc::before,
.ivo-rings__disc::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: radial-gradient(circle closest-side at 50% 50%,
              rgba(255, 99, 185, 0)     58%,
              rgba(255, 99, 185, .16)   82%,
              rgba(255, 99, 185, .11)   93%,
              rgba(255, 99, 185, 0)    100%);
  transform-origin: 50% 50%;
  animation: ivo-wave-disc 9s linear infinite;
  pointer-events: none;
}
.ivo-rings__disc::after { animation-delay: -4.5s; }

@keyframes ivo-wave-disc {
  0%   { transform: scale(.52); opacity: 0;   }
  10%  { transform: scale(.62); opacity: .55; }
  35%  { transform: scale(.80); opacity: .90; }
  70%  { transform: scale(.93); opacity: .45; }
  100% { transform: scale(1);   opacity: 0;   }
}

/* 兩個端點與中間的水平虛線 */
.ivo-rings__line {
  position: absolute;
  left: 8.3%;
  right: 50%;
  top: 50%;
  border-top: 1px dashed rgba(196, 0, 104, .35);   /* --ivo-btn 的透明階 */
}
.ivo-rings__dot {
  position: absolute;
  top: 50%;
  width: 7px;
  height: 7px;
  margin: -3px 0 0 -3px;
  border-radius: 50%;
  background: var(--ivo-title);
}
.ivo-rings__dot--out { left: 8.3%; }
.ivo-rings__dot--in  { left: 50%; }

/* 上排兩句提問（深色）與下排三個圈名（深粉），各自對齊在同一水平線上 */
.ivo-rings__q,
.ivo-rings__label {
  position: absolute;
  letter-spacing: .06em;
  white-space: nowrap;
}
.ivo-rings__q {
  top: 42.5%;
  font-size: 13px;
  line-height: 1.35;
  color: var(--ivo-text);     /* #292A2F，最壞情況（核心 #FF63BA）5.28:1 */
}
.ivo-rings__q--l { left: 3.2%; }
.ivo-rings__q--r { left: 37.2%; }

.ivo-rings__label {
  top: 54.4%;
  font-size: 12px;
  line-height: 1.3;
  /* 修正：原方案用 --ivo-btn #C40068 並宣稱「白底 5.92:1」，
     但這三個圈名不在白底上，label--3 正好壓在核心外緣。
     設計稿該處實測底色 rgb(255,175,226)：#C40068 只有 3.52:1（不過），
     #A30056 是 4.64:1（過）。#A30056 在色票清單內，
     而且比 #C40068 更接近我量到的設計稿墨跡 rgb(160,85,124)。
     警告：任何字都不能再往核心正中央移，#A30056 對純 #FF63BA 只有 2.88:1。 */
  color: var(--ivo-btn-hover);
}
.ivo-rings__label--1 { left: 16.5%; }
.ivo-rings__label--2 { left: 30.8%; }
.ivo-rings__label--3 { left: 45.1%; }

/* Step 3 差異卡：設計稿是「左濃右淡的極淡粉底 + 深色字」，不是濃洋紅底配白字。
   漸層純水平（90deg），逐欄取樣驗過（x=145 處 G=177 ⇒ α≈.50，
   f>0.65 之後持平在近白）。記號是白圓底 + 粉星號（放大確認過，方案沒看錯）。 */
.ivo-hint {
  position: relative;
  z-index: 1;                 /* 修掉同心圓畫在卡片之上的疊層 bug：
                                 .ivo-process__viz 是 z-index:0 的定位元素，
                                 會畫在 static 的 .ivo-hint 上面。 */
  display: flex;
  gap: 32px;
  align-items: flex-start;
  /* 修正：原方案寫死 max-width: 850px。850 只有在版心滿 1290 時才等於 8 欄，
     .ivo-wrap 是 min(90vw, 1290px)，1000–1434px 視窗下版心會縮到 90vw，
     卡片就會比左邊 span 8 的步驟區寬一大截（1100px 視窗：步驟 650 vs 卡片 850）。
     用百分比版跨欄公式：8c + 7g，c = (100% − 11g)/12 ⇒ 66.6667% − g/3。
     1290/gap30 → 850px（同原方案）；990/gap30 → 650px（正確跟著步驟區）。 */
  max-width: calc(66.6667% - var(--ivo-gap) / 3);
  margin-top: 56px;
  padding: 24px 32px;
  border-radius: var(--ivo-radius);
  background: linear-gradient(90deg,
              rgba(255, 99, 185, .62)   0%,
              rgba(255, 99, 185, .40)   9%,
              rgba(255, 99, 185, .28)  21%,
              rgba(255, 99, 185, .17)  37%,
              rgba(255, 99, 185, .03)  63%,
              rgba(255, 99, 185, .03) 100%),
              var(--ivo-white);
  color: var(--ivo-text);
}
.ivo-hint__mark {
  flex: 0 0 56px;
  height: 56px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--ivo-white);
  color: var(--ivo-deep);     /* 26px 記號、aria-hidden：白底 4.37:1，
                                 以大型文字 3:1 與「只給面與記號」都成立 */
  font-size: 26px;
  line-height: 1;
}
.ivo-hint__title {
  font-size: var(--ivo-fs-h3);
  font-weight: 700;
  letter-spacing: .06em;
  /* 修正：18px/700 = 13.5pt bold，還不到 WCAG 大型文字門檻（14pt bold），
     要 4.5:1。標題起點在卡片 120/850 ≈ 14%（記號 56 + gap 32 + padding 32），
     該處底色 α≈.35 ⇒ rgb(255,201,231)：#C40068 = 4.16:1（不過），
     #A30056 = 5.48:1（過）。設計稿標題最深墨跡 rgb(152,29,84) 也更接近 #A30056。 */
  color: var(--ivo-btn-hover);
}
/* 卡片收窄後原本的 max-width:62ch 已無作用，一併移除 */
.ivo-hint__text {
  margin-top: 12px;
  font-size: var(--ivo-fs-sm);
  line-height: 1.85;
  color: var(--ivo-text);     /* 最濃的左端 α=.62 底色上 7.56:1 */
}

/* ==========================================================================
   區段 05｜顧客評論 Slider
   實際 8 則輪播，桌機一次顯示 3 則
   ========================================================================== */
.ivo-voice { background: linear-gradient(180deg, var(--ivo-paper) 0%, #FFFBFC 100%); }
/* 關於頁的評論區在設計稿是白底（首頁那份是淡粉）。
   只有關於頁會出現「雙軌核心」接「顧客評論」這個組合，
   用相鄰選擇器就能區分，不必為了底色另外複製一份共用區段。 */
.ivo-dual + .ivo-voice { background: var(--ivo-white); }
.ivo-voice__title { margin-top: 20px; }

.ivo-slider { margin-top: 48px; }
.ivo-slider__viewport { overflow: hidden; }
.ivo-slider__track {
  display: flex;
  gap: var(--ivo-gap);
  transition: transform .6s cubic-bezier(.4, 0, .2, 1);
}
.ivo-slider__slide { flex: 0 0 calc((100% - var(--ivo-gap) * 2) / 3); }

.ivo-vcard {
  height: 100%;
  padding: 24px 26px 28px;
  background: var(--ivo-white);
  border-radius: var(--ivo-radius);
  box-shadow: var(--ivo-shadow);
}
.ivo-vcard__stars { color: var(--ivo-primary); font-size: 13px; letter-spacing: 3px; }
.ivo-vcard__who {
  text-align: left;
  margin-top: 10px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--ivo-line);
  font-size: var(--ivo-fs-sm);
  color: var(--ivo-title);
}
.ivo-vcard__text {
  margin-top: 18px;
  font-size: var(--ivo-fs-sm);
  line-height: 1.9;
  color: var(--ivo-text);
}

/* 控制列：左箭頭 + 圓點 + 右箭頭 */
.ivo-slider__nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  margin-top: 34px;
}
.ivo-slider__dots {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 10px;
}

/* 左右箭頭。設計稿只畫了圓點，但只有圓點的話
   使用者看不出可以操作，桌機也習慣找箭頭。 */
.ivo-arrow {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--ivo-primary);
  border-radius: 50%;
  background: var(--ivo-white);
  color: var(--ivo-btn);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  transition: background-color .25s ease, color .25s ease,
              border-color .25s ease, opacity .25s ease;
}
.ivo-arrow span { display: block; margin-top: -2px; }
.ivo-arrow:hover:not(:disabled) {
  background: var(--ivo-btn);
  border-color: var(--ivo-btn);
  color: #FFFFFF;
}
.ivo-arrow:disabled {
  opacity: .35;
  cursor: default;
  border-color: var(--ivo-line);
  color: var(--ivo-off);
}

/* 圓點本身 12px，點擊區用 ::before 補到 44×44 */
.ivo-dot {
  position: relative;
  width: 12px; height: 12px;
  padding: 0;
  border: 0;
  border-radius: var(--ivo-radius-pill);
  background: var(--ivo-pink-lt);
  cursor: pointer;
  transition: width .3s ease, background-color .3s ease;
}
.ivo-dot::before {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 44px; height: 44px;
  transform: translate(-50%, -50%);
}
.ivo-dot:hover { background: var(--ivo-primary); }
.ivo-dot.is-on { width: 34px; background: var(--ivo-deep); }

/* 拖曳中把游標換成抓取狀態，讓使用者知道可以拖 */
.ivo-slider__track { cursor: grab; }
.ivo-slider__track.is-dragging { cursor: grabbing; transition: none; }

/* ==========================================================================
   區段 06｜客戶 logo 牆（18 枚，6 欄 3 排）
   ========================================================================== */
.ivo-clients { background: var(--ivo-white); }
.ivo-clients__title { font-size: var(--ivo-fs-h2); letter-spacing: .06em; }
.ivo-clients__grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 24px;
  margin-top: 48px;
}
.ivo-client {
  display: grid;
  place-items: center;
  aspect-ratio: 1 / .62;
  padding: 18px;
  background: var(--ivo-white);
  border-radius: var(--ivo-radius-sm);
  box-shadow: var(--ivo-shadow);
}
.ivo-client img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  object-fit: contain;
}

/* ==========================================================================
   區段 07｜CTA 先更多認識你
   ========================================================================== */
/* 左側插圖要從區段最左緣延伸進來（設計稿逐列取樣，紫色手掌切齊畫布左緣）。
   右側文案欄不動，仍然是版心的第 7~12 欄，所以只改 figure。

   一、不能用常見的 margin-left: calc(50% - 50vw)。.ivo-cta__fig 是格線項目，
   格線項目的百分比外距是對「自己的格線區域」（span 6 = 630px）解析，
   不是對容器。1440 視窗會算成 315 − 720 = −405，整張圖被推出畫面。

   二、「區段內容寬」不可以用 100vw。有垂直捲軸時 100vw 比可用寬多一條捲軸，
   溝寬會多算半條（1440 配 15px 捲軸多 7.5px，圖往左溢出）。
   改用 100cqw：把 .ivo-cta 宣告成 inline-size 容器後，
   1cqw = 這個區段內容寬的 1%（捲軸不計入）。

   三、@supports 要測 container-type，不是測 cqw 這個單位能不能 parse。
   萬一 container-type 被壓縮工具丟掉而 cqw 還在，100cqw 會退成
   等同 100vw，測單位就會誤判為「支援」。

   四、min(0px, …) 是保險：萬一區段比版心還窄，算出來會是正值、
   反而把圖往右推而且裁不到；夾在 0 以下就只會退回原本的樣子。

   五、裁切用 clip 不用 hidden：hidden 會讓區段變成捲動容器，
   裡面日後放 position: sticky 會失效，子元素取得焦點時還可能被捲一下。
   clip 可以單獨用，overflow-y 維持 visible，
   .ivo-reveal 的 translateY(18px) 進場位移不會被夾掉。 */
.ivo-cta {
  background: var(--ivo-white);
  container-type: inline-size;   /* 100cqw 的量測基準 */
  overflow-x: clip;              /* 只在退路與異常情況下當裁切邊界 */
}
.ivo-cta__inner { align-items: center; }
/* 不要再給 .ivo-cta__fig 加 width。格線項目預設 justify-self: stretch，
   負外距是靠 stretch 換成寬度的（630 → 630 + 溝寬）；
   一旦寫了 width，stretch 失效，圖會整個往左平移，右邊跟文案之間開一個洞。 */
.ivo-cta__fig  {
  grid-column: span 6;
  margin: 0;
  margin-left: min(0px, calc((var(--ivo-wrap-w) - 100vw) / 2));
}
@supports (container-type: inline-size) {
  .ivo-cta__fig { margin-left: min(0px, calc((var(--ivo-wrap-w) - 100cqw) / 2)); }
}
.ivo-cta__copy { grid-column: span 6; padding-left: 30px; }
.ivo-cta__title { margin-top: 0; }
.ivo-cta__text  { margin-top: 24px; }
.ivo-cta__btn   { margin-top: 32px; }

/* ==========================================================================
   區段 08｜2026 價格
   ========================================================================== */
.ivo-price { background: var(--ivo-white); }
.ivo-price__inner { align-items: center; }
.ivo-price__copy { grid-column: span 5; }
.ivo-price__fig  { grid-column: span 7; margin: 0; }
.ivo-price__title { margin-top: 20px; }
.ivo-price__title small {
  display: block;
  margin-top: 8px;
  font-size: var(--ivo-fs-h3);
  font-weight: 500;
  letter-spacing: .12em;
}
.ivo-price__text { margin-top: 20px; font-size: var(--ivo-fs-sm); line-height: 1.9; }
.ivo-price__btn  { margin-top: 28px; }

/* ==========================================================================
   區段 09｜詢價表單
   欄位依新設計稿：大名＋稱謂、電話、Email、聯絡時間、完成時間、
   製作項目、預算範圍、需求簡述
   ========================================================================== */
.ivo-form-sec { background: linear-gradient(180deg, var(--ivo-paper) 0%, #FFFBFC 100%); }
/* 左欄（標題、說明、插圖、註記）比右欄的表單高，
   用 start 對齊的話表單會貼在上緣，下方空一大塊。
   改成垂直置中，兩欄的視覺重心才會對齊。 */
.ivo-form-sec__inner { align-items: center; }
.ivo-form-sec__copy { grid-column: span 5; }
.ivo-form-sec__body { grid-column: span 7; }
.ivo-form-sec__title { margin-top: 20px; }
.ivo-form-sec__text  { margin-top: 24px; font-size: var(--ivo-fs-sm); line-height: 1.9; }

/* 左欄改成單欄 grid：pill / 標題 / 說明各佔一列，插圖與註記共用第 4 列的
   同一格（設計稿就是這種疊放：註記的橫向範圍落在插圖的 bounding box 內，
   靠縱向錯開不相撞）。用兩欄 grid 排不出來，插圖會被壓到剩一半寬。
   插圖寬與註記左緣由同一組變數推導，不是各寫各的數字。 */
.ivo-form-sec__copy {
  /* 往版心外側的出血量。.ivo-wrap 是 min(90vw, 1290px)，
     1000~1433px 側邊留白是 5vw，但 vw 含垂直捲軸：視窗 1000、捲軸 15 時
     實際留白只有 42.5px，所以不能寫死 40px。3.6vw 在 1440 給到 51.8px，
     是安全前提下取得到的最大值（設計稿要 104px，本站做不到）。

     【2026-09-12 客戶指定改回 0】客戶說「不用到完全貼齊左邊」。
     這個變數保留不刪：下面的 --ivo-fig-w 與 .ivo-form-sec__note 的左緣
     都是從它推導的，日後要改回出血只需要動這一個地方。 */
  --ivo-fig-bleed: 0px;

  /* 插圖寬：設計稿量到 326px 為上限。減 226px 是留給註記的最小空間
     （最長一行 16 個全形字 × 13px = 208px，加 2px 色條與 16px 左內距）。
     少了這個下限，桌機一窄註記就會回捲成四五行。 */
  --ivo-fig-w: min(326px, calc(100% + var(--ivo-fig-bleed) - 226px));

  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-content: start;
}

/* .ivo-pill 是 inline-flex，變成 grid item 會被 blockify，
   預設 justify-items: stretch 會把膠囊框拉成整欄寬，要自己收回 start。 */
.ivo-form-sec__copy > .ivo-pill { justify-self: start; }

.ivo-form-sec__fig {
  grid-column: 1;
  /* 寫死的列號，依賴左欄剛好有 pill / 標題 / 說明三個前置子元素。
     日後若在說明段落前後插入元素，這個 4 要同步調整。 */
  grid-row: 4;
  justify-self: start;
  align-self: start;
  width: var(--ivo-fig-w);
  /* 下：咬掉一整個區段下內距，插圖底緣就切齊區段底緣（客戶指定）。
     用 var(--ivo-sec-y) 而不是寫死 80px，平板 72、手機 56 會自動跟著。
     左：出血歸零後這一項等於 0，保留式子是為了日後要改回出血時
     只需要動 --ivo-fig-bleed 一個地方。 */
  margin: 24px 0 calc(-1 * var(--ivo-sec-y)) calc(-1 * var(--ivo-fig-bleed));
}

/* 鏡像下在 <img>，絕對不要下在 figure。
   figure 帶 .ivo-reveal，而 .ivo-reveal.is-in { transform: none } 會在
   進場動畫結束那一刻把 scaleX(-1) 清掉，圖會自己翻回錯的方向。
   這個 bug 只在動畫跑完後才出現，靜態截圖驗收抓不到。 */
.ivo-form-sec__fig img { transform: scaleX(-1); }

.ivo-form-sec__note {
  grid-column: 1;
  grid-row: 4;
  align-self: start;
  /* 左緣＝插圖右緣再往左 14px（設計稿也是略為疊進插圖的 bounding box）。
     不相撞是可以證明的：圖檔 alpha bbox 內容貼齊右下，鏡像後貼齊左下、
     右側留 8.5% 透明邊，所以不透明內容不超過自身寬度的 91.5%，
     註記左緣減插圖不透明右緣 = 0.085W + 4px，恆為正。 */
  margin: 60px 0 0 calc(var(--ivo-fig-w) - var(--ivo-fig-bleed) - 14px);
  padding-left: 16px;
  border-left: 2px solid var(--ivo-pink-lt);
  font-size: var(--ivo-fs-xs);
  line-height: 1.9;
  color: var(--ivo-text-warm);
}

.ivo-form { display: grid; gap: 18px; }   /* 設計稿量到列距 18px */
.ivo-form__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px 24px;          /* 設計稿：列距 18、欄距 24 */
}
.ivo-form__field { display: flex; flex-direction: column; }
.ivo-form__field--full { grid-column: 1 / -1; }

.ivo-form input[type="text"],
.ivo-form input[type="tel"],
.ivo-form input[type="email"],
.ivo-form select,
.ivo-form textarea {
  width: 100%;
  min-height: 46px;                       /* 觸控目標，不降到設計稿的 41px */
  padding: 12px 16px;
  border: 1px solid var(--ivo-pink-lt);   /* 原本是 --ivo-line 中性灰，設計稿是淺粉 */
  border-radius: var(--ivo-radius-sm);
  background: var(--ivo-white);
  font-family: inherit;
  font-size: var(--ivo-fs-sm);
  color: var(--ivo-text);
  transition: border-color .25s ease, box-shadow .25s ease;
}
.ivo-form textarea { min-height: 141px; resize: vertical; line-height: 1.7; }

/* 字距只給 placeholder，不給輸入值。
   設計稿量的是 placeholder 的版面文字（每字多約 .13em）；
   加在 input 本體會連使用者輸入的 Email、電話、需求內文一起拉開，
   那幾欄最需要逐字核對，字距反而礙事。 */
.ivo-form input::placeholder,
.ivo-form textarea::placeholder {
  color: var(--ivo-text-warm);   /* 舊值 #9A9AA2 只有 2.79:1，而且不在色票內 */
  opacity: 1;
  letter-spacing: .13em;
}
/* select 顯示的是選項標籤不是輸入值，字距可以加在元素本身 */
.ivo-form select { letter-spacing: .13em; }
.ivo-form select:invalid { color: var(--ivo-text-warm); }
.ivo-form input:focus,
.ivo-form select:focus,
.ivo-form textarea:focus {
  /* 這裡刻意保留 outline: none，也刻意不改寫成 :focus-visible。
     Chrome 對文字輸入類控制項一律讓 :focus-visible 成立，滑鼠點一下也算。
     實測用 Input.dispatchMouseEvent 點 input、textarea、select，
     三者的 :focus-visible 都回 true，所以若讓它們吃上面那條通用焦點環，
     滑鼠使用者點一下欄位就會多冒出一個近黑色的框。
     欄位本來就有自己的聚焦指示：邊框由 --ivo-pink-lt 變成 --ivo-btn，
     聚焦前後同一像素 4.48:1、對白底 5.92:1，已經過 3:1，
     而且滑鼠與鍵盤表現一致。
     ⚠ 單選鈕是例外，不適用這段理由：radio 滑鼠點擊時 :focus-visible
     是 false，而且勾選後聚焦與沒聚焦長得一模一樣，所以它另外補了
     .ivo-page .ivo-form__radio input:focus-visible 的外框（見檔案前面）。 */
  outline: none;
  /* 原本 --ivo-primary 對白底只有 2.71:1，不到 WCAG 1.4.11 非文字元素的 3:1。
     focus 指示器改用 --ivo-btn（5.92:1）。 */
  border-color: var(--ivo-btn);
  box-shadow: 0 0 0 3px rgba(255, 99, 185, .16);
}
.ivo-form select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--ivo-btn) 50%),
                    linear-gradient(135deg, var(--ivo-btn) 50%, transparent 50%);
  background-position: calc(100% - 20px) 50%, calc(100% - 15px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 42px;
}

/* 先生／小姐 單選 */
.ivo-form__salute {
  display: flex;
  align-items: center;
  gap: 20px;
  min-height: 46px;        /* 這是 flex 容器不是被選取的文字元素，
                              不會被畫進 ::selection，可以用 min-height */
}
/* margin: 0 是「小姐」對齊的關鍵。
   Blocksy 的 label { margin: 0 0 .5em 0 }（特異性 0,0,1）只被
   label:last-child 歸零，所以第一顆 radio 多出 7px 下外距，
   兩顆在 align-items: center 之下就差了 3.5px，看起來像小姐往下掉。
   .ivo-form__radio 是 (0,1,0)，寫了 margin 就蓋得過，不需要 !important。

   同時拿掉原本的 min-height: 44px：撐觸控目標不用 padding 或 min-height，
   否則反白時整塊 44px 會被畫進 ::selection（本案已經踩過兩次）。 */
.ivo-form__radio {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: var(--ivo-fs-sm);
  line-height: 1.4;
  cursor: pointer;
}
/* 觸控目標 44×44 改由絕對定位的透明 ::after 提供，
   不參與排版、不進 ::selection。label 本身約 52px 寬，max() 保證最窄也有 44。 */
.ivo-form__radio::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(100%, 44px);
  height: 44px;
  transform: translate(-50%, -50%);
}
/* 設計稿是淺粉細圈配白底，約 15px，不是瀏覽器原生樣式 */
.ivo-form__radio input {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  margin: 0;
  flex: none;
  border: 1px solid var(--ivo-pink-lt);
  border-radius: 50%;
  background: var(--ivo-white);
  cursor: pointer;
  transition: border-color .2s ease, background-color .2s ease;
}
.ivo-form__radio input:checked {
  border-color: var(--ivo-btn);
  background: var(--ivo-btn);
  box-shadow: inset 0 0 0 3px var(--ivo-white);   /* 中心 8px 實心點 */
}
/* appearance: none 之後 accent-color 失效，高對比模式下圓圈會整顆消失。
   forced-colors 不是 max-width，不會觸發 make_base.py 的斷點守門。 */
@media (forced-colors: active) {
  .ivo-form__radio input {
    forced-color-adjust: none;
    border-color: CanvasText;
    background: Canvas;
  }
  .ivo-form__radio input:checked {
    border-color: Highlight;
    background: Highlight;
    box-shadow: inset 0 0 0 3px Canvas;
  }
}

.ivo-form__submit {
  width: 100%;
  min-height: 52px;
  margin-top: 15px;      /* 15 + 列距 18 = 33px，設計稿量到 33px */
  border: 0;
  border-radius: var(--ivo-radius-sm);
  /* 原本淺端是 --ivo-pink-3 #F76DB8，白字壓在上面只有 2.68:1，
     連大字門檻 3:1 都不到，是實質的無障礙缺陷不是風格問題。
     改用全站共用的 --ivo-grad。
     這顆是 width:100% 且原本沒有左右內距，標籤
     「送出表單，我們會在兩個工作天內回覆」約 305px，手機上按鈕只剩約 335px，
     文字會鋪到約 95.5%（4.47:1，不合格），所以補 padding-inline 把文字
     內縮到 90.4% 以內 = 4.69:1。這是文字內縮，不是拿 padding 撐觸控目標。
     ⚠ 手機斷點（max-width: 689.98px）已經把這個值收到 16px，
     換取「送出表單…」在 375px 不斷行。那裡重算過文字右緣的落點與底色
     對比（4.5:1 的臨界在漸層 96.16% 處，實測最遠只到 92.88%＝4.61:1），
     所以仍然合格。要改這裡的值之前，請先看那段註解。 */
  background-color: var(--ivo-btn);
  background-image: var(--ivo-grad);
  padding-inline: 32px;
  color: var(--ivo-white);
  font-family: inherit;
  font-size: var(--ivo-fs-body);
  font-weight: 500;
  letter-spacing: .06em;
  cursor: pointer;
  box-shadow: var(--ivo-grad-glow);
  transition: background-color .25s ease, box-shadow .25s ease;
}
.ivo-form__submit:hover {
  background-color: var(--ivo-btn-hover);
  background-image: var(--ivo-grad-hv);
  box-shadow: var(--ivo-grad-glow-hv);
}

/* 送出後的 Popup（styleguide 指定） */
.ivo-modal {
  position: fixed;
  inset: 0;
  z-index: 99;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(41, 42, 47, .55);
  opacity: 0;
  visibility: hidden;
  transition: opacity .3s ease, visibility .3s ease;
}
.ivo-modal.is-open { opacity: 1; visibility: visible; }
.ivo-modal__box {
  width: min(100%, 420px);
  padding: 40px 32px 32px;
  border-radius: var(--ivo-radius);
  background: var(--ivo-white);
  text-align: center;
  transform: translateY(12px);
  transition: transform .3s ease;
}
.ivo-modal.is-open .ivo-modal__box { transform: none; }
.ivo-modal__mark {
  width: 64px; height: 64px;
  margin: 0 auto 20px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--ivo-pink-lt);
  color: var(--ivo-btn);
  font-size: 30px;
}
.ivo-modal__text { margin-top: 12px; font-size: var(--ivo-fs-sm); color: var(--ivo-text); }
.ivo-modal__close { margin-top: 28px; }

/* ==========================================================================
   區段 10｜FAQ 手風琴
   ========================================================================== */
.ivo-faq { background: var(--ivo-white); }
.ivo-faq__title { margin-top: 20px; }
.ivo-faq__list {
  max-width: 882px;      /* 設計稿 FAQ 容器 1176px @1920 → 882px @1440 */
  margin: 48px auto 0;
  display: grid;
  gap: 14px;
}
.ivo-faq__item {
  border-radius: var(--ivo-radius);
  background: var(--ivo-white);
  box-shadow: var(--ivo-shadow);
  overflow: hidden;
}
.ivo-faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  width: 100%;
  min-height: 56px;
  padding: 16px 24px;
  border: 0;
  background: transparent;
  font-family: inherit;
  font-size: var(--ivo-fs-body);
  font-weight: 500;
  line-height: 1.6;
  text-align: left;
  color: var(--ivo-title);
  letter-spacing: .04em;
  cursor: pointer;
}
.ivo-faq__item.is-open .ivo-faq__q { color: var(--ivo-btn); }
.ivo-faq__icon {
  flex: 0 0 20px;
  width: 20px; height: 20px;
  position: relative;
  border-radius: 50%;
  background: var(--ivo-pink-lt);
  transition: background-color .25s ease;
}
.ivo-faq__icon::before,
.ivo-faq__icon::after {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 9px; height: 1.5px;
  background: var(--ivo-btn);
  transform: translate(-50%, -50%);
  transition: opacity .25s ease, transform .25s ease;
}
.ivo-faq__icon::after { transform: translate(-50%, -50%) rotate(90deg); }
.ivo-faq__item.is-open .ivo-faq__icon::after { opacity: 0; }

.ivo-faq__a {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows .35s ease;
}
.ivo-faq__item.is-open .ivo-faq__a { grid-template-rows: 1fr; }
.ivo-faq__a > div { overflow: hidden; }
.ivo-faq__a p {
  padding: 0 24px 22px;
  font-size: var(--ivo-fs-sm);
  line-height: 1.95;
  color: var(--ivo-text);
}

/* ==========================================================================
   平板　max-width: 999.98px
   ========================================================================== */
@media (max-width: 999.98px) {
  :root {
    --ivo-fs-h1: 38px;
    --ivo-fs-h2: 28px;
    --ivo-gap: 24px;
    --ivo-sec-y: 72px;
  }

  .ivo-grid { grid-template-columns: repeat(8, minmax(0, 1fr)); }
  .ivo-col-3, .ivo-col-4, .ivo-col-5, .ivo-col-6,
  .ivo-col-7, .ivo-col-8, .ivo-col-12 { grid-column: span 8; }

  /* Hero：文字在上、卡片群在下 */
  .ivo-hero__copy, .ivo-hero__visual { grid-column: span 8; }
  .ivo-hero__visual { margin-top: 48px; }
  /* 平板是單欄，整叢改置中。--ivo-hc-col 扣 20px 是留給旋轉溢出用的：
     bleed 歸零會讓旋轉外框剛好等於版心寬，而左 7.4 / 右 12.6 不對稱，
     margin-inline: auto 之後右側會凸出約 1.6px。
     left 補上 (rot-l − rot-r) / 2 = −2.6px，把不對稱吃掉，
     這樣「整叢」而不只是「未旋轉的盒子」才是真的置中。 */
  .ivo-hero__cards {
    --ivo-hc-col: calc(90vw - 20px);
    --ivo-hc-bleed-l: 0px;
    --ivo-hc-bleed-r: 0px;
    left: calc((var(--ivo-hc-rot-l) - var(--ivo-hc-rot-r)) / 2);
    margin-inline: auto;
  }

  /* 理念：改單欄，標題在前（與 DOM 順序一致，不必再對調） */
  .ivo-philo__list, .ivo-philo__copy { grid-column: span 8; }
  .ivo-philo__copy { order: 0; padding-left: 0; margin-bottom: 32px; }
  .ivo-philo__list { order: 0; }
  /* 理念區的上內距是寫死的 132px，不會跟著 --ivo-sec-y 縮，
     不覆寫的話平板鄰段只有 72px，132 是它的 1.83 倍。
     這裡不照桌機的 132 / 80 = 1.65 等比換算（那會得到 118.8，不是 8 的倍數），
     改取 112px：落在 8 的倍數上，對鄰段 72 是 1.56 倍，
     與手機的 88 / 56 = 1.57 倍同一個手感，三個斷點的收斂節奏才一致。 */
  .ivo-philo { padding-top: 112px; }
  .ivo-pcard__icon { flex-basis: 100px; }
  .ivo-pcard__icon svg { height: 72px; }

  /* 服務：三欄改兩欄 */
  .ivo-scard { grid-column: span 4; }

  /* 六步驟：三欄改兩欄，同心圓移到文字下方 */
  .ivo-process__copy, .ivo-process__viz { grid-column: span 8; }
  .ivo-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* 同心圓移到文字下方，整顆完整顯示、水平置中（客戶回報平板時偏右）。
     不能用 transform 置中：.ivo-reveal.is-in { transform: none } 會歸零。 */
  .ivo-rings {
    --ivo-rings-d: min(560px, 78vw);
    position: relative;
    top: auto;
    right: auto;
    width: var(--ivo-rings-d);
    height: var(--ivo-rings-d);
    margin: 40px auto 0;
  }
  .ivo-hint { max-width: none; }

  /* 評論：一次顯示 2 則 */
  .ivo-slider__slide { flex-basis: calc((100% - var(--ivo-gap)) / 2); }

  /* logo 牆：六欄改四欄 */
  .ivo-clients__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }

  /* CTA、價格、表單：單欄 */
  .ivo-cta__fig, .ivo-cta__copy,
  .ivo-price__copy, .ivo-price__fig,
  .ivo-form-sec__copy, .ivo-form-sec__body { grid-column: span 8; }
  /* 平板以下改上下堆疊，只出血左邊會變成歪的，直接取消。
     手機（689.98px）不必再寫一次，這一條已經涵蓋。 */
  .ivo-cta__fig  { margin-left: 0; }
  .ivo-cta__copy { padding-left: 0; margin-top: 32px; }
  .ivo-price__fig { margin-top: 32px; }
  .ivo-form-sec__body { margin-top: 40px; }
  /* 平板左欄變成整個版心寬。出血取消（窄視窗會貼到畫面邊），
     插圖與註記仍並排，但改用對欄寬的固定比例。
     驗算：690px 視窗清距 90px、1000px 視窗清距 91px，註記不會回捲。 */
  .ivo-form-sec__fig {
    width: min(300px, 42%);
    margin: 24px 0 0;
  }
  .ivo-form-sec__note {
    justify-self: start;
    margin: 60px 0 0 min(348px, 50%);
    max-width: 340px;
  }
}

/* ==========================================================================
   手機　max-width: 689.98px
   ========================================================================== */
@media (max-width: 689.98px) {
  :root {
    --ivo-fs-h1: 30px;
    --ivo-fs-h2: 24px;
    --ivo-fs-lead: 16px;
    --ivo-gap: 16px;
    --ivo-sec-y: 56px;
    /* 版心 88vw。原本寫在 .ivo-wrap，改成覆寫變數，
       版心寬度就維持單一來源，CTA 的出血算式不會跟這裡脫鉤。 */
    --ivo-wrap-w: 88vw;
  }
  .ivo-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .ivo-col-3, .ivo-col-4, .ivo-col-5, .ivo-col-6,
  .ivo-col-7, .ivo-col-8, .ivo-col-12 { grid-column: span 4; }
  .ivo-hero__copy, .ivo-hero__visual,
  .ivo-philo__list, .ivo-philo__copy,
  .ivo-process__copy, .ivo-process__viz,
  .ivo-cta__fig, .ivo-cta__copy,
  .ivo-price__copy, .ivo-price__fig,
  .ivo-form-sec__copy, .ivo-form-sec__body { grid-column: span 4; }

  .ivo-hero { padding-block: 48px 64px; }
  /* 同上。手機鄰段只有 56px，不覆寫的話 132 是它的 2.36 倍，
     全站垂直節奏就只有這一處沒跟著縮，其實比桌機更嚴重。
     取 88px：8 的倍數，對鄰段 56 是 1.57 倍，與平板的 112 / 72 同一個比例。 */
  .ivo-philo { padding-top: 88px; }
  .ivo-hero__actions { gap: 12px; }
  .ivo-hero__actions .ivo-btn { width: 100%; }

  /* Hero 卡片群：絕對定位改直向堆疊，只留三張避免過長 */
  /* 手機維持直向堆疊與「只留三張」，只加 1.4~1.8 度的微傾。
     旋轉讓卡片外框各邊超出約 2px，被側邊留白吸收，不會有橫向捲軸。 */
  .ivo-hero__cards {
    position: relative;
    isolation: auto;
    width: 100%;
    height: auto;
    left: 0;
    display: grid;
    gap: 14px;
  }
  /* 顯式寫 inset: auto，避免日後有人把 position 改回 relative 時
     整叢的絕對座標偷偷復活。width: 100% 也會蓋掉 --ivo-hc-w。 */
  .ivo-hcard { position: static; inset: auto; width: 100%; height: auto; overflow: visible; }
  .ivo-hcard--dark  { --ivo-rot: -1.6deg; }
  .ivo-hcard--pink  { --ivo-rot:  1.4deg; }
  .ivo-hcard--white { --ivo-rot:  1.8deg; }
  .ivo-hcard--gray, .ivo-hcard--violet { display: none; }

  /* 手機沒有 hover，待機晃動加上整叢浮動是這裡唯一的動態，要做得夠明顯。
     振幅取桌機的 0.7 倍（--ivo-hc-amp-m 已經算好）：直排之後卡片是整欄寬，
     角半徑從桌機的 126px 漲到 176~307px，同樣角度的線位移大得多。

     ⚠ 關鍵是把三張改成「同週期、同相位」，這不是偷懶，是省下振幅的成本。
       直排時相鄰兩張的間距只有 grid gap 14px，而基準角一正一負
       （−1.6／+1.4／+1.8），各自晃動時面對面的兩個角會相向靠攏。
       實測卡對卡最小淨空（外框對外框，窮舉相位）：
           異相位（沿用桌機做法）375px：amp 0° +5.41　0.6° +2.00
                                        1.2° −1.39　1.82° −4.88　2.38° −8.01px
           同相位（本方案）　　　375px：amp 0° +5.41　1.2° +5.41
                                        1.82° +5.41　2.38° +5.41　3.6° +5.35px
       同相位時三張的角度差維持定值，間距只剩二階的餘弦項，
       到 3.6° 也才少掉 0.06px，實務上等於免費。
       689px（手機斷點最寬處，卡片 606px）同相位在 amp 0~4.2° 全程維持 −1.83px，
       也就是既有的「卡角相吻」原樣不動、沒有惡化。
       代價是三張一起擺、少了各自為政的感覺，但手機只看得到三張又是直排，
       整疊一起輕晃反而更像一疊便條紙，而整叢浮動已經負責提供另一個節奏。

     ⚠⚠ 同相位是有前提的：三張必須永遠同步。所以下面一定要把
       hover 的 animation-play-state 還原成 running。
       只要暫停其中一張，它就會與另外兩張脫鉤，退化成上表的「異相位」那一列：
       375px −4.88px、600px −16.34px、689px −20.90px
       （對照：同相位是 +5.41／+0.22／−1.83px），畫面上就是一張卡的角
       整片壓進隔壁卡，截圖比對過確實看得出來。
       這在真手機上不會發生（沒有 hover），但
       @media (hover: hover) and (pointer: fine) 只問指標種類不問寬度，
       桌機把視窗拉窄到這裡、或平板外接滑鼠，就會踩到。

     水平方向：375px 版心 88vw、側邊留白 6vw = 22.5px（320px 時 19.2px），
     1.8° 基準角加 1.82° 晃動時單側外溢約 4.1px，離長出捲軸還很遠；
     線上把動畫跑滿 7.2 秒逐 30ms 取樣，1920 到 320 共 22 個寬度的
     scrollWidth 一律等於 clientWidth，最差差值 0.0px。

     ⚠ 這三條與上面的 .ivo-hcard--dark 等（同為 0,1,0）靠排在檔案更下方勝出，
       與原本 --ivo-hc-amp 用的是同一個機制。 */
  .ivo-hcard {
    --ivo-hc-amp: var(--ivo-hc-amp-m);
    --ivo-hc-per: calc(6.4s * var(--ivo-hc-rate));
    --ivo-hc-off: 0s;
  }

  /* hover 強調在這個斷點要還原。
     @media (hover: hover) and (pointer: fine) 只問指標種類、不問寬度，
     桌機瀏覽器把視窗拉窄到這裡一樣會命中，而此時五張卡是
     position: static 的整欄寬直排：兄弟縮 3% 等於每張橫向抽掉約 10px，
     讀起來是整列跳動不是強調；靜態流排版下 z-index: 6 也完全看不出來。
     只留 box-shadow 的變化當游標回饋。
     這兩條與上面 hover 區塊同特異性，靠排在後面勝出（本區塊在檔案更下方）。 */
  .ivo-hero__cards:has(.ivo-hcard:hover) .ivo-hcard:not(:hover) { scale: none; }
  /* animation-play-state 也要還原，而且是這兩條裡最要緊的一條。
     上面的同相位設計假設三張永遠同步；暫停任何一張就會讓它與另外兩張脫鉤，
     退化成異相位，實測最差淨空 375px −4.88px、600px −16.34px、689px −20.90px
     （同相位是 +5.41／+0.22／−1.83px），畫面上是一張卡的角整片壓進隔壁卡。
     真手機沒有 hover 碰不到，但 @media (hover: hover) and (pointer: fine)
     不含寬度條件，桌機視窗拉窄、或平板外接滑鼠就會命中。
     這兩條與上面的 hover 區塊同特異性，靠排在檔案更下方勝出。 */
  .ivo-hcard:hover { z-index: auto; animation-play-state: running; }

  .ivo-pcard { flex-direction: column; gap: 14px; padding: 22px 20px; }
  .ivo-pcard__icon { flex-basis: auto; }
  .ivo-pcard__icon svg { height: 64px; }

  /* 服務：兩欄改單欄 */
  .ivo-scard { grid-column: span 4; }
  .ivo-service__grid { row-gap: 36px; }

  /* 六步驟：兩欄改單欄 */
  .ivo-steps { grid-template-columns: 1fr; gap: 28px; }
  .ivo-hint { flex-direction: column; gap: 16px; padding: 22px 20px; }
  /* 修正：原方案用 min(420px, 92vw)，92vw 比版心 88vw 還寬，圓會被切掉一角，
     與 Patch D 註解說的「整顆完整顯示」不一致。改 88vw 對齊版心。 */
  .ivo-rings { --ivo-rings-d: min(420px, 88vw); margin-top: 28px; }
  /* 兩句提問在手機尺寸會互撞，只留三個圈名（整塊 viz 是 aria-hidden 裝飾） */
  .ivo-rings__q { display: none; }
  /* 修正：原方案只縮字級沒動位置，但 16.5/30.8/45.1% 這組間距是為 760px
     直徑訂的——手機直徑 294–420px 時間距只剩 42–60px，而「Narrative」在
     10px 下就要約 50px，配上 nowrap 會直接疊到下一個標籤（320px 視窗重疊約 8px）。
     手機改成在圓內平均攤開，320px 視窗下三個標籤結束在 61/137/213px，有餘裕。 */
  .ivo-rings__label { font-size: 10px; letter-spacing: 0; }
  .ivo-rings__label--1 { left: 6%; }
  .ivo-rings__label--2 { left: 33%; }
  .ivo-rings__label--3 { left: 60%; }

  /* 評論：一次顯示 1 則 */
  .ivo-slider__slide { flex-basis: 100%; }

  /* logo 牆：四欄改三欄 */
  .ivo-clients__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }

  /* 表單：兩欄輸入改單欄 */
  .ivo-form__row { grid-template-columns: 1fr; gap: 18px; }

  /* 手機沒有並排的寬度。grid-row 還原成 auto，兩者依 DOM 順序排到
     第 4、5 列，回到上下堆疊。鏡像保留，方向在任何尺寸都要對。 */
  .ivo-form-sec__fig {
    grid-row: auto;
    width: min(260px, 76%);
    margin: 24px 0 0;
  }
  .ivo-form-sec__note {
    grid-row: auto;
    justify-self: stretch;
    margin: 20px 0 0;
    max-width: none;
  }

  .ivo-faq__q { padding: 14px 18px; font-size: var(--ivo-fs-sm); }
  .ivo-faq__a p { padding: 0 18px 20px; }

  /* ── 窄機的單行保障：pill 與表單送出鈕 ─────────────────────────────
     客戶回饋（2026-09-12）：375px 下 pill 會斷成兩行，要求一律單行。
     最長的 pill 是 20 字的「和象牙設計合作品牌識別設計，流程是什麼？」，
     14px 配 .05em 字距時單行需要 332px，375px 的版心（88vw）只有 330px，
     只差 2px，所以不必動字身，收掉字距與內距就夠：
       字距 .05em → 0　　20 字省 20 × 0.7 = 14px
       左右內距 18 → 12px　省 12px
     合計 332 → 306px。實測餘裕：414px +58.3、375px +24.0、360px +10.8。
     ⚠ 這個餘裕換成文案長度非常薄，不要看到「有餘裕」就放心加字：
       375px　21 字 320px 仍單行，22 字 334px 就再破（只夠多 1 個全形字）
       360px　21 字就破（0 字餘裕）
       320px　21 字就破（0 字餘裕）
     pill 文案要加長，先回來重算，不然這個坑會原地再踩一次。
     沒有選「放寬版心」是因為那會動到全站每一個區塊，代價遠大於收益。
     也沒有用 white-space: nowrap：實測 320px 下三顆 20 字 pill 右緣會
     衝到 351.2px，而 scrollWidth 仍等於 clientWidth，代表它是被外層的
     overflow 無聲切掉，連水平捲軸都不會出現，比斷行更難發現。
     320px 是算術上的例外：20 字 × 14px = 280px 的字身再加 2px 外框就是
     282px，比 88vw 的 281.6px 還寬，內距歸零也排不進去。
     所以字級寫成 min(14px, 3.9vw)：3.9 是讓臨界落在 359px（14 ÷ 0.039），
     359px 以上一律維持 14px，主流機型一個像素都不動；
     320px 收到 12.48px，20 字 pill 實測 275.6px，塞得進 281.6px；
     280px 收到 10.92px，實測 244.4px，塞得進 246.4px。
     這條算式本身的下限在 260px（0.78w + 26 ≤ 0.88w），再窄就無解。
     vw 在桌機是含捲軸寬計算的，方向是安全的：實測 320px 視窗開經典捲軸時
     可用寬 305px、版心仍是 281.6px，字級照樣算出 12.48px，沒有溢出。 */
  .ivo-pill {
    letter-spacing: 0;
    padding-inline: 12px;
    font-size: min(14px, 3.9vw);
  }

  /* 送出鈕是同一個問題。標籤「送出表單，我們會在兩個工作天內回覆」17 字，
     16px 配 .06em 字距時單行需要 352.3px，375px 版心只有 330px。
     注意基底規則的 padding-inline: 32px 不是留白而是對比手段，
     收內距必須連著重算。--ivo-grad 是
     #A30056 0% → #C40068 60% → #EA007E 100%，白字要到 96.16% 的位置
     才掉到 4.5:1（0% 7.80、60% 5.92、100% 4.38）。
     這顆鈕的文字是置中的，所以真正決定右緣落點的是
     「（鈕寬 − 文字寬）÷ 2」而不是內距本身：內距收到 16px 後，
     文字右緣實測 375px 落在 91.21%、360px 與 320px 落在 92.88%，
     底色對比 4.67:1 / 4.61:1，都還在 4.5:1 之上
     （另用截圖逐像素取色複驗，得到 4.69 / 4.62，與算式一致）。
     字級只在 360.4px 以下才動：min(16px, 4.44vw) 在 360px 是 15.98px、
     320px 是 14.21px，17 字實測 241.4px，塞得進 281.6px。
     ⚠ 餘裕同樣很薄：375px 多 1 字仍單行、多 2 字就破，360px 以下多 1 字
     就破；這顆鈕的下限在 256px（0.7548w + 32 ≤ 0.88w）。
     .ivo-btn 不放進這裡是刻意的：最長標籤 6 字單行只需要 181.1px
     （19px × 6 + 字距 .08em × 6 + 內距 28 × 2 + 外框 1 × 2），
     320px 的版心 281.6px 綽綽有餘，實測 280～1920px 沒有一顆斷過，
     所以 --ivo-fs-btn 的 19px/700 與 --ivo-grad-cta 的 3.322:1
     原封不動，WCAG 大字門檻不受影響。 */
  .ivo-form__submit {
    letter-spacing: 0;
    padding-inline: 16px;
    font-size: min(16px, 4.44vw);
  }
}

/* ==========================================================================
   減少動態偏好
   關掉所有進場與轉場。這不只是無障礙考量，
   也是整頁截圖能拍得一致的前提（沒觸發的進場會蓋住標題）。
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .ivo-reveal { opacity: 1; transform: none; transition: none; }
  /* 傾斜是版面不是動效，減少動態偏好下要留著，
     否則上面那條 transform: none 會把評論卡的角度一起清掉。 */
  .ivo-hcard.ivo-reveal { transform: rotate(var(--ivo-rot, 0deg)); }
  .ivo-page *,
  .ivo-page *::before,
  .ivo-page *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  /* 兩組光圈的動效在這裡明確關掉，不只靠上面那條 blanket。
     blanket 的前綴是 .ivo-page，萬一 body class 被外掛過濾掉就不涵蓋；
     而且 blanket 只是把時間壓到 .001ms，這裡是真的把 animation 關掉。
     注意：新的 keyframes 0% 已經不是靜止外觀了（本體 0% 是最收縮），
     所以不能再靠「animation: none 會停在 0%」這個理由，
     要靠這裡的 transform / opacity !important 歸位。 */
  .ivo-rings__disc,
  .ivo-flow__ring::before {
    animation: none !important;
    transform: none !important;
    opacity: 1 !important;
  }
  /* 三道波是純動效產物，靜止下不該留在畫面上。
     這裡不能寫 opacity: 1，那會把波前定格成多出來的實環；
     display: none 最乾淨，也順便省掉合成層。 */
  .ivo-rings__disc::before,
  .ivo-rings__disc::after,
  .ivo-flow__ring::after {
    animation: none !important;
    display: none !important;
  }

  /* 評論卡的待機晃動點名關閉，理由與光圈那兩條相同：
     blanket 只把 animation-duration 壓成 .001ms，而且前綴依賴 .ivo-page。
     更關鍵的是 rotate 是獨立變換屬性，.ivo-reveal { transform: none }
     完全管不到它，必須自己歸零，否則動畫結束那一幀的角度會留在上面。 */
  .ivo-hcard {
    animation: none !important;
    rotate: none !important;
  }

  /* 整叢浮動同樣點名關掉，理由和上面那一條相同：blanket 的前綴依賴 .ivo-page，
     萬一 body class 被外掛過濾掉就不涵蓋；而 translate 是獨立變換屬性，
     .ivo-reveal { transform: none } 也管不到它。
     ⚠ 這裡不要寫成「不關就會定格在 keyframes 最後一格」，那是錯的：
       實測拿掉這一條之後，computed translate 仍然是 none
       （blanket 把 duration 壓成 1e-6s，而 animation-fill-mode 預設是 none，
       動畫結束後不留任何值）。這一條是防禦，不是在修一個現存的 bug，
       寫清楚才不會讓下一個人以為刪掉它就會壞。 */
  .ivo-hero__cards {
    animation: none !important;
    translate: none !important;
  }

  /* hover 的層次回饋（提層、陰影、兄弟縮小）保留，但全部瞬間切換。
     不逐項列 transition-duration：那種寫法是位置對應 transition 簡寫的五項，
     日後在簡寫裡增刪一項或調換順序就會靜默錯位（例如 .2s 跑到 opacity 上），
     而且不會有錯誤訊息。整條關掉最穩。
     附帶好處：兄弟瞬間縮到 .97，就沒有「已提層、兄弟還沒縮」的空窗，
     z-index 的延遲翻轉在這個模式下自然也不需要，
     幾何直接落在已驗過的 +0.73px。 */
  .ivo-hero__cards .ivo-hcard { transition: none !important; }
}

/* ==========================================================================
   關於頁與服務頁
   --------------------------------------------------------------------------
   註：本段的插圖與示意圖形（關於頁 Hero 的三顆流程圓、三項堅持的圖示）
   帶有設計稿原本的藍色與紫色。插圖用色不計入全站 10 顆色票的配額，
   那條規則管的是「介面元素」的用色，不是插圖本身。
   ========================================================================== */

/* ── 共用｜CTA 準備好完成你的理想設計了嗎 ──────────────────────────── */
/* 關於頁與服務頁共用 blocks/shared/cta-ready.html，改這一組兩頁同步，HTML 不動。
   本次三件事：文字欄右移、插圖放大、插圖改成貼齊區塊下緣往上長。 */
.ivo-ready {
  /* 下內距同時是插圖「推回去」的量，抽成變數，兩邊不會各寫一次 64。
     要調這一段的鬆緊請改這個變數，不要只改 padding-block，
     否則插圖會浮起來或被裁過頭。 */
  --ivo-ready-pad: 64px;

  padding-block: var(--ivo-ready-pad);
  background: linear-gradient(180deg, var(--ivo-paper) 0%, #FFFBFC 100%);

  /* 這一行是必要條件不是保險，拿掉會壞：
     .ivo-ready__fig 身上掛著 .ivo-reveal，進場前是 transform: translateY(18px)。
     插圖改成貼底之後，這 18px 一定會把圖推進頁尾裡，
     捲到這一段時會看到插圖凸出粉色色塊再縮回去。
     用 hidden 不用 clip：clip 在較舊的 Safari 會被當成無效值忽略。 */
  overflow: hidden;
}

.ivo-ready__inner { align-items: center; }

/* 文字欄從第 1 欄移到第 3 欄：右移 2 欄 = 220px，佔版心 1290 的 17.1%，
   是最接近客戶說的「再往右 20%」的整欄位移。
   移 3 欄會是 25.6%，而且右邊只剩 2 欄放插圖，插圖反而變小。
   欄數不變（span 6），標題不會比現在更早斷行。 */
.ivo-ready__copy { grid-column: 3 / span 6; }

.ivo-ready__fig {
  /* 最後 4 欄 = 110×4 − 30 = 410px，比原本的 340px 上限大約兩成，
     對應客戶說的「整個尺寸更大一些」。 */
  grid-column: 9 / -1;

  margin: 0;
  width: 100%;
  max-width: none;   /* 尺寸交給欄寬，不再用固定上限 */
  align-self: end;   /* 貼齊這一列下緣，不再跟按鈕齊高置中 */

  /* 貼底的量剛好是一個下內距。
     illust-chat.png 畫的是「手持手機」，那隻手是一塊沒有外框線的純白色塊，
     一路畫到圖檔的右邊界與下邊界（alpha 不透明範圍 x 104→1199、y 102→1194，
     右側與下側沒有任何透明留白），本來就是為出血而畫的。
     它在白底上看不出來，但這一段的底色是粉的，手臂是看得見的。
     所以對齊點取「圖檔下緣 = 區塊下緣」，手臂自然延伸到區塊與頁尾的交界。
     注意：不要用「合成到白底找差異」去量這張圖的邊界，那量不到白手臂，
     會誤判下方有約 9% 的透明邊而多沉一段，結果把手臂切掉。要重量請讀 alpha。 */
  margin-bottom: calc(-1 * var(--ivo-ready-pad));
}

/* 讓圖確實填滿欄寬。全站 reset 只給了 max-width: 100%，
   靠原始寬度大於欄寬「剛好」撐滿，寫明比較不會出事。 */
.ivo-ready__fig img { width: 100%; }
.ivo-ready__title { margin-top: 20px; }
.ivo-ready__text  { margin-top: 24px; font-size: var(--ivo-fs-sm); line-height: 1.95; }
.ivo-ready__btn   { margin-top: 30px; }

/* ── 關於｜Hero ────────────────────────────────────────────────────── */
.ivo-ahero { padding-block: 88px 104px; background: var(--ivo-white); }
.ivo-ahero__inner { align-items: center; }
.ivo-ahero__copy  { grid-column: span 6; }
.ivo-ahero__viz   { grid-column: span 6; }
.ivo-ahero__title { margin-top: 22px; }
.ivo-ahero__lead  { margin-top: 28px; }
.ivo-ahero__lead p + p { margin-top: 6px; }
.ivo-ahero__actions { display: flex; flex-wrap: wrap; gap: 16px; margin-top: 36px; }

/* 三段式流程圓：深度傾聽 → 弭平落差 → 視覺轉化

   設計稿實測（關於_01.jpg，1100px 寬，內容寬約 872）：
     圓心 (651.5,216) (808.5,268) (965.5,320)，三顆等大 D=149
     Δx=157、Δy=52 → 夾角 18.33°、圓心距 165.4（圓間留 8px，非相切）
     核心光暈  r/R  0%→α1.00  11%→.83  25%→.63  38%→.45
                    52%→.29   65%→.15  78%→.03  80%→0
     外圈淡環  r/R 80%→0  90%→.23（峰值）96%→.19  100%→.12

   關鍵：radial-gradient 預設 farthest-corner，在正方形裡 = R×1.414，
   所有 % stop 會被放大 1.414 倍，外圈淡環整圈落在 border-radius
   之外完全看不到，可見邊緣還會留一圈 α≈.08 的硬邊。
   一定要寫 closest-side，它在正方形裡剛好等於圓的半徑。 */

.ivo-flow {
  position: relative;
  width: 100%;
  max-width: 624px;          /* 桌機 6 欄 = 110×6−30 = 630，放得下 */
  margin-inline: auto;
  aspect-ratio: 624 / 341;   /* D 200.8 + 兩段 Δy 70.1 = 341 */
  margin-bottom: 56px;       /* 留給第三顆圓下方的兩行英文小字 */

  /* 三顆圓的色相。前兩個是既有色票，第三個是規範外的例外。 */
  --ivo-viz-pink:   255 99 185;    /* = --ivo-primary #FF63BA */
  --ivo-viz-purple: 186 75 254;    /* = --ivo-accent  #BA4BFF */
  --ivo-viz-blue:   109 190 255;   /* #6DBEFF：規範外，僅用於裝飾面，
                                      不承載任何文字，上線前需簽核 */
}

/* 單一節點 = 邊長等於圓直徑的正方形。
   D/W = 200.8/624 = 32.18%，Δx/W = 211.6/624 = 33.91%。
   設計稿 Δx/D = 1.0537（圓間有 5.4% 直徑的空隙），不是相切。 */
.ivo-flow__node {
  position: absolute;
  width: 32.18%;
  aspect-ratio: 1;
  text-align: center;
}

.ivo-flow__ring {
  position: relative;
  width: 100%;
  aspect-ratio: 1;
  border-radius: 50%;
}

/* 漸層本體整組搬到 ::before，呼吸動畫只掛這一層，stop 一個都沒改。
   為什麼要多這一層：HTML 是
     <div class="ivo-flow__ring"><span class="ivo-flow__dot"></span></div>
   .ivo-flow__dot 是 ring 的「子」節點，動畫掛在 ring 上會連圓心那顆
   11px 的實心點一起縮放。實心點有硬邊，縮放時邊緣重取樣看得出來在抖，
   而它又正好是整組圖唯一的對齊基準。::before 是 dot 的兄弟，縮它不動 dot。

   border-radius 一定要跟著寫在 ::before 上：兩條漸層都是 closest-side，
   超過半徑之後會以最後一個 stop 的顏色往外填滿，沒有 border-radius
   就會在四個角留下淡色方塊。

   文字不會抖也不會糊：.ivo-flow__label / __tw / __sub / __en 全都是
   .ivo-flow__ring 的「兄弟」而不是子節點，transform 不會繼承過去。 */
.ivo-flow__ring::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background:
    /* 外圈淡環（80%～100% 半徑，峰值在 90%） */
    radial-gradient(circle closest-side at 50% 50%,
      rgb(var(--ivo-flow-rgb) / 0)    80%,
      rgb(var(--ivo-flow-rgb) / .23)  90%,
      rgb(var(--ivo-flow-rgb) / .19)  96%,
      rgb(var(--ivo-flow-rgb) / .12) 100%),
    /* 核心光暈（中心飽和 → 80% 半徑收乾） */
    radial-gradient(circle closest-side at 50% 50%,
      rgb(var(--ivo-flow-rgb) / 1)     0%,
      rgb(var(--ivo-flow-rgb) / .83)  11%,
      rgb(var(--ivo-flow-rgb) / .63)  25%,
      rgb(var(--ivo-flow-rgb) / .45)  38%,
      rgb(var(--ivo-flow-rgb) / .29)  52%,
      rgb(var(--ivo-flow-rgb) / .15)  65%,
      rgb(var(--ivo-flow-rgb) / .03)  78%,
      rgb(var(--ivo-flow-rgb) / 0)    80%);
  transform-origin: 50% 50%;
  /* 同上方的例外說明。7.5s、ease-in-out，一樣只往內縮
     （scale 1 → .935、opacity 1 → .86）。幅度比同心圓大，
     是因為圓只有 200.8px，而且核心飽和，14% 的明度脈動在小圓上讀得到。
     這裡的單向安全可以直接推：核心漸層 0~80% 單調遞減，
     文字 ink 落在 0.21R~0.63R，收縮後對應到剖面 0.674R 以內，
     碰不到外圈淡環 80% 的上升緣，α 只降不升。
     實算（最嚴苛的紫圓 #BA4BFF，含下面的波前逐格掃描）：
       .ivo-flow__sub 5.74:1、.ivo-flow__tw 10.26:1，
       動畫沒有任何一格低於靜止值（粉、藍兩顆更高）。
     0% 是最收縮，對齊下面波前的誕生點；50% 是已驗收的靜止外觀。 */
  animation: ivo-breathe-ring 7.5s ease-in-out infinite;
}

@keyframes ivo-breathe-ring {
  0%, 100% { transform: scale(.935); opacity: .86; }
  50%      { transform: scale(1);    opacity: 1;   }
}

/* 波前掛 .ivo-flow__ring::after。
   注意這裡和同心圓不一樣：::after 是 ::before 的「兄弟」，不是子節點，
   不會吃到 ::before 的 scale 與 opacity，下面的半徑都是實際值，不需要複合。
   不掛 .ivo-flow__node：node 在 689.98px 以下會變成 position:static 的
   grid 列，寬度不再等於圓，inset:0 的假元素會變成橫跨整列的大橢圓。
   ::after 永遠貼著 .ivo-flow__ring 的方框，三個斷點都不用改。

   ::after 疊在 .ivo-flow__dot 之上不影響那顆 11px 實心點：
   波的內緣最小半徑 = .60 × .55 = 0.33R（桌機 33px、手機圓徑 124px 時 20.5px），
   遠大於圓點的 5.5px，那個位置的 α 恆為 0；圓點也不在被 transform 的元素裡，
   不會重取樣。兩層同色相，疊放順序不影響合成結果。

   最大 scale 1.06：波越出圓緣 3% 半徑後熄滅，三個斷點都撞不到鄰圓：
     桌機圓 200.8px → 越界 6.0px，圓間空隙 5.37%D = 10.8px
     平板圓最大 229.4px（900px 視窗，max-width:88%）→ 越界 6.9px，空隙 12.3px
     手機圓 124~180px → 越界 3.7~5.4px，grid column-gap 20px
   左右兩端只越到 .ivo-flow 的外面幾 px，版心兩側至少還有 5vw，
   不會產生水平捲軸；手機文字已移到第 2 欄的白底上，波也碰不到。 */
.ivo-flow__ring::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: radial-gradient(circle closest-side at 50% 50%,
    rgb(var(--ivo-flow-rgb) / 0)     60%,
    rgb(var(--ivo-flow-rgb) / .18)   84%,
    rgb(var(--ivo-flow-rgb) / .12)   94%,
    rgb(var(--ivo-flow-rgb) / 0)    100%);
  transform-origin: 50% 50%;
  animation: ivo-wave-ring 7.5s linear infinite;
  pointer-events: none;
}

@keyframes ivo-wave-ring {
  0%   { transform: scale(.55);  opacity: 0;   }
  10%  { transform: scale(.66);  opacity: .60; }
  35%  { transform: scale(.85);  opacity: .95; }
  70%  { transform: scale(1);    opacity: .45; }
  100% { transform: scale(1.06); opacity: 0;   }
}

/* 三顆各錯開 1/3 週期（7.5 ÷ 3 = 2.5s）。延遲值換算自原本的 −2T/3 與 −T/3，
   這組值的循環順序就是 1 → 2 → 3（週期性動畫看的是循環順序，不是誰先跑），
   方向與「深度傾聽 → 弭平落差 → 視覺轉化」一致，整組每 2.5s 吐出一道波。
   呼吸與波前必須吃同一組延遲，否則同一顆圓上兩層會脫相。
   用負值：一載入三顆就各自停在不同相位，沒有空窗。 */
.ivo-flow__node--2 .ivo-flow__ring::before,
.ivo-flow__node--2 .ivo-flow__ring::after { animation-delay: -5s; }
.ivo-flow__node--3 .ivo-flow__ring::before,
.ivo-flow__node--3 .ivo-flow__ring::after { animation-delay: -2.5s; }

/* 圓心小圓點。設計稿實測 (71,68,63)/(68,63,41)/(80,66,57)，
   跟 --ivo-text-warm #4D443F = (77,68,63) 幾乎完全吻合；
   --ivo-text #292A2F 太黑也太冷，不要用。 */
.ivo-flow__dot {
  position: absolute; left: 50%; top: 50%;
  width: 11px; height: 11px; margin: -5.5px 0 0 -5.5px;
  border-radius: 50%;
  background: var(--ivo-text-warm);
}

/* 中文標題壓在圓的上半部。設計稿上緣在圓頂下方 29.5/149 = 19.8% 直徑。
   line-height 一定要寫死：
   (1) 不寫會繼承 body 的 1.7，label 高 47.9px，下緣會壓到 line--a；
   (2) Blocksy 在 .entry-content 上動 line-height 時整塊會位移。 */
.ivo-flow__label {
  position: absolute;
  left: 50%; top: 20%;
  transform: translateX(-50%);
  white-space: nowrap;
}
.ivo-flow__tw {
  display: block;
  font-size: var(--ivo-fs-sm);
  line-height: 1.5;
  font-weight: 700;
  color: var(--ivo-title);
  letter-spacing: .04em;
}
.ivo-flow__sub {
  display: block;
  margin-top: 2px;
  font-size: var(--ivo-fs-xs);
  line-height: 1.4;
  color: var(--ivo-text);
}

/* 英文小字掛在圓的正下方。設計稿是圓底下方 22/149 = 14.8% 直徑。 */
.ivo-flow__en {
  position: absolute;
  left: 50%; top: 115%;
  transform: translateX(-50%);
  font-size: 12px;
  line-height: 1.5;
  letter-spacing: .06em;
  white-space: nowrap;
  color: var(--ivo-text-warm);
}

/* left 0 / 33.91% / 67.82%（67.82 + 32.18 = 100，剛好齊右）
   top  0 / 20.56% / 41.11%（Δy 70.1 ÷ H 341） */
.ivo-flow__node--1 { --ivo-flow-rgb: var(--ivo-viz-pink);   left: 0;       top: 0; }
.ivo-flow__node--2 { --ivo-flow-rgb: var(--ivo-viz-purple); left: 33.91%;  top: 20.56%; }
.ivo-flow__node--3 { --ivo-flow-rgb: var(--ivo-viz-blue);   left: 67.82%;  top: 41.11%; }

/* 圓心到圓心的灰色虛線，疊在圓之上（靠 DOM 順序，見 HTML patch）。
   圓心座標：x = 16.09% / 50% / 83.91%（W）
             y = 29.44% / 50% / 70.56%（H）
   線長 hypot(211.6, 70.1) = 222.9 → 35.72% W
   角度 atan(70.1/211.6) = 18.33°，與設計稿 atan(52/157) 相同。
   不用 border:dashed（dash 長度由瀏覽器決定），改用
   repeating-linear-gradient 精準指定 dash 6 / gap 13（週期 19），
   對應設計稿實測 dash 4.5 / gap 9.5 × 縮放 1.348。
   色：實測圓外虛線 (139,141,140)，rgba(41,42,47,.54) 合成後
   = (139,140,143) 吻合；.45 會偏淺成 (159,159,161)。 */
.ivo-flow__line {
  position: absolute;
  width: 35.72%;
  height: 1px;
  transform-origin: 0 50%;
  transform: rotate(18.33deg);
  background-image: repeating-linear-gradient(to right,
    rgba(41, 42, 47, .54) 0 6px,
    rgba(41, 42, 47, 0)   6px 19px);
  pointer-events: none;
}
.ivo-flow__line--a { left: 16.09%; top: 29.44%; }  /* 第一顆圓心 → 第二顆圓心 */
.ivo-flow__line--b { left: 50%;    top: 50%;    }  /* 第二顆圓心 → 第三顆圓心 */

/* ── 關於｜我們的成果 ──────────────────────────────────────────────── */
/* 設計稿這一段的底色上還有一整片有機曲線，
   少了它整區會顯得很空。與首頁理念區用同一套裝飾。 */
.ivo-results {
  position: relative;
  background: linear-gradient(180deg, var(--ivo-lilac) 0%, #FFFBFF 60%, #FFFEFF 100%);
  overflow: hidden;
}
.ivo-results::before {
  content: "";
  position: absolute;
  inset: 0;
  background: url("../img/deco-lines.png") no-repeat center center / cover;
  opacity: .5;
  pointer-events: none;
}
.ivo-results > * { position: relative; }
.ivo-results__title { margin-top: 20px; }
/* 設計稿兩欄之間的間距比一般格線大，卡片本身也更寬鬆 */
.ivo-results__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 30px 56px;
  margin-top: 56px;
}
/* 從 flex 改成 grid 有三個理由：
   1. 數字欄固定 136px，但 <p> 自己的寬度要等於文字寬（width: max-content），
      漸層才會「每個數字各自從自己的左邊起算」，與設計稿一致。
      維持 flex + min-width:136px 的話，「90%」只吃到約 80% 的漸層行程。
   2. grid 的 align-items 只作用在垂直軸，手機單欄時不會像 flex 那樣變成
      水平置中，所以垂直置中可以直接寫在這裡，手機端不用再補救。
   3. 欄寬用 minmax(136px, max-content)，日後數字變「1000+」也不會溢出。 */
.ivo-stat {
  display: grid;
  grid-template-columns: minmax(136px, max-content) minmax(0, 1fr);
  align-items: center;   /* 設計稿：卡片中心 771.5、數字墨跡中心 773，是垂直置中 */
  gap: 26px;
  padding: 34px 36px;
  background: rgba(255, 255, 255, .86);
  border-radius: var(--ivo-radius);
  box-shadow: var(--ivo-shadow);
}

/* 設計稿的數字是一道水平漸層（逐欄取樣：同一 x 的上下色值差 < 2%，
   所以是 90deg 不是斜向），左端約 #EA007F、右端約 #FFA8D4，
   四組各自從自己的左邊重新起算。

   右端 #FFA8D4 對白底只有 1.78:1，不能照抄。
   52px/700 在 WCAG 屬大字（≥ 18.66px bold），門檻是 3:1 不是 4.5:1，
   所以最淺端停在 --ivo-deep #EA007F（4.37:1），深端取 --ivo-btn-hover
   #A30056（7.80:1）。相對亮度 0.0846 → 0.1902，漸層看得出來。
   色票裡比 #EA007F 更亮的粉（#FF63BA 2.71:1、#F76DB8 2.68:1）
   連 3:1 都過不了，一律不能用在這裡。 */
.ivo-stat__num {
  width: max-content;
  font-size: 52px;       /* 取寬度對齊設計稿：「500+」墨跡寬 119px */
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: .01em;
  white-space: nowrap;   /* 避免「500」與「+」被拆行 */
  color: var(--ivo-deep);  /* 不支援 background-clip: text 時的退路 */
  /* 亮端用 --ivo-pink-3a 不是 --ivo-pink-btn-hi：background-clip: text 的字底
     是 .ivo-stat 卡面（rgba(255,255,255,.86) 疊在 .ivo-results 的漸層與
     deco-lines.png 上），合成後最暗約 Y 0.9275。
       #ED50A9 在其上只剩 3.093:1（餘裕 0.09，太薄）
       #EF2E96 在其上 3.554:1、白底 3.817:1 → 採用 */
  background-image: linear-gradient(90deg, var(--ivo-btn-hover) 0%, var(--ivo-pink-3a) 100%);
}
@supports (-webkit-background-clip: text) or (background-clip: text) {
  .ivo-stat__num {
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
  }
}
/* text-fill-color: transparent 會讓字在反白時整個消失，要把填色補回來。
   只動文字色與反白底色，沒有碰 padding / min-height，不會多出反白色塊。 */
.ivo-stat__num::-moz-selection {
  color: var(--ivo-white); -webkit-text-fill-color: var(--ivo-white); background-color: var(--ivo-btn);
}
.ivo-stat__num::selection {
  color: var(--ivo-white); -webkit-text-fill-color: var(--ivo-white); background-color: var(--ivo-btn);
}
/* 高對比模式與列印會拿掉背景圖，透明字會整個不見，退回單色 */
@media (forced-colors: active) {
  .ivo-stat__num { background-image: none; color: CanvasText; -webkit-text-fill-color: CanvasText; }
}
@media print {
  .ivo-stat__num { background-image: none; color: var(--ivo-btn); -webkit-text-fill-color: var(--ivo-btn); }
}
.ivo-stat__text { margin-top: 10px; font-size: var(--ivo-fs-sm); line-height: 1.85; color: var(--ivo-text); }

/* ── 關於｜三項堅持 ────────────────────────────────────────────────── */
.ivo-hold { background: var(--ivo-white); }
.ivo-hold__title { margin-top: 20px; }
.ivo-hold__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 30px;
  margin-top: 56px;
  text-align: center;
}
/* 設計稿的圖示約 190px 寬，先前手繪 SVG 只給 130px，
   放在整區裡明顯偏小，區塊看起來就空。 */
.ivo-hcard2__icon { margin: 0 0 4px; display: grid; place-items: center; }
.ivo-hcard2__icon img { width: 190px; height: auto; }
.ivo-hcard2__title { margin-top: 20px; }
.ivo-hcard2__sub {
  margin-top: 8px;
  font-size: var(--ivo-fs-sm);
  font-weight: 500;
  color: var(--ivo-btn);
}
.ivo-hcard2__text {
  margin-top: 16px;
  font-size: var(--ivo-fs-sm);
  line-height: 1.9;
  color: var(--ivo-text);
}

/* ── 關於｜雙軌核心能力 ────────────────────────────────────────────── */
.ivo-dual { background: linear-gradient(180deg, #FFFEFF 0%, var(--ivo-lilac) 100%); }
.ivo-dual__title { margin-top: 20px; }
.ivo-dual__grid { margin-top: 56px; row-gap: 48px; }
.ivo-dcard { grid-column: span 6; }
.ivo-dcard__fig {
  margin: 0;
  border-radius: var(--ivo-radius-sm);
  overflow: hidden;
  aspect-ratio: 630 / 420;
  background: var(--ivo-line);
}
.ivo-dcard__fig img { width: 100%; height: 100%; object-fit: cover; }
/* 標籤的底色是不承載長文的色塊，文字用深色確保對比 */
.ivo-dcard__tag {
  display: inline-block;
  margin-top: 22px;
  padding: 6px 16px;
  border-radius: 4px;
  font-size: var(--ivo-fs-h3);
  font-weight: 700;
  color: var(--ivo-title);
  letter-spacing: .02em;
}
.ivo-dcard__tag--pink   { background: #FFC9E6; }
.ivo-dcard__tag--violet { background: #E2C6FF; }
.ivo-dcard__text {
  margin-top: 18px;
  font-size: var(--ivo-fs-sm);
  line-height: 1.95;
  color: var(--ivo-text);
}

/* ── 服務｜精選作品案例 ────────────────────────────────────────────── */
.ivo-works { background: var(--ivo-white); }
.ivo-works__title { margin-bottom: 44px; }
/* 5 欄 2 排，整組收在 1290 的內容寬裡。
   設計稿原本是橫向溢出版面的連續帶狀，但客戶確認不要超出版面，
   所以改成一般格線，十張剛好排滿兩排。 */
.ivo-works__grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 20px;
}
.ivo-work {
  margin: 0;
  border-radius: var(--ivo-radius-sm);
  overflow: hidden;
  background: var(--ivo-line);
}
.ivo-work img { width: 100%; aspect-ratio: 242 / 158; height: auto; object-fit: cover; }
.ivo-works__more { margin-top: 44px; }

/* ── 平板 ──────────────────────────────────────────────────────────── */
@media (max-width: 999.98px) {
  .ivo-ready__copy, .ivo-ready__fig,
  .ivo-ahero__copy, .ivo-ahero__viz,
  .ivo-dcard { grid-column: span 8; }
  /* 堆疊之後插圖自己佔一列，貼底的負外距一定要解掉，否則會壓進頁尾。
     這個斷點 span 8 等於整個版心，所以 max-width 必須重新給，
     不然圖會跟著版心放大到七、八百 px。400px 是延續桌機 410px 的量體。 */
  .ivo-ready__fig {
    justify-self: center;
    align-self: auto;
    margin-top: 32px;
    margin-bottom: 0;
    max-width: 400px;
  }
  .ivo-ahero__viz { margin-top: 40px; }

  /* 客戶要求：平板的圓叢集抓在內容寬的 80~90%，且上下留白一致。
     88% 的實際尺寸（containing block = 整個版心）：
       690px 視窗 → 叢集 546.5、單顆圓 175.9
       900px 視窗 → 叢集 712.8、單顆圓 229.4
     上緣留白 = .ivo-grid row-gap（平板 24px）+ viz margin-top 40px = 64px
     下緣留白 = margin-bottom + .ivo-ahero padding-bottom 104px − 英文小字溢出量
     .ivo-flow__en 掛在 top:115% 會超出 .ivo-flow 下邊界，溢出量
       = 0.15 × 圓徑 + 兩行 13px/1.5 = 4.2478% × CB + 39px
     令下緣 = 64px 解得 margin-bottom = calc(4.248% − 1px)。
     實測 690 / 760 / 850 / 900 / 950 / 999.98px 六個寬度，上下都是 64.00px。
     （margin 的百分比一律以 containing block 的「寬度」換算，不是高度。）
     這三個前提有人動就要重算：.ivo-ahero padding-bottom 104px、
     平板 --ivo-gap 24px、.ivo-ahero__viz margin-top 40px。 */
  .ivo-flow { max-width: 88%; margin-bottom: calc(4.248% - 1px); }

  /* 字級各放大一級：平板圓徑 229px 已超過桌機的 200.8px，
     沿用 14/13/12 會讓字相對變小。 */
  .ivo-flow__tw  { font-size: 16px; }
  .ivo-flow__sub { font-size: 14px; }
  .ivo-flow__en  { font-size: 13px; }

  /* 改用「底邊定位」取代桌機的 top: 20%。
     top:20% 是把文字疊的頂邊釘住，字級一放大、圓一縮小，底邊就會往
     圓心（光暈最濃處）沉。實測 690px + 16/14 時底邊掉到 r/R = 0.081、
     α = 0.874，紫圓上的 #292A2F 只剩 4.43:1，低於 4.5。
     改成 bottom: 62% 之後底邊固定在圓心上方 12% 節點高，
     r/R 永遠 0.24、α 永遠 0.644，對比度與字級、圓徑、視窗寬完全脫鉤：
       紫 → #1A1B1F 7.29:1 / #292A2F 6.07:1　粉 → 8.88 / 7.39　藍 → 11.05 / 9.20 */
  .ivo-flow__label { top: auto; bottom: 62%; }

  .ivo-results__grid { grid-template-columns: 1fr; }
  .ivo-hold__grid { grid-template-columns: 1fr; gap: 44px; }
  .ivo-works__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
}

/* ── 手機 ──────────────────────────────────────────────────────────── */
@media (max-width: 689.98px) {
  .ivo-ready__copy, .ivo-ready__fig,
  .ivo-ahero__copy, .ivo-ahero__viz,
  .ivo-dcard { grid-column: span 4; }

  .ivo-ahero { padding-block: 56px 64px; }
  /* 上緣 = .ivo-grid row-gap（手機 16px）+ viz margin-top 48px = 64px，
     與下緣的 padding-bottom 64px 對齊。 */
  .ivo-ahero__viz { margin-top: 48px; }
  .ivo-ahero__actions .ivo-btn,
  .ivo-ready__btn .ivo-btn { width: 100%; }

  /* 三顆圓在手機改直向排列（圓在左、文字在右）。
     客戶要求：中文放大、英文不斷行接在敘述下方、整叢吃滿內容寬。
     圓徑 clamp(124px, 36vw, 180px)：414px → 149、360px → 129.6、320px → 124。
     上限 180 讓 689.98px 與平板起點 690px（圓徑 175.9）接得起來，斷點上不跳。
     用 36vw 不用 100vw，永遠小於版心 88vw，不會踩「有捲軸時 100vw 過寬」的坑。 */
  .ivo-flow {
    --ivo-flow-d: clamp(124px, 36vw, 180px);
    aspect-ratio: auto;
    height: auto;
    max-width: none;
    margin-bottom: 0;
    display: grid;
    gap: 30px;
  }
  .ivo-flow__line { display: none; }

  /* 四列（1fr / auto / auto / 1fr）的用意：把「中文兩行 + 英文一行」綁成一疊，
     再跟圓垂直置中。只用兩列時英文會被推到圓的正下方並多出一整列，
     那就是客戶說「文字有很大落差」的現況。
     min-height 不是「用 min-height 撐觸控目標」那個坑：整塊是 aria-hidden
     的裝飾、沒有可點擊元素，撐出來的空白列也沒有 line box。 */
  .ivo-flow__node {
    position: static;
    width: auto;
    aspect-ratio: auto;
    min-height: var(--ivo-flow-d);
    display: grid;
    grid-template-columns: var(--ivo-flow-d) 1fr;
    grid-template-rows: 1fr auto auto 1fr;
    align-items: center;
    gap: 6px 20px;
    text-align: left;
  }

  /* 圓跨四列，寬高都寫死：不會在文字比圓高時被 stretch 拉成橢圓。 */
  .ivo-flow__ring {
    grid-column: 1;
    grid-row: 1 / -1;
    width: var(--ivo-flow-d);
    height: var(--ivo-flow-d);
  }
  .ivo-flow__label {
    position: static;
    transform: none;
    grid-column: 2;
    grid-row: 2;
  }

  /* 手機字級 14/13 → 18/16。這兩行在手機都落在白底上（不壓圓）：
     #1A1B1F 17.21:1、#292A2F 14.32:1，都是 AAA。 */
  .ivo-flow__tw  { font-size: 18px; }
  .ivo-flow__sub { font-size: 16px; margin-top: 4px; }
  /* 英文改成中文敘述的下一行（同欄第 3 列），不再獨佔一列。
     br 在手機隱藏；HTML 端已在 br 前補了半形空白，
     否則隱藏之後會黏成「Listen& Discover」。 */
  .ivo-flow__en {
    position: static;
    transform: none;
    grid-column: 2;
    grid-row: 3;
    margin-top: 0;
    font-size: 13px;
    white-space: normal;
  }
  .ivo-flow__en br { display: none; }

  /* grid 單欄。align-items: center 在 grid 是垂直軸，單欄時沒有副作用，
     不必再補 align-items: flex-start。 */
  .ivo-stat { grid-template-columns: minmax(0, 1fr); gap: 10px; padding: 24px 22px; }
  .ivo-stat__num { font-size: 44px; }   /* 桌機 52 等比；「500+」約 102px，320px 也不溢出 */

  .ivo-works__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
}

/* ==========================================================================
   消息頁
   ========================================================================== */
.ivo-news { background: var(--ivo-white); }
.ivo-news--alt { background: linear-gradient(180deg, var(--ivo-paper) 0%, #FFFBFC 100%); }

.ivo-news__en {
  font-size: 22px;
  font-style: italic;
  font-weight: 500;
  letter-spacing: .04em;
  /* 原本是 --ivo-primary：22px / italic / 500 不是大字，門檻 4.5:1
     而它只有 2.714:1，是硬性失敗，也違反「主色永遠不承載文字」。 */
  color: var(--ivo-pink-md);
}
/* 原本 #F76DB8 = 2.680:1 同樣不合格。兩層仍有可辨識的深淺差。 */
.ivo-news__en span { color: var(--ivo-btn); }
.ivo-news__title { margin-top: 6px; }
.ivo-news__head { margin-bottom: 44px; }

.ivo-news__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 30px;
}

.ivo-ncard { display: flex; flex-direction: column; }
.ivo-ncard__fig {
  display: block;
  border-radius: var(--ivo-radius-sm);
  overflow: hidden;
  aspect-ratio: 410 / 230;
  background: var(--ivo-line);
}
.ivo-ncard__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .5s ease;
}
.ivo-ncard:hover .ivo-ncard__img { transform: scale(1.03); }
/* 沒有特色圖片時的佔位，維持卡片高度一致 */
.ivo-ncard__ph {
  display: block;
  width: 100%;
  height: 100%;
  background: linear-gradient(135deg, var(--ivo-pink-lt) 0%, var(--ivo-line) 100%);
}
.ivo-ncard__title {
  margin-top: 20px;
  font-size: var(--ivo-fs-body);
  font-weight: 500;
  line-height: 1.7;
  color: var(--ivo-title);
  letter-spacing: .02em;
}
/* 單行標題時連結只有 24px 高，不到觸控目標。
   改成 block 並給下限，兩行標題本來就超過，不受影響。 */
.ivo-ncard__title a {
  display: block;
  min-height: 44px;
}
.ivo-ncard__title a:hover { color: var(--ivo-btn); }
.ivo-ncard__text {
  margin-top: 12px;
  font-size: var(--ivo-fs-sm);
  line-height: 1.85;
  color: var(--ivo-text);
}
.ivo-ncard__more { margin-top: 4px; }
.ivo-news__more { margin-top: 44px; }
.ivo-news__empty {
  padding: 48px 0;
  text-align: center;
  font-size: var(--ivo-fs-sm);
  color: var(--ivo-text-warm);
}

@media (max-width: 999.98px) {
  .ivo-news__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 689.98px) {
  .ivo-news__grid { grid-template-columns: 1fr; gap: 36px; }
  .ivo-news__en { font-size: 19px; }
}

/* ==========================================================================
   作品列表與單頁
   ========================================================================== */
.ivo-projects { background: var(--ivo-white); }
.ivo-projects__title { margin-top: 6px; }
.ivo-projects__head { margin-bottom: 36px; }

/* 分類篩選列 */
.ivo-filter {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px 12px;
  margin-bottom: 48px;
}
.ivo-filter__item {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 6px 20px;
  border: 1px solid var(--ivo-line);
  border-radius: var(--ivo-radius-pill);
  font-size: var(--ivo-fs-sm);
  color: var(--ivo-text);
  letter-spacing: .04em;
  transition: background-color .25s ease, color .25s ease, border-color .25s ease;
}
.ivo-filter__item:hover {
  border-color: var(--ivo-primary);
  color: var(--ivo-btn);
}
.ivo-filter__item.is-on {
  background: var(--ivo-btn);
  border-color: var(--ivo-btn);
  color: #FFFFFF;
}

/* 作品卡 */
.ivo-works__grid--list { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 30px; }
.ivo-pcard2 { display: flex; flex-direction: column; }
.ivo-pcard2__fig {
  display: block;
  border-radius: var(--ivo-radius-sm);
  overflow: hidden;
  aspect-ratio: 410 / 290;
  background: var(--ivo-line);
}
.ivo-pcard2__img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; }
.ivo-pcard2:hover .ivo-pcard2__img { transform: scale(1.03); }
.ivo-pcard2__title {
  margin-top: 10px;
  font-size: var(--ivo-fs-body);
  font-weight: 500;
  line-height: 1.6;
  color: var(--ivo-title);
}
/* 卡片標題是獨立的觸控目標（圖片與標題是兩個連結），
   用 padding 把可點範圍撐到 44px，margin 相應調小，視覺間距不變。 */
.ivo-pcard2__title a { display: inline-block; padding-block: 10px; }
.ivo-pcard2__title a:hover { color: var(--ivo-btn); }
.ivo-pcard2__client {
  margin-top: 8px;
  font-size: var(--ivo-fs-sm);
  color: var(--ivo-text-warm);
  letter-spacing: .02em;
}

/* 作品單頁 */
.ivo-work-single { background: var(--ivo-white); }
.ivo-work-single__banner {
  margin: 0 0 44px;
  border-radius: var(--ivo-radius-sm);
  overflow: hidden;
}
.ivo-work-single__title { margin-bottom: 8px; }
.ivo-work-single__item {
  font-size: var(--ivo-fs-sm);
  color: var(--ivo-btn);
  letter-spacing: .04em;
}
.ivo-work-single__meta {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px 40px;
  margin: 32px 0 48px;
  padding: 28px 32px;
  background: var(--ivo-paper);
  border-radius: var(--ivo-radius);
}
.ivo-work-single__meta p {
  font-size: var(--ivo-fs-sm);
  line-height: 2;
  color: var(--ivo-text);
  white-space: pre-line;
}
.ivo-work-single__body img {
  border-radius: var(--ivo-radius-sm);
  margin-block: 20px;
}
.ivo-work-single__back { margin-top: 56px; }

@media (max-width: 999.98px) {
  .ivo-works__grid--list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 689.98px) {
  .ivo-works__grid--list { grid-template-columns: 1fr; gap: 36px; }
  .ivo-filter { gap: 8px; }
  .ivo-filter__item { padding: 6px 14px; font-size: var(--ivo-fs-xs); }
  .ivo-work-single__meta { grid-template-columns: 1fr; padding: 22px 20px; }
}

/* ===========================================================================
   服務內容頁（六頁共用）
   ---------------------------------------------------------------------------
   內容來自舊站的六篇服務長文，版面全部改用本站的切版邏輯：
     ivo-sdetail__hero   頁首：服務名 pill、標題、導言、按鈕
     ivo-toc             文章目錄（靜態產生，不靠 jQuery）
     ivo-prose           長文排版區
   後面接作品列表（IVO_PROJECTS）與共用的 cta-ready。
   ======================================================================== */

.ivo-sdetail-hero {
  /* 頁首後面緊接著目錄與長文，上下各 80px 會把第一段推得太遠，
     這裡收成 64/48，讓讀者一進來就看得到目錄。 */
  padding-block: 64px 48px;
  background: linear-gradient(180deg, var(--ivo-paper) 0%, var(--ivo-white) 100%);
}
.ivo-sdetail-hero + .ivo-sec--tight { padding-top: 0; }
.ivo-sdetail-hero__inner { max-width: 900px; margin-inline: auto; text-align: center; }
.ivo-sdetail-hero__title {
  margin-top: 20px;
  font-size: var(--ivo-fs-h1);
  line-height: 1.35;
  letter-spacing: .02em;
  color: var(--ivo-title);
}
.ivo-sdetail-hero__lead {
  margin-top: 24px;
  font-size: var(--ivo-fs-lead);
  line-height: 1.9;
  color: var(--ivo-text);
}
.ivo-sdetail-hero__btn { margin-top: 36px; }

/* 長文的閱讀寬度收窄到 760px。
   1290 是版面容器寬，不是閱讀寬；整段文字拉到 1290
   一行會到 60 個中文字以上，讀起來會跳行。 */
.ivo-prose { max-width: 760px; margin-inline: auto; }
.ivo-prose > * + * { margin-top: 20px; }
.ivo-prose h2 {
  margin-top: 64px;
  padding-top: 28px;
  border-top: 1px solid var(--ivo-line);
  font-size: var(--ivo-fs-h2);
  line-height: 1.5;
  font-weight: 700;
  /* 顏色與 h3 對調（客戶 2026-09-13 決定）。
     舊站是「粉色大標＋深色小標」，新站做成了相反的「深色大標＋粉色小標」。
     舊站 h2 用 #F940A6，對白底只有 3.33:1；改用色票裡的 --ivo-btn
     （#C40068）5.92:1，同樣是桃紅系但比舊站安全，也不新增色票。 */
  color: var(--ivo-btn);
}
/* 這裡原本有一條 .ivo-prose h2:first-child { margin-top: 0; padding-top: 0; border-top: 0 }，
   原意是「整段內文的第一個 h2 不要畫分隔線」，實際上是全站最大的間距缺陷：
   舊站的文章把每個章節包成 <section>，h2 一律是 section 的第一個元素子，
   所以 :first-child 把「每一個」h2 都打中，64px 上距與分隔線整批消失。
   實測（CDP、1440）：39 篇有 section 的文章，上一段文字底到 h2 文字頂只剩 5px，
   375 是 4px；5 篇沒有 section 的文章與 /cis-design/ 等三個服務頁是 98px / 81px。
   /viposter/、/ad/、/service-layout-design/ 三個頁面同樣中招（各 7、3、10 個 h2）。
   這條規則沒有任何「正當」的命中：實測 44 篇文章與 6 個用 .ivo-prose 的頁面，
   .ivo-prose 的第一個元素子一律是 SEO 外掛插入的 div.sc-icg-seo-gps-source，
   所以「h2 真的是內文第一個元素」這個情形一次都沒有發生，直接刪掉最乾淨。
   刪掉之後三個服務頁與 /product-design/ 完全不變（實測 1440 / 768 / 375 三組數字相同），
   其餘版面回到與它們一致的 98px（375 為 81px）＋一條 1px 的 --ivo-line 分隔線。 */
.ivo-prose h3 {
  /* 舊站的卡片樣式（客戶 2026-09-13 決定）。
     舊站實測（/2877/、1280px）：
       24.28px / 500　色 #313741　行高 48.56　字距 1.77px
       底 #F0F0F0　左條 10px solid #A430EB　內距 15.23px　上下距 24.28px
     改用色票對應的值，不新增顏色：
       底 #F0F0F0 → --ivo-line（#EBEBEE）
       左條 #A430EB → --ivo-accent（#BA4BFE，色相 275° 對舊站 277°，幾乎一致）
       字色 #313741 → --ivo-title（#1A1B1F），對 #EBEBEE 底是 15.6:1
     字級取 24px（舊站 24.28），字重留 600 而不是舊站的 500：
     500 在 Noto Sans TC 上與內文的 400 差別太小，卡片裡的標題會糊掉。
     圓角：舊站是 0，這裡給右側 8px（--ivo-radius-sm），
     與站上其他卡片一致；左側維持 0 才跟得上那條 10px 的邊條。 */
  margin-top: 40px;
  padding: 15px 18px;
  border-left: 10px solid var(--ivo-accent);
  border-radius: 0 var(--ivo-radius-sm) var(--ivo-radius-sm) 0;
  background: var(--ivo-line);
  font-size: 24px;
  line-height: 1.6;
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--ivo-title);
}
.ivo-prose h4 {
  margin-top: 28px;
  font-size: var(--ivo-fs-lead);
  line-height: 1.6;
  font-weight: 700;
  color: var(--ivo-title);
}
.ivo-prose p { font-size: var(--ivo-fs-body); line-height: 2; color: var(--ivo-text); }
.ivo-prose a { color: var(--ivo-btn); text-decoration: underline; text-underline-offset: 3px; }
.ivo-prose a:hover { color: var(--ivo-btn-hover); }
.ivo-prose strong { font-weight: 700; color: var(--ivo-title); }
/* 全域 reset 把 list-style 清掉了（.ivo-page :where(ul, ol)），
   長文區要把項目符號與編號拿回來，不然條列會變成一行行的散句。 */
.ivo-prose ul { padding-left: 1.4em; list-style: disc; }
.ivo-prose ol { padding-left: 1.6em; list-style: decimal; }
.ivo-prose li { font-size: var(--ivo-fs-body); line-height: 2; color: var(--ivo-text); }
.ivo-prose li + li { margin-top: 6px; }
/* 巢狀清單第二層的項目符號。
   舊站第二層吃瀏覽器預設的 circle（空心圓），本站的 .ivo-prose ul（0,1,1）
   把每一層都設成 disc，第二層就跟第一層一模一樣，讀不出階層。
   1440 實測：dm-design、catalog-design 共 5 組巢狀清單、12 個項目，
   舊站 list-style-type 全是 circle，新站全是 disc。
   全站 44 篇掃過只有這 5 組，沒有第三層，也沒有 li 裡面包 ol，
   所以只補第二層的 ul 這一條。
   .ivo-prose li ul 是 (0,1,2)，壓得過 .ivo-prose ul 的 (0,1,1)；
   文章目錄的 .toc-container 裡沒有巢狀清單（實測 0 個），不會被掃到。 */
.ivo-prose li ul { list-style: circle; }
/* 圖片的上下節奏。1440px 逐篇實測（舊站內文寬 800、本站 760）：
   舊站的內文圖與前後段落之間是 32px，圖與圖之間 37px
   （32 再加上 display: inline 的基線空隙 5px）；
   本站寫 8px，比自己的段落節奏 20px 還小，圖和內文黏成一團。
   activities-backplane 12 張、2023project02 14 張最明顯。
   改成 28px：對齊本檔 .ivo-prose h4 的段距，與舊站 32px 差 4px 看不出來，
   也不必為圖片再多立一個數字。

   width 一起補：舊站是所有內文圖都撐滿閱讀寬（實測 128 張全部 800px），
   本站只有下面 .ivo-prose figure img 那一條有 width: 100%，
   裸的 <p><img> 沒吃到，exhibition-key-visual 那張原生 554px 的圖
   因此只有 554px 寬，和同一頁其他 760px 的圖不齊邊（舊站是 800px）。
   全站掃過，內文圖最小原生寬就是這張 554px，沒有行內小圖會被這條拉大。 */
.ivo-prose img {
  border-radius: var(--ivo-radius-sm);
  width: 100%;
  margin-block: 28px;
}
/* text-align 這裡不寫。原本寫的是 center，但從來沒有生效：
   wp-adapt.css 的 .entry-content figure figcaption（0,1,2）
   壓過這條（0,1,1），實測 14 張圖說全部都是靠左。
   舊站的圖說也是靠左（computed start），所以畫面本來就對，
   留著這行只會讓人以為圖說是置中的。 */
.ivo-prose figcaption {
  margin-top: 10px;
  font-size: var(--ivo-fs-sm);
  line-height: 1.7;
  color: var(--ivo-text-warm);
}
/* 表格在手機上一定會超出，自己帶一個橫向捲動的外框 */
.ivo-prose__table { overflow-x: auto; }
.ivo-prose table { width: 100%; border-collapse: collapse; font-size: var(--ivo-fs-sm); }
.ivo-prose th, .ivo-prose td {
  padding: 12px 16px;
  border: 1px solid var(--ivo-line);
  line-height: 1.8;
  text-align: left;
  vertical-align: top;
}
.ivo-prose th { background: var(--ivo-paper); font-weight: 700; color: var(--ivo-title); }

/* 重點摘要。
   服務內容頁由 migrate_services.py 改寫成 ivo-summary，
   文章則是原樣搬過來的 article-summary（migrate_posts.py 不動內文），
   兩個名字指的是同一個東西，共用一組樣式就好。 */
.ivo-prose .ivo-summary,
.ivo-prose .article-summary {
  padding: 24px 28px;
  border-left: 4px solid var(--ivo-primary);
  border-radius: var(--ivo-radius-sm);
  background: var(--ivo-paper);
}
.ivo-prose .ivo-summary p,
.ivo-prose .article-summary p { margin: 0; font-size: var(--ivo-fs-body); line-height: 1.9; }

/* 文章目錄。
   ivo-toc 是服務內容頁產生的，toc-container 是文章內文自帶的，同樣共用。 */
.ivo-toc,
.ivo-prose .toc-container {
  max-width: 760px;
  margin: 0 auto 56px;
  padding: 28px 32px;
  border: 1px solid var(--ivo-line);
  border-radius: var(--ivo-radius);
  background: var(--ivo-white);
}
/* 上面那條 margin 的上緣是 0，而且 0,2,0 的權重蓋過 .ivo-prose > * + * 的 20px，
   所以文章裡的目錄不管前面接什麼，上緣一律貼死（1440 實測 39 篇全部是 0px），
   其中 15 篇前面就是重點摘要 article-summary，兩張卡片直接連在一起。
   另外 22 篇前面是只裝註解的空段落，它會 margin collapse-through，
   補上 40px 之後，對「上一段看得見的內容」而言一樣是 40px。
   服務內容頁的 .ivo-toc 是 .ivo-prose 外面的獨立區塊，位置自己排，
   不能一起加，所以只補在文章這一側。
   40px 對齊 .ivo-prose h3 的段距：讀起來是「換一個區塊」，
   而不是「同一段的下一行」。 */
.ivo-prose .toc-container { margin-top: 40px; }
.ivo-toc__title,
.ivo-prose .toc-title {
  margin-bottom: 14px;
  font-size: var(--ivo-fs-sm);
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--ivo-btn);
}
.ivo-toc ol,
.ivo-prose .toc-list { margin: 0; padding-left: 1.6em; list-style: decimal; }
/* 目錄是拿來掃視的清單，行距要比內文（line-height 2，也就是 32px）緊，
   不然九項排下來會比一整段內文還高。1.75 換算是 28px，
   跟內文差得出來，中文長標題換到第二行也還讀得動。
   選擇器從 .toc-list 換成 .toc-container：舊站有 22 篇文章的 <ol> 沒帶
   toc-list class（1440 實測 39 篇有目錄的文章裡有 22 篇），
   只寫 .toc-list 會漏掉一半以上，那幾篇會掉回 .ivo-prose li 的行高 2。
   權重 0,2,1 壓得過 .ivo-prose li 的 0,1,1。 */
.ivo-toc li,
.ivo-prose .toc-container li { font-size: var(--ivo-fs-body); line-height: 1.75; }
/* 項目之間的縫。文章原本吃 .ivo-prose li + li 的 6px，
   服務內容頁的 .ivo-toc 因為在 .ivo-prose 外面，原本是 0，兩邊統一成 16px。
   16 這個數字是被觸控目標反推出來的：每一列的可點高度要 44px，
   列本身只有 28px，差的 16px 只能由列與列之間的空隙來補，
   這樣才能同時滿足「可點 44px」與「相鄰兩列的可點範圍不重疊」。
   權重 0,2,2 壓得過 .ivo-prose li + li 的 0,1,2。 */
.ivo-toc li + li,
.ivo-prose .toc-container li + li { margin-top: 16px; }
/* 目錄的每一列都是獨立的觸控目標。
   原本用 a 的 padding-block: 10px 撐高，有三個問題：
   一是本案規定觸控目標不用 padding 或 min-height 撐；
   二是行高 2（32px）加上下各 10px 其實是 52px 不是舊註解寫的 44px，
   這 52px 正是客戶說「目錄行距太大」的來源；
   三是 inline-block 的基線落在最後一行，標題換行時序號會掉到第二行去
   （375 寬實測，39 篇共 162 個項目換行，每一個的序號都對到第二行）。
   改成 display: block：序號回到第一行，整列寬度都可以點。
   高度不足的部分改用絕對定位的透明 ::after 補到 44px，
   寫法與 .ivo-btn::after、.ivo-form__radio::after 同一套（置中加 max()），
   驗收/檢查.py 第 4 項的觸控目標公式認得這種寫法。
   用 max(100%, 44px) 而不是固定 44px，是為了讓手機上換行的兩行列
   （56px）維持整列可點，不會反而被裁成 44px。 */
.ivo-toc a,
.ivo-prose .toc-container a {
  position: relative;
  display: block;
  padding-block: 0;
  color: var(--ivo-text);
  text-decoration: none;
}
.ivo-toc a::after,
.ivo-prose .toc-container a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  height: max(100%, 44px);
}
.ivo-toc a:hover,
.ivo-prose .toc-container a:hover { color: var(--ivo-btn); text-decoration: underline; }

/* 文中的 CTA（舊站的 post-cta 圖片連結，改成本站的按鈕） */
.ivo-prose .ivo-inline-cta {
  margin-top: 48px;
  padding: 32px;
  border-radius: var(--ivo-radius);
  background: var(--ivo-paper);
  text-align: center;
}
.ivo-prose .ivo-inline-cta p { margin-bottom: 20px; font-size: var(--ivo-fs-lead); }

@media (max-width: 999.98px) {
  .ivo-sdetail-hero__title { font-size: 36px; }
}
@media (max-width: 689.98px) {
  .ivo-sdetail-hero__title { font-size: 28px; }
  .ivo-sdetail-hero__lead { font-size: var(--ivo-fs-body); line-height: 1.9; }
  .ivo-prose h2 { margin-top: 48px; font-size: 24px; }
  /* 卡片化之後手機要連內距與邊條一起收，
     330px 版心扁掉 10px 邊條加 36px 內距就只剩 284px。 */
  .ivo-prose h3 {
    padding: 12px 14px;
    border-left-width: 6px;
    font-size: 19px;
  }
  /* 這條原本只寫 .ivo-toc，文章的 toc-container 完全沒吃到，
     手機上還維持桌機的 28px 32px 內距與 56px 下距，一起補上。 */
  .ivo-toc,
  .ivo-prose .toc-container { padding: 22px 20px; margin-bottom: 40px; }
  /* 手機的區塊間距整體比桌機小一級，摘要到目錄跟著對齊 .ivo-prose h4 的 28px。
     這條在檔案裡的位置比上面那條 margin-top: 40px 晚，同權重後者勝。 */
  .ivo-prose .toc-container { margin-top: 28px; }
  .ivo-prose .ivo-inline-cta { padding: 24px 20px; }
}

/* ---------------------------------------------------------------------------
   舊站長文裡的語意區塊
   舊站的文章用了一組自訂 class（重點、名詞定義、觀點、FAQ、作者框），
   樣式掛在舊主題上。內容搬過來之後那些樣式不會跟著來，
   所以在這裡重新給一套，視覺語言對齊本站。
   ------------------------------------------------------------------------ */

/* 關鍵數據、本節重點、名詞定義：同一種樣式，靠左邊的色條區分輕重 */
.ivo-prose .key-stat,
.ivo-prose .key-takeaway,
.ivo-prose .term-definition {
  margin-top: 28px;
  padding: 22px 26px;
  border-left: 4px solid var(--ivo-primary);
  border-radius: var(--ivo-radius-sm);
  background: var(--ivo-paper);
}
.ivo-prose .key-stat p,
.ivo-prose .key-takeaway p,
.ivo-prose .term-definition p {
  margin: 0;
  font-size: var(--ivo-fs-body);
  line-height: 1.9;
}
.ivo-prose .key-stat { border-left-color: var(--ivo-accent); }
.ivo-prose .term-definition { border-left-color: var(--ivo-off); }

/* 三張卡片開頭的那個 <strong> 是「標籤」，不是一般的粗體字。
   舊站為它們寫了三條規則，搬家時沒跟著來，1440 實測 70 個 strong
   在新站全部掉回 .ivo-prose strong 的「16px／--ivo-title／inline」：

     舊站                                    新站（現況）
     .key-takeaway strong
       block、16px、#0E1B47、字距 .05em、下距 8px   inline、16px、--ivo-title
     .term-definition strong
       block、16px、#F940A6、字距 .05em、下距 8px   inline、16px、--ivo-title
     .key-stat strong
       inline、25px、#F940A6、右距 8px              inline、16px、--ivo-title

   結果是「本節重點：」「什麼是海報材質？」這些原本自成一行的小標，
   現在跟內文黏在一起；「150 磅 vs 250 磅」這種原本一眼看到的大粉字，
   現在跟內文一樣大。客戶說的「資料呈現跟原本有落差」，這是其中一項。
   影響 key-takeaway 34 個（22 篇）、term-definition 18 個（18 篇）、
   key-stat 18 個（12 篇）。
   這三個 class 在 page 也有：/service-layout-design/ 與 /viposter/ 兩頁
   各一組，會跟著套用（實測其餘 81 個 page 與 project 都沒有，不會被掃到）。

   數值換成本站的尺標與色票，不照抄舊站的 #0E1B47 與 #F940A6：
     標籤 14px（--ivo-fs-sm）：舊站標籤比內文小一級（內文 18、標籤 16），
       本站內文 16，同樣小一級就是 14。
     數字 20px：舊站的數字與 h3 同級（兩者都是 24.9952px），
       本站 .ivo-prose h3 是 20px，維持同一個關係。
     深標籤沿用 --ivo-title（卡片底 --ivo-paper 上 16.33:1），
     粉標籤與數字用 --ivo-btn（同一個底上 5.617:1，
       14px 粗體也過 AA 的 4.5:1，不必動用色票外的顏色）。

   選擇器收在 p > strong:first-child。全站 44 篇實測命中數與卡片數一一對應
   （34/34、18/18、18/19，少的那一個 key-stat 在 namecard，本來就沒有 strong），
   日後卡片內文多出第二個 strong 也不會被誤認成標籤。 */
.ivo-prose .key-takeaway p > strong:first-child,
.ivo-prose .term-definition p > strong:first-child {
  display: block;
  margin-bottom: 8px;
  font-size: var(--ivo-fs-sm);
  letter-spacing: .05em;
}
.ivo-prose .term-definition p > strong:first-child { color: var(--ivo-btn); }
.ivo-prose .key-stat p > strong:first-child {
  margin-right: 8px;
  font-size: 20px;
  /* 不讓它繼承 .key-stat p 的 1.9（20px 會撐成 38px），
     1.6 換算 32px，只比其他行的 30.4px 高 1.6px。 */
  line-height: 1.6;
  color: var(--ivo-btn);
}

/* 象牙設計的觀點。
   服務內容頁是 div.ivory-perspective 帶一個 h3 標題，
   文章是 blockquote.first-party-quote 帶一個 footer 署名，
   語意上是同一種區塊，共用底色與留白。 */
.ivo-prose .ivory-perspective,
.ivo-prose .first-party-quote {
  margin-top: 40px;
  padding: 32px 36px;
  border-radius: var(--ivo-radius);
  background: linear-gradient(135deg, var(--ivo-lilac) 0%, var(--ivo-paper) 100%);
}
/* 觀點區塊裡的小標不要跟著變成卡片。
   h3 改成卡片（底色、10px 邊條、內距、24px/600）之後，這條原本只重設
   margin 與顏色，padding / border-left / background / font-size 全部漏掉，
   結果是「灰底紫邊條的小卡片」套在紫漸層卡片裡面，實測 28 處、26 個頁面
   （.ivory-perspective 19 處、.first-party-quote 9 處，後者連重設都沒有，
   連 margin-top: 40px 也一起吃到）。
   這裡把卡片的每一個屬性都重設回純文字，字級回到卡片化之前的 20px。 */
.ivo-prose .ivory-perspective h3,
.ivo-prose .first-party-quote h3 {
  margin-top: 0;
  margin-bottom: 16px;
  padding: 0;
  border-left: 0;
  border-radius: 0;
  background: none;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: normal;
  color: var(--ivo-btn);
}
.ivo-prose .ivory-perspective p + p,
.ivo-prose .first-party-quote p + p { margin-top: 16px; }
/* blockquote 版的署名。這個 footer 在 blockquote 裡面，
   不會變成第二個 contentinfo 地標（那是作者框踩過的坑，見立案紀錄）。 */
.ivo-prose .first-party-quote p {
  font-size: var(--ivo-fs-lead);
  line-height: 1.9;
  color: var(--ivo-title);
}
.ivo-prose .first-party-quote footer {
  margin-top: 16px;
  font-size: var(--ivo-fs-sm);
  color: var(--ivo-text-warm);
  text-align: right;
}

/* 沒有掛 class 的裸 blockquote。
   舊站沒有替 blockquote 寫任何規則，它吃的是瀏覽器預設的
   margin: 1em 40px，靠那 40px 內縮跟內文區隔開。
   本站的 reset（.ivo-page :where(blockquote, figure) { margin: 0 }）
   是為了首頁的評論卡寫的，順手把這個內縮也歸零了。
   1440 實測 proposal-layout-design 那兩段「排版挑戰：…」：
     舊站 margin-left 40px、寬 720px、左緣 360px
     新站 margin-left 0px、寬 760px、左緣 340px ← 與一般段落完全同框
   讀者看不出那是引述。全站 44 篇只有這 2 個裸 blockquote，
   其餘 12 個都是 .first-party-quote，已經有自己的卡片樣式。

   復原做法不照抄 40px 的左外距：那是瀏覽器預設值不是設計決定，
   而且 520 寬時會把中文擠成一行十來個字。
   改成左內距加一條細軌，內縮與細軌互為備援，
   色用色票內的 --ivo-off（本來就是這一區 .term-definition 的軌條色），
   純裝飾、不承載文字。520 實測寬 457.6px、375 寬 330px，皆無水平溢出。 */
.ivo-prose blockquote:not([class]) {
  padding-left: 24px;
  border-left: 3px solid var(--ivo-off);
}

/* 文中 CTA：舊站是 aside.cta-box，一段帶箭頭的連結。
   這裡把連結本身做成按鈕，不另外包一層 markup。 */
/* 舊站這一段沒有外框卡片，就是一顆按鈕置中。
   先前給了 --ivo-paper 的粉底加 32px 內距，客戶看了說「有奇怪的背景色」。
   拿掉之後與舊站一致，上下留白改用 margin 撐，
   長文裡的辨識度由按鈕本身的量體與描邊負責，不需要再套一層底。 */
.ivo-prose .cta-box {
  margin-block: 48px;
  text-align: center;
}
.ivo-prose .cta-box p { margin: 0; }
/* 文中 CTA 的按鈕造型，對齊舊站。
   ---------------------------------------------------------------------------
   客戶看了短碼展開的結果說「明顯跟原本的網站不一樣」，比對之後確實差很多。

   舊站那顆是一張 8000×2067 的圖（uploads/2026/03/scimgM7wpz7.webp），
   兩種來源包的是同一張：
     ・HTML 形式  <aside class="cta-box"><p><a><img …></a></p></aside>　13 個
     ・短碼形式  [sc name="post-cta"]　　　　　　　　　　　　　　　　13 個
   兩種沒有重疊（同一篇只用一種），合計 26 個，所以改這一條 26 個一起對齊。

   舊站那張圖的造型：藥丸形、左紫右粉的漸層、白色粗體字、
   外圈一圈細描邊，按鈕本身約佔容器寬的 65%（800px 容器裡約 520×140）。
   改版前這裡是 160×52 的深紅實色小鈕，量體差了三倍以上。

   ⚠ 色系改過兩次，第二次才對，過程記在這裡免得日後又改回去。
   第一次用 --ivo-grad（#A30056 → #C40068 → #EA007E），對比度很漂亮（5.4:1），
   但客戶看了說顏色還是不像。實際取像素才發現問題在**色相**不在明度：
     舊站  #A87DE9 → #D073D0   紫 → 粉紫，色相 259° 到 300°
     那一版 #A50057 → #E7007C   全是紅紫系，色相只在 330° 附近游移
   左右亮度差 0.098 確實有漸層，但色相沒有跨度，看起來就是同一個顏色的深淺。

   現在改用色票裡的 --ivo-accent（#BA4BFE，色票註解寫的「輔助紫，裝飾用」）
   到 --ivo-pink-3a（#EF2D96，客戶指定的品牌漸層深端）。
   色相 272° → 330°，與舊站同樣是「紫到粉」的跨度，而且兩端都在色票內。

   對比度的取捨要講清楚：
     舊站紫端 #A87DE9 配白字 3.100:1
     舊站粉端 #D073D0 配白字 2.981:1  ← 連非文字的 3:1 都不到
     本版兩端      #BA4BFE 3.801:1、#EF2D96 3.828:1
   也就是**這一版比舊站好**，但仍然只過「大字」門檻（≥18.66px bold 時 3:1），
   過不了一般文字的 4.5:1。所以字級在每個斷點都必須 ≥19px 且 700，
   下面手機斷點的 19px 是硬下限，不可以再往下調。
   舊站那顆是圖片，文字是畫上去的，螢幕閱讀器讀不到、也選不起來，
   所以它「不合格」這件事在舊站是隱形的；換成真文字之後就要正面處理。

   不用 --ivo-grad-cta（#EF2D96 → #FFAFDB）的理由不變：
   亮端 #FFAFDB 配白字只有 1.69:1，白字會直接消失。 */
.ivo-prose .cta-box a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 用 min() 收在版心內：手機 330px 時取 92% 留一點側邊空隙，
     不會貼到螢幕邊。.cta-box 已經沒有內距，所以 100% 就是內文寬 760px。 */
  min-width: min(460px, 92%);
  /* 26px 上下內距讓按鈕高度到約 84px。舊站那顆本體是 795×146（在 1200px 的圖上），
     長寬比 5.44:1；460px 寬對應的高度就是 84.6px。原本 20px 只有 72px，偏扁。 */
  padding: 26px 40px;
  border-radius: var(--ivo-radius-pill);
  /* 實色底要留著：一是漸層算完之前的底色，二是 Lighthouse 與 axe
     只讀 background-color，留著才判得到合格值（與 .ivo-btn--solid 同樣的理由）。
     這裡用 --ivo-btn 而不是漸層的任一端：那兩端都只有 3.8:1，
     工具讀到會判不過，用 5.92:1 的實色當回報值才不會有假警報。 */
  background-color: var(--ivo-btn);
  /* 舊站那張圖的實際取色。
     ⚠ 取端點的時候要沿著按鈕本體掃，不要只取「圖片寬度的 0% 與 100%」：
     那張圖左右各有透明留白，取到的是黑色，第一次因此拿 75% 位置的
     #D073D0 當右端，整個右半邊都不夠紅。
     正確做法是先找出非白範圍（x 150~1079）再沿本體取 9 點，
     用 12% 的 #9A80F4 與 87% 的 #E270C4 線性外推兩端。
     粉端把亮度從 68% 降到 65% 讓白字對比從 2.782 升到 3.053，
     肉眼分不出來但過得了大字門檻。
     兩個值都是「還原舊站素材」不是新增品牌色，所以寫死 hex 不進色票：
     色票是介面用色的配額，這裡是把舊站的一張圖換成 CSS。 */
  background-image: linear-gradient(90deg, #8D83FC 0%, #EC5FB6 100%);
  color: #FFFFFF;
  font-size: 21px;
  font-weight: 700;
  letter-spacing: .06em;
  line-height: 1.5;
  text-decoration: none;
  /* 舊站那顆按鈕外圈有一條細描邊。用兩層 box-shadow 疊出來，
     不用 border：border 會吃進元素尺寸，而且會踩到
     background-origin 的平鋪接縫（立案紀錄第 129 條）。
     內圈白、外圈淡紫，配合漸層改成紫粉之後比原本的淡粉更相稱。 */
  /* 舊站的描邊是「一圈明顯的白色間隙 + 一條很淡的紫線」，
     間隙換算到 460px 寬的按鈕上約 9px，原本只給 3px 所以幾乎看不到。 */
  box-shadow: 0 0 0 8px #FFFFFF, 0 0 0 9px #D8C4F0;
  transition: box-shadow .25s ease, transform .25s ease;
}
.ivo-prose .cta-box a:hover {
  color: #FFFFFF;
  filter: brightness(.94);
  transform: translateY(-1px);
  box-shadow: 0 0 0 8px #FFFFFF, 0 0 0 10px var(--ivo-accent);
}
@media (max-width: 689.98px) {
  /* ⚠ 19px 是硬下限，不是手感。
     這顆按鈕的漸層兩端配白字只有 3.8:1，要靠 WCAG 的「大字」門檻
     （≥18.66px 且 bold 時門檻降到 3:1）才合格。
     降到 18px 就退回一般文字的 4.5:1，當場不合格。 */
  .ivo-prose .cta-box a { padding: 16px 22px; font-size: 19px; letter-spacing: .04em; }
}

/* 文中 CTA 按鈕（舊站原樣搬來的 a.wp-element-button）
   ---------------------------------------------------------------------------
   底色不是我們給的，也不是 WordPress 核心給的。逐層查過：
     wp-includes 的 block-library 只輸出 .wp-element-button{cursor:pointer}
     global-styles 只輸出 :root :where(.wp-element-button){font-style/font-weight/
       letter-spacing/text-transform: inherit}，沒有顏色
     Blocksy 的 theme.json 反而把 elements.button 的 color 關掉（那只是關編輯器控制項）
     真正的來源是 blocksy/static/bundle/main.min.css：
       .wp-element-button{ background-color: var(--theme-button-background-initial-color) }
     而客製器輸出的 uploads/blocksy/css/global.css 把那個變數指到色盤 1，
     也就是 #C40068。
   Blocksy 同時有給白字（--theme-button-text-initial-color: #ffffff），
   但上面那條 .ivo-prose a（0-1-1）贏過 Blocksy 的 .wp-element-button（0-1-0），
   把文字染成同一個 #C40068，對比度 1.00，整顆按鈕看不到文案。
   這裡用 0-2-0 的選擇器把按鈕接回本站的實心按鈕造型，順便關掉底線。

   【影響範圍，wp db query 全站掃過再用 CDP 逐頁核對】
   帶 wp-element-button 的內容共 26 個網址：
     23 篇文章各一顆裸的（就是看不見的那批）
     1 個「頁面」/viposter/ 也有一顆（服務內容頁同樣包在 .ivo-prose 裡），
       症狀與文章完全一樣，一起修好
     2 篇文章（/2023project01/、/logodesignrecom/）的那顆包在舊站的
       .cta-box 裡面，本來就吃 .ivo-prose .cta-box a 的藥丸造型，看得見，
       不屬於這次要修的對象，由最下面那條例外把它們排除。
   project 單頁與其餘 18 個頁面完全沒有 wp-element-button，不受影響。

   ⚠ 漸層刻意不用 .ivo-btn--solid 的 --ivo-grad-cta。
   那條的亮端 #FFAFDB 配白字只有 1.69:1，.ivo-btn 是靠 19px/700 的大字門檻
   （3:1）加上短文案才站得住。這裡的文案長到 44 個字，最寬一顆 760px
   已經佔滿整個版心，文字會一路壓到漸層最亮的一端，等於換一種方式看不見。
   改用 --ivo-grad（原本服務 .ivo-form__submit 的那條，停駐點本來就是為 4.5:1 挑的）。
   字級維持 Blocksy 繼承下來的 16px/700，屬 WCAG 小字，門檻 4.5:1。

   【對比度實測】取 Range.getClientRects 的文字實際右緣換算成漸層位置，
   再以 WCAG 相對亮度算白字對該點底色的對比。六個斷點都量：
     1440 / 1000 / 768　最差 4.55:1（/2519/ 595px 單行、/logo-design-price/ 657px 單行）
     480 　最差 4.88:1
     375 　最差 4.62:1（/2519/）
     320 　最差 4.80:1
   全數通過 AA；同一輪量到溢出 .ivo-prose 皆為 0、hscroll 皆為 0、
   按鈕高度最小 52px（觸控 ≥44 由 Blocksy 既有的行盒與 min-height 46px 提供，
   這裡沒有新增 padding 或 min-height，規範第 2 條）。
   ::selection 反白實測 24px 高、上下各內縮 14px，內距沒有被畫進反白。

   不放大到 19px，是因為最長那顆在手機已經要斷成三行，
   放大只會讓按鈕變成一整塊色，換來的大字門檻也用不到。 */
.ivo-prose .wp-element-button {
  color: #FFFFFF;
  /* 純色底保留：一是底圖生效前的底色，二是只讀 background-color 的
     檢測工具（Lighthouse、axe）會判 5.92:1 通過。 */
  background-color: var(--ivo-btn);
  /* ── 對齊舊站的文字按鈕（2026-09-13 第二次修正）
     舊站的 .wp-element-button 實測是：
       background-image: linear-gradient(135deg, #F940A6 0%, #A430EB 100%)
       border-radius: 99px　padding: 14px 32px　font: 17.72px/600
       box-shadow: oklch(0.58 0.28 330 / .3) 0 4px 20px（粉紫光暈）
     ⚠ 方向要注意：這顆是「粉 → 紫、35 度對角」，
     而 .cta-box 那顆（從 8000px 的圖換來的）是「紫 → 粉、90 度水平」。
     舊站本來就有兩種 CTA，不要為了一致而硬統一成同一條漸層。

     字級提到 19px 是必要的，不是美觀：
     粉端 #F940A6 配白字只有 3.327:1。舊站用 17.72px/600 不算大字，
     門檻是 4.5:1，所以舊站那顆其實不合格。
     19px 且 700 之後進入 WCAG 大字（≥ 18.66px bold），門檻降到 3:1 才過。
     順帶把按鈕高度從 52px 帶回約 58px，與舊站一致。
     ⚠ 19px 是硬下限，調小就當場不合格。 */
  background-image: linear-gradient(135deg, #F940A6 0%, #A430EB 100%);
  border-radius: 99px;
  font-size: 19px;
  font-weight: 700;
  box-shadow: 0 4px 20px rgba(233, 30, 140, .3);
  text-decoration: none;
  letter-spacing: .02em;
  /* 把文字左右內縮寫死，不要靠 Blocksy 客製器的 --theme-button-padding。
     --ivo-grad 對白字掉到 4.5:1 的位置在按鈕寬度的 96.3%，最寬一顆
     （760px 滿版）配 32px 內距時文字右緣落在 95.8% = 4.51:1，
     只剩約 4px 的餘裕；內距若被客製器調小就會無聲掉到門檻下。
     這是把文字往內收，不是拿 padding 撐觸控目標，
     與 .ivo-form__submit 的 padding-inline: 32px 同一個理由（規範第 2 條）。
     上下內距仍沿用 Blocksy 的 10px。 */
  padding-inline: 32px;
  /* 長文案斷行後第二行常常只剩三四個字，balance 會把每行拉平，
     順便把文字右緣往內收，漸層亮端就壓不到字。
     Safari 17.3 以下 / Firefox 120 以下不支援會退回一般斷行，
     退回後最差仍是上面單行那組 4.55:1，是美觀退化不是可及性退化。 */
  text-wrap: balance;
  /* Blocksy 的 ::before 會複製整條底圖再糊一層當陰影（顯示與否由這個
     變數控制，主題端寫的是 display: var(--theme-button-shadow, block)），
     疊在 --ivo-grad-glow 上會變兩層粉霧，關掉。 */
  --theme-button-shadow: none;
  /* Blocksy 預設 hover 上浮 3px（transform: var(--theme-button-transform,
     translate3d(0, -3px, 0))），本站按鈕是 1px。 */
  --theme-button-transform: translateY(-1px);
}
.ivo-prose .wp-element-button:hover {
  color: #FFFFFF;
  background-color: var(--ivo-btn-hover);
  /* hover 把同一條漸層整體壓深一階，維持粉紫的色相關係 */
  background-image: linear-gradient(135deg, #E52E93 0%, #8F22D4 100%);
  box-shadow: 0 6px 26px rgba(233, 30, 140, .42);
}
@media (max-width: 689.98px) {
  /* 手機版心只有 330px，最長的文案在 inline-flex 下會縮成一塊左右不齊的方塊，
     直接讓它佔滿整行。box-sizing 是 border-box（.ivo-page *），
     width: 100% 不會被 32px 左右內距撐爆（375 與 320 皆實測無溢出）。 */
  .ivo-prose .wp-element-button { display: flex; width: 100%; letter-spacing: 0; }
}
/* 例外：同時是 .cta-box 連結的那兩顆（/2023project01/、/logodesignrecom/）。
   .ivo-prose .cta-box a 只有 0-2-1，蓋得掉上面那條 0-2-0 的
   color / background-color / border-radius / display / padding，
   但 background-image、box-shadow、letter-spacing、text-wrap 與手機的 width
   它根本沒寫，所以會照樣生效；hover 那條 0-3-0 還會把底色換成 --ivo-btn-hover、
   把上浮從 Blocksy 的 3px 改成 1px、順便關掉 Blocksy 的 ::before 粉霧。
   實測沒擋的話藥丸按鈕會從 643.9px 長到 657.0px、純色底變成漸層、多一層陰影，
   跟站上其他 21 顆 .cta-box 按鈕就不一致了。
   這兩顆本來就看得見（白字對 #C40068 = 5.92:1），不是這次要修的對象，
   所以把被波及的屬性整組收回舊樣。
   兩個 Blocksy 變數用 initial（自訂屬性的 initial 等於「無效值」，
   var() 會退回主題自己寫的 fallback），不需要把 Blocksy 的預設值抄一遍。
   選擇器 0-3-0 贏過上面的 0-2-0（含 @media 裡那條），與 0-3-0 的 hover 規則同分
   但排在後面，所以 idle 與 hover 兩態都吃得到，不需要 !important。 */
.ivo-prose .cta-box .wp-element-button,
.ivo-prose .cta-box .wp-element-button:hover {
  /* 這兩顆在舊站是「文字版」不是「圖片版」，
     所以要把文字版的漸層、圓角與字級寫回來。
     不寫的話會拿到 .ivo-prose .cta-box a（0-2-1）的圖片版樣式，
     因為它贏過 .ivo-prose .wp-element-button（0-2-0）。
     這一條是 0-3-0，才蓋得回去。 */
  background-image: linear-gradient(135deg, #F940A6 0%, #A430EB 100%);
  border-radius: 99px;
  font-size: 19px;
  box-shadow: 0 4px 20px rgba(233, 30, 140, .3);
  /* 原本這裡是 background-image: none，會把上面那條漸層整個清掉，
     露出 background-color 的深紅。客戶回報的「重整一秒後變色」就是這個：
     瀏覽器先畫出漸層，解析到這一條之後改成純色。
     舊站這幾顆同樣是粉紫漸層，沒有理由拿掉，只留「不要滿版」那幾條。 */
  letter-spacing: normal;
  text-wrap: wrap;
  width: auto;
  --theme-button-shadow: initial;
  --theme-button-transform: initial;
}
/* 上面那條同時涵蓋 :hover，會把 hover 的加深版一起壓掉，
   這裡補一條 0-3-1 的把 hover 換回來。 */
.ivo-prose .cta-box a.wp-element-button:hover {
  background-image: linear-gradient(135deg, #E52E93 0%, #8F22D4 100%);
  box-shadow: 0 6px 26px rgba(233, 30, 140, .42);
}

/* FAQ：舊站用原生的 details / summary，不需要 JS */
.ivo-prose .faq-item {
  margin-top: 12px;
  border: 1px solid var(--ivo-line);
  border-radius: var(--ivo-radius-sm);
  background: var(--ivo-white);
}
.ivo-prose .faq-item summary {
  padding: 18px 52px 18px 22px;
  position: relative;
  font-size: var(--ivo-fs-body);
  font-weight: 700;
  line-height: 1.7;
  color: var(--ivo-title);
  cursor: pointer;
  list-style: none;
}
.ivo-prose .faq-item summary::-webkit-details-marker { display: none; }
.ivo-prose .faq-item summary::after {
  content: "";
  position: absolute;
  right: 22px;
  top: 50%;
  width: 10px;
  height: 10px;
  margin-top: -7px;
  border-right: 2px solid var(--ivo-btn);
  border-bottom: 2px solid var(--ivo-btn);
  transform: rotate(45deg);
  transition: transform .2s ease;
}
.ivo-prose .faq-item[open] summary::after { margin-top: -2px; transform: rotate(-135deg); }
.ivo-prose .faq-answer { padding: 0 22px 20px; }
.ivo-prose .faq-answer p { margin: 0; }
.ivo-prose .faq-answer p + p { margin-top: 12px; }

/* ── 作者框：文章收尾的署名卡 ─────────────────────────────────────────────
   舊版是四段平鋪的 <p>：名稱、簡介、服務、行動四塊一樣的字級、一樣的
   段距（14px），白底加一條 1px 灰框，沒有主體也沒有層級，
   客戶說的「呈現上非常沒有質感」就是這個。
   這裡不動任何一個字的文案，改成有五層階梯的署名卡：

     ① 品牌字標   104×36，頁首那一顆，讓讀者一眼認出是誰在說話
     ② 名稱       20px / 700 / --ivo-title
     ③ 簡介       16px / 1.95 / --ivo-text
     ④ 品牌標語   18px / 700 / --ivo-btn-hover，配 2px 左色條
     ⑤ 服務       14px / 1.85 / --ivo-text-warm（補充資訊，降一階）
     ⑥ 行動列     實色膠囊主按鈕 + 兩顆社群膠囊，同一列

   造型基準沿用本站既有的卡片家族，沒有另立一套：
     圓角      --ivo-radius 16px           （.ivo-pcard、.ivo-hint）
     陰影      --ivo-shadow                （.ivo-pcard）
     內距      32~36px                     （.ivo-pcard 28/30、.ivo-hint 24/32）
     名稱字級  20px                        （.ivo-prose h3，手機同樣降到 18px）
     左色條    2px 寬                       （.ivo-form-sec__note 也是 2px，
                                            但它的顏色是 --ivo-pink-lt，
                                            這裡改用 --ivo-primary 加重一階）
     膠囊按鈕  --ivo-radius-pill + --ivo-btn（.cta-box a，這裡降一階用 14px）
   沒有新增任何顏色，用到的全是既有 token。

   為什麼不用 grid 兩欄：資料庫裡的作者框實測有好幾種結構
   （有沒有 .author-box__content 包一層、簡介是一整段用 <br> 斷行
   還是被 wpautop 拆成好幾個沒有 class 的 <p>、標語有沒有自成一段）。
   grid 要求每個子元素都指定 grid-column，漏掉的就會走位。
   這裡讓所有段落留在正常流裡，字標用 ::before 疊在最前面，
   不同結構走同一組宣告都排得出同一個樣子（poster-price 那種被 wpautop
   拆散的版本，1440 實測卡片 760×458.4，只比乾淨版的 450.4 高 8px，
   那 8px 是 wpautop 多切出來的兩段段距，不是走位）。 */
.ivo-prose .author-box {
  position: relative;
  margin-top: 72px;
  padding: 32px 36px 34px;
  border: 1px solid var(--ivo-line);
  border-radius: var(--ivo-radius);
  box-shadow: var(--ivo-shadow);
  /* 兩層：左緣 4px 品牌漸層條 + 卡片白底。
     背景本來就會被 border-radius 裁掉，不需要 overflow: hidden
     （加了反而會把 :focus-visible 的 outline-offset 切掉）。
     色條只是面，不承載文字，符合「品牌漸層只鋪在沒有文字的面上」。 */
  background:
    linear-gradient(180deg, var(--ivo-pink-3a) 0%, var(--ivo-pink-3b) 100%)
      left / 4px 100% no-repeat,
    var(--ivo-white);
}
/* 品牌字標用與頁首同一顆（assets/img/logo-author.webp）。
   作者是工作室不是個人，用字標比塞一張假頭像誠實；讀者在頁首已經看過
   這顆字標，文末收尾會直接接上品牌記憶。媒體庫裡帶 logo 字樣的
   那一批全是客戶 logo，不能拿來當作者標記。

   這個檔是從 logo-header.png（480×162、64,988 bytes）縮成 208×72
   的 2x 裁切版再轉 webp，8,320 bytes，省掉 87%。
   顯示尺寸只有 104×36，原檔的解析度用不到，整張推上去是浪費。
   注意它與頁首那張「不是同一個 URL」：頁首吃的是媒體庫的
   scimgvqW5RC.webp（WordPress 自動轉檔改名過），所以共用不到快取，
   這確實是多一個請求，但 8KB 的量體可以接受。
   全站圖片本來就都是 webp（媒體庫自動轉），這裡不另外準備 png fallback。
   deploy.py 的 push_assets() 會掃 CSS 裡引用的 ../img/ 路徑一起推，
   regex 與副檔名無關，webp 會被正確帶上去。
   （這段註解刻意不寫成 u-r-l 加括號的完整形式：那支腳本的 regex
   不分辨註解與規則，寫了就會被當成真的有一張叫「…」的圖，
   每次部署都跳一行「CSS 引用了 … 但 assets/img 裡沒有這個檔」。） */
.ivo-prose .author-box::before {
  content: "";
  display: block;
  width: 104px;
  height: 36px;   /* 104 × 162 / 480 = 35.1，取 36 避開半像素 */
  margin-bottom: 20px;
  background: url("../img/logo-author.webp") no-repeat left center / 104px auto;
}
/* 名稱原本是 18px 的 --ivo-btn 粉字，跟底下的連結同色，看起來像一條連結，
   整塊都是粉的時候反而看不出哪一個才是要點的。
   改成 20px 的 --ivo-title（白底 17.21:1），粉色留給唯一的行動點。
   名稱裡的「象牙設計｜Ivory Design Studio」那個「｜」是文案本身
   （分隔中英名），不是裝飾，保留。 */
.ivo-prose .author-box .author-box__name {
  margin: 0;
  font-size: 20px;
  line-height: 1.6;
  letter-spacing: .01em;
  color: var(--ivo-title);
}
.ivo-prose .author-box .author-box__name strong { color: inherit; }
/* 沒有 class 的 <p> 一律當簡介：那是 wpautop 把「一行<br>一行」
   拆開之後產生的，7 篇文章長這樣。 */
.ivo-prose .author-box__bio,
.ivo-prose .author-box p:not([class]) {
  margin: 16px 0 0;
  font-size: var(--ivo-fs-body);
  line-height: 1.95;
  color: var(--ivo-text);
  text-wrap: pretty;
}
.ivo-prose .author-box__bio + p:not([class]),
.ivo-prose .author-box p:not([class]) + p:not([class]) { margin-top: 4px; }
/* 「設計是結果，理解是起點。」是品牌標語，不是一般的粗體。
   它在資料庫裡有兩種位置：夾在 .author-box__bio 最後一行（多數），
   或被 wpautop 拆成自己的一段 <p><strong>…</strong></p>（7 篇）。
   兩種都由這一組宣告接住，不必動資料庫。
   :not([class]) 會把 .author-box__name 那個 <p><strong> 排除掉。
   前一個 <br> 要收掉，不然標語上方會多出一整行空白。
   字色用 --ivo-btn-hover（白底 7.80:1）而不是 --ivo-btn：
   跟名稱的墨色分得開，也比主按鈕沉一階。 */
.ivo-prose .author-box__bio br:last-of-type { display: none; }
.ivo-prose .author-box__bio strong,
.ivo-prose .author-box p:not([class]) > strong:only-child {
  display: block;
  margin-top: 26px;
  padding-left: 18px;
  border-left: 2px solid var(--ivo-primary);
  font-size: var(--ivo-fs-lead);
  font-weight: 700;
  line-height: 1.7;
  letter-spacing: .02em;
  color: var(--ivo-btn-hover);
}
/* 服務範圍是補充資訊，降一階用暖灰（白底 9.48:1）。
   這裡刻意不再加一條左色條：標語已經有一條，兩條並排會互相抵消。 */
.ivo-prose .author-box__services {
  margin: 26px 0 0;
  font-size: var(--ivo-fs-sm);
  line-height: 1.85;
  color: var(--ivo-text-warm);
}
/* 行動列：主按鈕與兩顆社群連結排成同一列。
   舊站是「連結<br>｜連結<br>｜連結」三行直排，那三個全形「｜」與 <br>
   都不是文案，是舊站沒有元件可用時拿字元當項目符號畫的
   （螢幕閱讀器會把它讀成「豎線」）。結構在 functions.php 的
   ivo_author_box_cta() 輸出階段重組，連結文字與 href 原樣保留，
   資料庫不動。
   760 版心下實測：主按鈕 416.7 + 間距 16 + 社群 221.4 = 654，
   還在 686 的內容寬度裡，桌機是一列；放不下時 flex-wrap 會讓
   社群那一組整組掉到下一行（實測 768 與 691 就是這樣），不會破版。 */
.ivo-prose .author-box__cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 16px;
  margin: 28px 0 0;
  font-size: var(--ivo-fs-sm);
  line-height: 1.6;
}
/* 萬一 CSS 先上、functions.php 還沒上，至少「→」不會被拆到上一行 */
.ivo-prose .author-box__cta br { display: none; }
.ivo-prose .author-box__social { display: flex; flex-wrap: wrap; gap: 10px; }
/* 選擇器刻意寫到 (0,3,0)：.ivo-prose a 是 (0,2,0)，Blocksy 的
   [data-link] .entry-content a 是 (0,2,1)，兩個都比不過；
   線上實測 computed color 是白字、text-decoration 是 none，沒有被染回去。
   底色用實色 --ivo-btn 不用 --ivo-grad-cta：那條漸層的亮端 #FFAFDB
   配白字只有 1.69:1，只有 .ivo-btn 那種 19px/700 的大字撐得住
   （WCAG 大字門檻 3:1），這一顆是 14px，門檻 4.5:1。
   實色 #C40068 配白字 5.92:1。
   尺寸也刻意比 .cta-box 那顆文中按鈕小一階（16px/14px 內距 14/32），
   同一篇文章裡出現兩顆同文案的按鈕時，主次分得出來。 */
.ivo-prose .author-box__cta .author-box__go {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  max-width: 100%;
  padding: 12px 24px;
  border: 1px solid transparent;
  border-radius: var(--ivo-radius-pill);
  background-color: var(--ivo-btn);
  color: #FFFFFF;                 /* 5.92:1 */
  font-size: var(--ivo-fs-sm);
  font-weight: 700;
  line-height: 1.6;
  letter-spacing: .02em;
  text-align: center;
  text-decoration: none;
  transition: background-color .25s ease, box-shadow .25s ease, transform .25s ease;
}
.ivo-prose .author-box__cta .author-box__go:hover {
  background-color: var(--ivo-btn-hover);   /* 7.80:1 */
  color: #FFFFFF;
  transform: translateY(-1px);
  box-shadow: var(--ivo-grad-glow);
}
/* 社群膠囊的邊框只是造型，不是它的識別：Facebook / Instagram 是看得見的
   文字，而且用的是連結色 --ivo-btn（白底 5.92:1），
   1.4.11 由文字滿足，邊框走淺粉維持卡片家族的輕量感。
   底色維持白色：填 --ivo-pink-lt 再配 #C40068 實算只有 4.48:1，
   差 0.02 過不了 AA。 */
.ivo-prose .author-box__cta .author-box__soc {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  padding: 11px 18px;
  border: 1px solid var(--ivo-pink-lt);
  border-radius: var(--ivo-radius-pill);
  background-color: var(--ivo-white);
  color: var(--ivo-btn);
  font-size: var(--ivo-fs-sm);
  font-weight: 700;
  line-height: 1.6;
  text-decoration: none;
  transition: background-color .25s ease, border-color .25s ease, color .25s ease;
}
.ivo-prose .author-box__cta .author-box__soc:hover {
  border-color: var(--ivo-btn);
  background-color: var(--ivo-paper);
  color: var(--ivo-btn-hover);              /* paper 底 7.40:1 */
}
/* 觸控目標保險，抄 .ivo-btn::after 那一招：絕對定位的透明區塊，
   不會被畫進 ::selection 的反白背景（實測：整塊作者框選起來，
   主按鈕文字的 client rect 只有 20px 高，不是按鈕的 48.4，
   padding 沒被畫進去），也不影響版面。
   實測主按鈕 48.4px、社群 46.4px，本來就已經過 44，
   這一層是給日後調字級用的。驗收/檢查.py 第 204 行對 .author-box__cta 裡的
   連結是不豁免的（它只豁免一般行內連結），這兩個尺寸就是為了過那條檢查。 */
.ivo-prose .author-box__cta .author-box__go::after,
.ivo-prose .author-box__cta .author-box__soc::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  top: 50%;
  transform: translateY(-50%);
  height: 44px;
}
/* 保險：沒有被 functions.php 標成 __go / __soc 的連結（例如 CSS 已上、
   functions.php 還沒上的那個空窗，或日後有人新增一塊沒跑過重組的作者框），
   至少要保住 44px 的觸控目標，否則 驗收/檢查.py 第 204 行那條會直接紅
   （沒有這一段時，服務內容頁實測是 62.9×22.4，三筆紅字）。
   一樣用絕對定位的透明 ::after，不用 padding、不用 min-height。 */
.ivo-prose .author-box__cta a:not([class]) { position: relative; }
.ivo-prose .author-box__cta a:not([class])::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  top: 50%;
  transform: translateY(-50%);
  height: 44px;
}

/* 舊站的段落容器，本身不帶樣式，只要把裡面的間距接上就好 */
.ivo-prose .post-style > * + * { margin-top: 20px; }

@media (max-width: 689.98px) {
  .ivo-prose .key-stat,
  .ivo-prose .key-takeaway,
  .ivo-prose .term-definition { padding: 18px 20px; }
  .ivo-prose .ivory-perspective { padding: 24px 20px; }
  /* 作者框的手機版：字標與階層都留著，只把行動列收成上下兩排。
     主按鈕滿版（文案長，貼著文字寬會擠成一顆歪掉的膠囊），
     兩顆社群連結 flex: 1 平分一列，375 寬實測各 137×46.4、
     320 寬實測各 112.8×46.4，兩個斷點都還在 44 以上。 */
  .ivo-prose .author-box {
    margin-top: 56px;
    padding: 26px 22px 28px;
  }
  .ivo-prose .author-box::before { margin-bottom: 18px; }
  /* 這裡寫死 18px 不用 var(--ivo-fs-lead)：手機斷點把 --ivo-fs-lead
     降成 16px（第 1914 行），跟著降名稱就會與簡介同級，第一層階梯消失。
     18px 與手機版的 .ivo-prose h3 同級。 */
  .ivo-prose .author-box .author-box__name { font-size: 18px; }
  .ivo-prose .author-box__bio,
  .ivo-prose .author-box p:not([class]) { margin-top: 14px; }
  .ivo-prose .author-box__bio strong,
  .ivo-prose .author-box p:not([class]) > strong:only-child {
    margin-top: 22px;
    padding-left: 14px;
    font-size: var(--ivo-fs-body);
  }
  .ivo-prose .author-box__services { margin-top: 22px; }
  .ivo-prose .author-box__cta { gap: 12px; margin-top: 24px; }
  .ivo-prose .author-box__cta .author-box__go { width: 100%; padding-inline: 18px; }
  .ivo-prose .author-box__social { width: 100%; }
  .ivo-prose .author-box__cta .author-box__soc { flex: 1 1 0; }
  .ivo-prose .cta-box { padding: 24px 20px; }
  .ivo-prose .faq-item summary { padding: 16px 44px 16px 18px; }
  .ivo-prose .faq-answer { padding: 0 18px 18px; }
}

/* ===========================================================================
   文章單頁
   ---------------------------------------------------------------------------
   文章原本跑 Blocksy 預設版型：右邊掛一條「最新消息」側欄，內文被擠成
   990px 寬、行距 1.6、段落之間沒有間距，讀起來像後台的純文字預覽。
   這裡把文章接上服務內容頁那一套長文版型（ivo-prose，760px 置中）。

   內文是 migrate_posts.py 從舊站原樣搬過來的，不做改寫，
   所以舊站那組 class 直接在上面的規則裡認，不另外複製一份樣式。
   ======================================================================== */

/* 段落間距為什麼要特別處理：
   舊站的文章用 <section id="…"> 把每個 h2 章節包起來，
   ivo-prose > * + * 是直接子選擇器，只打得到 section 與 section 之間，
   section 裡面的 p、ul、figure 一個間距都沒有，整章文字會黏成一團。
   這是「行距完全跑掉」最主要的來源，比行距本身還嚴重。 */
.ivo-prose section > * + *,
.ivo-prose .wp-block-column > * + * { margin-top: 20px; }

/* 上面那條的權重是 0-1-1，與前面的 .ivo-prose h3 / .ivo-prose h4 打平，
   位置又在後面，所以 section 裡的小標段距被壓成 20px，
   與段落之間一模一樣，讀起來分不出「換一個小節」。
   實測（1440）：39 篇有 section 的文章，小標與上一段的距離 25px，
   5 篇沒有 section 的是宣告值 40px 換算出來的 45px，舊站是 50.19px。
   這裡把宣告值補回來，兩種文章一致（實測套用後一律 45px，375 同樣 45px）。
   h2 不用補：44 篇的 h2 全部是 section 的第一個元素子，吃不到 * + *。
   數值要與上面 .ivo-prose h3 / h4 的 margin-top 一起維護，改一邊會不同步。 */
.ivo-prose section > h3,
.ivo-prose .wp-block-column > h3 { margin-top: 40px; }
.ivo-prose section > h4,
.ivo-prose .wp-block-column > h4 { margin-top: 28px; }

/* 章節之間要比段落之間更開，否則 h2 的分隔線會貼著上一段 */
.ivo-prose section + section { margin-top: 0; }

/* 圖片。舊站的圖多半是 1890×1063 這種尺寸，
   沒有上限的話會直接撟滿 760 再被 Blocksy 的規則拉伸。 */
/* 圖的上下淨空。原本寫 margin: 0 是為了清掉瀏覽器預設的 1em 40px，
   但它的權重 0-1-1 同時壓過 .ivo-prose > * + *（0-1-0）與位置在前面的
   .ivo-prose section > * + *（同權重），圖與前一個元素之間只剩 8px 甚至 0px，
   那 8px 還是 .ivo-prose img 的 margin-block 從 figure 邊界摺疊出來的，不是刻意給的。
   實測：全站 139 張內文圖有 63 張的上緣淨空 ≤ 10px，舊站是 32px（無 section）
   與 18px（section 內）；2023project02 連續 14 張圖張張 8px，整串黏成一條。
   這裡只保留「清掉左右 40px」的原意，上緣給回 32px，對齊舊站的版塊間距。
   下緣維持 0：交給 * + * 的 20px，這樣末尾的圖不會把 margin 摺疊出 .ivo-prose，
   影響「內文末元素到上下篇導覽」那組已經調好的 64 / 48（實測套用前後都是 64）。 */
.ivo-prose figure { margin: 32px 0 0; }
.ivo-prose figure img { width: 100%; height: auto; display: block; }
/* 區塊編輯器的圖（figure.wp-block-image，全站 70 張）權重 0-2-0，
   蓋得過上面那條，不一起改的話那 70 張還是 8px。 */
.ivo-prose .wp-block-image { margin-block: 32px 0; }

/* 沒有 figure 包、直接掛在 .ivo-prose 底下的 <img>。
   .ivo-prose img 的 margin-block: 8px（0-1-1）壓過 .ivo-prose > * + *（0-1-0），
   上面兩條都打不到它。全站只有 activities-backplane 是這種寫法，12 張圖
   實測一律 8px（舊站 32 至 37px）。直接掛在 <section> 底下的 img 不用補，
   它吃得到 section > * + * 的 20px（proposal-layout-design 5 張，舊站 18px）。 */
.ivo-prose > img { margin-block: 32px 0; }
/* 圖說要貼著自己那一張圖，所以「figure 的直接子圖」不留上下外距。
   用直接子選擇器而不是 .ivo-prose figure img，是因為站上有第二種寫法：
   catalogue-graphic-design 是一個 <figure> 裡包三組 <p><img></p>
   再接一個 <figcaption>，那三張圖之間仍然需要間距；
   寫成後代選擇器會把它們一起壓成 0，三張圖黏成一張。 */
.ivo-prose figure > img { margin-block: 0; }
.ivo-prose img.alignnone { height: auto; }

/* 文章內的多欄區塊（wp-block-columns）
   ---------------------------------------------------------------------------
   舊站是區塊主題，WordPress 核心會替 columns 區塊掛上自己那段版型 CSS。
   本站的內文是整段 HTML 搬進資料庫的，沒有 wp:columns 的區塊註解，
   WordPress 解析不出這是區塊，那段核心 CSS 就不會被 enqueue
   （線上 base.css 裡 wp-block-columns 出現 0 次）。
   結果是 class 全在、樣式全沒有：欄位退回 flex-basis: auto、flex-grow: 0，
   .wp-block-columns 本身又是 flex-wrap: wrap，於是每一欄各佔滿一整列。

   1440px 實測（舊站內文寬 800、本站 760）：
     namecard         兩欄圖 384px → 760px、三欄圖 245.33px → 760px，共 11 張
     logodesignrecom  同上，共 9 張
     menudesign       兩欄圖，共 8 張
   三篇合計 28 張圖從並排變成一張一張往下疊，是這一輪落差最大的一項。

   核心原本用 min-width: 782px 當斷點，這裡改用本站的 689.98px，
   全站只准兩個斷點（make_base.py 有守門），不另外引進第三個數字。
   代價是 690～781px 之間本站維持並排、舊站已經疊起來，
   實測 690px 時兩欄各 298.5px、三欄各 191px，圖看得清楚，可以接受。

   min-width: 0 不是保險而是必要條件：flex 子項的預設 min-width 是 auto，
   欄裡那張 1920px 寬的圖會把欄撐開，整列溢出版心。 */
.ivo-prose .wp-block-columns {
  display: flex;
  flex-wrap: nowrap;
  align-items: normal;
  box-sizing: border-box;
}
.ivo-prose .wp-block-columns > .wp-block-column {
  flex-basis: 0;
  flex-grow: 1;
  min-width: 0;
  overflow-wrap: break-word;
}
/* 編輯器有手動指定欄寬的（inline style 帶 flex-basis）不要被 grow 拉回等寬 */
.ivo-prose .wp-block-columns > .wp-block-column[style*="flex-basis"] { flex-grow: 0; }
/* 欄內第一個與最後一個元素不再吃上面那組 28px，
   不然兩欄的圖會一起往下掉 28px，欄與欄之間也不齊頭。 */
.ivo-prose .wp-block-column > :first-child { margin-top: 0; }
.ivo-prose .wp-block-column > :last-child { margin-bottom: 0; }

/* 手機單欄。核心用的是 min-width: 782px，本站只准 999.98 與 689.98
   兩個斷點，所以改成在 689.98 以下才攤平，桌機版當預設。
   390px 實測：namecard 與 logodesignrecom 的每一欄都是 343.19px、
   橫向捲軸 0、圖片溢出 0。 */
@media (max-width: 689.98px) {
  .ivo-prose .wp-block-columns { flex-wrap: wrap; }
  .ivo-prose .wp-block-columns > .wp-block-column { flex-basis: 100%; flex-grow: 0; }
}

/* 表格的標題列。舊站用 <caption>，不處理會貼在表格上緣沒有間距 */
.ivo-prose caption {
  margin-bottom: 12px;
  font-size: var(--ivo-fs-sm);
  line-height: 1.7;
  color: var(--ivo-text-warm);
  text-align: left;
}

/* 特色圖片、上下篇導覽與分享列都是 Blocksy 的元件，
   內文收在 760 之後它們仍然是 1290，看起來會像掉出版心，
   特色圖尤其明顯：一張 1290 寬的大圖接著一段 760 寬的文字。 */
.single-post .ct-share-box,
.single-post .post-navigation {
  max-width: 760px;
  margin-inline: auto;
}
/* 特色圖要把兩個 :not() 一起寫上去才蓋得過。
   Blocksy 的規則是
     .ct-featured-image:not(.alignwide):not(.alignfull) { max-width: … }
   特異性 (0,3,0)，只寫 .single-post .ct-featured-image 是 (0,2,0)，
   推上去會發現圖還是滿版。這裡湊到 (0,4,0)。
   width 也要一起收，Blocksy 另外給了 width: var(--theme-block-width)。 */
.single-post .ct-featured-image:not(.alignwide):not(.alignfull) {
  width: 100%;
  max-width: 760px;
  margin-inline: auto;
}

/* 文章尾端：作者框 → 上下篇導覽 → 分享列 → 相關文章
   ---------------------------------------------------------------------------
   分享列與上下篇導覽是 Blocksy 的元件，輸出在 .entry-content 之後，
   也就是在我們的 .ivo-sec 外面（.ivo-sec 是 .entry-content 裡面那一層）。
   functions.php 用輸出緩衝把導覽搬到分享列前面，這裡只負責間距。

   改之前實測（1440px，44 篇全部一樣）：作者框底到導覽頂 373.3px，
   拆開是 64（回消息列表的 margin-top）＋46.5（按鈕高）
   ＋80（.ivo-sec 的 padding-bottom）＋50（分享列 margin-top）
   ＋82.8（分享列高）＋50（分享列與導覽的 margin 摺疊）。
   客戶反映「導覽離作者欄位太遠」講的就是這一段。

   這裡讓作者框底到導覽頂剛好 64px（手機 48），與「內文 → 作者框」
   同一個節奏。第二輪客戶回饋之後這 64 改由導覽自己的 margin-top 提供：
   .ivo-sec 在文章單頁的上下內距已經歸零，文章區與頁首頁尾之間的距離
   改由客製器的「內容區間距」（80 / 60 / 40）負責，
   詳見下面「文章區的上下間距」那一段。

   分享列那一條要連 [data-location="bottom"] 一起寫，有兩個理由：
   一是 Blocksy 的 .ct-share-box[data-location=bottom] 是 (0,2,0)，
   只寫兩個 class 會打平、靠檔案順序取勝，帶上屬性選擇器湊成 (0,3,0)
   才是穩的；二是客製器日後若打開「上方分享列」，
   那一組不該被這條 margin-top 波及。

   刻意不寫 margin-bottom：重排成功時分享列是 <article> 的最後一個元素，
   Blocksy 的 .ct-share-box[data-location=bottom]:not(:last-child)
   本來就不成立，實測 margin-bottom 已經是 0。反過來，萬一哪天
   Blocksy 改了標記、重排靜默失效，順序退回「分享列 → 導覽」，
   這時候就需要 Blocksy 那 50px 把兩塊分開；寫死 0 會讓它們黏在一起
   （實測：寫 0 是 0px，不寫是 50px）。

   相關文章是一整條粉色色帶，Blocksy 不留上緣間距，重排後分享列變成
   最後一個元素，色帶會直接貼上分享圖示。這一段間距改由
   main > div.ct-container-full 的下內距（內容區間距 80 / 60 / 40）提供，
   色帶自己的 margin-top 歸零，兩者疊起來才不會變成 144px。

   手機（max-width: 689.98px）的導覽由 theme_mod
   single_blog_post_post_nav_visibility 控制，主機實查 desktop、tablet、
   mobile 三個都是 true，導覽在手機看得到。若日後又被關掉，
   手機尾端就是「作者框 → 分享列」，間距 40。 */
.single-post .ivo-page > .ivo-sec { padding-block: 0; }
.single-post .ct-featured-image { margin-bottom: 64px; }
.single-post .post-navigation { margin-top: 64px; }
.single-post .ct-share-box[data-location="bottom"] { margin-top: 48px; }
.single-post .ct-related-posts-container { margin-top: 0; }

/* 文章區的上下間距：交給 Blocksy 的客製器
   ---------------------------------------------------------------------------
   客戶回饋（第二輪）：「不能就針對文章區該有的間距呈現嗎？就是我們自己
   設定的內容區塊的間距，也是 80px、60px、40px 呀。」

   那組 80 / 60 / 40 是客製器的 contentAreaSpacing，主機實查值就是
   desktop 80px、tablet 60px、mobile 40px。Blocksy 把它編譯成 :root 的
   --theme-content-vertical-spacing（輸出在 uploads/blocksy/css/global.css），
   再由主題 static/bundle/main.min.css 的
   [data-vertical-spacing*="top"] 與 [data-vertical-spacing*="bottom"]
   兩條規則變成 main > div.ct-container-full 的上下內距。

   第一輪的腳本 11 把 44 篇文章的 content_area_spacing 寫成 none，
   inc/components/vertical-spacing.php 讀到 none 就完全不輸出
   data-vertical-spacing，CSS 選不到，那條路整個斷掉；當時的理由是
   我們的 .ivo-sec 自帶 80px，兩層會疊成 160。第二輪改成
   「只留一層，而且留客製器控制得到的那一層」：腳本 22 把文章的三個
   post meta 清掉回到繼承，這裡把 .ivo-sec 在文章單頁的上下內距歸零。

   歸零只發生在 .single-post。頁面（page）的版面是我們自己的 blocks，
   每個區段本來就帶 --ivo-sec-y，那邊真的會變兩倍，
   所以 page 的 post meta 維持 none，兩邊互不影響。

   .ivo-sec 的上內距原本擔兩件事：文章區的上緣間距、以及特色圖到內文的
   距離。前者移交客製器，後者留在版面裡，改用 .ct-featured-image 的下外距
   64（手機 48）接手。要注意 Blocksy 給 .ct-featured-image 本身就有
   下內距（實測桌機 50px、手機 30px），所以「照片底緣 → 內文」眼睛看到的
   是桌機 50 + 64 = 114、手機 30 + 48 = 78，改前是桌機 50 + 80 = 130、
   手機 30 + 56 = 86，兩邊都略為收緊。
   站上 42 篇有特色圖，另外 2 篇沒有（/2784/、/2630/），
   那兩篇的內文直接吃客製器的 80 / 60 / 40。

   實測（CDP 注入覆寫，未部署，沒有用 !important）：
   44 篇 × 1440 與 375 共 88 次，main > div.ct-container-full 的上下內距
   一律 80 / 40，特色圖底到內文首元素 64 / 48（42 篇，另 2 篇無圖），
   分享列底到相關文章色帶頂 80 / 40，scrollWidth − clientWidth 一律 0。
   內文末元素到導覽頂 40 篇是 64 / 48，另外 4 篇是 84 / 68 或
   111.19 / 95.19，那 4 篇改前改後完全相同，來自內文最後一個元素
   自己的 margin-bottom，不是這一輪造成的。
   再測 1000 / 999 / 768 / 520 / 320 五個寬度：1000 仍是 80、999 起 60、
   520 與 320 是 40，斷點是 Blocksy 的 999.98px 與 689.98px，
   所以 1000px 仍算桌機。

   ⚠ 內距歸零之後 .ivo-sec 不再擋 margin 摺疊。.ivo-prose 第一個元素的
     margin-top 會一路摺疊出去，實測 44 篇目前全部是 0，所以沒有影響；
     日後若有人在文章開頭放帶 margin-top 的元素，實測行為是：
     無特色圖時「容器內距 ＋ 那個 margin」（相加，不是取大值），
     有特色圖時與圖的下外距取大值。 */

/* 相關文章的標題連結補觸控目標。
   這是 Blocksy 元件的既有缺陷，不是這一輪改出來的：
   h4.related-entry-title 的 <a> 是 inline，實測 380×24，
   驗收/檢查.py 第 4 項在每一篇文章都會報一筆「380x24」未達標。
   移除「回消息列表」之後文章尾端只剩導覽與這一區，順手一起補掉。

   撐高度用絕對定位的透明 ::after，不用 padding 也不用 min-height：
   那兩種都會被畫進 ::selection 的反白背景，本案踩過三次
   （頁尾電話與 email、.ivo-btn、目錄連結）。
   寫法與 .ivo-btn::after、.ivo-prose .toc-container a::after 同一套，
   驗收腳本的觸控目標公式認得這種「置中 + max()」的形式。
   用 max(100%, 44px) 而不是固定 44px：標題兩行時是 48px，
   固定值反而會把可點範圍裁小。
   ::after 只在 <a> 自己的行盒範圍內橫向展開（left/right 都是 0），
   相鄰兩張卡之間有 grid gap，不會互相蓋到。 */
.single-post .related-entry-title a {
  position: relative;
  display: inline-block;
}
.single-post .related-entry-title a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  height: max(100%, 44px);
}

@media (max-width: 689.98px) {
  .ivo-prose caption { font-size: var(--ivo-fs-xs); }
  .single-post .ct-featured-image { margin-bottom: 48px; }
  .single-post .post-navigation { margin-top: 48px; }
  .single-post .ct-share-box[data-location="bottom"] { margin-top: 40px; }
}

/* ===== 手機斷行工具（客戶回饋：手機版標題要自然斷行）=====
   為什麼要做這三個類別：中文每個字之間都是合法斷點，瀏覽器的貪婪換行
   會把一行塞到再多一個字就溢出為止，於是手機常出現兩種醜況：
   末行只剩一兩個字（孤字），以及一個詞被從中間剖開。
   桌機與平板的版心夠寬，斷行本來就對，所以三個類別全部只在手機斷點生效，
   690px 以上完全沒有作用，桌機與平板一行都不會跑掉。

   .ivo-nowrap-m  手機時把包住的這一段變成不可拆的詞組，換行只會落在詞組之間。
                  選它而不用硬塞 br，是因為它會自適應：寬度夠時兩個詞組仍然
                  合成一行，大螢幕手機不會被多切一刀。
                  包進來的字數要自己確認 320px（版心 281.6px）放得下，
                  否則 nowrap 會把版心撐破，而且不會有任何警告。
                  實測上限：24px 標題最多 11 個全形字（約 267px），
                  16px 內文最多 17 個全形字（約 272px），
                  作品卡標題（16px、320px 時卡片寬 281.59px）最多 14 個全形字
                  （實測 226.25px）。
   .ivo-hide-m    手機時隱藏的行內內容。目前只用在客戶 logo 牆標題中間那個
                  全形空白：CSS 只折疊 space、tab、newline，U+3000 不在其中，
                  斷行後它會掛在第一行行尾，置中標題實測往左偏 12.7px
                  （375px 時第一行左右留白 63.23 對 37.80），所以手機要隱藏。
   .ivo-only-m    只在手機出現的 br。桌機預設值 display: none 一定要寫在
                  媒體查詢外面，否則 br 在桌機會照常換行。
                  它會無條件切一刀，在 520 到 689px 這段本來放得下一行的
                  手機上也會被切開，所以能用 .ivo-nowrap-m 解決的就不要用它。 */
.ivo-only-m { display: none; }

@media (max-width: 689.98px) {
  .ivo-nowrap-m { white-space: nowrap; }
  .ivo-hide-m   { display: none; }
  .ivo-only-m   { display: inline; }
}


/* ===== 2. WordPress 與 Blocksy 適配（來源：ivory-wp/共用/wp-adapt.css）===== */

/* ==========================================================================
   WordPress 與 Blocksy 適配層
   --------------------------------------------------------------------------
   版面樣式的單一來源是 ivory-homepage/assets/css/style.css。
   這一份只放「上稿到 WordPress 之後才需要的」修正，
   由 腳本/make_base.py 把兩者合併成 共用/base.css 再 deploy。

   不要把版面樣式寫進這裡，改版時會不同步。
   ========================================================================== */

/* ── 1. 讓區段突破 WordPress 的內容寬度限制 ──────────────────────────
   WordPress 的區塊佈局會給 .entry-content 掛上 is-layout-constrained，
   規則是「直接子元素套 max-width: 內容寬度並置中」。
   我們的 <section> 是直接子元素，於是整個區段連同背景色
   都被切成 1290px，淺粉色區塊左右兩側會露出白邊，
   看起來就像顏色斷在奇怪的地方。

   正確的分工是：
     section  負責背景，要 100% 全寬
     .ivo-wrap 負責內容寬度，維持 1290px 置中
   所以這裡把 section 的 max-width 解開，內容寬度仍由 .ivo-wrap 控制。 */
.entry-content > .ivo-sec,
.entry-content > .ivo-modal,
.is-layout-constrained > .ivo-sec,
.is-layout-constrained > .ivo-modal {
  max-width: none;
  width: 100%;
  margin-inline: 0;
}

/* Blocksy 的文章內容容器本身也可能有左右內距，
   會讓滿版背景兩側各缺一塊。 */
.ivo-page .entry-content {
  padding-inline: 0;
}

/* 自訂 HTML 區塊外層偶爾會多包一層 wp-block-html，一併解開 */
.entry-content > .wp-block-html {
  max-width: none;
  width: 100%;
}

/* ── 2. 按鈕文字色 ─────────────────────────────────────────────────
   Blocksy 依 body 的 data-link 屬性給 .entry-content 內的連結上色，
   選擇器是 [data-link="type-3"] .entry-content a 這種形式，
   特異性 (0,2,1) 高過我們的 .ivo-btn--solid (0,1,0)，
   結果主按鈕變成深粉底配深灰字，對比只剩 1.4:1。
   這裡用同等以上的特異性把文字色搶回來。 */
[data-link] .entry-content .ivo-btn--solid,
[data-link] .entry-content .ivo-btn--solid:hover,
[data-link] .entry-content .ivo-btn--solid:focus,
[data-link] .ivo-bfooter .ivo-btn--solid {
  color: #FFFFFF;
}
[data-link] .entry-content .ivo-btn--ghost,
[data-link] .entry-content .ivo-btn--ghost:focus {
  color: var(--ivo-btn);
}
[data-link] .entry-content .ivo-btn--ghost:hover {
  color: #FFFFFF;
}
/* MORE 連結與卡片內的連結同樣會被 data-link 的規則吃到 */
[data-link] .entry-content .ivo-more,
[data-link] .entry-content .ivo-more:hover {
  color: var(--ivo-btn);
}

/* Blocksy 會給 entry-content 的連結加底線或 hover 底線，
   我們的按鈕與 MORE 自己處理視覺回饋，不需要。 */
.entry-content .ivo-btn,
.entry-content .ivo-more,
.entry-content .ivo-faq__q {
  text-decoration: none;
}

/* ── 3. 區塊編輯器的 block-gap ──────────────────────────────────────
   WordPress 會給 .entry-content 的子元素套 --wp--style--block-gap
   的上下外距（預設 24px）。

   先前只處理了相鄰區段的 margin-top，漏了「最後一個區段的
   margin-bottom」，結果每一頁的最後一段與頁尾之間都多出 24px，
   而且因為兩邊底色不同，那條白色縫隙特別明顯。
   這裡把區段的上下外距一次歸零，間距全部交給 padding 控制。 */
.entry-content > .ivo-sec,
.entry-content > .ivo-modal,
.is-layout-constrained > .ivo-sec {
  margin-block: 0;
}

/* ── 4. 頁尾主體｜Before Footer 的 Content Block ────────────────────
   掛在 blocksy:footer:before。底下那條粉紅色帶不在這裡，
   那是 Blocksy 頁尾建構器 bottom-row 的 copyright 元件，
   客戶可以自己在客製器改社群連結。 */
.ivo-bfooter { background: var(--ivo-pink-lt); padding-block: 56px 44px; }
.ivo-bfooter__inner {
  display: grid;
  grid-template-columns: 150px 1fr 1fr 1.3fr;
  gap: 40px;
  align-items: start;
}
.ivo-bfooter__logo img { width: 62px; height: auto; }
.ivo-bfooter__brand-en { font-size: var(--ivo-fs-sm); font-weight: 500; color: var(--ivo-title); letter-spacing: .06em; }
.ivo-bfooter__brand-tw { margin-top: 4px; font-size: var(--ivo-fs-sm); color: var(--ivo-title); }
.ivo-bfooter__copy { margin-top: 22px; font-size: var(--ivo-fs-xs); line-height: 1.7; color: var(--ivo-text-warm); }

.ivo-bfooter__head {
  font-size: var(--ivo-fs-sm);
  font-weight: 500;
  letter-spacing: .08em;
  color: var(--ivo-title);
}
.ivo-bfooter__list { margin-top: 18px; display: grid; gap: 16px; list-style: none; padding: 0; }
.ivo-bfooter__list li { font-size: var(--ivo-fs-sm); color: var(--ivo-text); }
/* 觸控目標用「絕對定位的 ::after」撐，不要動連結自己的盒子。
   先前寫 min-height: 44px + inline-flex，連結變成 44px 高的行內盒，
   反白時 ::selection 的背景就是整個 44px，電話與 email 被框出
   一塊比旁邊「TEL　電話｜」高一倍的色塊，那就是客戶說的莫名其妙的高度。
   （padding-block 也一樣會被畫進反白背景，換湯不換藥。）
   改成純 inline 之後背景只包住文字本身，偽元素再把可點區域補回 44px：
   20.8 + 12 + 12 = 44.8px。偽元素不承載文字，不影響行高也不影響反白。 */
.ivo-bfooter__list a {
  position: relative;
  color: inherit;
  text-decoration: none;
}
.ivo-bfooter__list a::after {
  content: "";
  position: absolute;
  inset: -12px -6px;
}
.ivo-bfooter__list a:hover { color: var(--ivo-btn); }
.ivo-bfooter__note { margin-top: 14px; font-size: var(--ivo-fs-xs); line-height: 1.85; color: var(--ivo-text); }

/* ── 5. Blocksy 頁尾的 copyright 色帶 ──────────────────────────────── */
.ct-footer [data-row="bottom"] {
  background: linear-gradient(90deg, var(--ivo-pink-3) 0%, var(--ivo-deep) 50%, var(--ivo-pink-3) 100%);
}
.ct-footer [data-row="bottom"],
.ct-footer [data-row="bottom"] a,
.ct-footer [data-row="bottom"] strong { color: #FFFFFF; }
/* 觸控目標同樣用 ::after 撐，理由見上面頁尾清單那一段。
   這裡踩過兩次：
     第一版 min-height: 44px + inline-flex + align-items: center，
     連結文字在 44px 的盒子裡置中，但左右的純文字是 baseline 對齊，
     兩套對齊規則對不上，「快找整合顧問」比「本網站由」高了幾個 px。
     第二版改 inline-block + padding-block: 12px，baseline 對齊了，
     但 padding 會被畫進 ::selection 的背景，反白時每個連結
     各自框出一塊比旁邊純文字高的色塊，高度還參差不齊。
   純 inline + 偽元素兩個問題一起解：baseline 自然對齊，
   反白背景只包文字，可點區域仍是 20.8 + 24 = 44.8px。
   左右只擴 6px，相鄰連結之間隔著「｜」還有 20px 以上，不會互相蓋到。 */
.ct-footer [data-row="bottom"] a {
  position: relative;
  text-decoration: none;
}
.ct-footer [data-row="bottom"] a::after {
  content: "";
  position: absolute;
  /* 上下 13 不是 12：這一列的文字高度實測是 19px，
     擴 12 只到 43，差 1px 過不了 44 的門檻。 */
  inset: -13px -6px;
}
.ct-footer [data-row="bottom"] a:hover { text-decoration: underline; }

/* ── 6. 頁首 logo 的觸控目標 ────────────────────────────────────────
   Blocksy 給 .site-logo-container 的高度是 --logo-max-height（38px），
   連結本身不到 44。改成 content-box 再加上下各 3px 內距，
   連結總高變成 44，img 走 height: inherit 仍然是 38px，視覺不變。
   （用 ::after 補點擊區無效：偽元素不會改變元素自己的
   getBoundingClientRect，驗收腳本量到的還是 38。） */
.ct-header [data-id="logo"] .site-logo-container {
  box-sizing: content-box;
  padding-block: 3px;
}

/* ── 5b. 頁尾色帶的三段式排列 ──────────────────────────────────────
   桌機是一整行（品牌｜社群三個｜維護 credit），
   手機一行擠不下，斷成三行並置中。
   斷行不能用 <br>：桌機也會跟著斷。改成三個 part 加兩個分隔符，
   手機把分隔符隱藏、part 改 block，桌機維持 inline 就是原本那一行。
   內容在 Blocksy 客製器的 copyright 欄位（腳本/14_頁尾色帶斷行.php）。 */
.ct-footer [data-row="bottom"] .ivo-credit__part { display: inline; }

@media (max-width: 999.98px) {
  .ivo-bfooter__inner { grid-template-columns: 1fr 1fr; gap: 32px; }
}
@media (max-width: 689.98px) {
  .ivo-bfooter { padding-block: 44px 36px; }
  .ivo-bfooter__inner { grid-template-columns: 1fr; gap: 28px; }

  /* 手機版整個頁尾資訊區水平置中。
     logo 是 div 裡的 inline img，text-align 就能置中，
     清單是 grid，靠繼承下來的 text-align 置中每一列。 */
  .ivo-bfooter { text-align: center; }
  .ivo-bfooter__inner { justify-items: center; }
  .ivo-bfooter__list { width: 100%; }

  /* 色帶斷成三行。
     行距 18 是量出來的，不是估的：同一個頁尾上方的聯絡清單
     .ivo-bfooter__list（TEL／ADD／TIME）row-gap 16px、行盒 23.8、
     字高 20，兩行文字之間的實際留白是 19.8px。
     credit 這一列字高 19、行盒 20.8，margin-top 18 換算出來的實際留白
     同樣是 19.8px（18 加上行盒與字高的差 1.8），同一個頁尾的兩塊
     堆疊文字節奏就對齊了。上一版 26px 的實際留白是 27.8px，
     比上面那塊鬆四成，而且大於自己的行盒 20.8，三行會被讀成三個
     獨立區塊，客戶說的「間距太大」就是這個。

     選擇器要用 ~ 不能用 +：兩個 part 中間隔著一個 .ivo-credit__sep，
     它雖然 display: none，但還在 DOM 裡，+（緊鄰的下一個兄弟）不會匹配。
     第一個 part 前面沒有別的 part，~ 一樣不會選到它。
     要單獨選第三行就再串一次 ~，不要用 :nth-of-type(2)：
     part 與 sep 都是 span，nth-of-type 只看標籤不看 class，
     會把 sep 一起數進去，選到的是分隔符不是第二個 part。 */
  .ct-footer [data-row="bottom"] .ivo-credit__sep { display: none; }
  .ct-footer [data-row="bottom"] .ivo-credit__part { display: block; }
  .ct-footer [data-row="bottom"] .ivo-credit__part ~ .ivo-credit__part { margin-top: 18px; }

  /* 行距收窄之後，可點區一定要同時改成上下不對稱，兩件事不能拆開做。
     三行的列距是行盒 20.8 加 margin 18 等於 38.8px，而每個可點區要
     45px，任何相鄰的兩列連結都會重疊 6.2px；疊到的那一段由後畫的
     第三行接管（「快找整合顧問」x 146.9 至 228.1 正好壓在 Instagram
     x 159.8 至 225.2 的正下方），實測 Instagram 的可點高會從 45 掉到
     38，而且 驗收/檢查.py 是讀 ::after 的 inset 反推、看不到重疊，
     驗收還是綠的。

     這裡靠兩個現成的空間把 45px 鋪開：第一行「Ivory Design 象牙設計」
     沒有連結，第二行可以往上借；第三行下方還有色帶的內距可以往下借。
     第二行往上 17、往下 9，第三行往上 10、往下 16，
     兩者各自都是 19 加 26 等於 45px，交界處 9 加 10 等於 19，
     小於列距留白 19.8，剛好貼齊不重疊。
     往下的 16 是跟 .ct-container 的 padding-block 借的（Blocksy 給的
     16／16，不是我們寫的），可點區下緣離色帶底還有 0.9px。

     三件事會讓這組數字失效，之後動到要一起重算：
       1. 客製器的 copyright 文案若讓第一行也出現連結，
          第一行與第二行就會重疊 6.2px（18px 的列距只夠兩列連結）。
       2. Blocksy 的 .ct-container padding-block 不再是 16。
       3. 上面那個 margin-top 再被調整。
     另外記一筆既有問題（與本段無關，改前就有）：視窗 320px 時
     Blocksy 的回頂按鈕會壓到 Threads 的右下角，實測可完全點擊的
     最大矩形改前 65×35、改後 65×31，兩者都已經不足 44 高。

     一樣不能改用 padding 或 min-height，那會被畫進 ::selection 的
     反白背景，理由見上面第 5 段。 */
  .ct-footer [data-row="bottom"] .ivo-credit__part a::after { inset: -17px -6px -9px; }
  .ct-footer [data-row="bottom"] .ivo-credit__part ~ .ivo-credit__part ~ .ivo-credit__part a::after { inset: -10px -6px -16px; }
}

/* ── 7. figcaption 的對齊 ───────────────────────────────────────────
   Blocksy 用 .entry-content figcaption（特異性 0,1,1）把圖說置中，
   蓋過我們 reset 的 .ivo-page :where(figcaption)（0,1,0）。
   評論卡的署名用的是 figcaption，不處理的話會飄到卡片中間。 */
.entry-content .ivo-vcard__who,
.entry-content .ivo-hcard__who,
.entry-content figure figcaption {
  text-align: left;
}

/* ── 8. 文章單頁的 Blocksy 元件 ──────────────────────────────────────
   文章接上本站長文版型之後才第一次做驗收，Blocksy 自己那幾個元件
   （hero 的分類連結、分享列）的觸控目標都不到 44。 */

/* hero 的分類連結。文字高只有 17px，一樣用 ::after 擴，
   不用 padding，免得反白時多一塊色塊（頁尾那個坑）。
   左右只擴 4px：兩個分類之間只隔一個頓號，擴太多會互相蓋到。 */
.single-post .entry-meta a {
  position: relative;
  text-decoration: none;
}
.single-post .entry-meta a::after {
  content: "";
  position: absolute;
  inset: -14px -4px;
}

/* 分享列的圖示按鈕實測 39×39，本身就是方塊不是行內文字，
   直接把尺寸撐到 44 就好，不會影響反白。 */
.single-post .ct-share-box [class*="ct-"] a,
.single-post .ct-share-box a {
  min-width: 44px;
  min-height: 44px;
}

/* ── 9. 聯絡頁：視窗比內容高時，表單區下方那片白 ────────────────────
   Blocksy 的 sticky footer 是這樣搭的：
     #main-container { min-height: 100vh; display: flex; flex-direction: column }
     main.site-main  { flex-grow: 1 }
   內容不足一屏時，多出來的高度全部堆進 main，用意是把頁尾壓到視窗底部，
   不讓它浮在半空、下面露出 body 的白底。一般頁面內容都夠長，看不出來。

   聯絡頁只有一個表單區段，整頁 1040px。視窗一超過這個高度，
   .ivo-form-sec 的粉色漸層在 742px 就結束，接著是 main 撐出來的白塊，
   一路白到頁尾。實測 1080 高的視窗空 40px、1440 高空 400px、
   1600 高空 560px，螢幕越大越明顯（客戶回報的就是這個）。

   不能改用 #main-container { min-height: 0 } 解：那等於關掉 sticky footer，
   頁尾會跟著內容上移，視窗底部反而露出更大一塊 body 白底。

   作法是把 main 多出來的高度沿著 container → article → entry-content
   一路往下傳，交給 .ivo-form-sec 自己吃掉，並讓它把內容垂直置中。
   漸層因此連續到頁尾，留白也均分到表單上下兩側，看起來是刻意的留白。

   只掛 page-id-31：首頁也有一個 .ivo-form-sec，但首頁內容 7367px
   遠超過任何視窗高，沒有多餘空間可分，掛上去只是多一層無作用的 flex。
   實測套用前後首頁的 main 高、表單段位置、版心寬完全相同。

   .ivo-modal 是 position: fixed，不在 flex flow 裡，不受影響。

   【2026-09-13 第一版做壞了，這裡記一下】
   第一版只寫了 display:flex 與 flex:1 1 auto，結果表單區的粉色漸層
   從滿版縮成 1290px，左右各吐出一塊白邊（1920 視窗各 315px、
   2560 視窗各 635px），手機版也一樣縮（375 視窗縮成 330px）。

   兇手是 Blocksy 的這一條：

       [class*=ct-container] { margin-inline: auto; }

   在 block layout 下，auto 的左右 margin 會被算成 0，元素照常撐滿父層；
   一旦父層變成 flex container，auto margin 的意義就完全不同了：
   它會吸收掉所有剩餘空間，把 flex item 壓縮到自己的 max-content 寬度
   （這裡是 .ivo-wrap 的 min(90vw, 1290px)）再置中。
   所以整條鏈上每一層都必須把 margin-inline 明確歸零並要求撐滿，
   只寫 align-self: stretch 沒有用，auto margin 的優先權比它高。

   驗收時要量的是「表單區段自己的寬度」，不是 .ivo-wrap 的寬度。
   第一版的驗證腳本只量 .ivo-wrap（前後都是 1290，看起來沒事），
   整片白邊就這樣被漏掉了。 */
.page-id-31 #main.site-main,
.page-id-31 #main.site-main > .ct-container-full,
.page-id-31 #main.site-main > .ct-container-full > article,
.page-id-31 #main.site-main > .ct-container-full > article > .entry-content {
  display: flex;
  flex-direction: column;
}
.page-id-31 #main.site-main > .ct-container-full,
.page-id-31 #main.site-main > .ct-container-full > article,
.page-id-31 #main.site-main > .ct-container-full > article > .entry-content,
.page-id-31 #main.site-main .ivo-form-sec {
  flex: 1 1 auto;
  margin-inline: 0;
  width: 100%;
  max-width: none;
}
.page-id-31 #main.site-main .ivo-form-sec {
  display: flex;
  flex-direction: column;
  justify-content: center;
}