
/* ---- assets/tokens/brand-raw.css ---- */
/* Layer 1: RAW BRAND TOKENS
 * 私有：僅 yufong-site-kit 內部使用，view 禁止直接引用。
 * Lint rule 14（raw token leak）會偵測 view CSS 中的 var(--raw-*) 並 fail。
 */
:root {
  /* Teal scale (主色) */
  --raw-teal-50:   #e7eef1;
  --raw-teal-200:  #95b6c1;
  --raw-teal-25:   #eaf6fd;   /* 表格列 hover 的淡藍（交付物 --yr-blue-0） */
  --raw-teal-400:  #1a6e83;
  --raw-teal-500:  #0b4f5c;
  --raw-teal-600:  #073b45;   /* P2 分組列，比 teal-500 深一階（MAINLINES 第 14 列） */
  --raw-teal-700:  #073544;

  /* 2026-09-19 第 55 步：bid-analysis 原本寫死在自己那份的藍階，收進來共用。
   * 編號照亮度排（Rec.709 luma）：100 最亮、550 比 500 再暗一階。
   * 值一個位元都沒改，所以畫面不變——改的只是「寫在哪裡」。 */
  --raw-teal-100:  #e0eff3;   /* 淺藍區塊底（交付物 --brand-bg-soft） */
  --raw-teal-300:  #2c96b1;   /* 亮藍：按下狀態底、分類色邊條（交付物 --yr-blue-1） */
  --raw-teal-350:  #1c6f87;   /* 中階藍字（交付物 --yr-blue-2） */
  --raw-teal-450:  #0d6275;   /* 主色 hover（交付物 --brand-primary-hover） */

  /* Gold scale (輔色) */
  --raw-gold-50:   #f6f0e3;
  --raw-gold-200:  #d4bc9a;
  --raw-gold-400:  #b49a64;
  --raw-gold-600:  #8c6d4f;
  /* 2026-09-19 第 55 步：淺金底上的小字要再深一階才過 AA
   * （--raw-gold-600 疊在淺金底上只有 4.19）。交付物 --yr-gold-aa-deep。 */
  --raw-gold-700:  #7d603f;

  /* Slate (中性偏藍深灰，導覽預設字／表格一般欄，7.73:1 對白底) */
  --raw-slate-600: #41565e;

  /* Gray scale */
  --raw-gray-50:   #fafbfc;
  --raw-gray-100:  #f7f9fa;
  --raw-gray-200:  #e3e8ea;
  --raw-gray-300:  #c8d3d7;   /* 強調橫線（交付物 --ui-line-strong） */
  /* 🔴 2026-09-16 估價師裁示：只把灰字調深（「改 KPI 標籤、註解等灰字、
   * 三欄卡淡藍底上的小標籤就好」）。#6b7b82 對白底 4.39、對淡藍底 #e7eef1 只有 3.74，
   * 低於 12px 小字的 AA 門檻 4.5。新值 #5e6c73 對這五種底色分別是
   * 5.40／5.21／5.11／4.75／4.60，最差的那個留 0.1 餘裕（剛好 4.50 會踩到浮點邊界）。
   * 同一輪比稿的橘 --raw-state-warn、金 --raw-gold-*、磚橘 --raw-clay-500
   * 估價師**沒有**要改，維持原值。 */
  /* 2026-09-19 第 55 步：比 gray-500 亮一階的次要文字色，四種淺底同時過 4.5
   * （白 4.925／card-alt 4.754／gray-100 4.664／金 10% 疊白 4.529）。
   * 交付物 --ui-text-muted。 */
  --raw-gray-450:  #63737a;
  --raw-gray-500:  #5e6c73;
  --raw-gray-900:  #1a2a2f;
  --raw-white:     #ffffff;

  /* Clay (P1 一級強調，MAINLINES 第 10 列磚橘) */
  --raw-clay-500:  #b8622a;

  /* State colors */
  --raw-state-good: #1f5f3a;
  --raw-state-warn: #d18b3c;
  --raw-state-bad:  #c0392b;
  /* 2026-09-19 第 55 步：橘色當**字色**用的加深版（--raw-state-warn 對白底只有 2.81）。
   * 圖釘／邊框那類非文字用途仍用 --raw-state-warn。交付物 --ui-warn-aa。 */
  --raw-state-warn-deep: #9a5a18;
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/tokens/tokens.css ---- */
/* Layer 2: SEMANTIC TOKENS
 * 所有 view / 元件 CSS 只能用這層。Lint rule 14 擋 raw token 洩漏。
 */
:root {
  /* Surface */
  --surface-page:         var(--raw-gray-50);
  --surface-section-deep: var(--raw-teal-500);
  --surface-section-soft: var(--raw-gray-50);
  --surface-card:         var(--raw-white);
  --surface-card-alt:     var(--raw-gray-50);   /* KPI 區塊底（交付物 --ui-card-alt） */
  /* 2026-09-19 第 55 步（估價師裁示「以規劃樣板頁為主統一」）。
   * 以下五個是 bid-analysis 原本寫死在自己那份的底色，收進來共用。 */
  --surface-page-alt:     var(--raw-gray-100);  /* 頁面底，比 --surface-page 冷一階（交付物 --ui-bg） */
  --surface-brand-soft:   var(--raw-teal-100);  /* 淺藍區塊底（交付物 --brand-bg-soft） */
  --surface-link-bright:  var(--raw-teal-300);  /* 亮藍：按下狀態底、分類色邊條（交付物 --yr-blue-1） */
  --surface-accent-soft:  rgba(180, 154, 100, 0.10);   /* 金色 10% 淺底：pill／chip（交付物 --yr-gold-soft） */
  --surface-warn-soft:    rgba(209, 139, 60, 0.12);    /* 警示淺底（交付物 --ui-warn-soft） */
  --surface-bad-soft:     rgba(192, 57, 43, 0.12);     /* 錯誤淺底（交付物 --ui-bad-soft） */
  --surface-row-hover:    var(--raw-teal-25);   /* 表格列 hover 淺藍 (ML-HTML 第 7 列) */
  --surface-group-head:   var(--raw-teal-600);  /* P2 分組列，比欄名列再深一階 (第 14 列) */
  --surface-overlay:      rgba(11, 79, 92, 0.94);
  --surface-glass:        rgba(255, 255, 255, 0.72);   /* 毛玻璃頁首半透明白底 (ML-HTML 第 5 列) */
  /* 地圖首屏的色罩（ML-HTML 第 102 列，估價師 2026-09-18 裁示 A「色罩加深到 75%」）。
     🔴 **0.75 是算出來的不是配的**：白字要在電子地圖最亮的那 1% 畫素上過
     15px 小字的 4.5，就要濃到這個程度。調低它等於讓標題在亮處讀不到，
     而且畫面上不會有人發現。要動先重跑第 22 步的 measure_hero_contrast.py。
     ⚠️ 代價：色罩越濃地圖紋理被壓得越平，正射航照的細節指標 26 → 7。 */
  --surface-map-tint:     rgba(11, 79, 92, 0.75);
  --surface-track:        var(--raw-teal-50);          /* 刻度條底槽 (第 17b／17d 列) */
  /* 表格外框：值的權威源＝smart-valuation static/css/lvr-search-building.css:1686
     的 .lvr-comp-table-wrap（第 7 列註明），半透明是原檔就有的，不是這裡新配的。 */
  --surface-table-wrap:   rgba(255, 255, 255, 0.94);

  /* Text */
  --text-primary:    var(--raw-gray-900);
  --text-on-deep:    var(--raw-white);
  --text-accent:     var(--raw-gold-400);
  --text-accent-aa:  var(--raw-gold-600);
  --text-muted:      var(--raw-gray-500);
  --text-on-deep-muted: var(--raw-gold-200);
  --text-secondary:  var(--raw-slate-600);   /* 導覽預設字／表格一般欄 (ML-HTML 第 13、23 列) */
  --text-link-strong: var(--raw-teal-500);   /* 導覽 hover／目前所在／按下 的深藍字 */
  --text-link-mid:   var(--raw-teal-400);    /* 中階藍字：KPI 膠囊、次級強調 */
  /* 2026-09-19 第 55 步：以下四個是 bid-analysis 原本寫死的字色，收進來共用。 */
  --text-link-soft:  var(--raw-teal-350);    /* 比 --text-link-mid 再亮一階的藍字（交付物 --yr-blue-2） */
  --text-muted-aa:   var(--raw-gray-450);    /* 次要文字，四種淺底同時過 AA（交付物 --ui-text-muted） */
  --text-accent-aa-deep: var(--raw-gold-700);  /* 淺金底上的小字（交付物 --yr-gold-aa-deep） */
  --text-warn-aa:    var(--raw-state-warn-deep);  /* 橘色當字色用的加深版（交付物 --ui-warn-aa） */
  /* P1 重點欄三階強調色 (ML-HTML 第 10 列)。一般欄走 --text-secondary。
   * ⚠️ 一級與二級的 Rec.709 灰階只差 1.2，黑白列印靠字重分階（第 12 列），
   * 不要以為顏色夠用。 */
  --text-emph-1:     var(--raw-clay-500);    /* 一級 磚橘 */
  --text-emph-2:     var(--raw-gold-600);    /* 二級 深金 */
  --text-emph-3:     var(--raw-teal-500);    /* 三級 深藍 */

  /* Action / Interactive */
  --action-primary-bg: var(--raw-teal-500);
  --action-primary-bg-hover: var(--raw-teal-450);  /* 2026-09-19 第 55 步（交付物 --brand-primary-hover） */
  --action-primary-fg: var(--raw-white);
  --action-accent-bg:  var(--raw-gold-400);
  --action-accent-fg:  var(--raw-gray-900);

  /* State */
  --state-good: var(--raw-state-good);
  --state-warn: var(--raw-state-warn);
  --state-bad:  var(--raw-state-bad);

  /* Lines / borders */
  --line-subtle: var(--raw-gray-200);
  --line-strong: var(--raw-gray-300);
  --line-faint:  var(--raw-teal-50);
  --line-table-wrap:    rgba(216, 226, 231, 0.95);   /* 表格外框線，同上權威源 */
  --line-group-on-deep: rgba(255, 255, 255, 0.35);   /* P2 組界線畫在深色表頭上 (第 14 列) */
  --line-group-on-body: rgba(11, 79, 92, 0.28);      /* 同一條線畫在白色資料列上 */
  --line-accent-soft:   rgba(26, 110, 131, 0.3);     /* KPI 膠囊外框 */
  --line-accent-subtle: var(--raw-gold-200);         /* 章節目錄的分段短線 (ML-HTML 第 40 列) */
/* 章節左條（一般章）。沿革：畫布原案是近白底金字 → 實測底對白卡只有 1.04 對比、
 * 字對底 4.55，估價師回報「難以識別」→ 一度改淡金底深藍字 → 2026-09-15 第二輪
 * 估價師裁示「改成綠底白字」，綠＝品牌 teal-500。白字對它 8.98，遠超 AA。 */
--surface-chapter-rail: var(--raw-teal-500);
--line-chapter-rail:    var(--raw-teal-700);   /* 右側細線比底再深一階 */
--surface-chapter-rail-on-deep: var(--raw-teal-400);  /* 深底區塊裡的左條：與深底同色會融進背景，淺一階分開 (2026-09-24) */
--text-chapter-rail:    var(--raw-white);   /* 章節左條的字（白字對綠底 8.98:1） */

  /* Typography */
  /* 2026-09-15：標題字型全案黑體（ML-HTML 第 3 列改判）。原為 'Noto Serif TC' 宋體，
   * 依 ~/.claude/assets/yufong/brand.json 的 cjk_heading = Source Han Sans TW Medium 改掉。 */
  --font-display: 'Noto Sans TC', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-body:    'Noto Sans TC', sans-serif;
  --font-numeric: 'Inter', 'Noto Sans TC', sans-serif;

  /* Type scale：字級階梯 C 收斂版（ML-HTML 第 20 列，2026-09-15 裁示）
   * 固定 px 不用 clamp；標題字重 500 依 ~/.claude/assets/yufong/brand.json
   * 的 cjk_heading = Source Han Sans TW Medium（出自宇豐睿星guideline_2026_F.pdf p25/p26）。
   * KPI 數字軌是另一條（Inter 40/300、26/300、22/600），在 KPI 元件層定義，不放這裡。 */
  --fs-display: 40px;   --lh-display: 1.2;   --fw-display: 500;
  --fs-h2:      24px;   --lh-h2:      1.4;   --fw-h2:      500;
  --fs-h3:      17px;   --lh-h3:      1.5;   --fw-h3:      700;
  --fs-body:    15px;   --lh-body:    1.85;  --fw-body:    400;
  /* 第六階，2026-09-16 裁示「B 六階」（ML-HTML 第 80 列）。底座本來就有 15 條字級
   * 落在 12.75~13.5px，而階梯在 12 與 15 之間是空的。只補字級，不配 lh/fw（會變死 token）。 */
  --fs-small:   13px;
  --fs-note:    12px;   --lh-note:    1.6;   --fw-note:    400;
  --fs-table:   11.5px; --lh-table:   1.6;

  /* Spacing (4px grid，與六份交付物同階；2026-09-15 由 7 階換成 8 階，見 ML-HTML 第 22 列) */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px;

  /* Radius */
  --r-sm: 4px;  --r-md: 8px;  --r-lg: 12px; --r-xl: 16px;

/* 固定頁首高度。表格表頭的 sticky top 必須跟著它，所以收成一個 token 不要兩邊各寫一次。
 * 沿革：裁示第 5 列原本 56px → 一度改 48px（註解寫的，無確認紀錄）→ 實際生效卻是 40px，
 * 三個數字打架。2026-09-16 出四案模擬畫面（evidence/2026-09-16-ml-html-step21/
 * mock_header_height.html）給估價師看過實物後定案：
 * 🔴 **頁首 44px ／ logo 36px**（上下各 4px 空隙；40/36 只剩 2px 太貼邊，56 則第一屏吃太多）。
 * logo 高度在 assets/components/logo/logo.css 的 .logo-header，兩個值要一起看。 */
--site-header-h: 44px;

  /* Shadow */
  --shadow-card: 0 1px 3px rgba(0,0,0,0.08);
  /* 章節標題卡與 KPI 卡用的柔陰影，帶品牌藍。值取自 /design 畫布
     8bQRCQvRcs15iaciH6CVp5 與 G73SvXqdbu8XJjpCpdemk3 的 .plate。 */
  --shadow-card-soft: 0 2px 12px rgba(11,79,92,0.06);
  --shadow-card-hover: 0 4px 12px rgba(0,0,0,0.12);
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/tokens/components.css ---- */
/* Layer 3: COMPONENT TOKENS */
:root {
  /* KPI card (ML-HTML 第 17／18 列，值取自 /design 畫布 VePeRJfG35CwxWrh45ZWan)
   * 🔴 2026-09-15 對回畫布後三處改動：
   *   number-fg 金 → 深藍（畫布的大數字是深藍配白底，與頁面後面的 KPI 白卡一致）
   *   radius r-md 8px → r-xl 16px、padding sp-6 32px → sp-5 24px（畫布實值） */
  --kpi-card-bg:        var(--surface-card);
  --kpi-card-number-fg: var(--text-link-strong);
  --kpi-card-label-fg:  var(--text-link-strong);
  --kpi-card-sub-fg:    var(--text-muted);
  --kpi-card-radius:    var(--r-xl);
  --kpi-card-padding:   var(--sp-5);
  --kpi-track-bg:       var(--surface-track);
  --kpi-pill-fg:        var(--text-link-mid);
  --kpi-pill-border:    var(--line-accent-soft);

  /* Table (ML-HTML 第 7 列。外框值的權威源＝smart-valuation
     static/css/lvr-search-building.css:1686 的 .lvr-comp-table-wrap) */
  --table-wrap-border:  var(--line-table-wrap);
  --table-wrap-bg:      var(--surface-table-wrap);
  /* P2 分組列的**固定高度**。sticky 疊加要用同一個值，不可以另外寫一個數字：
     2026-09-15 實測，分組列靠 font-size×line-height＋padding 長出來的高度是
     23.5px，而欄名列的 top 寫死 22px ⇒ 兩列一黏住就疊 1.5px，computed style
     每一項都「正確」但畫面上兩條深藍會壓在一起。 */
  --table-group-h:      24px;
  --table-groupline-on-head: var(--line-group-on-deep);
  --table-groupline-on-body: var(--line-group-on-body);

  /* Hero */
  --hero-deep-bg:       var(--surface-section-deep);
  --hero-deep-fg:       var(--text-on-deep);
  --hero-deep-accent:   var(--text-accent);
  --hero-padding-y:     var(--sp-8);
  --hero-map-tint-bg:   var(--surface-map-tint);

  /* Section */
  --section-deep-bg:    var(--surface-section-deep);
  --section-deep-fg:    var(--text-on-deep);
  --section-soft-bg:    var(--surface-section-soft);
  --section-soft-fg:    var(--text-primary);
  --section-plain-bg:   var(--surface-card);
  --section-plain-fg:   var(--text-primary);
  --section-padding-y:  var(--sp-7);

  /* Three-card */
  --three-card-clause-tint: var(--raw-teal-50);
  --three-card-detail-tint: var(--raw-gray-100);
  --three-card-view-tint:   var(--raw-gold-50);

  /* Popup */
  --popup-bg:        var(--surface-card);
  --popup-shadow:    var(--shadow-card-hover);
  --popup-radius:    var(--r-md);

  /* Cite chip */
  --cite-chip-bg: var(--raw-gold-50);
  --cite-chip-fg: var(--text-accent-aa);
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/tokens/data-palettes.css ---- */
/* DATA PALETTES
 * 資料導向色盤（permit group, zoning class, ownership type）。
 * 每段以「@palette NAME」標註，lint 用此 marker 把這些 hex 列入白名單。
 */

/* @palette permit-group */
:root {
  --palette-permit-1: var(--raw-gold-400);
  --palette-permit-2: #2c96b1;
  --palette-permit-3: var(--raw-state-warn);
  --palette-permit-4: var(--raw-state-good);
  --palette-permit-5: #6e3b3b;
  --palette-permit-6: #4a2028;
  --palette-permit-7: var(--raw-teal-500);
  --palette-permit-ungrouped: #95a5a6;
}

/* @palette zoning */
:root {
  --palette-zoning-z3-res:        #c8d895;
  --palette-zoning-z3-1-res:      var(--raw-gold-600);
  --palette-zoning-z-mixed-road:  var(--raw-state-warn);
  --palette-zoning-z-other:       #c8c8c8;
  --palette-zoning-z-com-1:       #ffe7a8;
  --palette-zoning-z-com-2:       #ffce6e;
  --palette-zoning-z-com-3:       #ffae3a;
  --palette-zoning-z-com-4:       #ff8c00;
  --palette-zoning-z-road:        #d8d8d8;
}

/* @palette ownership */
:root {
  --palette-ownership-private:  #6ab2cf;
  --palette-ownership-national: #f4b0c8;
  --palette-ownership-city:     #f4d164;
  --palette-ownership-mixed:    #b8b8b8;
}

/* @palette chart-series
 * 圖表系列色。2026-09-18（ML-HTML 第 37 步）由估價師改定「方案 2：藍打底，
 * 磚橘當重點」——原本六個色全擠在藍金兩個色相，四個系列以上就分不開
 * （實測深金↔teal-400 全彩 ΔE 14.9、teal-700↔teal-500 只有 8.9，門檻 15）。
 * 順序照「印成黑白也分得開」排；灰階最小間距 9.2（tests/test_chart_palette.py 門檻 8）。
 * ⚠️ 磚橘與深金灰階只差 1.2，兩者不並用——深金已退出系列色。
 * 超過 6 個系列請改用分面小圖，不要再加色。
 */
:root {
  --chart-series-1: var(--raw-teal-500);  /* teal-500  灰階  65.5 */
  --chart-series-2: var(--raw-clay-500);  /* clay-500  灰階 112.2 */
  --chart-series-3: #6ab2cf;  /* own-private  灰階 164.8；同檔 --palette-ownership-private */
  --chart-series-4: #f4d164;  /* own-city  灰階 208.6；同檔 --palette-ownership-city */
  --chart-series-5: var(--raw-teal-700);  /* teal-700  灰階  44.3 */
  --chart-series-6: var(--raw-gold-400);  /* gold-400  灰階 155.6 */
  --chart-series-muted: #9aacb5;   /* 其他／未分類 */
}

/* @palette chart-ink
 * 色塊**上面**的字（堆疊長條的段內數字）要白還是深。
 * 🔴 舊版寫死白字；色票裡的 gold-400 對白只有 2.64:1、teal-200 只有 2.1:1，
 * 白字落在那種色塊上根本看不見。這裡逐色照 WCAG 挑：
 * 白字撐得住 4.5:1 就用白，撐不住換深字。
 */
:root {
  --chart-ink-1: var(--raw-white);   /* 白字對 #0b4f5c 是 9.18:1 */
  --chart-ink-2: var(--raw-gray-900);   /* 白字對 #b8622a 是 4.36:1 */
  --chart-ink-3: var(--raw-gray-900);   /* 白字對 #6ab2cf 是 2.36:1 */
  --chart-ink-4: var(--raw-gray-900);   /* 白字對 #f4d164 是 1.48:1 */
  --chart-ink-5: var(--raw-white);   /* 白字對 #073544 是 13.11:1 */
  --chart-ink-6: var(--raw-gray-900);   /* 白字對 #b49a64 是 2.71:1 */
}

/* @palette chart-diverge
 * 熱力圖冷暖色階（ML-HTML 第 37 步，估價師選 M1：中點＝全表中位數）。
 * 低於中點往冷色走、高於往暖色走，中點本身接近白。
 * ⚠️ 這不是彩虹色階——兩個色相加一個中性中點，方向本身有意義。
 * ⚠️ 冷暖**一定要配圖例**，否則看不出冷色是「低」還是「另一類」。
 * ⚠️ 中點是逐案參數：預設全表中位數，個案可改成勘估標的單價。
 * 濃度上限沿用畫圖端的 0.55（不另開 CSS token，免得變成第二個事實來源）；
 * 兩個色相各自量過：teal-500 天花板 0.60、clay-500 0.83，都有餘裕。
 */
:root {
  --chart-diverge-cool: var(--raw-teal-500);   /* 低於中點 */
  --chart-diverge-warm: var(--raw-clay-500);   /* 高於中點 */
}

/* @palette chart-direction
 * 漲跌軌，只給「方向有意義」的圖用（＝KPI 形狀 7 帶中心基準的比率）。
 * 紅漲綠跌是沿用已出貨交付物的既有慣例
 * （appraisal-comparison-viz/assets/template.html 的 positive=#c0392b／negative=#2e8b57），
 * 不是本輪新訂。⚠️ 那份檔的 positive／negative 指的是**數值方向**不是好壞，
 * 台灣習慣正值配紅、負值配綠，命名沒錯；本檔改用 up／down 命名讓這件事一眼看得出來。
 * （真正命名反了的是 valuation-dashboard 那份的 `--green:#a05151`，兩件事不要混。）
 * ⚠️ KPI 卡本身不准用紅綠（ML-HTML 第 17i 列），這兩個色只進圖表。
 */
:root {
  --chart-up:      var(--raw-state-bad);   /* 上／正 灰階  84.7 */
  --chart-down:    #2e8b57;   /* 下／負 灰階 115.5 */
  --chart-zero:    var(--raw-gray-500);   /* 中心基準線 */
}

/* @palette chart-chrome
 * 座標軸與格線。取自品牌色階，不另開一套中性灰。
 */
:root {
  --chart-grid:        var(--raw-teal-50);   /* teal-50，格線 */
  --chart-axis-line:   var(--raw-teal-200);   /* teal-200，軸線 */
  --chart-axis-label:  var(--raw-gray-500);   /* gray-500，刻度文字 */
  --chart-value-label: var(--raw-gray-900);   /* gray-900，資料標籤 */

  /* 滑鼠懸浮時那一根變成的顏色（ML-HTML 第 98 列，估價師裁示 B「只加懸浮標示」）。
   * 用磚橘＝配色方案 2 的重點色（第 95 列「藍打底，磚橘當重點」），
   * 不寫成 var(--chart-series-2)——那是「第 2 個系列」的意思，
   * 兩者現在剛好同色但語意不同，寫成 series-2 之後改系列順序就會連帶改到懸浮色。
   * ⚠️ 已知限制：某個系列本身就是磚橘時，滑過它顏色不會變，只會浮出小框。 */
  --chart-hover:       var(--raw-clay-500);   /* clay-500，懸浮重點色 */
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/typography/fonts.css ---- */
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@400;500;700&family=Inter:wght@300;400;600;700&display=swap');

html, body {
  font-family: var(--font-body);
  color: var(--text-primary);
  /* 字級階梯 C 的內文階（第 20 列）當作全站預設，頁面不必為了每段內文加 class。
     沒有這兩行時內文是瀏覽器預設 16px／normal，與裁示的 15px／1.85 不同。 */
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}

/* 2026-09-15：標題字型全案黑體（ML-HTML 第 3 列改判），Display 改吃字級階梯 C 的
 * --font-display（Noto Sans TC）與 --fw-display（500）。原為 Noto Serif TC weight 900。
 * Inter 補 300：KPI 數字軌大數字用 40px/300（ML-HTML 第 17a、20 列）。
 */
.display, [data-typography="display"] {
  font-family: var(--font-display);
  font-size: var(--fs-display);
  font-weight: var(--fw-display);
  line-height: var(--lh-display);
}

/* 字級階梯 C 的其餘四階（第 20 列）。`.display` 之外原本一階都沒有元件可用，
 * 模擬頁只好自己寫 `.mk-h3`／`.mk-cap`——那正是「交付物各自重刻版面」的來源。
 * class 一律加 `yk-` 前綴：交付物常自帶 `.note`／`.body`，不加前綴會互相覆蓋。
 */
.yk-h2, [data-typography="h2"] {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  font-weight: var(--fw-h2);
  line-height: var(--lh-h2);
}
.yk-h3, [data-typography="h3"] {
  display: flex;
  align-items: center;
  margin: 18px 0 8px;
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-h3);
  line-height: var(--lh-h3);
  color: var(--text-link-strong);
}
/* 節標題右側的出處標註。估價交付物每一張表都要標出處，沒有這個位置的話
   交付物只能自己寫 inline style（lint 的 style-attr 會擋）。 */
.yk-h3-src {
  margin-left: auto;
  font-size: var(--fs-note);
  font-weight: var(--fw-note);
  color: var(--text-muted);
}
.yk-body, [data-typography="body"] {
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: var(--fw-body);
  line-height: var(--lh-body);
}
.yk-note, [data-typography="note"] {
  font-family: var(--font-body);
  font-size: var(--fs-note);
  font-weight: var(--fw-note);
  line-height: var(--lh-note);
  color: var(--text-muted);
}
/* 節標題左側的金色小條：模擬頁的 .mk-bar，估價師看過的那個長相 */
.yk-h3-bar {
  flex: none;
  display: inline-block;
  width: 16px;
  height: 2px;
  background: var(--text-accent);
  border-radius: 2px;
  margin-right: 9px;
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/typography/em-weight-guard.css ---- */
/* EM WEIGHT GUARD
 * <em> 預設斜體 + 繼承 weight 容易跑掉。2026-09-15 前的理由是宋體 fallback；
 * 宋體已全面拿掉（ML-HTML 第 3 列改判），但黑體一樣會發生，故本檔保留。
 * 強制 display 區內所有 em：取消斜體、強制繼承 weight、改用 color 強調。
 */
.hero em,
.section-title em,
.kpi-label em,
.three-card em {
  font-style: normal;
  font-weight: inherit;
  color: var(--text-accent);
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/layout/reset.css ---- */
/* 全域重置。
 * 🔴 /design 的每一張畫布都寫了 `*{box-sizing:border-box}`，底座整棵 assets 樹
 * 原本一處都沒有（2026-09-15 grep 實證 0 命中）。差別在第 15 列「首欄凍結寬
 * 148px／66px」上會直接看見：沒有它，`padding: 8px 10px` 會把兩欄撐成
 * 168px／86px，合計 254px 而不是裁示的 214px。
 */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* 🔴 body 的瀏覽器預設 margin 是 8px，底座從來沒有歸零（2026-09-15 第 9 步實測）。
 * 後果：每一份交付物的「滿版」區塊都內縮 8px——1440px 視窗裡 hero 實際寬 1424、
 * 左緣在 x=8；sticky 頁首在未捲動時也浮在距頂 8px 的位置，看起來像頁面有白外框。
 * 不會報錯、只會整頁偏一點，所以八個步驟都沒被發現。 */
body {
  margin: 0;
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/layout/grid.css ---- */
.yk-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--sp-5);
}
.yk-col-span-3  { grid-column: span 3; }
.yk-col-span-4  { grid-column: span 4; }
.yk-col-span-6  { grid-column: span 6; }
.yk-col-span-8  { grid-column: span 8; }
.yk-col-span-12 { grid-column: span 12; }

/* 圖片不得撐爆容器。原圖常比欄寬大（about 頁人物照 600px），沒有這條的話
   單檔模式把圖 inline 進來之後，窄螢幕會整頁橫向溢出。2026-09-15 實測補。 */
img, svg, video {
  max-width: 100%;
  height: auto;
}
.yk-grid > * { min-width: 0; }

/* 自動換欄的卡片列。交付物原本自己寫 `.card-grid{grid-template-columns:
   repeat(auto-fit,minmax(260px,1fr))}`，那是版型（H1 的判準之一）。
   收進底座之後交付物只掛 class，不再各自定義一份。
   ⚠️ `-2` 是「就是要兩欄」的覆寫（招商簡報對照、SWOT 四卡那種成對內容），
   必須排在基底之後才蓋得過去。 */
.yk-autogrid {
  display: grid;
  gap: var(--sp-4);
  margin-top: var(--sp-4);
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.yk-autogrid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 640px) {
  .yk-autogrid-2 { grid-template-columns: minmax(0, 1fr); }
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/layout/container.css ---- */
.yk-container-sm { max-width: 720px;  margin: 0 auto; padding: 0 var(--sp-5); }
.yk-container-md { max-width: 960px;  margin: 0 auto; padding: 0 var(--sp-5); }
.yk-container-lg { max-width: 1200px; margin: 0 auto; padding: 0 var(--sp-6); }
.yk-container-xl { max-width: 1440px; margin: 0 auto; padding: 0 var(--sp-6); }

/* 頁面主體的上下留白。交付物原本各自寫 inline style 或自己一條 CSS，
   收成一個 class 之後 lint 的 style-attr 規則才擋得住「又自己寫版型」。 */
.yk-page { padding-block: var(--sp-6) var(--sp-7); }

/* 間距工具。理由同 .yk-page：交付物原本一處一個 inline style（bid-analysis 實測
   44 種，其中 30 種就是這三個屬性加一個 --sp token），收成 class 之後 lint 的
   style-attr 才擋得住「又自己寫版型」。
   ⚠️ 名字裡的數字＝用到的 --sp 編號，錯位是看不出來的錯，tests/test_spacing_utilities.py 在釘。 */
.yk-mt-2 { margin-top: var(--sp-2); }
.yk-mt-3 { margin-top: var(--sp-3); }
.yk-mt-4 { margin-top: var(--sp-4); }
.yk-mt-5 { margin-top: var(--sp-5); }
.yk-mb-3 { margin-bottom: var(--sp-3); }
.yk-mb-4 { margin-bottom: var(--sp-4); }
.yk-mb-5 { margin-bottom: var(--sp-5); }
.yk-pl-4 { padding-left: var(--sp-4); }
.yk-pl-5 { padding-left: var(--sp-5); }
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/layout/responsive.css ---- */
@media (max-width: 1024px) {
  .yk-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .yk-col-span-3 { grid-column: span 3; }
  .yk-col-span-4, .yk-col-span-6 { grid-column: span 6; }
  .yk-col-span-8, .yk-col-span-12 { grid-column: span 6; }
}
@media (max-width: 640px) {
  .yk-grid { grid-template-columns: 1fr; gap: var(--sp-4); }
  [class*="yk-col-span-"] { grid-column: 1; }
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/hero/hero.css ---- */
.hero {
  padding: var(--hero-padding-y) 0;
  position: relative;
}
.hero-deep-solid {
  background: var(--hero-deep-bg);
  color: var(--hero-deep-fg);
}
/* 字級階梯 C（第 20 列）。原本是 900 ＋ clamp(2.2rem,4vw,3.4rem)——
   兩者都查無規範依據，且 clamp 會讓窄螢幕下章標比頁面大標還大。 */
.hero-title {
  font-family: var(--font-display);
  font-weight: var(--fw-display);
  font-size: var(--fs-display);
  line-height: var(--lh-display);
  letter-spacing: 0.5px;
  margin: 0 0 var(--sp-4);
}
.hero-title em {
  font-style: normal;
  font-weight: inherit;
  color: var(--hero-deep-accent);
}
/* 🔴 ML-HTML 第 27 步：這條原本不存在。`hero-deep-solid.html.j2` 印得出
   `.hero-subtitle`、manifest 也把 subtitle 列為 prop，但全部 CSS 查無定義
   ⇒ 傳了就是一個裸的 <p>（繼承 body 15px、深底上拿 --hero-deep-fg 的白）。
   規範樣板頁自己每次都在餵這個 prop，展示的就是那個裸的長相。
   值全部取自已裁示的階梯與既有語意 token，沒有新造的數字。 */
.hero-subtitle {
  font-size: var(--fs-h2);
  line-height: var(--lh-h2);
  font-weight: 400;
  color: var(--text-on-deep-muted);
  margin: 0 0 var(--sp-4);
}
.hero-lead {
  font-size: var(--fs-body);
  line-height: 1.7;
  max-width: 56ch;
  color: var(--text-on-deep-muted);
}
.hero-kpis {
  margin-top: var(--sp-6);
  display: flex;
  gap: var(--sp-6);
  flex-wrap: wrap;
}
.hero-deep-image {
  /* ⚠️ 顏色層必須放最後——background 簡寫把顏色寫在圖層前面，整條宣告會被視為
     無效而丟棄（底圖完全不顯示，且不報錯）。2026-09-15 實測修正。 */
  background: var(--hero-bg-image, none) center/cover no-repeat, var(--surface-overlay);
  background-blend-mode: multiply;
  color: var(--hero-deep-fg);
  min-height: 60vh;
}
.hero-bg-overlay {
  position: absolute; inset: 0;
  background: linear-gradient(135deg, rgba(11,79,92,0.94) 0%, rgba(11,79,92,0.62) 100%);
  pointer-events: none;
}
.hero-inner { position: relative; }
.hero-meta { display: flex; gap: var(--sp-4); margin-bottom: var(--sp-5); flex-wrap: wrap; }
.hero-meta-chip {
  padding: var(--sp-1) var(--sp-4);
  background: rgba(255,255,255,0.1);
  color: var(--text-on-deep-muted);
  border-radius: var(--r-sm);
  font-size: var(--fs-small);
}


/* ===========================================================================
   light-editorial ── 「A＋B，但不要左邊金條」（估價師 2026-09-15 裁示）
   七案比稿 artifact/WtKQBR6ti9EGWARKBCjYFb 選定。
   為什麼不是深底版：① 白底讓 hero 與後面白底章節銜接，沒有「進了彩色區塊又
   出來」的斷點；② 數字是深藍字配白底，與頁面後面的 KPI 白卡同一個長相
   ——深底版的數字一直與白卡不一致；③ 列印不吃墨。
   代價：沒有色塊撐場，層級全靠頂部金線＋眉標＋字級差，留白不能被壓縮。
   =========================================================================== */
.hero-light-editorial {
  background: var(--surface-card);
  color: var(--text-primary);
  /* 🔴 2026-09-15 估價師裁示：「頁首固定列和頁面之間不要有金色長條」。
   * 原本這裡有一條 3px 的金漸層上框（七案比稿 A 案的「頂部金線」），
   * 緊貼在固定頁首下方，看起來像頁首多了一條裝飾邊。已移除。
   * 層級改由眉標（金色字距 4px）＋ 字級差撐，白底維持整片不斷點。 */
}
.hero-eyebrow {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0 0 var(--sp-5);
  font-size: var(--fs-note);
  font-weight: 500;
  letter-spacing: 4px;
  color: var(--text-accent-aa);
}
.hero-eyebrow-rule {
  flex: none;
  width: 28px;
  height: 1px;
  background: var(--text-accent-aa);
}
.hero-light-editorial .hero-title em {
  color: var(--text-accent-aa);
}
.hero-light-editorial .hero-lead {
  color: var(--text-secondary);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}
/* 四個關鍵數字：不做卡片，用 1px 細線分隔，讓白底維持整片 */
.hero-stats {
  display: flex;
  margin-top: var(--sp-7);
  border-top: 1px solid var(--line-subtle);
  padding-top: var(--sp-5);
}
.hero-stat {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: 0 var(--sp-6);
  border-left: 1px solid var(--line-subtle);
}
.hero-stat:first-child {
  padding-left: 0;
  border-left: 0;
}
.hero-stat:last-child {
  padding-right: 0;
}
.hero-stat-value {
  font-family: var(--font-numeric);
  font-weight: 300;
  font-size: 30px;
  line-height: 1;
  color: var(--text-link-strong);
  font-variant-numeric: tabular-nums;
}
.hero-stat-unit {
  font-size: var(--fs-small);
  color: var(--text-muted);
  margin-left: 4px;
}
.hero-stat-label {
  font-size: var(--fs-note);
  line-height: var(--lh-note);
  color: var(--text-muted);
}
/* 375px：四欄橫排每欄只剩約 80px，數字會被截。改兩欄，欄間線換成列間線。 */
@media (max-width: 640px) {
  .hero-stats {
    flex-wrap: wrap;
    gap: var(--sp-4) 0;
  }
  .hero-stat {
    flex: 0 0 50%;
    padding: 0 var(--sp-4);
  }
  .hero-stat:nth-child(odd) {
    padding-left: 0;
    border-left: 0;
  }
  .hero-stat-value {
    font-size: 24px;
  }
}

.hero-light-band {
  background: var(--surface-section-soft);
  color: var(--text-primary);
}
/* 🔴 .hero-lead 的基準色是 --text-on-deep-muted（淡金 #d4bc9a），那是給深底 hero 用的。
 * light-editorial 有覆寫，light-band **漏了** → 淡金落在近白底 #fafbfc，
 * 實測對比 1.77，等於整段導言看不見。2026-09-16 第 22 步用真 Chromium 逐段量到。 */
.hero-light-band .hero-lead {
  color: var(--text-secondary);
}
/* ===========================================================================
   map-tint ── 地圖當底圖，整張圖壓一層深色罩，標題用白字
   🔴 估價師 2026-09-18 裁示 A（ML-HTML 第 102 列）。
   ⚠️ 同一天他先選 C「字放底板」、當天改選 A，所以這一段是**改過的**，
      舊的 `map-panel`（底板版）已整段撤掉，不要以為還有兩個變體可選。
   =========================================================================== */
/* 為什麼是 75%：**這個數字是算出來的，不是配的**（第 22 步）。
 * 白字要在電子地圖**最亮的那 1% 畫素**上過 15px 小字的 4.5，色罩就要濃到 75%。
 * 調低它等於讓標題在亮處讀不到，而且畫面上不會有人發現——
 * 亮處通常只佔一小塊，肉眼掃過去以為沒事。要動它請先重跑
 * `evidence/2026-09-16-ml-html-step22/measure_hero_contrast.py`。
 *
 * **代價寫在前面**：色罩越濃，地圖的紋理被壓得越平。實測正射航照的「地圖細節」
 * 指標從 26 掉到 7。這是估價師選 A 時已經看過真圖之後接受的取捨。
 *
 * 🔴 **堆疊順序是這個元件的必要條件不是裝飾。** 舊的 `map-overlay` 變體
 * （2026-09-16 已刪）色罩是 `position:absolute` 而文字容器是 `static`，
 * 實測 `elementFromPoint` 打在標題上回的是**色罩**——字是隔著一層藍在看。
 * 這裡文字容器明寫 `position:relative; z-index:1` 把它提到色罩之上，
 * `tests/test_hero_map_tint.py` 有一條專門釘這件事。
 */
.hero-map-tint {
  /* ⚠️ 顏色層必須放最後——background 簡寫把顏色寫在圖層前面，整條宣告會被
     視為無效而丟棄（底圖完全不顯示，且不報錯）。同 .hero-deep-image。 */
  background: var(--hero-bg-image, none) center/cover no-repeat, var(--surface-page);
  position: relative;
  min-height: 60vh;
}

.hero-map-tint-layer {
  position: absolute;
  inset: 0;
  background: var(--hero-map-tint-bg);
  /* 色罩不吃滑鼠：它鋪滿整個 hero，吃了會擋掉底下任何連結。 */
  pointer-events: none;
}

/* 🔴 把文字容器提到色罩之上。少了這兩行，字就在色罩底下——
 * 那正是舊變體的 bug，而且畫面上只是「顏色怪怪的」，不會壞給你看。 */
.hero-map-tint .hero-inner {
  position: relative;
  z-index: 1;
}

/* 白字。🔴 不可以留任何吃 --text-primary 的後代：舊變體標題吃墨黑，
 * 疊在正射航照上實測只有 1.57（門檻 4.5），整行標題等於看不見。 */
.hero-map-tint .hero-title {
  color: var(--hero-deep-fg);
}
/* 🔴 副標也要**純白**，不可以用 --text-on-deep-muted（淺金）。
 * 那是實測抓到的：淺金在深底**卡片**上沒問題，但這裡底下是半透明色罩加一張地圖，
 * 電子地圖那張量到 **2.58**（門檻 3.0）—— 不及格。純白量到 4.86。
 * ⚠️ 第 41 步第一版就是寫淺金，理由是「與其他深底變體一致」。
 * 一致性不能凌駕可讀性，而且**只有跑畫素量測器才看得出來**，肉眼與 DOM 都不會說。 */
.hero-map-tint .hero-subtitle,
.hero-map-tint .hero-lead {
  color: var(--hero-deep-fg);
}

/* 🔴 `map-overlay` 變體已於 2026-09-16 刪除（估價師裁示）。
 * 理由：實測**沒有任何交付物在用它**，而它本身有兩個缺陷——
 *   ① 色罩是 position:absolute、文字容器是 static ⇒ 色罩畫在字的上面；
 *   ② 標題吃 --text-primary 墨黑，疊在任意底圖上沒有任何保證，
 *      在正射航照上實測只有 1.57（門檻 3.0）。
 * 四案比稿留在 evidence/2026-09-16-ml-html-step22/mock_hero_map.html，
 * 日後真的要做地圖首屏時從那裡接著選，不要憑印象重刻。 */
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/section/section.css ---- */
.section { padding: var(--section-padding-y) 0; }
.section-deep { background: var(--section-deep-bg); color: var(--section-deep-fg); }
.section-soft { background: var(--section-soft-bg); color: var(--section-soft-fg); }
/* 白底區塊。KPI 大卡列自己的底是 --surface-card-alt(#fafbfc)，與 section-soft
   的底同值，放在 soft 上整片圓角面板會消失；要看得到它就得坐在白底上。 */
.section-plain { background: var(--section-plain-bg); color: var(--section-plain-fg); }
/* 白底 hero 直接接白底區塊時，hero 的下 padding(64px) 與區塊的上 padding(48px)
   會疊成 112px 的空白帶——兩邊同色，中間什麼都沒有，看起來像頁面破圖。
   實測（1440px）：疊加時 hero 內文到 KPI 面板 127px，去掉後 79px。 */
.hero-light-editorial + .section-plain { padding-top: 0; }
/* 字級階梯 C（第 20 列）。同 .hero-title：拿掉 900 與 clamp。 */
.section-title {
  font-family: var(--font-display); font-weight: var(--fw-h2);
  font-size: var(--fs-h2); line-height: var(--lh-h2); margin: 0 0 var(--sp-5);
}
.section-title em {
  font-style: normal; font-weight: inherit; color: var(--text-accent);
}
.section-lead { font-size: var(--fs-body); line-height: 1.75; max-width: 64ch; color: inherit; opacity: 0.85; }
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/kpi-card/kpi-card.css ---- */
/* ===========================================================================
   KPI ── ML-HTML 第 17 列（七種形狀）× 第 18 列（三種排法）
   兩者是獨立的兩條軸：**形狀**決定一個指標怎麼畫，**排法**決定整個區塊放頁面
   哪個位置。估價師 2026-09-15：「又不是每個 KPI 都剛好是能這樣畫」——所以
   沒有「一種卡片通吃」這回事。
   實值逐字取自 /design 畫布 KPI artifact/VePeRJfG35CwxWrh45ZWan
   （Main＝L1 大卡列、RowStrip＝L2 指標列、TableForm＝L4 表格）。
   數字軌＝Inter 細體，與標題軌（Noto Sans TC）分開，見第 20 列。
   =========================================================================== */

/* ---------------- 排法 L1 大卡列（首頁／章節開頭） ---------------- */
.kpi-row {
  display: grid;
  gap: var(--sp-4);
  padding: var(--sp-5);
  background: var(--surface-card-alt);
  border-radius: var(--r-xl);
}
.kpi-row-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.kpi-row-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.kpi-card {
  min-width: 0;
  padding: var(--kpi-card-padding);
  border-radius: var(--kpi-card-radius);
  background: var(--kpi-card-bg);
  box-shadow: var(--shadow-card-soft);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}
.kpi-label {
  font-size: 14px;
  font-weight: 700;
  color: var(--kpi-card-label-fg);
}
.kpi-sub {
  font-size: var(--fs-note);
  line-height: 1.5;
  color: var(--kpi-card-sub-fg);
  /* 卡片等高時把註解壓到底，數字與標題才對得齊 */
  margin-top: auto;
}

/* ---------------- 形狀 1／3 單值：大數字 40px，不畫刻度 ----------------
   形狀 3（比率但無基準）＝形狀 1 不加刻度，不另外給 class：查不到法定上限就
   不假裝有基準（第 17c 列）。 */
.kpi-number {
  font-family: var(--font-numeric);
  font-weight: 300;
  font-size: 40px;
  line-height: 1;
  color: var(--kpi-card-number-fg);
  font-variant-numeric: tabular-nums;
}
.kpi-unit {
  font-size: 16px;
  font-weight: 400;
  color: var(--text-muted);
  margin-left: 3px;
}
/* 形狀 4 區間：字串比單值長一倍，降到 26px（第 17d 列） */
.kpi-number-mid { font-size: 26px; }
/* 形狀 5 分類值（文字）：中字級 22px，**不放大成 40px**——大字級只給數字 */
.kpi-number-text {
  font-family: var(--font-body);
  font-size: 22px;
  font-weight: 600;
  letter-spacing: 0.5px;
  line-height: 1.3;
}

/* ---------------- 形狀 2 有目標的比率 ----------------
   刻度上限固定為目標的 3 倍（目標 ≤10% → 軸到 30%），各案刻度一致才可比。
   資料值走自訂屬性 --kpi-fill／--kpi-mark，inline style 只留自訂屬性宣告，
   lint 的 style-attr 規則因此放行。
   ⚠️ 比率遠超目標時（第 17g 列）條填滿並標「超出」，刻度**不**跟著放大。 */
.kpi-gauge {
  height: 6px;
  border-radius: 3px;
  background: var(--kpi-track-bg);
  position: relative;
  overflow: hidden;
}
.kpi-gauge > i {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  border-radius: 3px;
  background: var(--chart-series-3);
  width: var(--kpi-fill, 0%);
}
.kpi-gauge > span {
  position: absolute;
  top: -3px;
  bottom: -3px;
  width: 1px;
  background: var(--text-emph-1);
  left: var(--kpi-mark, 0%);
}

/* ---------------- 形狀 4 區間條 ----------------
   min-width 3px：區間寬度近零（久云嘉潤日出 109.4–109.8，佔軸 0.6%）也要看得到。 */
.kpi-rangebar {
  height: 6px;
  border-radius: 3px;
  background: var(--kpi-track-bg);
  position: relative;
}
.kpi-rangebar > i {
  position: absolute;
  top: 0;
  bottom: 0;
  border-radius: 3px;
  background: var(--chart-series-3);
  min-width: 3px;
  left: var(--kpi-lo, 0%);
  width: var(--kpi-w, 0%);
}

/* ---------------- 形狀 7 帶中心基準的比率（第 17i 列） ----------------
   與形狀 2（有上限目標）、形狀 3（無基準）都不同：**方向有意義，中心點才是零**。
   數字一律帶正負號或相對 100% 標示；刻度以中心點為 0 向兩側長。
   ⚠️ 不用紅綠上色——會與價差欄負值、P1 一級的磚橘撞（第 17i、24 列）。
   ⚠️ 這是七個形狀裡**唯一沒有 /design 畫布**的一個，畫法是第 17i 列寫下的
   工程參數；要改長相前先補一張畫布給估價師看。 */
.kpi-bipolar {
  height: 6px;
  border-radius: 3px;
  background: var(--kpi-track-bg);
  position: relative;
}
.kpi-bipolar > b {
  position: absolute;
  top: -3px;
  bottom: -3px;
  left: 50%;
  width: 1px;
  background: var(--line-strong);
}
.kpi-bipolar > i {
  position: absolute;
  top: 0;
  bottom: 0;
  border-radius: 3px;
  background: var(--chart-series-3);
  min-width: 3px;
  /* --kpi-side: 0% 表示往右長、50% 表示往左長的起點，由渲染端算好 */
  left: var(--kpi-side, 50%);
  width: var(--kpi-w, 0%);
}

/* ---------------- 形狀 6 清單（多值） ----------------
   膠囊自動換行，**卡片高度由內容決定不得鎖死**（筆數不可控，第 17f 列）。 */
.kpi-pills {
  display: flex;
  gap: var(--sp-2);
  flex-wrap: wrap;
}
.kpi-pill {
  padding: 5px 13px;
  background: var(--surface-row-hover);
  border: 1px solid var(--kpi-pill-border);
  color: var(--kpi-pill-fg);
  border-radius: 9999px;
  font-size: var(--fs-note);
  font-weight: 600;
  letter-spacing: 0.5px;
}

/* ---------------- 排法 L2 指標列（章節中段小結） ----------------
   一條窄橫列，無卡片無底色，高度約 L1 的三分之一，插在行文中間不切斷段落。 */
.kpi-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  border-top: 1px solid var(--line-strong);
  border-bottom: 1px solid var(--line-strong);
  background: transparent;
}
.kpi-strip > div {
  flex: 1 1 0;
  min-width: 0;
  padding: 14px var(--sp-4);
  border-left: 1px solid var(--line-subtle);
}
.kpi-strip > div:first-child {
  border-left: 0;
  padding-left: 0;
}
/* L2 的每一格仍然是同一張 kpi-card（markup 只有一份，不另開一套），
   在這裡把卡片的殼脫掉：無底色、無陰影、無圓角、無內距。 */
.kpi-strip .kpi-card {
  background: none;
  box-shadow: none;
  border-radius: 0;
  padding: 0;
  gap: 0;
}
/* L1 是「數字在上、標題在下」，L2 反過來是「小標在上、數字在下」。
   用 order 把同一份 markup 翻過來，不要為了順序再寫一套模板。 */
.kpi-strip .kpi-label {
  order: -1;
  font-size: 11px;
  letter-spacing: 1px;
  color: var(--text-muted);
  font-weight: 600;
  margin-bottom: 6px;
}
/* L2 六欄時每欄只剩約 120px，分類值與區間都要降字級（第 18 列） */
.kpi-strip .kpi-number { font-size: 24px; }
.kpi-strip .kpi-number-mid { font-size: 17px; }
.kpi-strip .kpi-number-text { font-size: var(--fs-body); line-height: 1.35; }
.kpi-strip .kpi-unit { font-size: var(--fs-note); }
.kpi-strip .kpi-sub { font-size: 11px; margin-top: 5px; line-height: 1.45; }
.kpi-strip .kpi-pill { font-size: 11px; padding: 3px 9px; }

/* ---------------- 排法 L4 表格（那一段本來就有表格時） ----------------
   指標當表格讀，套第 7 列的表格樣式；數字最大只到 14px，沒有主次是已知代價。 */
.table td.kpi-k {
  font-weight: 700;
  color: var(--text-link-strong);
  width: 150px;
}
.table td.kpi-v {
  font-family: var(--font-numeric);
  font-weight: 600;
  color: var(--text-link-strong);
  font-size: 14px;
  /* 🔴 這裡**不可以**寫 white-space:nowrap。原本有，2026-09-15 第 12 步的 H5 抓到：
     值欄不是只放數字，也放地址與段名（「臺北市信義區雅祥段一小段」）。不換行時
     表格被撐到 780px，塞進 498px 寬的兩欄卡片，再被外框的 overflow:clip 裁掉——
     右半邊的字看不到也捲不到。雅祥重跑時 ch21 的 7 張實績卡全中，接底座之前是 0 張。
     數字本身不會因為可換行而斷開（「1,234」裡沒有換行機會）；真的要把數字與單位
     綁在一起的地方，交付物已經有 `.bid-nowrap` 在用。 */
}
.table td.kpi-v .kpi-unit { font-size: 11px; }
.table td.kpi-n { color: var(--text-muted); }
.table .kpi-pill { font-size: 11px; padding: 2px 9px; }

/* 刻度條的兩種間距（L1 卡片內／L2 窄列內） */
.kpi-bar-gap { margin: 4px 0 2px; }
.kpi-bar-slim { height: 4px; margin-top: 6px; }

/* hero 內的 KPI 列沿用舊排法（橫向 flex），不套 L1 的卡片格線 */
.hero-kpis .kpi-card {
  flex: 1 1 200px;
  min-width: 200px;
}

/* ---------------- 手機 375px ---------------- */
@media (max-width: 640px) {
  .kpi-row-3,
  .kpi-row-4 { grid-template-columns: 1fr; }
  .kpi-strip > div {
    flex: 1 1 100%;
    border-left: 0;
    padding-left: 0;
    border-top: 1px solid var(--line-subtle);
  }
  .kpi-strip > div:first-child { border-top: 0; }
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/chip/chip.css ---- */
.chip {
  display: inline-block;
  padding: var(--sp-1) var(--sp-2);
  border-radius: var(--r-sm);
  font-size: var(--fs-small);
  line-height: 1.4;
  font-family: var(--font-body);
}
.chip-default {
  background: var(--line-subtle);
  color: var(--text-primary);
}
.chip-accent {
  background: var(--cite-chip-bg);
  color: var(--cite-chip-fg);
}
.chip-info {
  background: var(--surface-section-deep);
  color: var(--text-on-deep);
}
/* chip 的容器。沒有它的話一排 chip 會靠 inline-block 的空白字元當間距，
   換行後行距也不受控（about 頁的「業務範圍」原本自造 .chip-list 但沒有樣式）。 */
.chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/portrait-grid/portrait-grid.css ---- */
.portrait-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-6);
}
.portrait-grid-item {
  margin: 0;
  display: flex;
  flex-direction: column;
}
.portrait-grid-frame {
  aspect-ratio: 4 / 5;
  background: var(--line-subtle);
  border-radius: var(--r-md);
  overflow: hidden;
}
.portrait-grid-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center bottom;
}
.portrait-grid-caption {
  padding-top: var(--sp-4);
}
.portrait-grid-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-h3);
  color: var(--text-primary);
}
.portrait-grid-title {
  font-size: var(--fs-small);
  color: var(--text-muted);
  margin-top: var(--sp-1);
}
.portrait-grid-bio {
  /* 估價師 2026-09-18 裁示（MAINLINES 第 124 列裁示⑥）：簡介是要讀的句子不是標籤，
     走內文那一階。職稱 .portrait-grid-title 是標籤，維持 --fs-small 不動。 */
  font-size: var(--fs-body);
  color: var(--text-primary);
  line-height: 1.6;
  margin-top: var(--sp-2);
}
@media (max-width: 768px) {
  .portrait-grid { grid-template-columns: 1fr; }
}

/* 深底區塊（.section-deep）裡的人物卡：名字與簡介原本釘死 --text-primary（深色），
   放進深底會變成深字壓深底、整段看不見。改吃容器色，職稱換深底專用的弱化色。 */
.section-deep .portrait-grid-name,
.section-deep .portrait-grid-bio {
  color: inherit;
}
.section-deep .portrait-grid-title {
  color: var(--text-on-deep-muted);
}
/* 深底區塊裡、但包在章節卡片（白卡）內：回到一般白底的弱化色。
   卡片自己把字色設回 --text-primary，名字與簡介跟著繼承，只有職稱要另外還原。 */
.section-deep .chapter-header-card .portrait-grid-title {
  color: var(--text-muted);
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/card/card.css ---- */
.card {
  background: var(--surface-card);
  border-radius: var(--r-md);
  padding: var(--sp-5);
}
.card-default {
  box-shadow: var(--shadow-card);
}
.card-outlined {
  border: 1px solid var(--line-subtle);
}
.card-elevated {
  box-shadow: var(--shadow-card-hover);
}
.card-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-h3);
  color: var(--text-primary);
  margin: 0 0 var(--sp-4);
}
.card-body {
  color: var(--text-primary);
  font-size: var(--fs-body);
  line-height: 1.65;
}
.card-meta {
  margin-top: var(--sp-4);
  font-size: var(--fs-small);
  color: var(--text-muted);
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/legend/legend.css ---- */
.legend {
  background: var(--surface-card);
  border-radius: var(--r-md);
  padding: var(--sp-4) var(--sp-5);
  box-shadow: var(--shadow-card);
  font-family: var(--font-body);
}
.legend-title {
  font-weight: 700;
  font-size: var(--fs-small);
  color: var(--text-primary);
  margin-bottom: var(--sp-2);
}
.legend-items {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}
.legend-item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-small);
  color: var(--text-primary);
}
.legend-dot {
  width: 12px;
  height: 12px;
  border-radius: var(--r-sm);
  display: inline-block;
  flex-shrink: 0;
  background: var(--legend-dot-color, var(--line-strong));
}
.legend-label {
  flex: 1;
}
.legend-count {
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/popup/popup.css ---- */
/* popup（ML-HTML 第 63 列 → 第 104 列改成三套）
 * 🔴 2026-09-16 估價師：「popup 要重新設計，嚴格禁止用這個有色條」——
 *    舊的 `.popup-severity-good { box-shadow: …, inset 4px 0 0 0 var(--state-good) }`
 *    那條左側滿高色邊**已刪除**，不要再加回來。
 *
 * 🔴 2026-09-18 估價師裁示「popup採用5-1、6-1、深化 6-上-2」⇒ **三套**，
 *    由呼叫端依卡片有多寬、版面塞不塞得下來挑：
 *      .popup-line ＝左側短線（5-1）：3px 短線垂直置中，**不吃卡片寬度**
 *      .popup-rail ＝左側窄欄（6-1）：30px 近白底欄＋直排狀態二字＋三格豎刻度
 *      .popup-top  ＝上方帶子（6-上-2）：狀態字靠左、刻度靠右兩端對齊，
 *                    **內容拿回全寬**，代價是卡片高度多約 34px
 *
 * 🔴 **三套都帶「黑白也分得出三階」的訊號**（估價師 2026-09-18 同意補）：
 *    line 靠**線的長度**（24%／40%／64%）、rail 與 top 靠**刻度格數**（1／2／3 格）。
 *    為什麼一定要：`--state-warn` 橘對白底只有 **2.81**，12px 小字的 AA 門檻是 4.5；
 *    而且黑白列印時綠與紅的灰階只差 6.0，會併成同一階。
 *    他退掉「滿高色條」的理由正是這個，所以顏色**不可以是唯一的訊號**。
 *
 * ⚠️ rail 的欄與 top 的帶子都是**近白底**（--surface-card-alt），不是狀態色實底；
 *    帶顏色的只有字與刻度。
 * ⚠️ rail 從第 63 列的 34px 改成 **30px**（估價師點名 6-1 那個寬度），
 *    但**刻度留著**——6-1 原案沒有刻度，那會退回「只靠顏色」。
 *    也就是這一版＝6-1 的寬度＋6-2 的刻度，不是兩個平行版本。
 */
.popup {
  position: relative;
  background: var(--popup-bg);
  border-radius: var(--popup-radius);
  box-shadow: var(--popup-shadow);
  padding: var(--sp-4) var(--sp-5);
  min-width: 200px;
  max-width: 360px;
  font-family: var(--font-body);
}
.popup-title {
  font-weight: 700;
  font-size: var(--fs-body);
  color: var(--text-primary);
  margin-bottom: var(--sp-2);
}
.popup-body {
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--text-primary);
}

/* 狀態色只掛在容器上，靠 currentColor 傳給指示物；標題與內文一律維持墨黑。 */
.popup-severity-good { color: var(--state-good); }
.popup-severity-warn { color: var(--state-warn); }
.popup-severity-bad  { color: var(--state-bad); }
.popup-severity-good > .popup-title, .popup-severity-good > .popup-body,
.popup-severity-warn > .popup-title, .popup-severity-warn > .popup-body,
.popup-severity-bad  > .popup-title, .popup-severity-bad  > .popup-body {
  color: var(--text-primary);
}

/* ---- 第一套：左側短線（5-1，線長分三階） */
.popup-line { padding-left: var(--sp-5); }
.popup-line-mark {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: var(--popup-line-h, 40%);
  border-radius: 0 2px 2px 0;
  background: currentColor;
}
/* 🔴 長度本身就分得出三階，黑白列印不再損失資訊。
 * 顏色照舊，但它不再是唯一的訊號——這是第 104 列補上去的，
 * 5-1 原案只有顏色。 */
.popup-severity-good .popup-line-mark { --popup-line-h: 24%; }
.popup-severity-warn .popup-line-mark { --popup-line-h: 40%; }
.popup-severity-bad  .popup-line-mark { --popup-line-h: 64%; }

/* ---- 第二套：左側窄欄＋刻度（6-1 的寬度＋刻度） */
.popup-rail {
  display: flex;
  padding: 0;
  overflow: hidden;
}
.popup-rail-col {
  flex: 0 0 30px;   /* 6-1 的寬度（第 63 列是 6-2 的 34px） */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  padding: var(--sp-3) 0;
  background: var(--surface-card-alt);
  border-right: 1px solid var(--line-subtle);
}
.popup-rail-text {
  writing-mode: vertical-rl;
  font-size: var(--fs-note);
  font-weight: 700;
  letter-spacing: 3px;
  line-height: 1;
  color: currentColor;
}
.popup-ticks {
  display: flex;
  flex-direction: column;
  gap: 2px;
  align-items: center;
}
.popup-ticks i {
  display: block;
  width: 10px;
  height: 3px;
  border-radius: 1px;
  background: var(--line-strong);
}
.popup-ticks i.on { background: currentColor; }
.popup-rail-body {
  flex: 1 1 auto;
  min-width: 0;
  padding: var(--sp-4) var(--sp-5);
}
.popup-rail-body > .popup-title,
.popup-rail-body > .popup-body { color: var(--text-primary); }

/* ---- 第三套：上方帶子（6-上-2，兩端對齊） --------------------------------
 * 估價師 2026-09-16 追加問「方案6能不能變成上方不要在左側」，
 * 2026-09-18 在三個上方版裡點名 **6-上-2**（狀態字靠左、刻度靠右兩端對齊）。
 * 刻度落在視線掃到卡片右緣的位置，與比較表數字欄靠右的習慣一致；
 * 一排卡片並排時三個刻度會對成一條線。
 *
 * 取捨（實測，第 21 步）：內容寬從左側版的 338px 回到**全寬 368px**，
 * 代價是卡片高度從 216px 變 **250px**。窄卡片選這個、長列表選 line。
 */
.popup-top {
  padding: 0;
  overflow: hidden;
}
.popup-top-bar {
  display: flex;
  align-items: center;
  /* 🔴 兩端對齊就是這一行——6-上-1 與它的差別只有這個。 */
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-5);
  background: var(--surface-card-alt);
  border-bottom: 1px solid var(--line-subtle);
}
.popup-top-text {
  font-size: var(--fs-note);
  font-weight: 700;
  letter-spacing: 3px;
  line-height: 1.4;
  color: currentColor;
}
/* 橫刻度：與 .popup-ticks 同一套語彙，只是排成一列。
 * ⚠️ 不要把這兩者合併成一條規則——豎的要 10×3、橫的要 12×3，
 * 合併之後改其中一個會靜默改到另一個。 */
.popup-hticks {
  display: flex;
  gap: 3px;
  align-items: center;
}
.popup-hticks i {
  display: block;
  width: 12px;
  height: 3px;
  border-radius: 1px;
  background: var(--line-strong);
}
.popup-hticks i.on { background: currentColor; }
.popup-top-body {
  padding: var(--sp-4) var(--sp-5);
}
.popup-top-body > .popup-title,
.popup-top-body > .popup-body { color: var(--text-primary); }
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/table/table.css ---- */
/* ===========================================================================
   表格元件 ── ML-HTML 第 7 列（樣式層）＋ 第 8~16 列（讀法層 P1／P2／P3）
   實值逐字取自 /design 畫布：
     T-AD artifact/PVoYsAWGvNJ1DAfhcUeVCz（深色表頭、sticky、hover 淺藍）
     T-EH artifact/2vcx8aho75LMMs9Bv2RMA7（P1 案 G、P2 DirGroupHead、P3 DirFrozen）
   圓角與邊框的權威源＝smart-valuation/static/css/lvr-search-building.css:1686
   的 `.lvr-comp-table-wrap`（第 7 列註明）。
   =========================================================================== */

/* ---------------- 樣式層（第 7 列） ---------------- */
.table-wrap {
  border-radius: var(--r-md);
  border: 1px solid var(--table-wrap-border);
  background: var(--table-wrap-bg);
  /* 🔴 不可以寫 overflow:hidden。hidden 會讓這個外框變成**捲動容器**，表頭的
     `top: var(--site-header-h)` 就改成「距外框頂端」解析，整條深色表頭壓在前
     兩列資料上面（2026-09-15 估價師看模擬頁第一眼就抓到）。
     `clip` 一樣裁掉圓角外的東西，但不建立捲動容器，sticky 仍相對視窗解析。
     瀏覽器實測：hidden → th 距容器頂 57px；clip → 1px，圓角照樣 8px。 */
  overflow: clip;
}
.table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-body);
  font-size: var(--fs-table);
  line-height: var(--lh-table);
  color: var(--text-primary);
  /* 數字欄一律等寬數字（第 7 列註明「雅祥那套沒開，屬缺陷非選項」） */
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}
.table th,
.table-th {
  position: sticky;
  top: var(--site-header-h);
  z-index: 2;
  background: var(--surface-section-deep);
  color: var(--text-on-deep);
  text-align: left;
  font-weight: 600;
  letter-spacing: 0.5px;
  padding: 8px 10px;
  line-height: 1.35;
}
.table td,
.table-td {
  padding: 8px 10px;
  /* 列區隔＝每列 1px 橫線，不用斑馬紋（第 7 列） */
  border-top: 1px solid var(--line-subtle);
  vertical-align: middle;
}
.table tbody tr:first-child td,
.table tbody tr:first-child .table-td {
  border-top: 0;
}
.table tbody tr:hover td,
.table-row:hover .table-td {
  background: var(--surface-row-hover);
}
/* 數字欄：右對齊＋數字軌字型。
 * 🔴 `th.table-num` 要單獨寫一條：`.table th` 的 `text-align:left` 權重(0,1,1)
 * 高過 `.table-num`(0,1,0)，只寫後者的話**資料右對齊、表頭還靠左**，
 * 而且測試只數 class 抓不到（Codex 2026-09-15 指出）。 */
.table-num {
  font-family: var(--font-numeric);
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.table th.table-num,
.table-th.table-num {
  text-align: right;
}
/* 密度變體：比預設再緊一階，給欄數特別多的表 */
.table-dense th,
.table-dense td,
.table-dense .table-th,
.table-dense .table-td {
  padding: var(--sp-1) var(--sp-2);
}

/* ---------------- 讀法層 P1 重點欄（第 9~13 列） ----------------
   只加粗＋變色，不加底色不加框線（第 9 列）。
   螢幕靠顏色、列印靠字重兩軌並行（第 12 列）：700／600／500／400。
   一般欄壓成深灰（第 13 列，對白底 7.73:1），不用淺灰。 */
.table-p1 td,
.table-p1 .table-td {
  color: var(--text-secondary);
  font-weight: 400;
}
.table-p1 td.table-p1-1 {
  color: var(--text-emph-1);
  font-weight: 700;
  font-size: 12.5px;
}
.table-p1 td.table-p1-2 {
  color: var(--text-emph-2);
  font-weight: 600;
}
.table-p1 td.table-p1-3 {
  color: var(--text-emph-3);
  font-weight: 500;
}

/* ---------------- 讀法層 P2 分組表頭（第 14 列，方案 E） ----------------
   分組列比欄名列更深（上深下淺），組界垂直線從分組列一路貫到每一列資料。 */
.table-p2 thead tr.table-group th {
  top: var(--site-header-h);
  background: var(--surface-group-head);
  font-size: 10px;
  letter-spacing: 2px;
  text-align: center;
  /* 高度釘死，讓下一列的 sticky 疊加算得準（見 --table-group-h 的註解） */
  height: var(--table-group-h);
  line-height: var(--table-group-h);
  padding: 0 8px;
}
.table-p2 thead tr.table-cols th {
  /* 疊在分組列下方。**必須**用同一個 token，不要在這裡寫死數字。 */
  top: calc(var(--site-header-h) + var(--table-group-h));
}
.table-p2 .table-groupline {
  box-shadow: inset -1px 0 0 var(--table-groupline-on-head);
}
.table-p2 td.table-groupline {
  box-shadow: inset -1px 0 0 var(--table-groupline-on-body);
}

/* ---------------- 讀法層 P3 首欄凍結（第 15 列，F2） ----------------
   釘住路段＋門牌兩欄，固定寬 148px／66px＝214px。
   ⚠️ 這兩個寬度要成立，`assets/layout/reset.css` 的 box-sizing 必須生效，
   否則 padding 會把它們撐成 168／86px。 */
.table-frozen {
  overflow: auto;
  border-radius: var(--r-md);
  border: 1px solid var(--table-wrap-border);
}
.table-frozen .table {
  border-collapse: separate;
  border-spacing: 0;
}
.table-frozen .table th,
.table-frozen .table td {
  white-space: nowrap;
}
.table-frozen .table th {
  /* 容器自己可捲，表頭改黏容器頂端而不是頁首 */
  top: 0;
}
/* 🔴 P2 疊在 P3 上面時，上面那兩條 `.table-p2 thead tr.table-*` 的權重比
 * `.table-frozen .table th` 高（多一個 class），於是表頭又跑回「距容器頂 40px／64px」
 * ——整條深色表頭浮在捲動框裡壓住前兩列資料，正是 2026-09-15 估價師抓到的那個長相。
 * 在凍結容器裡兩列表頭一律從 0 起算。 */
.table-frozen .table-p2 thead tr.table-group th {
  top: 0;
}
.table-frozen .table-p2 thead tr.table-cols th {
  top: var(--table-group-h);
}
.table-frozen .table td {
  border-top: 0;
  border-bottom: 1px solid var(--line-subtle);
}
/* 🔴 凍結欄不能寫成 `.table-frozen .table th:nth-child(n)`：分組表頭那一列的
 * 第二格是**第二個分組**（帶 colspan），不是第二欄，會被釘到 left:148px 而與
 * 第一組重疊、與底下資料欄錯位（Codex 2026-09-15 指出）。
 * 所以只釘「資料列」與「欄名列」。⚠️ 已知限制：P2＋P3 同時開時，分組列會跟著
 * 橫向捲動而不釘住——那是本版刻意的取捨，正確做法要算出哪幾個分組覆蓋凍結欄，
 * 留給日後真的有這種表時再做。
 * ⚠️ 固定寬要配 max-width＋截字才成立：只有 width＋min-width 時，長路名在自動
 * 排版下會把首欄撐過 148px，而第二欄仍釘在 left:148px，橫捲就疊在一起。
 * 截字是第 15 列已寫明的代價（「路段長的會截字」）。 */
.table-frozen .table thead tr:not(.table-group) th:nth-child(1),
.table-frozen .table tbody td:nth-child(1) {
  position: sticky;
  left: 0;
  z-index: 3;
  width: 148px;
  min-width: 148px;
  max-width: 148px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.table-frozen .table thead tr:not(.table-group) th:nth-child(2),
.table-frozen .table tbody td:nth-child(2) {
  position: sticky;
  left: 148px;
  z-index: 3;
  width: 66px;
  min-width: 66px;
  max-width: 66px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.table-frozen .table tbody td:nth-child(1),
.table-frozen .table tbody td:nth-child(2) {
  /* 釘住的欄要有不透明底，否則捲動時會看到底下的資料透出來 */
  background: var(--surface-card);
}
/* 釘住欄的預設是深色粗體（它是整列的身分），但**不得蓋掉 P1 的三階強調**。
 * 🔴 2026-09-15 實測：P1 把第 0 欄標成一級時，這條規則的權重（含 :nth-child）
 * 高過 `.table-p1 td.table-p1-1`，磚橘 700 被打回墨黑 600——三層可疊加是第 8 列
 * 明訂的能力，疊起來卻互相吃掉，而且只在「一級剛好落在釘住欄」時才看得出來。 */
.table-frozen .table tbody td:nth-child(1):not([class*="table-p1-"]),
.table-frozen .table tbody td:nth-child(2):not([class*="table-p1-"]) {
  font-weight: 600;
  color: var(--text-primary);
}
.table-frozen .table thead tr:not(.table-group) th:nth-child(1),
.table-frozen .table thead tr:not(.table-group) th:nth-child(2) {
  z-index: 4;
}
.table-frozen .table tbody tr:hover td {
  background: var(--surface-row-hover);
}

/* ---------------- 可捲動框：表頭黏容器頂端（ML-HTML 第 87 列點名的底座缺口） ----------------
 *
 * 🔴 這是量出來的洞，不是預防性規則：`valuation-dashboard` 的檢核表裝在
 * `max-height:600px; overflow:auto` 的框裡，而底座上面那條 `.table th` 的
 * `top: var(--site-header-h)` 在**捲動容器裡**是「距容器頂端 56px」，於是整條深色
 * 表頭往下浮、壓在前兩列資料上面。唯一釘住 top:0 的 `.table-frozen` 會**連帶凍結
 * 前兩欄**（148／66px 固定寬＋截字），只是想要一個會捲的框的人不該被迫接受截字。
 * ⇒ 缺的就是「可捲動的框、表頭黏容器頂端、**不**凍結欄」這一種。
 *
 * 做法與 `chart.css` 的 `.chart-freeze`（第 49 步，熱力圖列名黏左緣）同一族：
 * **sticky ＋ 不佔版面 ＋ 列印時關掉**。差別在方向與「不佔版面」怎麼做到——
 * 圖那邊黏的是外加的一層 SVG，要用負外距把自己的寬度扣回去才不會把圖往右推；
 * 表格這邊黏的是 `<th>`**本體**，它本來就在表格的排版流裡，**沒有多出來的寬度要扣**。
 * ⇒ 負外距在這裡不該寫，寫了反而會把第一列資料往上拉。
 */
.table-scroll {
  /* 框的高度由呼叫端給：`render_table(scroll_h="600px")` 會寫成 --table-scroll-h。
     fallback 只在有人手刻 `.table-scroll` 又忘了給的時候生效（同 --chart-canvas-w）。 */
  max-height: var(--table-scroll-h, 60vh);
  overflow: auto;
  border-radius: var(--r-md);
  border: 1px solid var(--table-wrap-border);
  background: var(--table-wrap-bg);
}
/* 🔴 這條是整件事的核心：容器自己可捲，表頭要黏**容器**頂端而不是頁首。
 * 權重 `.table-scroll .table th` (0,2,1) 壓得過上面 `.table th` 的 (0,1,1)。
 * ⚠️ 表頭的底色由上面那條 `.table th` 給（--surface-section-deep），**不可以改成透明**：
 * 它要蓋住捲上來的資料列。改透明的畫面是「數字從表頭底下透出來」，不報錯。 */
.table-scroll .table th {
  top: 0;
}
/* P2 兩列表頭疊在一起時也要從 0 起算（同 `.table-frozen` 那兩條的理由）：
 * `.table-p2 thead tr.table-group th` 是 (0,3,1)，不在這裡壓過去的話分組列
 * 又回到「距容器頂 56px」，整條表頭浮在框裡壓住資料。 */
.table-scroll .table-p2 thead tr.table-group th {
  top: 0;
  /* 🔴 分組名**不准換行**。`--table-group-h` 是釘死的 24px，但 `height` 管不住
     內容撐高：2026-09-19 實測 375px 上長分組名換成兩行，分組列變 48px，而欄名列
     還黏在 `top: 24px` ⇒ 捲動後欄名列**壓住分組列下半 24px**。
     這條是把「疊加值算得準」的前提補起來，不是裝飾。
     代價是分組名太長時會撐寬欄位（配下面手機那組的 max-content，靠橫捲看完）。 */
  white-space: nowrap;
}
.table-scroll .table-p2 thead tr.table-cols th {
  top: var(--table-group-h);
}
/* ---------------- 手機 375px：兩處必須退化 ---------------- */
@media (max-width: 640px) {
  /* 桌機外框是 overflow:clip（為了裁 8px 圓角），但 8 欄的比較標的表在 375px
     會被裁掉右邊四欄而且捲不到。手機改成可橫向捲。 */
  .table-wrap {
    overflow-x: auto;
  }
  /* 🔴 光給外框 overflow-x 不夠。表格是 width:100%，會被壓回 295px，欄寬不足
     就逐字換行——2026-09-15 實測 375px 下「信義區松仁路 100 號」被拆成一字一行，
     單一資料列高 163.6px，而橫向捲根本用不到（欄已經擠爛了）。
     改成 max-content＋不換行，欄寬回到內容需要的寬度，真的靠橫捲看完。 */
  /* 🔴 `.table-scroll` 要**一起**吃這兩條（2026-09-19 cross-review 指出、實測證實）：
     漏掉的話 375px 上表格被壓回 309px、欄位逐字換行，資料列高從 34.9px 變成
     **126.8px**。它自己就是捲動容器，橫捲本來就成立，缺的只是欄寬。
     ⚠️ 但**不可以**跟著吃下面那條 `position: static`——`.table-wrap` 關掉 sticky 是因為
     它在手機上才變成捲動容器、表頭的 top 會解析錯；`.table-scroll` 從頭到尾都是捲動
     容器、top 一直是 0，關掉它等於把本輪做的事在手機上整個拿掉。 */
  .table-wrap .table,
  .table-scroll .table {
    width: max-content;
    min-width: 100%;
  }
  .table-wrap .table th,
  .table-wrap .table td,
  .table-scroll .table th,
  .table-scroll .table td {
    white-space: nowrap;
  }
  /* 🔴 容器一旦可捲，`overflow-y` 也被瀏覽器一併算成 auto，表頭的 top 於是變成
     「距容器頂端」，直接壓在前兩列資料上面（2026-09-15 手機截圖看出來的）。
     手機關掉 sticky——這就是「看得到全部欄位」換來的已知代價。 */
  .table-wrap .table th,
  .table-wrap .table thead tr.table-group th,
  .table-wrap .table thead tr.table-cols th {
    position: static;
  }
  /* .table-frozen 本來就是捲動容器，表頭黏容器頂端沒問題，維持 sticky；
     但它的 P2 疊加值也要照 .table-frozen 的 0 起算——這條在桌機已經寫過，
     手機沿用同一份，不另寫數字。 */
}

/* ── 列印：把框拆掉 ──
 *
 * 🔴 紙上沒有捲軸。留著 max-height 的話，框高之外那幾十列**整批不印**——
 * 與第 49 步熱力圖「15 個數字只印出 2 個」是同一種失效：畫面上看得到、紙上沒有。
 * 實測（`evidence/2026-09-19-ml-html-step51/`，20 列的表裝進 220px 的框）：
 * 沒有這兩條時紙上只印出 **5／20 列**，有這兩條是 20／20。
 *
 * ⚠️ 這裡**刻意不把表頭改回 `position: static`**。第一版寫了那一條，理由是
 * 「sticky 會讓 `<thead>` 的跨頁重複印失效」——`probe_thead_repeat_51.py` 拿
 * 60 列跨三頁的表兩個版本各印一次，**static 與 sticky 都是印 2 次，完全相同**，
 * 那個理由是我編的、不成立。框拆掉之後沒有捲動祖先，sticky 本來就不作用。
 * 說不出必要性的規則就不留（同第 50 步刪掉多餘的 @media print 那兩條）。
 */
@media print {
  .table-scroll {
    max-height: none;
    overflow: visible;
  }
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/view-tab/view-tab.css ---- */
.view-tab {
  border-bottom: 1px solid var(--line-subtle);
  font-family: var(--font-body);
}
.view-tab-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  gap: var(--sp-5);
}
.view-tab-item {
  position: relative;
}
/* N1 底線式五態 (ML-HTML 第 23 列)。已捲過的章不加 class、不變淡。 */
.view-tab-link {
  display: inline-block;
  padding: var(--sp-4) var(--sp-1);
  color: var(--text-secondary);
  text-decoration: none;
  font-weight: 400;
  font-size: var(--fs-small);
  transition: color 0.15s ease, box-shadow 0.15s ease;
}
.view-tab-link:hover {
  color: var(--text-link-strong);
  box-shadow: inset 0 -2px 0 var(--line-subtle);
}
.view-tab-link:focus-visible {
  outline: 2px solid var(--text-accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
.view-tab-link:active {
  color: var(--text-link-strong);
  box-shadow: inset 0 -3px 0 var(--text-accent-aa);
  transform: translateY(1px);
}
.view-tab-active .view-tab-link,
.view-tab-link[aria-current="page"] {
  color: var(--text-link-strong);
  font-weight: 500;
  box-shadow: inset 0 -2px 0 var(--text-accent);
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/three-card/three-card.css ---- */
.three-card {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-4);
  font-family: var(--font-body);
}
.three-card-cell {
  padding: var(--sp-5);
  border-radius: var(--r-md);
}
.three-card-clause { background: var(--three-card-clause-tint); }
.three-card-detail { background: var(--three-card-detail-tint); }
.three-card-view   { background: var(--three-card-view-tint); }
.three-card-label {
  font-size: var(--fs-note);
  font-weight: 700;
  color: var(--text-muted);
  letter-spacing: 0.05em;
  margin-bottom: var(--sp-2);
}
.three-card-text {
  color: var(--text-primary);
  font-size: var(--fs-body);
  line-height: 1.65;
}
.three-card-cite {
  margin-top: var(--sp-2);
  font-size: var(--fs-note);
  color: var(--text-muted);
}
@media (max-width: 768px) {
  .three-card { grid-template-columns: 1fr; }
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/cite-chip/cite-chip.css ---- */
.cite-chip {
  display: inline-block;
  padding: 0 var(--sp-1);
  margin-left: 2px;
  border-radius: var(--r-sm);
  background: var(--cite-chip-bg);
  color: var(--cite-chip-fg);
  font-size: var(--fs-note);
  line-height: 1.4;
  font-family: var(--font-numeric);
  font-weight: 600;
  vertical-align: super;
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/button/button.css ---- */
.button {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-5);
  border: 0;
  border-radius: var(--r-sm);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: 600;
  line-height: 1.4;
  cursor: pointer;
  text-decoration: none;
  transition: opacity 0.15s ease;
}
.button:hover { opacity: 0.88; }
.button:focus-visible {
  outline: 2px solid var(--text-accent);
  outline-offset: 2px;
}
.button-primary {
  background: var(--action-primary-bg);
  color: var(--action-primary-fg);
}
.button-secondary {
  background: var(--action-accent-bg);
  color: var(--action-accent-fg);
}
.button-ghost {
  background: transparent;
  color: var(--text-primary);
  border: 1px solid var(--line-strong);
}
.button-icon {
  padding: var(--sp-2);
  background: transparent;
  color: var(--text-primary);
}
.button-icon .button-label { display: none; }
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/form/form.css ---- */
.form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  font-family: var(--font-body);
}
.form-label {
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--text-primary);
}
.form-input,
.form-textarea,
.form-select {
  padding: var(--sp-2) var(--sp-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--surface-card);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: var(--fs-body);
}
.form-input:focus,
.form-textarea:focus,
.form-select:focus {
  outline: 2px solid var(--text-accent);
  outline-offset: 1px;
}
.form-textarea {
  min-height: 80px;
  resize: vertical;
}
.form-checkbox {
  width: 16px;
  height: 16px;
  accent-color: var(--action-primary-bg);
}
.form-checkbox + .form-label,
.form .form-checkbox {
  margin-right: var(--sp-2);
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/state/state.css ---- */
.state {
  padding: var(--sp-7);
  text-align: center;
  font-family: var(--font-body);
  border-radius: var(--r-md);
  background: var(--surface-card);
}
.state-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-h3);
  color: var(--text-primary);
  margin-bottom: var(--sp-2);
}
.state-body {
  color: var(--text-muted);
  font-size: var(--fs-body);
  line-height: 1.65;
}
.state-empty   .state-title { color: var(--text-muted); }
.state-loading .state-title { color: var(--text-muted); }
.state-error   .state-title { color: var(--state-bad); }
.state-success .state-title { color: var(--state-good); }
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/density/density.css ---- */
.density-comfortable {
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;
  --sp-7: 48px;
}
.density-compact {
  --sp-4: 12px;
  --sp-5: 16px;
  --sp-6: 24px;
  --sp-7: 32px;
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/section-rhythm/section-rhythm.css ---- */
.section-rhythm {
  display: flex;
  flex-direction: column;
}
.section-rhythm-item {
  padding: var(--section-padding-y) 0;
}
.section-rhythm-body {
  font-family: var(--font-body);
  color: inherit;
  line-height: 1.7;
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/sub-title/sub-title.css ---- */
/* 小節標題：章底下再分的那一層（「三、近鄰地區成交水準」）。

   估價師 2026-09-18 裁示②（MAINLINES 第 124 列）：左側加一條導引線，
   正式收進底座。在此之前底座沒有這一層，第 37 步的比稿頁與第 61 步的
   估價儀表板各自繞過去一次（後者把三個 h3 升格成獨立章節），正是
   「未來能適用在各種情境」要避免的重複。 */
.sub-title {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  border-inline-start: 2px solid var(--line-accent-subtle);
  padding-inline-start: var(--sp-4);
  margin-top: var(--sp-5);
  margin-bottom: var(--sp-3);
}
.sub-title-text {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  line-height: var(--lh-h3);
  font-weight: var(--fw-h3);
  color: var(--text-primary);
}
.sub-title-note {
  font-size: var(--fs-small);
  color: var(--text-muted-aa);
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/logo/logo.css ---- */
.logo {
  display: inline-block;
  line-height: 0;
}
.logo img {
  display: block;
  height: 100%;
  width: auto;
  max-width: 100%;
}
.logo-header {
  /* 🔴 2026-09-16 估價師看過四案模擬畫面後定案：頁首 44px 配 logo 36px
     （上下各 4px）。⚠️ 640px 以下 site-header.css 會覆寫回 24px 讓位給導覽。 */
  height: 36px;
}
.logo-footer {
  height: 48px;
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/chapter-header/chapter-header.css ---- */
.chapter-header {
  font-family: var(--font-body);
}

/* ============ S0 卡片版 (variant=card) ============ */
/* 🔴 2026-09-24 估價師裁示（ML-HTML 第 6 列）：卡片版**整章內容都在卡片裡**，
 * 左條延伸到章尾；長章不拆卡，改由左條的章號跟著捲（見 .chapter-header-bar-inner）。
 * 舊版只包章名、內容排在卡外，估價師：「這不是我要的意思」。 */
.chapter-header-card {
  display: grid;
  grid-template-columns: 44px 1fr;
  background: var(--surface-card);
  /* 放在深底區塊時，卡內字不能繼承深底的白字 */
  color: var(--text-primary);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-card-soft);
  /* 用 clip 不用 hidden：hidden 會讓卡片變成捲動容器，
     左條章號與表格的固定表頭（position: sticky）都會失效。 */
  overflow: clip;
  margin: var(--sp-6) 0 var(--sp-5);
}
.chapter-header-main {
  min-width: 0; /* 寬表格在卡內橫捲，不撐破 grid */
  padding: 0 var(--sp-5) var(--sp-5);
}
.chapter-header-main > .chapter-header-body {
  padding-left: 0;
  padding-right: 0;
}

/* 左條。⚠️ 以下實值全部對齊 /design 畫布 G73SvXqdbu8XJjpCpdemk3 的 .rail，
 * 不是自己重新配色——一般章是**近白底＋金字＋右側細線**，
 * 只有重點章才整條金（金漸層＋白字）。 */
.chapter-header-bar {
  /* grid 預設 stretch：左條自動延伸到章尾 */
  padding: 18px 0 14px;
  /* 🔴 2026-09-15 估價師：「米灰色的卡片左側顏色難以識別」。實測底色 #fafaf9
     對白卡只有 1.04 對比（看不出有一條）、金字對它 4.55（12px 直排中文擦邊）。
     改成淡金底＋金色細線＋深藍字：底對白卡 1.14 且有色相差、字對底 11.54。
     「一般章淡、重點章整條金」這個對比關係不變，只是把兩個對比都拉到讀得到。 */
  background: var(--surface-chapter-rail);
  border-right: 1px solid var(--line-chapter-rail);
  color: var(--text-chapter-rail);
}
/* 章號、分類、刻度跟著捲，停在頁首下方（2026-09-24 裁示「章號跟著捲」）。
   純 CSS sticky，不用 JS；捲到章尾會被卡片底邊帶走。 */
.chapter-header-bar-inner {
  position: sticky;
  top: calc(var(--site-header-h) + var(--sp-4));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-1);
}
/* 深底區塊裡：左條與深底同色（teal-500）會融進背景、卡片看起來少一截，
   改淺一階。重點章的金漸層不受影響（下一條規則特異度較高）。 */
.section-deep .chapter-header-bar {
  background: var(--surface-chapter-rail-on-deep);
}
/* 重點章：整條金漸層、白字、不留右側線 */
.chapter-header-highlight .chapter-header-bar,
.section-deep .chapter-header-highlight .chapter-header-bar {
  background: linear-gradient(
    180deg,
    var(--text-accent) 0%,
    var(--text-accent-aa) 100%
  );
  border-right: none;
  color: var(--text-on-deep);
}
.chapter-header-bar-num {
  font-family: var(--font-numeric);
  font-size: var(--fs-small);
  font-weight: 700;
}
.chapter-header-bar-cat {
  writing-mode: vertical-rl;
  text-orientation: upright;
  letter-spacing: 4px;
  font-size: 12px;
  font-weight: 700;
  margin: var(--sp-2) 0 10px;
}
/* 刻度：5 段靜態橫條，落在目前進度那一段變長變金。
 * 估價師 2026-09-15 對「隨滾動變大的動態版」裁示「太複雜就維持目前這樣」，
 * 「目前這樣」＝畫布上的這 5 條靜態橫條。 */
.chapter-header-bar-tick {
  width: 12px;
  height: 3px;
  border-radius: 2px;
  /* 左條改綠底後，原本的淺灰刻度在深底上看不見；改用半透明白，
     與重點章（金漸層）用同一套做法。 */
  background: rgba(255, 255, 255, 0.35);
}
.chapter-header-bar-tick-on {
  width: 20px;
  background: var(--text-accent);
}
.chapter-header-highlight .chapter-header-bar-tick {
  background: rgba(255, 255, 255, 0.35);
}
.chapter-header-highlight .chapter-header-bar-tick-on {
  background: var(--text-on-deep);
}
.chapter-header-body {
  padding: var(--sp-5);
}
.chapter-header-eyebrow {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-note);
  color: var(--text-muted);
  letter-spacing: 0.5px;
}
.chapter-header-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  font-weight: var(--fw-h2);
  line-height: var(--lh-h2);
  color: var(--text-primary);
}

/* ============ C2 全寬眉標版 (variant=light) ============ */
.chapter-header-light {
  margin: var(--sp-7) 0 var(--sp-5);
}
.chapter-header-light .chapter-header-eyebrow {
  letter-spacing: 1.6px;
}
.chapter-header-headline {
  display: flex;
  align-items: baseline;
  gap: var(--sp-4);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--line-subtle);
}
.chapter-header-bignum {
  flex: 0 0 64px;
  width: 64px;
  text-align: right;
  font-family: var(--font-numeric);
  font-size: 40px;
  font-weight: 300;
  line-height: 1;
  color: var(--text-accent-aa);
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/site-header/site-header.css ---- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5);
  height: var(--site-header-h);
  padding: 0 var(--sp-5);
  background: var(--surface-glass);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line-subtle);
}
/* 頁首是 sticky 的，錨點捲過去時目標會被它蓋掉一個頁首高。
   導覽頁籤全都是錨點，沒有這條的話每個頁籤點下去標題都藏在頁首後面。 */
html {
  scroll-padding-top: var(--site-header-h);
}
.site-header-brand {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
}
.site-header-context {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fs-note);
  font-weight: 500;
  color: var(--text-muted);
  padding-left: var(--sp-4);
  border-left: 1px solid var(--line-subtle);
}

.site-header-nav {
  display: flex;
  align-items: center;
  min-width: 0;
  /* 分頁多於畫面放得下的量時，在頁首內橫向捲，不把整頁撐寬。
   * 🔴 這三行原本只寫在 `@media (max-width: 640px)` 裡，所以 641px~約 950px
   * 這一段沒有任何收容機制：導覽直接畫到視窗外、整頁長出橫向捲軸，
   * 最後一兩格點不到。實測（第 53 步）備標分析七格導覽在 890px 就開始溢出，
   * 加第八格「地圖」之後提前到 950px——不是加格造成的，是加格讓它更明顯。
   * 寬螢幕沒東西可捲，所以這幾行在桌機上不改變任何外觀。 */
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.site-header-nav::-webkit-scrollbar {
  display: none;
}
/* 導覽放進 56px 頁首：去掉 view-tab 自己的下邊框（頁首已有）並壓縮上下 padding */
.site-header-nav .view-tab {
  border-bottom: none;
}
.site-header-nav .view-tab-link {
  padding-top: var(--sp-2);
  padding-bottom: var(--sp-2);
  white-space: nowrap;
}

/* 窄螢幕：讓位給導覽。橫向捲那三行已提升到上面的基礎規則（所有寬度都適用），
 * 這裡只留手機才要做的讓位：縮 logo、藏案名、縮分頁間距、不換行。
 * 實測 375px 下五個分頁會在 56px 的頁首裡直排斷字（「申請須知」變三行），
 * 整列導覽被切掉一半。頁首高度是第 5 列的裁示不能變，所以讓它捲。 */
@media (max-width: 640px) {
  .site-header {
    gap: var(--sp-3);
    padding: 0 var(--sp-3);
  }
  /* 桌機把 logo 放大到 32px（估價師 2026-09-15：「Logo過小」），但 375px 下
     橫式 lockup 會撐到 224px、佔掉 60% 寬度，導覽被擠到只剩一個半分頁。
     手機縮回 24px（168px 寬），把空間讓給導覽；桌機維持放大。 */
  .site-header .logo-header {
    height: 24px;
  }
  .site-header-context {
    display: none;
  }
  .site-header-nav .view-tab-list {
    gap: var(--sp-4);
    flex-wrap: nowrap;
  }
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/site-footer/site-footer.css ---- */
.site-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-4);
  padding: var(--sp-6) var(--sp-5);
  background: var(--surface-card);
  border-top: 1px solid var(--line-subtle);
}
.site-footer-brand {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
}
.site-footer-name {
  font-size: var(--fs-note);
  color: var(--text-muted);
  line-height: var(--lh-note);
}
.site-footer-links {
  display: flex;
  gap: var(--sp-5);
}
.site-footer-link {
  font-size: var(--fs-small);
  color: var(--text-secondary);
  text-decoration: none;
}
.site-footer-link:hover {
  color: var(--text-link-strong);
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/map/map.css ---- */
/* map —— Leaflet 地圖的容器與品牌化（ML-HTML 第 6 步）
 *
 * 底座不打包 Leaflet 本身；這支只負責「地圖在版面裡佔多大、邊框圓角跟表格一致、
 * 圖例放哪、出處寫哪」，以及把 Leaflet 預設的藍色 UI 換成品牌色。
 *
 * ⚠️ 下半段那些 .leaflet-* 覆寫會被 lint 的 vendor 遮罩略過（vendorcss.py 依選擇器
 * 判定），所以那幾條寫錯 lint 不會擋，改動時自己看畫面。
 */

.map-figure {
  position: relative;   /* 圖例在寬螢幕疊在地圖上，錨點是這裡不是 .map-canvas */
  /* 自成一個堆疊脈絡：圖例的 z-index 才不會跟固定頁首（z-index 100）比大小。
     Leaflet 自己的 pane 用 400~700，圖例要蓋過它們，所以數字必須大——
     沒有 isolate 的話那個數字會漏到全頁層級去壓住頁首。 */
  isolation: isolate;
  margin: 0 0 var(--sp-5);
  font-family: var(--font-body);
}

.map-canvas {
  position: relative;
  width: 100%;
  height: var(--map-height, 420px);
  border-radius: var(--r-md);
  border: 1px solid var(--line-subtle);
  background: var(--surface-section-soft);
  overflow: hidden;
}

/* 圖例疊在地圖右上；窄螢幕落到地圖**下方**，不壓掉可視範圍。
 * ⚠️ 圖例在 DOM 上是 .map-canvas 的**兄弟**不是子元素——放在裡面的話，
 * .map-canvas 是固定高度＋overflow:hidden，窄螢幕改成 position:static
 * 只會卡在地圖左上角疊著，根本落不到下方。 */
.map-legend-slot {
  position: absolute;
  top: var(--sp-3);
  right: var(--sp-3);
  z-index: 500;
  max-width: 240px;
}

.map-caption {
  margin-top: var(--sp-2);
  font-size: var(--fs-note);
  line-height: var(--lh-note);
  color: var(--text-muted);
}

.map-source {
  margin-top: var(--sp-1);
  font-size: var(--fs-note);
  color: var(--text-muted);
}

@media (max-width: 640px) {
  .map-canvas {
    /* 🔴 fallback 要串起來：只寫 var(--map-height-sm, 300px) 的話，
       呼叫端用 height= 給的 --map-height 在窄螢幕會被強制蓋成 300px
       （2026-09-16 第 32 步修；chart.css 是同一個 bug，第 31 步已修）。 */
    height: var(--map-height-sm, var(--map-height, 300px));
  }
  .map-legend-slot {
    position: static;
    max-width: none;
    margin-top: var(--sp-2);
  }
}

/* ---- Leaflet 品牌化覆寫 ---- */
.leaflet-container {
  font-family: var(--font-body);
  background: var(--surface-section-soft);
}
.leaflet-container a {
  color: var(--text-link-strong);
}
.leaflet-bar a {
  color: var(--text-primary);
  border-bottom-color: var(--line-subtle);
}
.leaflet-bar a:hover {
  background: var(--line-faint);
}
.leaflet-popup-content-wrapper {
  border-radius: var(--r-md);
  box-shadow: var(--shadow-card-hover);
}
.leaflet-control-attribution {
  font-size: var(--fs-note);
  background: var(--surface-glass);
  color: var(--text-muted);
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/chart/chart.css ---- */
/* chart —— 圖表的外殼（ML-HTML 第 31 步）
 *
 * 底座**不打包任何圖表函式庫**，同 map 不打包 Leaflet 的先例。這支只負責
 * 「圖在版面裡佔多大、標題副標怎麼排、圖說與出處寫哪」——也就是三支畫圖的
 * skill 各寫了一套、三套命名還不一樣的那一層。
 *
 * 盤點（第 31 步，Explore 逐檔 grep）：
 *   valuation-dashboard         Chart.js 4.4.1  自己的 .chart-box／.chart-box.tall
 *   appraisal-comparison-viz    ECharts 5       自己的 .chart-card／-head／-title／-sub／-note
 *   appraisal-firm-comparison-viz ECharts 5     自己的 .afc-chart
 * 前兩支的系列色全部寫死 hex，只有第三支讀底座的 --chart-series-*。
 *
 * 🔴 **class 名刻意避開那三套**：底座若也叫 `.chart-title`／`.chart-card`／`.chart-note`，
 * 那兩支既有交付物的同名 CSS 會與底座互相覆寫，**已經交出去的網頁外觀會變**。
 * 所以這裡用 map 的同一套詞彙（figure／plot／caption／source），撞名數為 0。
 * 未來那幾支要遷移過來時，是刪自己那份、改用這裡的名字，不是反過來。
 *
 * 系列色：用 --chart-series-1~6（裁示第 24 列，兩兩灰階差 ≥19.8），漲跌用
 * --chart-up／--chart-down。圖表函式庫讀不到 CSS 變數，呼叫端要自己
 * getComputedStyle 取出來餵給它——寫法見 references/recipe-new-page.md。
 */

/* 🔴 外框用陰影不用 1px 線（第 32 步遷移時抓到）：底座 `.card` 的預設變體是
 * `box-shadow: var(--shadow-card)`、**沒有** border，1px 線是 `.card-outlined`
 * 才有的變體。chart 第一版寫死了 border，等於讓每一張圖表都變成 outlined，
 * 與 `.card` 預設不一致，也牴觸使用者「卡片不要 1px 外框」那條硬規則。
 * 遷移 appraisal-firm-comparison-viz 時實測：原本 `card card-default` 量到
 * border 0px，換成 chart-figure 後變 1px，圖表容器寬度 1088 → 1086。 */
.chart-figure {
  margin: 0 0 var(--sp-5);
  padding: var(--sp-5);
  border-radius: var(--r-md);
  background: var(--surface-card);
  box-shadow: var(--shadow-card);
  font-family: var(--font-body);
}

.chart-figure-head {
  margin-bottom: var(--sp-4);
}

.chart-figure-title {
  font-size: var(--fs-h3);
  font-weight: 700;
  line-height: var(--lh-h3);
  color: var(--text-primary);
}

.chart-figure-sub {
  margin-top: var(--sp-1);
  font-size: var(--fs-small);
  line-height: var(--lh-note);
  color: var(--text-muted);
}

/* 高度給預設值而不是 aspect-ratio：Chart.js 與 ECharts 都在初始化時量容器高，
 * 容器高度若由內容決定，兩邊都會量到 0 而畫出一條線。
 *
 * 🔴 position:relative 不是裝飾（第 32 步遷移 valuation-dashboard 時實測到）：
 * Chart.js 在 `maintainAspectRatio:false` 下量的是 canvas 的**父層**。父層若沒有
 * 自己的尺寸，量到的高度會被剛畫好的 canvas 再撐大，下一格又量一次——實測一張
 * 300px 的長條圖長到 **9648px**，而且 console 一個字都不吐。所以 canvas 版的
 * chart_id 掛在裡面的 <canvas> 上，.chart-plot 是外面那個有尺寸的框。 */
.chart-plot {
  position: relative;
  display: block;
  width: 100%;
  height: var(--chart-height, 320px);
}

/* 🔴 直接放 SVG 進 .chart-plot 的人一定會中 layout/grid.css 那條全站的
 * `img, svg, video { max-width:100%; height:auto }`（它防的是圖片撐爆容器）：
 * SVG 的 height="100%" 被蓋成 auto，於是依 viewBox 比例長高、溢出這個有尺寸的框。
 *
 * 第 31／34 步的解法是要呼叫端自己 inline `style="height:100%"`，但那一招
 * **與底座自己的 lint 規則 `style-attr` 打架**（inline style 只准設自訂屬性）——
 * 等於底座一邊叫人這樣寫、一邊把這樣寫的交付物判違規。改成由底座出這條規則，
 * 呼叫端什麼都不必做，兩個問題一起沒了。
 *
 * ⚠️ 這條同時也蓋掉 ECharts SVG renderer 產生的 <svg>（它是 .chart-plot 的直接子層），
 * 那正是第 34 步列為「未實測」的那個風險。canvas renderer 不受影響。 */
.chart-plot > svg {
  /* 只蓋 height：全站那條規則動的是 height（`max-width` 不是問題所在），
     連 width 一起蓋會覆寫呼叫端自己寫在 <svg width="…"> 上的寬度，
     副作用超出要解的問題（2026-09-17 cross-review 指出）。
     display:block 要留——SVG 預設是 inline，底下會多出一條行高的空隙。 */
  display: block;
  height: 100%;
}

/* ⚠️ 這條的兩個前提，用的人要知道（cross-review 指出，屬文件不屬程式）：
 * ① **SVG 必須是 .chart-plot 的直接子層**。在外面多包一層 <div> 這條就不生效，
 *    SVG 會回去吃 grid.css 的 height:auto 而溢出。
 * ② **不要在 .chart-plot 裡直接放圖示用的小 <svg>**，它會被撐滿整個圖表框。
 *    要放就包一層，或改用 CSS 背景。 */

.chart-caption {
  margin-top: var(--sp-3);
  font-size: var(--fs-note);
  line-height: var(--lh-note);
  color: var(--text-muted);
}

.chart-source {
  margin-top: var(--sp-1);
  font-size: var(--fs-note);
  color: var(--text-muted);
}

/* ── 懸浮標示（ML-HTML 第 98 列，估價師 2026-09-18 裁示 B「只加懸浮標示」）──
 *
 * 滑過某一根／某一點，它變成重點色並浮出一個小框寫類別與數值。
 * 🔴 **不要動畫**。同一輪比稿的 C 版（捲進畫面才長出來）他看過之後沒有選，
 * 而且捲動驅動的動畫被別人嵌進 iframe 時會**靜默失效**
 * （實測 evidence/2026-09-18-ml-html-step38/probe_pane_iframe.py：
 * 內層文件從頭到尾沒捲過，時間軸一開始就 finished，長條直接滿高，不報錯）。
 *
 * 🔴 **這一段不需要任何 JS**，底座「不出 JS」那條（第 13 列）不受影響：
 * 我們的圖是伺服器端產好的 inline SVG，`<g>:hover` 是純 CSS。
 * ⚠️ 反過來說，**Chart.js／ECharts 畫的圖套不到這一段**——canvas 沒有可以 hover
 * 的子元素，ECharts 的 SVG 也不是我們的 class。那兩支用它們自己的 tooltip，
 * 外觀對齊靠 chart-shape.json 的 hover 欄位，不是靠這幾條 CSS。
 *
 * 標記長這樣（形狀數值全部從 assets/tokens/chart-shape.json 的 hover 組讀）：
 *
 *   <g class="chart-hover">
 *     <rect class="chart-hit" .../>            ← 透明感應區，鋪滿整個類別欄
 *     <rect class="chart-mark" .../>           ← 真的那根長條／那個點
 *     <text>112.6</text>                        ← 原本就印在圖上的數值標籤
 *     <g class="chart-tip"> ... </g>           ← 小框，預設 opacity:0
 *   </g>
 *
 * 🔴 `.chart-hit` 要畫在 `.chart-mark` **之前**：SVG 後畫的蓋在上面，
 * 感應區畫在後面會把長條整根蓋掉，長條自己的 hover 反而收不到。
 * 🔴 感應區鋪滿整欄不是偷懶：長條最窄 8px（chart-shape bar.min_width），
 * 只靠長條本身當感應區的話滑鼠要對得很準，實測會被當成「沒有互動」。
 */
.chart-hover .chart-mark {
  /* 秒數與緩動照抄 motion.css 既有的 0.18s ease（同 .yk-lift）。
   * 🔴 底座沒有 --dur-* ／--ease-* 這兩族 token，寫下去整條宣告會失效，
   * 而且畫面上看不出來（第 38 步查 getComputedStyle 才發現的坑）。
   * 要開那兩族 token 是另一個決定，不是在這裡順手加。 */
  transition: fill 0.18s ease;
}

.chart-hover:hover .chart-mark {
  fill: var(--chart-hover);
}

.chart-tip {
  opacity: 0;
  transition: opacity 0.18s ease;
  /* 小框自己不吃滑鼠：它浮在圖形上方，吃了會在小框與長條之間來回閃。 */
  pointer-events: none;
}

.chart-hover:hover .chart-tip {
  opacity: 1;
}

/* 尊重系統設定與紙本，同 motion.css 的三條安全線。
 * ⚠️ 這裡只關「變化的過程」，不關「變化本身」——顏色仍然會換，
 * 只是不再有 0.18 秒的漸變。關掉互動不是這條規則的職責。 */
@media (prefers-reduced-motion: reduce) {
  .chart-hover .chart-mark,
  .chart-tip {
    transition: none;
  }
}

/* 列印：小框一律不出現。紙本沒有滑鼠，印出來的圖與 A 版完全相同
 * ——這正是估價師選 B 不必擔心紙本變差的原因。 */
@media print {
  .chart-tip {
    display: none;
  }
}

@media (max-width: 640px) {
  .chart-figure {
    padding: var(--sp-4);
  }
  .chart-plot {
    /* 🔴 fallback 要串起來：只寫 var(--chart-height-sm, 260px) 的話，
       呼叫端用 height= 給的 --chart-height 在窄螢幕會被強制蓋成 260px
       （2026-09-16 cross-review 抓到）。⚠️ map.css 有同一個寫法，未修。 */
    height: var(--chart-height-sm, var(--chart-height, 260px));
  }
}

/* ── 手機上左右滑（ML-HTML 第 111 列，估價師 2026-09-19 裁示「先 B，之後補 A」）──
 *
 * 現況的問題是量出來的（第 110 列）：圖的畫布固定 1040 寬，390px 的手機上圖框
 * 只剩 260px，整張被等比壓到 0.250 倍，規範裡 12 單位的字在螢幕上只有 3.00px。
 * 裁示的解法是 **B：畫布不動，外面包一層可以左右滑的框**——手機上看到的圖與
 * 報告裡那張一模一樣，只是一次看不完。
 *
 * 🔴 **框的寬高都要等於畫布自己的尺寸**，這是整條規則的地基：
 * SVG 是 meet 縮放，寬或高只要少一點，縮放比就改由另一邊決定、字**無聲**變小
 * （第 48 步實測踩過兩次：A 版 0.72、B 版 0.955，自己用寬度算都會算成 1.0）。
 * 所以呼叫端傳 `canvas="1040x294"`，由 `render_chart()` 把兩個值一起寫進來，
 * 不給呼叫端只寫一半的機會。
 *
 * 🔴 `justify-content: safe center` 的 `safe` 不可以拿掉：flex 置中一旦溢出，
 * 左半邊會捲不到（改不了的老問題）。`safe` 的意思是「放不下就改成靠左」。
 */
.chart-scroll {
  display: flex;
  justify-content: safe center;
  overflow-x: auto;
  overflow-y: hidden;
}

.chart-scroll > .chart-plot {
  flex: 0 0 auto;
  /* 畫布多寬就多寬。桌機上容器比它寬，safe center 會把它擺回正中央，
     與沒有這層框的時候畫面相同；手機上它比容器寬，於是可以左右滑。 */
  /* fallback 是舊行為（跟著容器寬）：`render_chart(canvas=…)` 一定會把它寫進來，
     這個值只在有人手刻 .chart-scroll 又忘了設它的時候生效。 */
  width: var(--chart-canvas-w, 100%);
  /* 🔴 高度也要在這裡再釘一次，不可以只釘寬度（2026-09-19 cross-review 抓到）：
     底下那條 `@media (max-width: 640px)` 會把 .chart-plot 的高度換成
     `--chart-height-sm`，交付物只要設了它，手機上圖框就比畫布矮——SVG 改由高度
     決定縮放（實測 240px 時是 0.764 倍），字跟著變小，而黏在左緣那層還停在畫布高，
     兩層列名就錯開。這條 selector 是 (0,2,0)，壓得過那條 (0,1,0)。
     ⚠️ 沒用捲動框的圖不受影響，`--chart-height-sm` 對它們照舊有效。 */
  height: var(--chart-height, 320px);
}

/* 🔴 熱力圖的列名要黏在框的左緣（同一次裁示的配套之一）：捲到右邊之後，
 * 左邊那欄區名會跟著捲出去，剩一排數字看不出是哪一區（第 48 步截圖
 * `m48_s-heat_right_390.png`）。
 *
 * 做法是**在原本那批列名上面，蓋一份不會捲走的**：黏住的那層畫的是同一批字、
 * 同一個座標系、同樣 1.0 的縮放，所以沒捲動的時候它與底下那批**逐像素重合**，
 * 看不出有兩層；一捲動，底下那批走掉、黏住的這層留在原位。
 *
 * 🔴 `margin-right` 是負的自己寬度：這層不佔版面寬度，圖本身的幾何完全沒動
 * ——不這樣做的話整張圖會被往右推，其餘九種圖也跟著位移。
 * 🔴 底色不可以是透明：它要蓋住捲過來的格子。
 */
.chart-freeze {
  flex: 0 0 auto;
  position: sticky;
  left: 0;
  z-index: 1;
  width: var(--chart-freeze-w, 0px);
  margin-right: calc(-1 * var(--chart-freeze-w, 0px));
  height: var(--chart-height, 320px);
  background: var(--surface-card);
  pointer-events: none;
}

/* 同 `.chart-plot > svg`：全站 grid.css 那條 `svg { height: auto }` 會讓它
 * 依 viewBox 比例長高，縮放比就不是 1.0，兩層字也就對不齊了。 */
.chart-freeze > svg {
  display: block;
  height: 100%;
}

/* ── 列印：整張攤平（同一次裁示的配套之二）──
 *
 * 🔴 這不是預防性的規則，是量出來的洞：第 48 步把比稿頁**真的印成 PDF 再抽字**，
 * B 版那張熱力圖在紙上 **15 個數字只印出 2 個**（那 2 個還是色帶圖例的低／高兩端），
 * 4 個區名倒是全印出來了——也就是格子裡的值整批被框裁掉。紙上沒有手指可以滑。
 *
 * 攤平＝把框還原成一般區塊、讓圖依紙寬等比縮小。字在紙上會跟著變小，
 * 但紙的解析度遠高於螢幕，而且「小」與「根本沒印出來」是兩回事。
 * 黏住的那層要關掉：圖縮小之後它還留在 1.0，會與底下那批錯開、變成兩份列名。
 */
/* ── 窄畫布重畫（A 版，ML-HTML 第 112 列，同一次裁示的後半段「之後補 A」）──
 *
 * B（左右滑）把字救回 12px，但手機上一次只看得到一小塊：第 49 步量到 320px 的
 * 螢幕上，熱力圖黏住的列名佔掉 168px、圖框只有 190px，同時讀得到的格子只剩
 * **22px**——比一個「163萬元／坪」還窄。A 是另一條路：**換一張窄的畫布重畫**，
 * 一眼看完，代價是欄名要縮寫、數值要少幾位、直向長條要改成橫向。
 *
 * 🔴 兩張圖同時寫進 HTML，用 media query 換，**不出一行 JS**（底座第 13 列）。
 * 🔴 窄版的 SVG 要 `height:auto`：讓縮放比完全由寬度決定。釘死高度的話又會回到
 * meet 縮放那個坑——高度一矮，縮放比改由高度決定，字無聲變小（第 48 步踩兩次）。
 * ⚠️ 畫布固定 190，而圖框在 320～640px 之間是 190～510px，所以同一張窄版在大一點的
 * 手機上會等比放大（最多 2.68 倍）。比例沒走樣，只是變大。
 */
.chart-narrow {
  display: none;
}

.chart-narrow > svg {
  display: block;
  width: 100%;
  /* 🔴 不可以寫 height:100%：那會讓縮放比改由容器高度決定。 */
  height: auto;
}

/* 🔴 `screen and` 不是裝飾（2026-09-19 cross-review 的建議，實測採納）：
 * 沒有它的話，列印預覽只要落在 640px 以下，這一組就會把原版藏起來，
 * 而紙上一張圖都不剩。加了 `screen`，列印時這一組根本不套用——
 * `.chart-narrow` 停在預設的 display:none、原版由底下的 `@media print` 攤平，
 * **不必再靠「誰排在後面」決定勝負**。
 * ⚠️ 上面那組 `@media (max-width: 640px)`（padding 與 --chart-height-sm）是舊的，
 * 與這件事無關，刻意不動。
 * (0,2,0) 壓得過上面那條 (0,1,0) 的 `.chart-scroll{display:flex}`。 */
@media screen and (max-width: 640px) {
  .chart-scroll.chart-has-narrow {
    display: none;
  }
  .chart-narrow {
    display: block;
  }
}

@media print {
  .chart-scroll {
    display: block;
    overflow: visible;
  }
  /* 🔴 紙上一律印原版那張，窄版不上紙（資訊比原版少）。
     這裡**不必**再寫一次「把窄版關掉／把原版放回來」：窄版預設就是 display:none，
     而換版那一組限定了 `screen`，列印時根本不套用。第一版寫了那兩條、靠「排在後面
     才會贏」壓過去——能動，但把正確性押在檔案順序上，cross-review 指出後改成現在這樣。 */
  .chart-scroll > .chart-plot {
    width: 100%;
    /* 高度改由內容決定，圖才不會縮小之後在框裡留一大片空白。
       `.chart-plot > svg` 的 height:100% 遇到 auto 高度會退回 SVG 自己的比例。 */
    height: auto;
  }
  .chart-freeze {
    display: none;
  }
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/motion/motion.css ---- */
/* motion —— 質感動態（ML-HTML 第 84 列）
 *
 * 估價師 2026-09-16 看過三版比稿（none／css／js）後裁示選 **B「只用 CSS」**：
 * 進場淡入、卡片浮起、捲動逐段浮現、整數數字跑動。**底座維持 0 個 JS**
 * （第 50 列），所以帶千分位小數的數字（3,403.6／103.5）刻意不跑——那是 C 版
 * 才有的，代價是每份交付物多帶一支程式。
 *
 * 三條安全線，動這支之前先看懂：
 *
 * ① **數字一定看得到**。`.yk-count` 是用 @property + counter() 做的，不支援
 *    @property 的瀏覽器會印不出 counter。所以整段包在
 *    `@supports (animation-timeline: view())` 裡——那個條件比 @property 更嚴，
 *    支援它的一定支援 @property。不支援就整段不套：動畫沒了，**數字還在**。
 * ② **列印一律不動**。`@media print` 把動畫全關、把真文字的顏色還原。
 *    ⚠️ 2026-09-16 實測（evidence/…step32/probe_print_long.py）：40 章的長頁
 *    不捲動直接列印，**有沒有這段都印得出最後一章** ⇒ 在 Chromium 145 的列印
 *    路徑上，這段是防呆不是必要，而且**沒有判準證明它在做事**。留著是防別的
 *    引擎，不要拿它宣稱「列印已經守住了」。
 * ③ **尊重系統設定**。`prefers-reduced-motion: reduce` 一律不動，同 ②。
 *
 * class 一律 yk- 前綴（lint 的 kit class 白名單認這個前綴）。
 */

@keyframes yk-fade-up {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* 進場淡入：載入時由上往下逐格浮現。-2／-3／-4 是同一排裡的錯開。 */
.yk-fade-in {
  animation: yk-fade-up 0.45s cubic-bezier(0.2, 0.7, 0.3, 1) both;
}
.yk-fade-in-2 {
  animation-delay: 0.08s;
}
.yk-fade-in-3 {
  animation-delay: 0.16s;
}
.yk-fade-in-4 {
  animation-delay: 0.24s;
}

/* 卡片浮起：只在滑鼠移上去時。陰影用 token，不寫死。 */
.yk-lift {
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.yk-lift:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-card-hover);
}

/* 捲動逐段浮現：純 CSS，靠 animation-timeline: view()，不需要任何程式。 */
.yk-reveal {
  animation: yk-fade-up 0.5s cubic-bezier(0.2, 0.7, 0.3, 1) both;
  animation-timeline: view();
  animation-range: entry 0% entry 40%;
}

/* 整數數字跑動。終值由每個元素自己用 inline style 給 --yk-count-to，
 * keyframes 只寫一條通用的——2026-09-16 實測 Chromium 145 可以插值
 * （evidence/2026-09-16-ml-html-step32/probe_count_var.py，546 與 1035 同頁各跑各的）。
 * 真文字留在 DOM 裡、只是變透明，counter 疊在上面絕對定位，所以不會改變版面寬度，
 * 而且一關動畫就看得到真的數字。 */
@supports (animation-timeline: view()) {
  @property --yk-count-n {
    syntax: "<integer>";
    initial-value: 0;
    inherits: false;
  }
  @keyframes yk-count {
    to {
      --yk-count-n: var(--yk-count-to, 0);
    }
  }
  /* 🔴 藏真文字用 visibility 不用 color:transparent（2026-09-16 規範樣板頁截圖
   * 抓到）：`color: transparent` 之後 ::before 的 `currentColor` 解出來也是透明，
   * 數字整個看不見——判準當時只量了「有沒有 counter」與「真文字顏色」，
   * 沒量「counter 畫出來的那個看不看得見」，所以全綠。
   * visibility:hidden 的元素仍然佔版面（寬度不會跳），子元素可以自己宣告
   * visible 把自己顯示回來。 */
  .yk-count {
    position: relative;
    visibility: hidden;
    counter-reset: ykcount var(--yk-count-n);
    animation: yk-count 1.1s cubic-bezier(0.2, 0.7, 0.3, 1) forwards;
  }
  .yk-count::before {
    content: counter(ykcount);
    position: absolute;
    left: 0;
    top: 0;
    visibility: visible;
  }
}

@media (prefers-reduced-motion: reduce) {
  .yk-fade-in,
  .yk-reveal,
  .yk-count {
    animation: none !important;
  }
  .yk-lift {
    transition: none;
  }
  .yk-count {
    visibility: visible;
  }
  .yk-count::before {
    content: none;
  }
}

@media print {
  .yk-fade-in,
  .yk-reveal,
  .yk-count {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
  .yk-count {
    visibility: visible;
  }
  .yk-count::before {
    content: none;
  }
}
/* ---- end yufong-site-kit ---- */
  

/* ---- assets/components/doc-shell/doc-shell.css ---- */
/* ===========================================================================
   文件型骨架 ── ML-HTML 第 2 列「文件型（單頁捲動）」那一半。
   左欄＝章節目錄（sticky 在頁首下緣），右欄＝內文。
   =========================================================================== */
.yk-doc-shell {
  display: grid;
  /* 左欄用 auto 而不是固定值：收起時 .yk-toc 自己縮成窄軌，欄寬跟著縮，
     正文就變寬。寬度改寫在 .yk-toc 上（ML-HTML 第 41 列）。 */
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--sp-6);
  max-width: 1440px;
  margin: 0 auto;
  padding: 0 var(--sp-6);
  align-items: start;
}
.yk-doc-main {
  min-width: 0;
}
.yk-toc {
  width: var(--toc-w, 232px);
  /* 展開：欄寬先長，條目才淡進來（下面 .yk-toc-sect 的 delay） */
  transition: width .24s cubic-bezier(.2, .7, .3, 1);
  position: sticky;
  /* 頁首是 sticky 的，目錄從 0 起算會被它蓋掉一整列 */
  top: calc(var(--site-header-h) + var(--sp-4));
  max-height: calc(100vh - var(--site-header-h) - var(--sp-6));
  overflow-y: auto;
  padding: var(--sp-4) 0;
  border-right: 1px solid var(--line-subtle);
}
/* 章與章之間的分段（ML-HTML 第 40 列「深化 3」）：留白加大，標題上方壓一條
   24px 金色短線。129 條的長目錄捲動時才看得出跨到下一章。
   短線用 ::before 的 background 畫，不用 border——2px 以上的 border 會被
   lint 的 border-decoration 擋（同下面 .yk-toc-active 的理由）。 */
/* 🔴 收起與展開要「錯開」跑，不能同時：條目還看得見的時候欄寬就開始縮，
   條目會被擠成直排（實測 90ms 那一格整排字變成一直條）。
   收起＝條目先淡出、欄寬後縮；展開＝欄寬先長、條目後淡入。 */
.yk-toc-sect {
  transition: opacity .12s ease .16s, visibility .12s .16s;
}
.yk-toc-group {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-note);
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  padding: var(--sp-5) var(--sp-2) var(--sp-2) var(--toc-num-w, 44px);
}
.yk-toc-group::before {
  content: "";
  position: absolute;
  left: var(--toc-num-w, 44px);
  top: var(--sp-3);
  width: 24px;
  height: 1px;
  background: var(--line-accent-subtle);
}
/* ⚠️ 第 41 列動了兩層結構：每一章包進 .yk-toc-sect，而且 nav 的第一個子元素
   變成整欄收起鈕。所以 `:first-child` 在這兩層都不成立（實測第一個章名上方會
   冒出一條不該有的短線）。改成從收起鈕往後數第一個 sect。 */
.yk-toc-hide-btn + .yk-toc-sect > .yk-toc-group {
  padding-top: 0;
}
/* 第一組沒有上方留白，短線會壓到字上，直接不畫 */
.yk-toc-hide-btn + .yk-toc-sect > .yk-toc-group::before {
  display: none;
}
.yk-toc-group-text {
  min-width: 0;
  flex: 1 1 auto;
}
/* 章號固定一欄、靠右切齊（ML-HTML 第 40 列「深化 2」）：4 層條號的尾碼
   （4.2.1.1 與 3.3）切在同一條線上，縮排只發生在章名那一側。 */
.yk-toc-link {
  display: grid;
  grid-template-columns: var(--toc-num-w, 44px) minmax(0, 1fr);
  align-items: baseline;
  padding: var(--sp-2) var(--sp-3) var(--sp-2) 0;
  font-size: var(--fs-note);
  line-height: 1.5;
  color: var(--text-secondary);
  text-decoration: none;
}
.yk-toc-link:hover {
  color: var(--text-link-strong);
  background: var(--surface-row-hover);
}
.yk-toc-link:focus-visible {
  outline: 2px solid var(--text-accent);
  outline-offset: -2px;
  border-radius: var(--r-sm);
}
/* 目前所在章：照第 23 列頁首那條的做法用 inset box-shadow，不用 border——
   2px 以上的 border-left 是裝飾線，lint 的 border-decoration 會擋。 */
.yk-toc-active {
  color: var(--text-link-strong);
  font-weight: 500;
  box-shadow: inset 3px 0 0 var(--text-accent);
}
.yk-toc-active .yk-toc-num {
  color: var(--text-accent);
  font-weight: 600;
}
.yk-toc-num {
  text-align: right;
  padding-right: var(--sp-3);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  color: var(--text-muted);
}
.yk-toc-text {
  min-width: 0;
}

/* ===== 收折（ML-HTML 第 41 列）=================================================
   估價師裁示 2026-09-16：「兩個都要」（整欄收起 ＋ 逐章折起）、「預設展開」。
   兩者都是 checkbox + label，**底座維持 0 個 JS**——第 13 步選 A 案時已釘死。
   整欄收起不用 :has()：左欄是 auto 欄，.yk-toc 自己縮成窄軌，欄寬就跟著縮。
   收起後留 36px 窄軌而不是整個消失——不留就沒有東西可以點回來。
   ⚠️ checkbox 不用 hidden：hidden 會讓它不能 tab，鍵盤就打不開了。 */
.yk-toc-hide,
.yk-toc-fold {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.yk-toc-hide-btn,
.yk-toc-fold-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  cursor: pointer;
  color: var(--text-muted);
  border-radius: var(--r-sm);
}
/* 整欄收起鈕黏在目錄頂端：目錄自己會內捲，不黏就會捲出視野 */
.yk-toc-hide-btn {
  position: sticky;
  top: 0;
  z-index: 1;
  margin-left: auto;
  background: var(--surface-page);
}
.yk-toc-hide-btn:hover,
.yk-toc-fold-btn:hover {
  color: var(--text-link-strong);
  background: var(--surface-row-hover);
}
.yk-toc-hide:focus-visible ~ .yk-toc .yk-toc-hide-btn,
.yk-toc-fold:focus-visible ~ .yk-toc-group .yk-toc-fold-btn {
  outline: 2px solid var(--text-accent);
  outline-offset: -2px;
}
/* 箭頭用 1.5px 的兩條邊轉 45 度畫——border 寬度 <2px，不是 lint 擋的裝飾線 */
.yk-toc-caret {
  width: 6px;
  height: 6px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transition: transform .18s ease;
}
.yk-toc-hide-btn .yk-toc-caret {
  transform: translateX(1px) rotate(135deg);
}
.yk-toc-fold-btn .yk-toc-caret {
  transform: translateY(-1px) rotate(45deg);
}
.yk-toc-hide:checked ~ .yk-toc .yk-toc-hide-btn .yk-toc-caret,
.yk-toc-fold:checked ~ .yk-toc-group .yk-toc-fold-btn .yk-toc-caret {
  transform: translateX(-1px) rotate(-45deg);
}
/* 折起／展開的過場（ML-HTML 第 41b 列，估價師裁示「連條目滑動」）。
   高度用 grid-template-rows 1fr→0fr 補：`height: auto` 不能過場，而寫死高度
   在條目數不一樣的章上一定會錯。外層當 grid、內層 overflow:hidden 收邊。 */
.yk-toc-items {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows .22s cubic-bezier(.2, .7, .3, 1), opacity .16s ease;
}
.yk-toc-items-inner {
  overflow: hidden;
}
.yk-toc-fold:checked ~ .yk-toc-items {
  grid-template-rows: 0fr;
  opacity: 0;
}
.yk-toc-hide:checked ~ .yk-toc {
  width: 36px;
  overflow: hidden;
  transition: width .24s cubic-bezier(.2, .7, .3, 1) .12s;
}
/* ⚠️ 這裡不能用 display:none——那樣欄寬會在條目消失的同一瞬間跳掉，
   看不出目錄收到哪裡去。改成淡出＋visibility，欄寬自己滑。 */
.yk-toc-hide:checked ~ .yk-toc > .yk-toc-sect {
  opacity: 0;
  visibility: hidden;
  transition: opacity .12s ease, visibility .12s;
}

/* 232px 的側欄在窄螢幕會把內文擠成一欄字寬，收起來改用頁內錨點 */
@media (max-width: 1023px) {
  .yk-doc-shell {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
    padding: 0 var(--sp-4);
  }
  .yk-toc {
    display: none;
  }
}

/* 文件型內文的語意標籤基準（ML-HTML 第 10 步）。
   長文交付物的 <h2>/<h3>/<h4>/<ul>/<ol> 沒有 class，吃的是瀏覽器預設字級，
   與第 20 列的字級階梯對不上；交付物為此一處一個 inline style（bid-analysis
   實測 66 處）。基準收進底座之後交付物只寫語意標籤。
   ⚠️ 兩層都包 :where()，特異度歸零——元件自己的 class（chapter-header-title
   等）一律蓋得過去。不包的話 `.yk-doc-main h2` 會贏過 `.chapter-header-title`。 */
:where(.yk-doc-main) :where(h2) {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  font-weight: var(--fw-h2);
  line-height: var(--lh-h2);
  margin: var(--sp-6) 0 var(--sp-4);
}
:where(.yk-doc-main) :where(h3) {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-h3);
  line-height: var(--lh-h3);
  color: var(--text-link-strong);
  margin: var(--sp-5) 0 var(--sp-3);
}
:where(.yk-doc-main) :where(h4) {
  font-family: var(--font-display);
  font-size: var(--fs-body);
  font-weight: var(--fw-h3);
  line-height: var(--lh-h3);
  color: var(--text-link-strong);
  margin: var(--sp-4) 0 var(--sp-2);
}
:where(.yk-doc-main) :where(p) {
  margin: 0 0 var(--sp-3);
}
:where(.yk-doc-main) :where(ul, ol) {
  margin: var(--sp-3) 0;
  padding-left: var(--sp-5);
}
:where(.yk-doc-main) :where(li) {
  margin-bottom: var(--sp-1);
}

/* 使用者在系統裡關掉動態效果時一律不過場（第 41b 列的動畫都走這裡） */
@media (prefers-reduced-motion: reduce) {
  .yk-toc,
  .yk-toc-sect,
  .yk-toc-items,
  .yk-toc-caret {
    transition: none;
  }
}
/* ---- end yufong-site-kit ---- */
  