/* 全站字型走日式圓潤（丸ゴシック）：中文＝自架 jf open 粉圓（SIL OFL，漢字衍生自
   日本圓體 Kosugi Maru，涵蓋繁中股名）；數字/拉丁＝M PLUS Rounded 1c（同圓體血統）。
   目的：把冷硬的系統黑體換成溫暖一致的圓體，降低介面壓力感。

   "Num" 用 unicode-range 只接管數字與拉丁字母，中文落回粉圓——這樣數字/拉丁能單獨用
   size-adjust 與粉圓中文光學等高（兩套字度量不同）。M PLUS Rounded 1c 是比例圓體，
   但**數字本身預設就是等寬**（該字型的 lining figures 天生等寬，不靠 OpenType tnum
   特性）——實測 body 的 font-variant-numeric:tabular-nums 在此字型下是 no-op、不是
   救援：0–9 在任何字重下量出來都是同一個 advance width。這行舊註解曾誤植「非等寬、
   已與使用者取捨」，但欄位右對齊（.num／text-align:right）配等寬數字本來就會逐位
   對齊，柔和圓體與數字對齊從來不是要取捨的兩難。 */
@font-face {
  font-family: "Num";
  src: url("/vendor/fonts/mplus-rounded-400.woff2") format("woff2");
  font-weight: 400; font-display: swap; size-adjust: 92%;
  unicode-range: U+0030-0039, U+0041-005A, U+0061-007A, U+0025, U+002B, U+002C, U+002D, U+002E, U+002F, U+003A;
}
/* 數字最粗到中等（ui82）：500 以上全部用中等檔，粗體檔已拿掉。CSS 裡寫 600／700／800 的規則照留，
   數字一律畫成中等、中文一律不加粗（見下一段）；層次靠字的大小與亮度。 */
@font-face {
  font-family: "Num";
  src: url("/vendor/fonts/mplus-rounded-500.woff2") format("woff2");
  font-weight: 500 900; font-display: swap; size-adjust: 92%;
  unicode-range: U+0030-0039, U+0041-005A, U+0061-007A, U+0025, U+002B, U+002C, U+002D, U+002E, U+002F, U+003A;
}
/* 中文圓體（單一字重）。~2MB，font-display:swap 先顯示系統字再換。
   **宣告 100–900 是刻意的**（ui82，使用者看過對照圖後選「中文一律不加粗」）：瀏覽器把這個檔案當成涵蓋
   所有字重，就不會再合成粗體。ui79 曾改成只宣告 400 讓 600 以上合成粗體，圓體合成出來偏糊，使用者
   回報太粗。不用 font-synthesis：canvas（ECharts、個股報告用 html2canvas 匯出的 PNG/PDF）不吃它，
   但吃這裡的字重範圍，網頁和 canvas 才會一樣細。 */
@font-face {
  font-family: "Huninn";
  src: url("/vendor/fonts/huninn.woff2") format("woff2");
  font-weight: 100 900; font-display: swap;
}

:root {
  /* Design tokens — color */
  --bg-primary: #070b12;
  --bg-workspace: #0b121d;
  --bg-panel: #0f1927;
  --bg-card: #142033;
  --bg-card-hover: #192941;
  --bg-sunken: #050910;
  --border-subtle: #1b2a3d;
  --border-default: #2b3e57;
  --border-strong: #3e5878;
  --text-primary: #f2f6fb;
  --text-secondary: #aab8ca;
  /* 舊值 #718198 在卡片底 #142033 上只有 **4.12**（低於 AA 4.5），而它用在 11–12px
     的小字、光總覽就 56 處——正是對比要求最嚴的尺寸。這個 token 一直沒被量過
     （本檔對 --up/--down 有明文量測紀律，卻漏了用量最大的這一個）。
     新值 #7d8da6 維持同一冷藍色相、只提亮度：卡片 **4.85**、panel 5.24、sunken 5.92。 */
  --text-muted: #7d8da6;
  /* 只給「停用的控制項」——WCAG 1.4.3 明文豁免 inactive UI，故不要求 4.5。
     **不可拿來當一般次要文字用**，那是 --text-muted 的工作。 */
  --text-disabled: #4d5b6e;
  --color-up: #ff6375;
  --up-soft: #3a1822;
  --color-down: #32c98b;
  --down-soft: #103126;
  --color-flat: #8997a9;
  --accent: #f5b544;
  --info: #70b8ff;
  --chart-foreign: #57c7ff;   /* 冰藍：外資 */
  --chart-trust: #a78bfa;     /* 霓虹紫：投信 */
  --chart-dealer: #f4b860;    /* 琥珀金：自營商 */
  --chart-ma-short: #7bc5ff;
  --chart-ma-mid: #52dbc2;
  --chart-ma-long: #f4c761;
  --chart-ma-major: #c494ff;
  /* 副圖系列色（ui96：總覽大盤 K 線、個股頁三大法人共用）。色相沿用外資藍、投信紫、自營金，亮度降到
     深色模式的帶內。dataviz 的 validate_palette.js 在 --bg-card #142033 與 #101a29 上都通過：最接近的一對
     （藍、紫）色盲模擬差 12.9（標準 8）、一般視覺差 18.9（下限 15），三色對底色都 ≥3:1。
     --series-1-deep：成交金額長條（整排長條用同色相再暗一階，不搶 K 線；對 #142033 是 2.69:1、對 #101a29
     是 2.87:1，刻意低於 3:1：成交金額是背景脈絡，數值看讀數列）；--series-avg：10 日均量線
     （參考線用中性淺灰）。上面的 --chart-foreign 等保留：ECharts 主題與其他圖還在用。 */
  --series-1: #229ed4;
  --series-2: #7557c1;
  --series-3: #bf8527;
  --series-1-deep: #17698f;
  --series-avg: #c3cfdd;
  --warning: #f2a64b;
  --danger: #ff7a66;

  /* 族群輪動每週資金流向（ui81）：法人沿用 --info、大戶用紫。資金流向不是漲跌，不可用紅綠；
     也不用琥珀（琥珀是「目前選取」）。兩色在 --bg-card 上 7.78／5.31:1。 */
  --race-inst: var(--info);
  --race-big: #a07cff;

  /* 側欄圖示依分組上色（ui81）：五個色相都避開紅綠（行情方向）與琥珀（選取）。
     非文字對比（WCAG 1.4.11 ≥3:1）實測：側欄底 --bg-panel 上 5.74～10.05、琥珀選取列上 4.48～7.85。 */
  --nav-market: #70b8ff;
  --nav-picks: #a07cff;
  --nav-stock: #e879f9;
  --nav-adv: #5fd3e6;
  --nav-sys: #a3b1c6;

  /* Design tokens — compact spacing */
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 12px;
  --space-lg: 16px;
  --space-xl: 24px;
  --space-2xl: 32px;

  /* Design tokens — dense financial typography */
  --font-size-xs: 12px;
  --font-size-table: 13px;
  --font-size-data: 14px;
  --font-size-sm: 15px;
  --font-size-base: 16px;
  --font-size-md: 18px;
  --font-size-lg: 22px;
  --font-size-xl: 28px;
  --font-size-hero: 44px;

  /* Legacy aliases keep existing screens stable while the redesign rolls out. */
  /* 表面階梯：背景 → 工作區 → 模組 → 數據卡。保留低飽和深藍黑，讓台股紅綠
     成為唯一的行情訊號；陰影只用來分離平面，不製造額外的「意義色」。 */
  --sunken: var(--bg-sunken);
  --bg: var(--bg-primary);
  --panel: var(--bg-panel);
  --panel-raised: var(--bg-card);
  --card: var(--bg-card);
  --card-hover: var(--bg-card-hover);
  --border: var(--border-default);
  --gridline: rgba(115, 142, 176, 0.18);
  --shadow-panel: 0 14px 34px rgba(0, 0, 0, 0.24), 0 2px 8px rgba(0, 0, 0, 0.18);
  --shadow-card: 0 5px 15px rgba(0, 0, 0, 0.18);
  --shadow-card-hover: 0 12px 24px rgba(0, 0, 0, 0.28);
  --text: var(--text-primary);
  --muted: var(--text-muted);
  --label: var(--text-secondary);
  /* 市場方向：紅漲綠跌（台股慣例，與西方相反）。這組紅綠只代表市場方向，
     系統狀態訊息一律不得使用，否則「成功」會讀成「跌」。
     亮度下限由「小字對比」決定，不是由「好不好看」決定：--up/--down 會用在
     .card-chg(14px)、.rank-row(13px) 這類小字上，在 --card 底上必須 ≥ 4.5:1(WCAG AA)。
     歷史值：#e8404a 3.58、#1f9e6e 4.20 皆不合格；#f56069/#25b37d 在舊卡片底 #232b36 上
     是 4.60/5.32。**深色改版後底色換成 --bg-card #142033、色值換成 --color-up #ff6375 /
     --color-down #32c98b，實測 5.68 / 7.68**（本次重量）。
     再調暗就會掉出 AA——調整前請先量對比，別抄上面任何一組舊數字。 */
  --up: var(--color-up);
  --down: var(--color-down);
  /* 同一組紅綠有兩種用途，對比要求正好相反，一個 token 服務不了兩邊：
     --up/--down     ＝「深色底上的文字」，要夠亮才過 AA（見上）。
     --up-fill/--down-fill ＝「白字底下的色塊」（權值股卡、熱力圖，皆由 sectorColor 產生），
                        要夠暗白字才讀得到；實測白字對比 5.52 / 5.34。
     用亮色當色塊會把白字壓到 3.11 / 2.68。無文字的填色（漲跌家數條、K 線）沿用亮色即可。 */
  --up-fill: #c62b38;
  --down-fill: #127a53;
  --up-dim: var(--up-soft);
  --down-dim: var(--down-soft);
  --flat: var(--color-flat);
  /* 琥珀只有一個工作：目前選取／你在這裡 */
  /* 字級：中文基準比一般拉丁介面高一階（方塊字筆畫密，需要更大級數）。
     xs=次要註記 data=密集數據列（排行/明細） sm=標籤 base=內文/表格 md=個股名稱
     lg=統計值 xl=卡片值 hero=大盤 */
  --fs-xs: var(--font-size-xs);
  --fs-data: var(--font-size-data);
  --fs-sm: var(--font-size-sm);
  --fs-base: var(--font-size-base);
  --fs-md: var(--font-size-md);
  --fs-lg: var(--font-size-lg);
  --fs-xl: var(--font-size-xl);
  --fs-hero: var(--font-size-hero);
  --sp-1: var(--space-xs);
  --sp-2: var(--space-sm);
  --sp-3: var(--space-md);
  --sp-4: var(--space-lg);
  --sp-5: var(--space-xl);
  /* 圓角只有三級：r=一般元件 r-lg=大容器 r-pill=膠囊。全站走日式圓潤，基準 6px。 */
  --r: 6px;
  --r-lg: 8px;
  --r-pill: 999px;

  /* 動效 token：三級時長＋一條標準緩動。收合抽屜（.sidebar.open）、法人研究室圖表
     （app.js IR_ANIMATION_MS）在此之前各自訂了自己的毫秒數；這裡不是新發明一套時長，
     是把散落的 .14s/.16s/.3s/180ms/260ms 收進同一份可調的刻度，日後要統一調快/調慢
     只改這裡。--ease-out 是全站唯一緩動函式——沒有第二種「更有彈性」的曲線，財務工作台
     的動效只做「確認、不搶戲」，不需要 spring 的表演感。 */
  --dur-fast: 140ms;
  --dur-base: 180ms;
  --dur-med: 320ms;
  --dur-slow: 900ms;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  /* 系統狀態的三種底：資訊（藍）、警示（琥珀）、錯誤（赭）。**不是行情色**——成功不可用綠、
     錯誤不可用紅，否則「成功」會被讀成「跌」。原本散在十幾條規則裡寫死（琥珀系就有 5 種值）。 */
  --info-surface: #1c2733;
  --warn-text: #f0c474;
  --warn-surface: #34270f;
  --err-text: #f0a878;
  --err-surface: #33241a;
  --neutral-surface: rgba(125, 141, 166, .12);
  /* 琥珀實心底（選中的分段鈕、杯柄 chip）上的文字 */
  --on-accent: #1a1a1a;
  /* 原生控制項（勾選框、下拉選單、日期／時間選擇器、捲軸）跟著深色；勾選色用中性藍，
     不用琥珀（琥珀是「目前選取」，7 個條件勾選框全亮琥珀會稀釋它）。 */
  color-scheme: dark;
  accent-color: var(--info);
}
/* 使用者要求「減少動畫」時，全站動效歸零——比逐一在每條規則後面補 media query 可靠，
   也是唯一能保證新加的動效不會被漏掉的做法。KPI 閃動（見 .kpi-item.flash）與抽屜滑入
   （.sidebar.open）等既有動效都靠這一條收斂，不必各自重複判斷。 */
@media (prefers-reduced-motion: reduce) {
  *, ::before, ::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  font-family: "Num", "Huninn", "Microsoft JhengHei", "PingFang TC", "Noto Sans TC", system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-base);
  /* 字級整體放大一級後補上行距：原本沒有 line-height（＝normal，約 1.2），
     16px 正文在深色底上會黏成一團。密集表格與排行列在各自的規則裡有更緊的
     line-height，會蓋過這一行，不受影響。 */
  line-height: 1.5;
  background: radial-gradient(circle at 88% -10%, #142642 0, transparent 34%), var(--bg);
  color: var(--text);
}
/* 個股名稱是全站掃視最頻繁的字串，給它自己的一級 */
.stock-name { font-size: var(--fs-md); font-weight: 600; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45); }
/* 數字欄：右對齊讓位數對齊（取代散落各處的 inline text-align:right） */
.num { text-align: right; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* 按下回饋：往下沉 1px（只動 transform，不影響版面）；停用的不動。全站原本 0 條 :active。 */
button:not(:disabled):active, .file-label:active, .cup-chip:active, .se-chip:active { transform: translateY(1px); }

/* ===== 全域版面：頂列 + 固定側欄 + 唯一主捲動區 ===== */
.app {
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr);
  height: 100vh;
  overflow: hidden;
}
.topbar {
  /* 頂欄是**常駐 chrome**，不吃 body 的 1.5 行距：字級放大一級後，資料日期／更新時間
     這兩串在 375px 下會折成兩行，實測把頂欄從 58px 撐到 82px——永久吃掉 3% 的畫面
     （同下面 .gs-toggle 那條「絕對定位才回得到 56px」的取捨）。1.25 是量出來的：
     只加這行 71px、只縮字 66px、兩者都做 59px（基準 58px）。 */
  line-height: 1.25;
  grid-column: 1 / 3;
  display: flex; justify-content: space-between; align-items: center;
  padding: 12px 24px; background: linear-gradient(100deg, var(--panel), var(--panel-raised));
  border-bottom: 1px solid var(--border); box-shadow: 0 5px 18px rgba(0, 0, 0, 0.2); z-index: 2;
}
/* nowrap＋不可收縮：字級放大一級後，標題單行需要 302px 而 1024px 視窗只分到 298px，
   **差 4px 就折成兩行**，把頂欄從 59px 撐到 80px（只有中間寬度會中，1904/1280/375 都測不到）。
   讓搜尋框去吸收這 4px（min-width:0 才收得動），標題保持完整——它是品牌名，不截斷。 */
.topbar h1 { font-size: var(--fs-lg); margin: 0; letter-spacing: 1px; white-space: nowrap; flex: 0 0 auto; }
.topbar .gsearch { min-width: 0; }
.topbar h1 .zh { font-size: var(--fs-sm); color: var(--label); margin-left: 8px; }
.actions { display: flex; align-items: center; gap: 14px; }
/* 頂欄兩個日期（ui80）：「2026-10-01」是一個讀數，拆成「2026-10-」／「01」兩行就讀不出是哪一天。
   app.js 把日期包進 .tb-date，不准從中間折，要換行只能在標籤與日期之間（768–860px 就是
   「資料日期：」一行、日期一行，頂欄仍 59px）。**601–700px 例外**：那段頂欄是單列且不換行，
   日期不讓就全部由搜尋框吸收，實測 650px 剩 81px、601px 剩 32px（原本 145／125px），所以那段
   維持原狀（日期仍可能從中間折、頂欄 70–100px，是既有狀況）。
   桌機（≥861px）兩串整段不折、頂欄維持一列 59px。1024px 時這一列實測要 1041px（標題 302＋
   搜尋 260＋資料日 168＋「落後 1 天」66＋更新時間 170＋間距與左右內距 76），差的 17px 原本由
   搜尋框與日期一起分攤，日期被擠成兩行、頂欄 59→65px；現在全部由搜尋框吸收——它本來就是這一列
   會縮的那一格（flex: 0 1 260px）。三塊之間另留至少 16px：原本 1041px 以下沒有任何空隙，搜尋框
   貼著「股力智富」與「資料日期」；1073px 以上 space-between 分到的間距本來就超過 16px，看不出差別。 */
@media (max-width: 600px), (min-width: 701px) {
  .topbar .tb-date { white-space: nowrap; }
}
@media (min-width: 861px) {
  .topbar { column-gap: 16px; }
  .topbar .data-date, .topbar #last-updated { white-space: nowrap; }
}
/* 861–960px 全額排一列的話搜尋框只剩 41–140px，所以收掉兩個年份（資料日期 44px、更新時間 41px）
   與標題字距（22px）。最差情況 861px＋徽章「落後 10 天」，搜尋框仍有 148px，放得下提示字
   「搜尋代號或名稱」（131px）；961px 起年份回來，最差也有 141px。 */
@media (min-width: 861px) and (max-width: 960px) {
  .topbar .lu-y, .topbar .dd-y { display: none; }
  .topbar h1 { letter-spacing: 0; }
}

/* ===== 頂欄全域搜尋 ===== */
.gs-toggle { display: none; }        /* 只在手機出現，桌機/平板由常駐搜尋框取代 */
.gsearch { position: relative; flex: 0 1 260px; min-width: 0; }
.gsearch input {
  width: 100%; background: var(--sunken); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r);
  padding: 7px 12px; font: inherit; font-size: var(--fs-sm);
}
.gsearch input:focus { outline: none; border-color: var(--accent); }
.gsearch input::placeholder { color: var(--muted); }
/* 浮層要蓋在下面的內容上：.topbar 已經是 z-index 2，這裡再高一階就夠，
   不必動到 .sidebar(1) / .kpi-sticky(5) 那幾層的既有關係。 */
.gs-list {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 20;
  margin: 0; padding: 4px; list-style: none; max-height: 320px; overflow-y: auto;
  background: var(--panel-raised); border: 1px solid var(--border-strong);
  border-radius: var(--r-lg); box-shadow: var(--shadow-panel);
}
.gs-list[hidden] { display: none; }
.gs-item {
  display: flex; align-items: baseline; gap: var(--sp-2);
  padding: 7px 10px; border-radius: var(--r); cursor: pointer; font-size: var(--fs-sm);
}
/* 鍵盤的「目前這一項」與滑鼠 hover 共用同一個外觀——兩種操作看到的是同一個狀態，
   分開設計只會讓使用者以為那是兩件事。 */
.gs-item:hover, .gs-item.on { background: rgba(112, 184, 255, 0.12); }
.gs-code { font-weight: 700; color: var(--text); }
.gs-name { color: var(--label); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gs-empty { padding: 8px 10px; color: var(--muted); font-size: var(--fs-sm); }

/* 資料新鮮度徽章。**不用紅綠**：那兩色鎖給行情漲跌，拿紅色表示「資料過期」會在
   一片紅漲綠跌裡被讀成行情。過期沿用全站唯一的「注意這格」語彙＝琥珀；
   當日就用弱化的中性色，不搶版面（沒有問題的時候不需要被看見）。 */
.freshness { font-size: var(--fs-xs); padding: 2px 7px; border-radius: 999px; white-space: nowrap; }
.freshness:empty { display: none; }
.freshness.ok { color: var(--muted); border: 1px solid var(--border); }
.freshness.stale { color: var(--accent); border: 1px solid var(--accent); }
.muted { color: var(--muted); font-size: var(--fs-sm); }
.small { font-size: var(--fs-sm); }
.data-date { color: var(--text); font-size: var(--fs-base); font-weight: 600; }
.help-link { color: var(--info); font-size: var(--fs-sm); text-decoration: none; cursor: pointer;
  display: inline-flex; align-items: center; min-height: 24px; background: none; border: 0; padding: 0; font-family: inherit; }
.help-link:hover { text-decoration: underline; }
.wave-help { background: var(--sunken); border: 1px solid var(--border); border-radius: var(--r); padding: 12px 14px; font-size: var(--fs-base); line-height: 1.8; margin-bottom: 8px; }
.wave-help b { color: var(--text); }
.dot { font-size: var(--fs-xs); margin: 0 2px 0 8px; }
.dot.orange { color: var(--accent); }
.dot.blue { color: var(--info); }

.sidebar {
  grid-column: 1; grid-row: 2;
  min-height: 0; padding: var(--space-sm) 6px;
  background: var(--bg-panel); border-right: 1px solid var(--border-subtle);
  overflow-y: auto; overscroll-behavior: contain; scrollbar-gutter: stable;
  scrollbar-width: thin; scrollbar-color: var(--border-default) transparent;
  box-shadow: 8px 0 22px rgba(0, 0, 0, 0.14); z-index: 1;
}
.sidebar-nav { display: flex; flex-direction: column; gap: 2px; min-height: 100%; }
.nav { display: flex; align-items: center; gap: var(--space-sm); min-height: 36px; padding: 8px 12px; border: 1px solid transparent; border-radius: var(--r); color: var(--text); text-decoration: none; cursor: pointer; font-size: var(--font-size-sm); line-height: 1.2; transition: background var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out); }
/* 線條圖示（ui79，Tabler 內嵌 sprite，見 index.html 開頭）：顏色跟著文字（stroke=currentColor），
   線寬全站 1.75。導覽圖示平常用次要色、滑過提亮、選中跟文字一起變琥珀。 */
.svg-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.i { width: 1em; height: 1em; flex: 0 0 auto; display: block; fill: none; stroke: currentColor;
  stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
.nav .ico, .nav-more .ico { display: inline-flex; align-items: center; justify-content: center; font-size: 20px; color: var(--label); }
/* 依分組上色（ui81）：平常、滑過、選取中都是分組色；選取中只有整列（文字、底色、左側直條）變琥珀。
   ≤600px 的 .nav-group 是 display:contents，自訂屬性仍沿 DOM 樹繼承，底部列與抽屜一樣吃得到。
   「更多」按鈕不是頁面，維持 .nav-more .ico 的 --label。 */
.nav-group[data-group="market"] { --nav-c: var(--nav-market); }
.nav-group[data-group="picks"] { --nav-c: var(--nav-picks); }
.nav-group[data-group="stock"] { --nav-c: var(--nav-stock); }
.nav-group[data-group="adv"] { --nav-c: var(--nav-adv); }
.nav-group[data-group="sys"] { --nav-c: var(--nav-sys); }
.nav .ico { color: var(--nav-c, var(--label)); }
.nav-more .i-open { display: none; }
.with-ico { display: inline-flex; align-items: center; gap: 7px; }
/* 按鈕圖示跟著按鈕文字色（currentColor）再降 25% 不透明度：平常接近 --label，滑過跟著提亮，
   選中（琥珀實心、--on-accent 深字）時也跟著變深。寫死 --label 的話，選中時淺灰圖示疊在琥珀上只有 1.11:1。 */
.with-ico .i { font-size: 1.1em; opacity: .75; }
.gs-toggle .i { font-size: 20px; }
/* hover 用中性提亮：琥珀只留給「目前選取／注意這格／警示／焦點」，滑過就亮琥珀會稀釋它 */
.nav:hover { color: var(--text-primary); background: rgba(125, 141, 166, .10); border-color: rgba(125, 141, 166, .28); }
.nav.active { color: var(--accent); background: rgba(245, 181, 68, 0.13); border-color: rgba(245, 181, 68, 0.48); font-weight: 700; box-shadow: inset 3px 0 0 var(--accent); }
/* 導覽分組：每組帶小標題；系統組釘在側欄底部（如同看盤軟體把設定放最下） */
.nav-group { display: flex; flex-direction: column; gap: 2px; }
.nav-group + .nav-group { margin-top: var(--space-sm); }
.sidebar .nav-group-sys { margin-top: auto; }   /* 特異度需勝過上面的相鄰選擇器，設定才會釘底 */
.nav-group-title { font-size: var(--font-size-xs); color: var(--muted); font-weight: 700; letter-spacing: 1px; padding: 4px 12px 1px; }
/* 可收合的群組（目前只有「進階」）。**只在 >860px 生效**：860px 以下側欄是 56px 圖示軌、
   群組標題被藏起來（見該處媒體查詢），手機更是把 .nav-group 設成 display:contents 交給
   「更多」面板——那兩種模式下都沒有標題可以點，收起來就再也打不開了。 */
@media (min-width: 861px) {
  .nav-group[data-collapsible] > .nav-group-title { cursor: pointer; user-select: none; }
  .nav-group[data-collapsible] > .nav-group-title::after { content: "  ▾"; }
  .nav-group[data-collapsible].collapsed > .nav-group-title::after { content: "  ▸"; }
  .nav-group[data-collapsible].collapsed > .nav { display: none; }
}

.content {
  grid-column: 2; grid-row: 2;
  min-width: 0; min-height: 0; position: relative;
  overflow-x: hidden; overflow-y: auto; scrollbar-gutter: stable;
}
.terminal-workspace {
  background:
    radial-gradient(circle at 86% -12%, rgba(38, 107, 176, 0.14), transparent 34rem),
    linear-gradient(145deg, rgba(255, 255, 255, 0.015), transparent 28%);
}
.dashboard-view { position: relative; }

/* 公開模式（/public/overview）：只開放總覽。用隱藏而非移除 DOM——app.js 在模組層對
   各視圖元素綁事件，移除節點會讓綁定拋 TypeError 而整頁掛掉。 */
body[data-public] .nav:not([data-view="overview"]) { display: none; }
body[data-public] .nav-group-title { display: none; }             /* 只剩總覽，群組標題無意義 */
body[data-public] .nav-more { display: none; }                      /* 手機底部列的「更多」：只剩一頁時無處可去 */
body[data-public] .nav-group + .nav-group { margin-top: 0; }        /* 空群組不留間距 */
body[data-public] .status-bar { display: none; }

/* 視圖內容交由右側 .content 統一捲動，避免多層垂直捲軸。 */
.view { display: none; min-height: 100%; overflow: visible; padding: 22px 28px 32px; flex-direction: column; gap: 16px; }
.view.active { display: flex; }

/* 系統狀態訊息刻意避開紅綠軸：在紅漲綠跌的介面裡，綠色「成功」會被讀成「跌」。
   改用中性／琥珀／藍，讓紅綠只代表市場方向。 */
.status-bar { padding: 7px 20px; font-size: var(--fs-sm); flex: 0 0 auto; }
.status-bar.ok { background: var(--info-surface); color: var(--info); }
.status-bar.warn { background: var(--warn-surface); color: var(--warn-text); }
.status-bar.err { background: var(--err-surface); color: var(--err-text); }
.err-text { color: var(--err-text); }   /* 內文錯誤/破壞性操作提示：同系統錯誤色，避開市場紅 */
.stale-note { padding: 8px 20px; font-size: var(--fs-data); line-height: 1.5; background: var(--warn-surface); color: var(--warn-text); border-bottom: 1px solid var(--border); flex: 0 0 auto; }
.hidden { display: none !important; }

/* ===== 指標卡 ===== */
/* ===== 總覽上方那一帶（ui93）：上面一條「市場方向」（判讀），下面一條數字。
   規則、門檻與顯示文字全在 stocks_power_rich/market_direction.py；這裡只管長相。 ===== */
.overview-top {
  display: grid; grid-template-columns: minmax(0, 1fr);
  align-items: stretch; gap: 0; min-width: 0; overflow: hidden; flex: 0 0 auto;
  background: linear-gradient(120deg, var(--panel-raised), var(--panel));
  border: 1px solid var(--border-strong); border-radius: var(--r-lg); box-shadow: var(--shadow-panel);
}
/* 判讀：左邊結論（跨兩列、右側髮絲分隔），右邊上面 5 個標籤、下面判讀句／風險提醒／非投資建議／依據說明 */
.dir-panel {
  display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 24px; row-gap: 6px;
  align-items: center; padding: 14px 18px; min-width: 0;
}
.dir-lead { grid-row: span 2; padding-right: 24px; border-right: 1px solid var(--border-subtle); }
.dir-head { display: flex; align-items: baseline; gap: 8px; }
.dir-title { font-size: var(--fs-md); font-weight: 700; color: var(--text); }
.dir-date { font-size: var(--fs-xs); color: var(--muted); }
.dir-main { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-top: 2px; }
/* 結論與標籤照行情的紅綠：偏多紅、偏空綠，中性與資料不足灰 */
.dir-verdict { font-size: 34px; line-height: 1.05; font-weight: 700; letter-spacing: .06em; }
.dir-verdict.bull { color: var(--up); }
.dir-verdict.bear { color: var(--down); }
.dir-verdict.neutral { color: var(--text-secondary); }
.dir-verdict.na { color: var(--muted); }
.dir-counts { font-size: var(--fs-sm); color: var(--label); white-space: nowrap; }
.dir-counts b.up { color: var(--up); }
.dir-counts b.down { color: var(--down); }
/* 標籤：外框色與偏多空字表示方向，數值本身不上色 */
.dir-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.dir-tag { display: inline-flex; align-items: baseline; gap: 6px; padding: 3px 10px; border-radius: 999px;
  background: var(--sunken); border: 1px solid var(--border-subtle); font-size: var(--fs-xs); white-space: nowrap; }
.dir-tag .k { color: var(--muted); }
.dir-tag .v { color: var(--text); }
.dir-tag .s { font-weight: 600; color: var(--muted); }
.dir-tag.bull { border-color: color-mix(in srgb, var(--up) 55%, transparent); }
.dir-tag.bull .s { color: var(--up); }
.dir-tag.bear { border-color: color-mix(in srgb, var(--down) 55%, transparent); }
.dir-tag.bear .s { color: var(--down); }
.dir-foot { display: flex; flex-wrap: wrap; gap: 4px 16px; align-items: baseline; }
.dir-foot .ms-verdict { margin-top: 0; font-size: var(--fs-sm); }
.dir-risks { display: contents; }
/* 風險提醒：⚠ 加琥珀字，全站「警示」的顏色；不算進偏多偏空，觸發才出現 */
.dir-risk { font-size: var(--fs-sm); color: var(--warn-text); }
.dir-note { font-size: var(--fs-xs); color: var(--muted); }
.dir-rules { margin: 0; padding-left: 1.2em; font-size: var(--fs-xs); line-height: 1.6; color: var(--text-secondary); }
.dir-rules li + li { margin-top: 4px; }

/* 置頂 KPI 條：sticky 的定位基準是最近的捲動祖先，也就是 .content（overflow-y:auto），
   不是 .view 本身——.view 是 overflow:visible，捲動一律交給 .content（見該規則的既有
   註解）。空字串時（尚無資料）不佔版位，避免頁面一開始就留一條空白列。 */
.kpi-sticky {
  position: sticky; top: 0; z-index: 5;
  display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: var(--sp-3);
  background: var(--panel-raised); border: 1px solid var(--border-strong); border-radius: var(--r-lg);
  padding: 8px 16px; box-shadow: var(--shadow-panel);
}
.kpi-sticky:empty { display: none; }

.kpi-item { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.kpi-k { font-size: var(--fs-xs); color: var(--muted); white-space: nowrap; }
.kpi-v { font-size: var(--fs-sm); font-weight: 700; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kpi-v .up { color: var(--up); }
.kpi-v .down { color: var(--down); }
/* KPI 條是全站更新後第一眼看的東西，值變了要看得出來——但不是每一項都有方向
   （成交金額／資料狀態不是漲跌），所以不能借紅綠，改用全站既有的「中性提醒」語彙
   --info（同 .rank-row:hover 的 rgba(112,184,255,…) 那份藍），淡入即淡出，
   不常駐、不需要文字說明。只在值真的變了才加這個 class（見 app.js renderKpiSticky），
   首次進頁不閃——否則六格同時亮起來是雜訊，不是訊號。 */
.kpi-item.flash { border-radius: var(--r); animation: kpi-flash var(--dur-slow) var(--ease-out); }
@keyframes kpi-flash {
  0% { background: rgba(112, 184, 255, 0.22); }
  100% { background: transparent; }
}

/* 六格目前一路平鋪：前 4 格（加權指數／成交金額／漲跌家數／三大法人）是「今天發生了
   什麼」的原始行情，後 2 格（市場方向／資料狀態）是「這個頁面自己算出來的判讀」——
   兩種性質不一樣，但視覺上完全等重，眼睛掃過去分不出段落。用既有的中性藍 --info
   （同 rank-row hover／KPI 更新提醒那份語彙）給後兩格一點極淡的底色，不靠邊框或
   分隔線（欄數會隨螢幕寬度換行，靠 nth-child 加左邊框在窄螢幕會斷在錯的地方；
   底色是逐格套用，跟版面怎麼換行無關）。只是分組提示，不是新的狀態語彙。
   ⚠️ 不能用負 margin 把底色撐出格子邊界來製造「連成一塊」的錯覺——手機 3 欄斷點
   的 gap 只有 4px 8px（見下方 375px 那條），比桌機 --sp-3(12px) 窄很多，實測負
   margin 6px 會讓相鄰格子的底色重疊 4px。也不能用 padding 加底色範圍：.kpi-item
   是 grid item，同一列的格高由該列最高的項目決定（grid 預設 align-items:stretch），
   幫 5、6 兩格加 padding 會讓它們變高，把整列格高一起撐高，1–4 格反而多出一段
   說不出理由的空白。用 inset box-shadow 純粹畫色、不佔版面（box-shadow 不影響
   layout），填滿格子既有的框、大小完全不變，四個斷點都安全。 */
.kpi-item:nth-child(5), .kpi-item:nth-child(6) {
  border-radius: var(--r); box-shadow: inset 0 0 0 100px rgba(112, 184, 255, .07);
}

@media (max-width: 1050px) {
  .kpi-sticky { grid-template-columns: repeat(3, minmax(0, 1fr)); row-gap: 6px; }
}
/* 1040px 以下判讀那條改上下排：結論在上、標籤在下。左右排時結論那欄是 auto、計數不換行，有「無資料」
   或「資料不足」時會寬到 414px，861～975px 實測把右邊的標籤擠出這一帶、偏多偏空那幾個字被裁掉（最終
   審查）。最寬的標籤（加權位階 季線之上、月線之下 中性）約 214px，1001～1023px 剛好只剩這麼寬，取 1040 留餘裕。 */
@media (max-width: 1040px) {
  .dir-panel { display: flex; flex-direction: column; align-items: stretch; gap: 10px; }
  .dir-lead { padding-right: 0; border-right: 0; }
}
@media (max-width: 500px) {
  .kpi-sticky { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* 數字那條：加權指數、漲跌家數、三大法人合計一行排完，放不下時各塊依序換行 */
.market-strip { min-width: 0; padding: 12px 18px; border-top: 1px solid var(--border-subtle); }
.ms-main { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 28px; min-width: 0; }
.ms-index { display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap; }
.ms-index .ms-label { font-size: var(--fs-sm); color: var(--muted); }
.ms-index .ms-val { font-size: 30px; font-weight: 700; line-height: 1.1; }
.ms-index .ms-chg { font-size: var(--fs-md); font-weight: 600; }
.ms-main #breadth { flex: 1 1 360px; min-width: 0; }
.ms-main .breadth-nums { margin-top: 0; }
.ms-inst { display: flex; align-items: baseline; gap: var(--sp-4); flex-wrap: wrap; }
.ms-inst .ms-i { display: flex; align-items: baseline; gap: 8px; }
.ms-inst .ms-i-k { font-size: var(--fs-sm); color: var(--muted); }
.ms-inst .ms-i-v { font-size: var(--fs-lg); font-weight: 700; white-space: nowrap; }
/* 一句判讀：指數方向 vs 內部廣度的關係，是整個首屏唯一回答「今天健不健康」的句子，
   但原本跟旁邊隨便一個 muted 說明字級／字色完全一樣（16px、--label、weight 400）——
   眼睛沒有理由在這裡停下來。改用 --text（全站最亮的文字色）＋600 字重，讓它讀起來
   像「結論」而不是「註解」。不是新色、不是新裝飾，只是把既有的強調字色借過來用在
   它真正該用的地方。背離時才套 .alert（見下方「注意」語彙）——.alert 現在只需要疊
   加它自己特有的 3 樣東西（背景／padding／琥珀外框），文字色與字重已經是基準值。 */
.ms-verdict { font-size: var(--fs-md); font-weight: 600; color: var(--text); margin-top: var(--sp-2); }
.ms-verdict:empty { display: none; }
.ms-verdict.alert { display: inline-block; background: var(--sunken); border-radius: var(--r);
  padding: 4px 10px; outline: 1px solid var(--accent); }

/* 「注意這格」的唯一語彙：琥珀外框。外框與填色是兩條正交的軸，所以它能疊在
   紅漲綠跌的卡片上而不打架——不必為了標注異常再引入第四個色相。
   只用在兩處：背離的判讀句、異常讀數的卡片。 */
.card.alert { outline: 1px solid var(--accent); outline-offset: -1px; }
/* 位階狀態章：比較期間與判讀結果分離，不用長度映射百分比，避免長條被誤讀成進度。
   中性資訊色不搶行情紅綠；極端值的「注意」語意仍只由卡片琥珀外框負責。 */
.card-rank {
  order: 5; display: flex; align-items: center; justify-content: space-between; gap: 8px;
  align-self: stretch; max-width: 100%; min-height: 25px; margin-top: 7px;
  color: var(--muted); font-size: var(--fs-xs); line-height: 1.2; white-space: nowrap;
  cursor: help;
}
/* 沒有位階的卡留下的空位（app.js 的 cardWrap）：只佔位，不是「這裡有解釋」 */
.card-rank-slot { cursor: default; }
.card-rank-period { overflow: hidden; text-overflow: ellipsis; }
.card-rank-badge {
  display: inline-flex; align-items: baseline; gap: 6px; flex: 0 0 auto;
  padding: 4px 8px; border-radius: 5px;
  background: rgba(125, 141, 166, .1); color: var(--label);
  box-shadow: inset 0 0 0 1px rgba(125, 141, 166, .18);
}
/* 高位、低位都只是「落在近 N 日分布的尾端」——用同一個中性藍，高或低由文字說。
   原本高位用行情紅（--up-soft），等於把融券、維持率的高位階畫成「漲」。 */
.card-rank-badge.rank-high,
.card-rank-badge.rank-low {
  background: rgba(112, 184, 255, .16); color: var(--info);
  box-shadow: inset 0 0 0 1px rgba(112, 184, 255, .4);
}
.card-rank-badge strong { color: inherit; font-weight: 850; }

/* 標題有兩階：.section-head（這一段回答什麼問題）＞ .group-title（單一區塊）。
   層級全靠字級／字重／亮度／留白拉開，不加新裝飾——本頁的視覺重點已經花在
   判讀句與位階標籤上，標題再搶就變成兩個都不突出。
   琥珀在此刻意缺席：--accent 只表「你在這裡」與「注意這格」，拿來裝飾標題會稀釋那兩個意思。 */
/* 每個 section-head 上方一律留 --sp-5：第一個緊接 AI 摘要，那個間距正好把
   「結論」與下面的「證據」分開，所以不需要為它另設例外。
   （曾試過 .section-head:first-of-type 減少間距——:first-of-type 是比對元素型別(div)
   而非 class，#view-overview 的第一個 div 是 .market-strip，該規則永遠不會命中。） */
.section-head { display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap;
  margin: var(--sp-2) 0 calc(-1 * var(--sp-1)); }
.sh-title { font-size: var(--fs-lg); font-weight: 700; color: var(--text); letter-spacing: 1px; }
.sh-desc { font-size: var(--fs-sm); color: var(--muted); }

/* ===== 總覽網格：card-group 兩三欄並排，取代原本「每個區塊都佔滿一整列、一路往下疊」
   的直向堆疊。實測改版前總覽頁高 5,158px＝7.2 個螢幕（viewport 720px），根因是
   `.view` 是 flex-direction:column，任何一個 .card-group 都不可能並排——卡片在群組
   *內部*已經是 4 欄網格（見下面 .card-grid, .cards），問題出在群組本身。
   只在 #view-overview 生效，用 ID 疊過 `.view.active{display:flex}` 的 specificity（
   兩者都是 2 個 class 選擇器的權重，ID 保證贏，不必依賴誰寫在檔案後面）——其他視圖
   仍是既有的 flex column，`.table-wrap.fill`／交易帳本頁的版面設計依賴那個語意，
   不能全站改。

   **2026-08 第二輪改用 12 欄固定格線，不再用 auto-fit。** auto-fit 只會生出「等寬的
   N 欄」，於是每個區塊只能佔 1 欄或整列——**沒有辦法佔 2 欄**。實測 1560px 視窗下
   內容區 1305px、auto-fit 給 3 欄各 406px，一列放 2 個區塊就必然剩下 477px 空著
   （期貨籌碼／期權情緒、分布圖／國際市場兩列都是），這正是使用者說的「太多空白」。
   改成 12 欄之後每個區塊自己宣告 span，同一列的 span 相加剛好 12，空白就消失；
   span 還依「該區塊有幾張卡」挑，讓同列的卡片列數一致 → 高度自然齊平 →
   短區塊被拉高的內部留白（實測 186/189px）同時解決。 */
#view-overview.active {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  align-content: start;
}
/* span 是內容決定的，不是好看決定的：卡片多的給多一點欄寬，讓它跟同列的鄰居
   排出一樣多的卡片列數。改動任一區塊的卡片數時，這裡的 span 要跟著重算。 */
#view-overview.active .span-7 { grid-column: span 7; }
#view-overview.active .span-6 { grid-column: span 6; }
#view-overview.active .span-5 { grid-column: span 5; }
#view-overview.active .span-3 { grid-column: span 3; }
/* 窄視窗：12 欄切不出有意義的區塊寬度（每欄只剩 ~50px），全部改為單欄堆疊。
   斷點取 1180px——低於此值內容區約 900px，12 欄的 span-3 會窄到放不下一列榜單。 */
@media (max-width: 1180px) {
  #view-overview.active > * { grid-column: 1 / -1 !important; }
}
/* 圖表類與本身已經很寬的區塊（法人排行的雙欄榜單、大盤×籌碼對照、個股熱力圖、K線、
   AI 摘要）標 .span-full 佔滿整列寬度；section-head／chart-row／ai-head 用元素選擇器
   而非逐一加 class，因為它們的「必須滿版」是結構性的、不會有例外。 */
#view-overview.active .overview-top,
#view-overview.active #kpi-sticky,
#view-overview.active .section-head,
#view-overview.active .chart-row,
#view-overview.active .ai-head,
#view-overview.active #market-summary,
#view-overview.active .span-full {
  grid-column: 1 / -1;
}

/* 區塊標題：左豎線分層。原本還有一條琥珀漸層底線，在只有單階標題時是好看的收尾，
   但補上 .section-head 後，8 條琥珀底線會與新的一階互相競爭、也稀釋琥珀的語意，
   故移除——階層改由上面那階承擔，這階只要能被認出是標題即可。 */
/* 表面層次：group 是「安靜的區域邊界」、card 才是真正的「抬起的單元」——兩者原本各自
   疊 bg-step＋border＋shadow 三種訊號，24 張卡的密集網格於是讀成「框中框，兩層都在
   搶著浮起來」。改成每一級只主打一種訊號：group 只留 bg-step＋髮絲邊（拿掉陰影，
   它是被動的分組容器，不需要看起來浮著）；card 保留 bg-step＋陰影（它才是真的會
   hover 抬升的互動單元），邊框降到髮絲級——bg-step 與陰影已經足夠把卡片跟底色分開，
   一條清楚的邊框只是第三票。.card.alert 用的是 outline 不是 border（見下方），
   兩者互不影響，邊框變安靜後琥珀外框反而更醒目。不動間距、不動任何一張卡的資訊密度。 */
.card-group { background: rgba(16, 26, 40, 0.72); border: 1px solid var(--border-subtle); border-radius: var(--r-lg); padding: 12px; }
.group-title {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
  font-size: var(--fs-md); color: var(--label); font-weight: 600;
  /* margin 用 shorthand 而非只設 margin-bottom：曾經借給股期概況的頁面標題 <h2>
     用過（h2 有瀏覽器預設 margin-top，只設 bottom 蓋不掉），2026-09 發現那其實是
     誤用——頁面標題該比照 .picks-title-row h2（見下方 #view-ssf 專屬規則），已
     改回不掛這個 class。現在的用法全是 <div>（預設無邊距），shorthand 對它是
     無操作（本來就是 0），純粹維持寫法一致，不必改回 margin-bottom。 */
  margin: 0 0 var(--sp-2);
}
/* 收合：點標題列收合／展開，狀態存 localStorage（見 app.js initCollapsibleGroups）。
   只用 CSS 隱藏「標題以外的所有直接子項」，不管該區塊內部長什麼樣（卡片網格／
   排行榜／圖表都一樣一行處理），不必為每種區塊內容各寫一條收合規則。
   只有帶 data-key 的 .card-group 才可收合——那是 app.js 用來記住狀態的鍵，
   沒有鍵的區塊（目前沒有這種情況，但保留這個防呆）點了也不會收合，總比
   收合後找不到鍵、狀態存不住、下次重整又彈開要好。 */
.card-group[data-key] > .group-title { cursor: pointer; user-select: none; }
.card-group[data-key] > .group-title::before { content: "▾  "; color: var(--muted); font-size: 11px; }
.card-group[data-key].collapsed > .group-title::before { content: "▸  "; }
.card-group[data-key].collapsed > .group-title { margin-bottom: 0; }
.card-group[data-key].collapsed > *:not(.group-title) { display: none; }
/* auto-fit 而非固定 repeat(4, …)：欄數以「.cards 自己實際拿到多寬」決定，不是猜視窗
   多寬。改成總覽網格（見上面 #view-overview.active 那段）後，.cards 的容器 .card-group
   窄至 ~360px，若還套用固定 4 欄／舊有的 viewport 斷點（1240px→3欄、800px→2欄），
   斷點依據的是「視窗」寬度而非「容器」寬度，於是在 1280px 寬視窗下這裡仍套 4 欄，
   實測卡片被壓到 102px、文字溢出（`外資買賣超 -57.32 億` 顯示不全）。auto-fit 天生
   吃的就是容器自己的寬度，換掉固定欄數後兩個 viewport 斷點也一併移除——它們現在
   只會跟 auto-fit 打架，不是互補。（`.cards` 目前只有總覽這四張卡在用，改動範圍可控。） */
.card-grid, .cards {
  display: grid;
  /* 138px＋10px 間距是量出來的，不是估的：台股大盤那組有 8 張卡、佔滿整列時容器內寬
     1217px，要讓 auto-fit 排得下 8 欄需要 8×138+7×10=1174 ≤ 1217。維持 150px/16px 只排得下
     7 欄，第 8 張會單獨掉到第二列變成孤兒。反過來也不能再壓：實測 8 欄搭 16px 間距時
     卡片只剩 138px，`自營買賣超 -194.29 億` 這種數值就會被 .card-val 的 nowrap 擠出格子
     （現在 143px 剛好放得下）。窄容器仍由 auto-fit 自然降成 4/3 欄，不必寫斷點。 */
  grid-template-columns: repeat(auto-fit, minmax(152px, 1fr));
  gap: 10px;
}
/* min-width:0 是必要的結構護欄：grid item 預設 min-width:auto（＝min-content），
   所以卡片裡任何一行 nowrap 的長文字（.card-val/.card-note）都會把該欄撐得比
   minmax(150px,1fr) 寬，整列欄寬就不齊了——症狀是「某一張卡變寬、其他卡跟著位移」。
   有了 min-width:0，欄寬回歸 grid 控制，過長的內容改以溢出呈現（仍看得出來要修文案）。 */
.card {
  display: flex; flex-direction: column;
  /* min-height 從 136px 降到 104px：實測卡片內容最矮 49px、最高 103px，加上 24px 內距
     最高也只需 127px，所以「高卡片」不受這個下限影響（它們本來就靠內容自己撐開）；
     被墊高的是佔多數的矮卡片——實測內容 67px 的卡片被撐成 146px，等於每張白白空 55px，
     24 張就是整頁看起來很空的主因。降到 104px 只砍掉純空白，不動任何一張卡的內容。 */
  min-width: 0; min-height: 116px; padding: 12px var(--space-md);
  background: var(--bg-card); border: 1px solid var(--border-subtle); border-radius: var(--r-lg);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.045), var(--shadow-card);
  transition: transform var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}
.card:hover { transform: translateY(-2px); border-color: var(--border-default); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.07), var(--shadow-card-hover); }
/* 有懸停說明的卡片：標籤加點狀底線當「這裡有解釋」的視覺提示 */
.card[title] { cursor: help; }
.card[title] .card-label { text-decoration: underline dotted var(--muted); text-underline-offset: 3px; }
.card-label { order: 0; font-size: var(--font-size-sm); color: var(--text-secondary); }
.card-label .asof { color: var(--muted); font-weight: 400; font-size: var(--fs-xs); }
/* 26px 只留給數字。修飾語（淨多/淨空/散戶偏多）與單位降級後，「淨空 75,198 口」
   才不會在 175px 的卡片裡把「口」擠到第二行——而且真正的讀數本來就是數字，
   修飾語只是標籤，兩者同一級是排版上的誤植。 */
/* 23px 而非 --fs-xl(26px)：卡片變窄（台股大盤一列 8 張、每張約 150px）之後 26px 在
   格子裡偏「喊」，參考的儀表板類介面數值多在 20–22px。這只是觀感刻度，**不動
   「卡片裡最大的字只給數字」那條分層規則**——修飾語仍走 .card-qual、單位走 .card-unit。 */
.card-val { order: 2; color: var(--text-primary); font-size: 25px; font-weight: 700; margin: auto 0 var(--space-xs); line-height: 1.15; white-space: nowrap; }
.card-qual { order: 1; font-size: var(--font-size-sm); color: var(--text-muted); margin-top: var(--space-xs); line-height: 1.2; }
.card-qual + .card-val { margin-top: auto; }
.card-unit { font-size: var(--fs-sm); font-weight: 400; margin-left: 3px; }
.card-note { order: 3; font-size: var(--font-size-xs); color: var(--text-muted); margin-top: 3px; white-space: nowrap; }
.card-chg { order: 4; align-self: flex-end; font-size: var(--font-size-sm); margin-top: var(--space-xs); }
.card-metric {
  order: 2; display: flex; align-items: baseline; justify-content: space-between;
  min-width: 0; gap: var(--space-sm); margin-top: 9px;
}
.card-metric .card-val { flex: 0 1 auto; margin: 0; }
.card-metric .card-chg {
  flex: 0 0 auto; align-self: baseline; margin: 0; white-space: nowrap;
  font-size: 19px; font-weight: 700; line-height: 1.15; text-align: right;
}
.card-qual + .card-metric { margin-top: 5px; }
/* 總覽卡片數字一行、漲跌一行（總覽改版第二件）：一列 6 張時每張約 204px、內容寬約 182px，
   「數字＋漲跌」同一行最長要 240px；國際市場 7 張時同一行最多擠出 139px。手機（≤600px）
   另有自己的堆疊規則（漲跌靠右），寫在後面、會蓋過這裡的對齊。 */
.stat-board .card-metric { flex-direction: column; align-items: flex-start; gap: 1px; }
.stat-board .card-metric .card-chg { align-self: flex-start; font-size: 15px; text-align: left; }
.card-trend { order: 4; min-width: 0; margin-top: 8px; }
.card-trend-head {
  display: flex; justify-content: space-between; gap: 8px; margin-bottom: 2px;
  color: var(--muted); font-size: 11px; line-height: 1.2; white-space: nowrap;
}
.card-trend-head span:last-child { font-variant-numeric: tabular-nums; }
.card-trend-bars {
  position: relative; display: grid; grid-template-columns: repeat(var(--trend-n), minmax(0, 1fr));
  align-items: stretch; gap: 3px; height: 44px; overflow: hidden;
}
.card-trend-bars::after {
  content: ""; position: absolute; z-index: 0; left: 0; right: 0; top: 50%;
  border-top: 1px solid var(--border-default);
}
.card-trend-bar { position: relative; z-index: 1; min-width: 0; }
.card-trend-bar::before {
  content: ""; position: absolute; left: 12%; right: 12%;
  /* 柱高＝比例（JS 給的 --bar-r，0～1）× 半格高度，中線到上下緣各留 2px；最矮 3px 才看得到 */
  height: max(3px, calc(var(--bar-r, 0) * (50% - 2px)));
  border-radius: 2px 2px 0 0;
}
.card-trend-bar.up::before { bottom: 50%; background: var(--up); }
.card-trend-bar.down::before { top: 50%; background: var(--down); border-radius: 0 0 2px 2px; }
.card-trend-bar.flat::before { top: calc(50% - 1px); height: 2px; background: var(--flat); border-radius: 1px; }
.up { color: var(--up); }
.down { color: var(--down); }
.flat { color: var(--muted); }
/* 增減不是行情漲跌的卡（整戶維持率、追繳戶數、融資占市值、信用交易占比）：方向仍由 ▲▼
   與柱子在中線上／下表達，但不借用紅綠——紅綠鎖給行情方向。 */
.stat-cell.dir-neutral .up, .stat-cell.dir-neutral .down { color: var(--text-secondary); }
.stat-cell.dir-neutral .card-trend-bar.up::before,
.stat-cell.dir-neutral .card-trend-bar.down::before { background: var(--info); }

/* StatBoard / StatCell：密集讀數是一張表，不是 8 張彼此競爭的卡。容器統一處理圓角與
   邊界；cell 只保留一層色階和 1px 分隔，hover 不位移，掃視時數字不會跳。 */
.stat-board {
  grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px;
  overflow: hidden; background: var(--border-subtle); border: 1px solid var(--border-subtle);
  border-radius: calc(var(--r-lg) - 2px); font-variant-numeric: tabular-nums;
}
/* 台股大盤、期貨籌碼：欄數跟著卡片區（.card-group）的寬度走，不看視窗。兩組的卡片區一樣寬，
   所以在同一個寬度一起切換、欄位永遠上下對齊；12 與 6 都能被 6／3／2 整除，不會有半空的列。
   門檻是實測（總覽改版第二件，2026-10）：任何一張卡的任何一行文字超出卡片，就退一級。
   6 欄要容器 ≥933px（注入長數字實測：窄卡數字 22px、補充說明 11px 之後，最寬的是 10日均量的
   漲跌「▲3,172 (+34.58%)」），取 936 留 3px 餘裕——1280px 視窗的卡片區 943px，維持 6 欄。
   舊的 1240／1200／1100／1000px 視窗斷點已移除——寫在後面的 @media 會蓋過這裡。 */
.card-group[data-key="tw"], .card-group[data-key="fut"] { container: statboard / inline-size; }
.stat-board--tw, .stat-board--fut { grid-template-columns: repeat(6, minmax(0, 1fr)); }
@container statboard (max-width: 935px) {
  .stat-board--tw, .stat-board--fut { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@container statboard (max-width: 499px) {
  .stat-board--tw, .stat-board--fut { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* 國際市場 7 張（美國 VIX 從期貨籌碼搬來，總覽改版第二件）：7 → 4 → 2 欄，跟著卡片區寬度走；
   4 欄與 2 欄時最後一張佔兩格，把列補滿。門檻實測：7 欄時每張至少約 170px（比特幣的漲跌
   「▲1,234.56 (+1.10%)」要約 164px），4 欄時每張至少約 162px。 */
.card-group[data-key="intl"] { container: intlboard / inline-size; }
.stat-board--intl { grid-template-columns: repeat(7, minmax(0, 1fr)); }
@container intlboard (max-width: 1199px) {
  .stat-board--intl { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .stat-board--intl > .stat-cell:last-child { grid-column: span 2; }
}
@container intlboard (max-width: 649px) {
  .stat-board--intl { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.stat-board > .stat-cell {
  min-height: 92px; padding: 9px 11px; border: 0; border-radius: 0;
  background: var(--bg-card); box-shadow: none;
  transition: background-color var(--dur-base) var(--ease-out);
}
.stat-board > .stat-cell:hover { transform: none; background: var(--bg-card-hover); box-shadow: none; }
/* 重點指標（KEY_METRICS）只靠標籤加亮加粗——本站階層一律來自字級／字重／亮度。
   原本另加亮底＋藍框、數字大 1px，讀起來像「被選取」而不是「比較重要」（ui79 拿掉）。 */
.stat-cell.key-metric .card-label { color: var(--text-primary); font-size: 16px; font-weight: 800; }
.stat-board > .stat-cell > .card-val { margin: 9px 0 var(--space-xs); }
.stat-board > .stat-cell > .card-qual + .card-val { margin-top: 5px; }
/* 同一列的卡片內容長短不一（有的多一行補充說明）：長條與位階貼齊卡片底部，同一列的長條才在同一個高度。 */
.stat-board > .stat-cell > .card-trend { margin-top: auto; padding-top: 8px; }
/* 窄卡片數字 25→22px（卡片自己的容器查詢）：一列 6 張在 1280px 視窗每張約 156px，25px 的
   「▲129,645 張」擠出 3px（2026-10-06 雛形實測）。補充說明同時 12→11px：融資餘額的
   「金額 9,999.9 億　▼999.9」在 12px 要 138px（數字等寬，任何五位數金額配上百億的增減都一樣長，
   大跌日就會出現），不縮的話 6 欄要到容器 967px 才排得下，1280px 視窗只能 3 欄（最終審查）。 */
.stat-board > .stat-cell { container: statcell / inline-size; }
@container statcell (max-width: 169px) {
  .card-val { font-size: 22px; }
  .card-note { font-size: 11px; }
}
.stat-board > .muted { grid-column: 1 / -1; padding: 14px; background: var(--bg-card); }


/* ===== 漲跌家數 ===== */
.breadth-nums { display: flex; align-items: center; gap: 10px 16px; flex-wrap: wrap; font-size: var(--fs-sm); font-weight: 700; margin-bottom: 7px; }
.breadth-nums .up { color: var(--up); }
.breadth-nums .down { color: var(--down); }
.breadth-nums .flat { color: var(--muted); }
.bd-lim { font-size: var(--fs-xs); font-weight: 700; padding: 1px 7px; border-radius: var(--r-pill); }
.bd-lim.up { background: var(--up-dim); color: #f0a0a0; }
.bd-lim.down { background: var(--down-dim); color: #7ee2a0; }
.breadth-bar { display: flex; height: 22px; border-radius: var(--r); overflow: hidden; border: 1px solid var(--border); }
.breadth-bar .seg { height: 100%; }
.breadth-bar .seg.up { background: var(--up); }
.breadth-bar .seg.flat { background: var(--flat); }
.breadth-bar .seg.down { background: var(--down); }

/* ===== 杯柄選股 ===== */
.cup-list { display: flex; flex-wrap: wrap; gap: 6px; flex: 0 0 auto; margin-bottom: 6px; }
.cup-chip { display: inline-flex; align-items: center; gap: 6px; background: var(--card); border: 1px solid var(--border);
  border-radius: var(--r-pill); padding: 6px 13px; font-size: var(--fs-md); font-weight: 600; color: var(--text); text-decoration: none; white-space: nowrap; cursor: pointer; }
.cup-chip:hover { border-color: var(--border-strong); }
.cup-chip.active { background: var(--accent); color: var(--on-accent); font-weight: 700; border-color: var(--accent); }
.cup-chip .cup-r { font-size: var(--fs-xs); opacity: .8; }
.cup-chip.active .cup-r { opacity: 1; }
.cup-chip.pick { border-color: var(--info); }               /* 同時符合選股名單：藍框標記（狀態不用行情綠） */
.cup-chip.pick::before { content: "●"; color: var(--info); font-size: 10px; margin-right: -2px; }
.cup-chip.active.pick::before { color: var(--on-accent); }
.cup-group { display: flex; flex-direction: column; gap: 6px; flex: 0 0 auto; }
.cup-group-head { color: var(--label); font-size: var(--fs-sm); }
.cup-group-head .num { margin: 0 3px; color: var(--text-primary); font-weight: 500; }
.cup-next { margin-left: 6px; color: var(--muted); font-size: var(--fs-xs); }
.cup-r-hint { color: var(--muted); font-size: var(--fs-xs); }
.cup-detail { display: flex; flex-direction: column; gap: 10px; flex: 0 0 auto; }
.cup-risk { color: var(--muted); font-size: var(--fs-sm); line-height: 1.7; }
.cup-risk b { color: var(--text); font-weight: 500; }   /* 數字最粗中等（ui82） */
.cup-conds-head { color: var(--label); font-size: var(--fs-sm); margin-bottom: 6px; }
.cup-conds td { white-space: normal; vertical-align: top; }
.cup-conds td.num { white-space: nowrap; }
.cup-conds .cup-ok { color: var(--text); }
.cup-conds .cup-pend { color: var(--muted); }        /* 不成立與「明日盤中判斷」都不著紅綠 */

/* ===== 權值股貢獻（色階卡片，供海期監控共用） ===== */
/* 漲跌幅排行榜：三欄的緊湊清單（代號／名稱／漲跌%）。名稱用 min-width:0 + ellipsis，
   長名稱（如「聯華電子」以上）才不會把漲跌% 擠出格子——同 .card 那條 grid item 預設
   min-width:auto 會被內容撐開的教訓。整列可點，跳到個股查詢。 */
.mover-list { display: flex; flex-direction: column; gap: 1px; }
.mover-row { display: grid; grid-template-columns: 44px minmax(0, 1fr) auto; gap: 8px;
  align-items: baseline; padding: 5px 6px; border-radius: var(--r);
  color: inherit; text-decoration: none; font-size: var(--fs-data); }
.mover-row:hover { background: var(--bg-card-hover); }
.mover-code { color: var(--muted); font-size: var(--fs-xs); }
.mover-name { color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mover-pct { font-weight: 650; font-variant-numeric: tabular-nums; }
.mover-empty { padding: 10px 6px; }
.mv-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(122px, 1fr)); gap: 6px; }
.mv-card { border: 1px solid var(--border); border-radius: var(--r); padding: 8px 10px; color: #fff; overflow: hidden; }
/* 權值股卡的個股名在紅/綠底色上，陰影讓白字浮起、更清楚有層次 */
.mv-name { font-size: var(--fs-md); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5); }
.mv-contrib { font-size: 20px; font-weight: 800; margin-top: 2px; line-height: 1.1; }
.mv-contrib .mv-unit { font-size: var(--fs-xs); font-weight: 400; margin-left: 2px; }
.mv-sub { font-size: var(--fs-xs); opacity: .92; margin-top: 2px; white-space: nowrap; }
/* 海期卡片：垂直三層——名稱獨佔整行（不截斷）、價格為主角、漲跌%/點數分列下方。
   名稱給滿整張卡片寬度才放得下 5 字中文名（小那斯達克）與長英文名（Alphabet），
   不再與價格搶同一行而被 ellipsis 切掉。 */
.of-name { font-size: var(--fs-data); font-weight: 700; white-space: nowrap; line-height: 1.25; }
.of-price { font-size: var(--fs-md); font-weight: 800; white-space: nowrap; margin-top: 1px; line-height: 1.15; }
.of-bot { display: flex; justify-content: space-between; align-items: baseline; gap: 8px;
  font-size: var(--fs-data); margin-top: 3px; font-weight: 600; }
.of-time { font-size: var(--fs-xs); opacity: .8; font-weight: 400; margin-left: 6px; }

/* ===== 當日漲跌族群 ===== */
.sectors { display: grid; grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); gap: 6px; }
/* 產業強弱橫條：熱力圖的緊湊預設檢視（原本 820px 是總覽單一最大區塊）。
   只列最動的 8 個產業（見 app.js renderHeatmapCompact），約 8 列 × 26px ≈ 210px。
   無文字疊在色塊上，故用亮色 C.up/C.down（同漲跌家數條、K 線的既有 token 規則，
   與熱力圖本體的白字用 --up-fill/--down-fill 是不同情境，不要混用）。 */
.sectors-compact { display: flex; flex-direction: column; gap: 4px; }
.hm-bar-row { display: grid; grid-template-columns: 92px 1fr 64px; align-items: center; gap: var(--space-sm); }
.hm-bar-name { font-size: var(--fs-sm); color: var(--label); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hm-bar-track { position: relative; height: 8px; background: var(--sunken); border-radius: 4px; overflow: hidden; }
.hm-bar-fill { position: absolute; top: 0; left: 0; height: 100%; border-radius: 4px; }
.hm-bar-fill.up { background: var(--up); }
.hm-bar-fill.down { background: var(--down); }
.hm-bar-fill.flat { background: var(--muted); }
.hm-bar-val { font-size: var(--fs-sm); font-weight: 700; text-align: right; white-space: nowrap; }
.hm-bar-val.up { color: var(--up); }
.hm-bar-val.down { color: var(--down); }
.sector { background: var(--card); border: 1px solid var(--border); border-left: 3px solid var(--muted); border-radius: var(--r); padding: 7px 9px; display: flex; justify-content: space-between; align-items: center; gap: 8px; font-size: var(--fs-base); }
.sector .sec-name { color: var(--label); font-size: var(--fs-md); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sector .sec-chg { font-weight: 700; white-space: nowrap; }
.sector.up { border-left-color: var(--up); }
.sector.up .sec-chg { color: var(--up); }
.sector.down { border-left-color: var(--down); }
.sector.down .sec-chg { color: var(--down); }
.sector.flat .sec-chg { color: var(--muted); }

.sector-stocks:not(:empty) { margin-top: 10px; }
.ss-head { display: flex; align-items: center; gap: 8px; font-size: var(--fs-sm); margin-bottom: 8px; }
.ss-head .ss-close { margin-left: auto; cursor: pointer; color: var(--muted); padding: 0 6px; }
.ss-head .ss-close:hover { color: #fff; }
.ss-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 6px; }
.ss-cell { background: var(--card); border: 1px solid var(--border); border-left: 3px solid var(--muted); border-radius: var(--r); padding: 6px 8px; display: flex; justify-content: space-between; align-items: center; gap: 6px; font-size: var(--fs-base); }
.ss-cell .ss-name { color: var(--label); font-size: var(--fs-md); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ss-cell .ss-chg { font-weight: 700; white-space: nowrap; }
.ss-cell.up { border-left-color: var(--up); } .ss-cell.up .ss-chg { color: var(--up); }
.ss-cell.down { border-left-color: var(--down); } .ss-cell.down .ss-chg { color: var(--down); }
.ss-cell.flat .ss-chg { color: var(--muted); }

/* ===== 族群輪動：頁首（一句摘要＋小標籤＋指標說明） =====
   頁首沿用 ui72 起的 class 名稱；圖本身見下一段（ui83）。 */
.flow-command { align-items: center; }
/* pretty：窄欄時最後一行不會只剩一兩個字 */
.flow-command .flow-headline { color: var(--text-secondary); font-size: var(--fs-sm); text-wrap: pretty; }
.flow-chips { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: 6px; max-width: 640px; }
/* #flow-chip-list 只是包住小標籤的容器，不能自己成一個 flex item，
   否則裡面的 chip span 會被關進單一格子，擠不進 .flow-chips 的 flex 排列 */
.flow-chip-list { display: contents; }
.flow-chip { display: inline-flex; align-items: center; min-height: 24px; padding: 2px 9px; border: 1px solid var(--border-subtle);
  border-radius: 999px; background: var(--bg-sunken); color: var(--muted); font-size: var(--fs-xs); white-space: nowrap; }
.flow-help-btn { min-height: 28px; color: var(--info); font: inherit; font-size: var(--fs-xs); cursor: pointer; }
.flow-help-btn:not(:disabled):hover { border-color: var(--info); }
/* 第一次載入完成前、載入失敗／尚無資料時 #flow-help-body 是空的，按鈕停用，不要讓它看起來還能點開一個空面板 */
.flow-help-btn:disabled { opacity: .5; cursor: default; }
/* 杯柄頁的「重新計算」（.ctf）在 POST 期間會鎖住（cupRecompute 的 btn.disabled）：鎖住的樣子同上，不然看起來還能按 */
.ctf:disabled { opacity: .5; cursor: default; }
.flow-help[popover] { position: fixed; inset: auto; margin: 0; width: min(440px, calc(100vw - 32px)); max-height: min(70vh, 560px);
  overflow: auto; padding: 12px 14px; color: var(--text); background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-panel); }
.flow-help h4 { margin: 0 0 8px; color: var(--text-primary); font-size: var(--fs-sm); }
.flow-help dl { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 5px 12px; margin: 0; font-size: var(--fs-xs); line-height: 1.5; }
.flow-help dt { color: var(--label); font-weight: 700; white-space: nowrap; }
.flow-help dd { margin: 0; color: var(--text-secondary); }
.flow-help p { margin: 10px 0 0; }
/* .picks-command 只在 ≤1100px 才把右欄疊到下一行；小標籤靠左要跟那個斷點對齊才有意義。 */
@media (max-width: 1100px) {
  .flow-chips { justify-content: flex-start; max-width: none; }
}

/* ===== 族群輪動：每週資金流向（ui83：ECharts 內建賽跑，流入／流出兩張） =====
   兩張 ECharts 圖（realtimeSort 只比長度、不分正負，所以每張只放正值）、右下角大字週次、時間軸 sticky。
   資金流向不是漲跌：不用紅綠，也不用琥珀。卡片本身是 container：兩張圖並排看的是卡片寬度、不是視窗。 */
.race { container: race / inline-size; display: flex; flex-direction: column; gap: 6px; padding: 12px 12px 0;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--r-lg); }
.race-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 6px 12px; }
.race-head h3 { margin: 0; color: var(--text-primary); font-size: var(--fs-sm); white-space: nowrap; }
/* 選了大戶、這週沒有大戶時的說明；空的時候寬度是 0，按鈕靠 space-between 留在右邊不動 */
.race-title { display: flex; align-items: baseline; column-gap: 8px; min-width: 0; }
.race-sort-note { color: var(--muted); font-size: var(--fs-xs); white-space: nowrap; }
.race-sort { display: inline-flex; gap: 4px; }
.race-sort-btn:disabled { opacity: .45; cursor: default; }
.race-sort-btn:disabled:hover { border-color: var(--border); color: var(--text); }
/* 每張圖要約 370px（名稱 84px＋長條＋數字），卡片內寬放不下兩張就上下排，流入在上 */
.race-charts { position: relative; display: grid; grid-template-columns: minmax(0, 1fr); gap: 4px 16px; }
@container race (min-width: 760px) {
  .race-charts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.race-side { min-width: 0; }
.race-side-head { color: var(--text-secondary); font-size: var(--fs-xs); }
.race-chart-box { position: relative; }
.race-chart { width: 100%; }
.race-empty-msg { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  color: var(--muted); font-size: var(--fs-sm); pointer-events: none; }
.race-empty-msg[hidden] { display: none; }
/* 大字週次：像比賽的計時器，淡色、不擋長條（並排與上下排都在流出那張的右下角）。底邊放在 x 軸線上方：
   圖的最下面 24px 是 x 軸的刻度數字（grid.bottom），壓在上面會擋到字 */
.race-stamp { position: absolute; right: 8px; bottom: 32px; color: var(--text-primary); opacity: .14;
  font-size: clamp(22px, 4cqi, 44px); line-height: 1; white-space: nowrap; pointer-events: none; }
/* 時間軸貼在畫面底部：播放與拖動時要一直按得到。背景要不透明，捲到下面時才不會透出來。 */
.race-timeline { position: sticky; bottom: 0; z-index: 2; display: flex; align-items: center; gap: 8px;
  margin: 4px -12px 0; padding: 8px 12px 10px; background: var(--card); border-top: 1px solid var(--border-subtle);
  border-radius: 0 0 var(--r-lg) var(--r-lg); }
.race-btn { display: inline-flex; flex: 0 0 auto; align-items: center; justify-content: center; width: 36px; height: 36px;
  padding: 0; color: var(--text); background: var(--bg-sunken); border: 1px solid var(--border); border-radius: var(--r); cursor: pointer; }
.race-btn:hover:not(:disabled) { border-color: var(--border-strong); color: var(--text-primary); }
.race-btn:disabled { opacity: .45; cursor: default; }
.race-play .i-pause, .race-play.playing .i-play { display: none; }
.race-play.playing .i-pause { display: inline-block; }
.race-track { position: relative; flex: 1 1 auto; min-width: 0; padding-bottom: 14px; }
.race-track input[type="range"] { display: block; width: 100%; margin: 0; }
/* 刻度的左右內縮約等於 range 拇指寬度的一半，點才會對到拇指停的位置 */
.race-ticks { position: relative; height: 8px; margin: 2px 8px 0; }
.race-tick { position: absolute; top: 0; width: 7px; height: 7px; margin-left: -3.5px;
  border: 1.5px solid var(--race-big); border-radius: 50%; background: transparent; }
.race-tick.has-big { background: var(--race-big); }
.race-tick-label { position: absolute; top: 9px; left: 50%; transform: translateX(-50%); color: var(--muted);
  font-size: var(--fs-xs); white-space: nowrap; }
/* 頭尾的標籤靠邊對齊。用 JS 標的 class，不用 :last-child：時間軸最後一個子元素是 .race-cur，不是最後一個圓點 */
.race-tick.is-first .race-tick-label { left: -4px; transform: none; }
.race-tick.is-last .race-tick-label { left: auto; right: -4px; transform: none; }
/* 正在跑的日期：目前這週的圓點下方，和起訖標籤同一行、比它們亮；左右位置由 raceUpdateCur 依進度設定 */
.race-cur { position: absolute; top: 9px; color: var(--text-primary); font-size: var(--fs-xs); font-weight: 600;
  white-space: nowrap; pointer-events: none; }
.race-cur:empty { display: none; }
.race-week { flex: 0 0 auto; color: var(--text-secondary); font-size: var(--fs-xs); white-space: nowrap; }
@media (min-width: 601px) {
  .race-sort .race-sort-btn { padding: 4px 11px; min-height: 28px; font-size: var(--fs-xs); }
}
@media (max-width: 600px) {
  .race { padding: 10px 8px 0; }
  /* 標題列只剩約 46px 空位、放不下說明：疊在標題下方並預留一行高，出現或消失都不推動版面 */
  .race-title { flex-direction: column; align-items: flex-start; }
  .race-sort-note { min-height: 1.5em; line-height: 1.5; }
  /* 手機底部分頁列是 fixed（57px＋安全區），.content 的捲動範圍延伸到它後面，時間軸要貼在它上面 */
  .race-timeline { bottom: calc(57px + env(safe-area-inset-bottom, 0px)); margin-inline: -8px; padding-inline: 8px; flex-wrap: wrap; }
  .race-week { order: 5; flex-basis: 100%; }
}
/* 個股表（點族群看個股，取代交叉選股）。表格在自己的框內橫捲（一般 .table-wrap：上方有圖，不能用 .fill） */
.race-detail { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
.race-detail-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; }
.race-detail-head h3 { margin: 0; color: var(--text-primary); font-size: var(--fs-sm); white-space: nowrap; }
.race-sector-pick { display: inline-flex; align-items: center; gap: 6px; color: var(--text-secondary); font-size: var(--fs-xs); }
.race-sector-pick select { min-height: 32px; padding: 4px 8px; color: var(--text); background: var(--card);
  border: 1px solid var(--border); border-radius: var(--r); font-size: var(--fs-sm); }
.race-detail-meta { color: var(--text-secondary); font-size: var(--fs-xs); }
.race-hint, .race-stocks-note { margin: 0; color: var(--muted); font-size: var(--fs-xs); }
#race-stocks-table td.race-picked { text-align: center; color: var(--info); }

/* ===== Phase 1 — institutional ranking / selection tables ===== */
.rank-split { display: flex; gap: var(--space-md); }
.rank-col {
  flex: 1; min-width: 0; overflow: hidden;
  background: var(--bg-sunken); border: 1px solid var(--border-default); border-radius: var(--r-lg);
}
.rank-h {
  position: sticky; top: 0; z-index: 1;
  margin: 0; padding: var(--space-sm) var(--space-md);
  background: var(--bg-panel); border-bottom: 1px solid var(--border-strong);
  font-weight: 700; font-size: var(--font-size-data);
}
.rank-row {
  display: flex; justify-content: space-between; align-items: center; gap: var(--space-sm);
  min-height: 31px; padding: var(--space-xs) var(--space-md);
  border-bottom: 1px solid var(--border-subtle); font-size: var(--font-size-table);
  transition: background-color var(--dur-fast) var(--ease-out);
}
#rank-buy .rank-row:nth-child(even), #rank-sell .rank-row:nth-child(even) { background: rgba(112, 184, 255, 0.04); }
.rank-row:hover { background: rgba(112, 184, 255, 0.10); }
.rank-row span { font-weight: 700; white-space: nowrap; }
.rank-row span.up, .rank-row span.down { padding: 2px 5px; border-radius: 3px; }
.rank-row span.up { background: var(--up-soft); }
.rank-row span.down { background: var(--down-soft); }
#rank-buy .rank-row a.stock, #rank-sell .rank-row a.stock {
  min-height: 24px; padding-block: 2px;
}
/* 法人買賣超排行（總覽改版第二件）：前 10 名、每列標名次。折成兩小欄之後由左往右讀會把
   第 1 名和第 6 名當成同一列（股期頁的未平倉榜踩過），標了名次才不會讀錯。只動 #rank-buy／
   #rank-sell：每日財經新聞頁也用 .rank-split／.rank-row 的樣式。 */
#rank-buy, #rank-sell { counter-reset: rank; }
#rank-buy > .rank-row, #rank-sell > .rank-row { counter-increment: rank; }
#rank-buy > .rank-row::before, #rank-sell > .rank-row::before {
  content: counter(rank); flex: 0 0 1.7em;
  color: var(--muted); font-size: var(--fs-xs); font-variant-numeric: tabular-nums;
}
#rank-buy > .rank-row > a.stock, #rank-sell > .rank-row > a.stock { flex: 1 1 auto; min-width: 0; }
/* 寬度夠時買超、賣超各分兩小欄（1–5｜6–10），先由上往下排滿 5 列再換欄；第 n 名與第 n+5 名
   在同一個格線列、共用列高，名稱偶爾折行也對得齊。斑馬紋改依「畫面上的第幾列」上色
   （第 2、4、7、9 名），否則兩小欄會變成棋盤格。門檻實測：每小欄至少約 260px。 */
.card-group[data-key="rank"] { container: rankboard / inline-size; }
@container rankboard (min-width: 1080px) {
  #rank-buy, #rank-sell {
    display: grid; grid-template-rows: repeat(5, auto); grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
  }
  #rank-buy > .rank-row:nth-child(n+6), #rank-sell > .rank-row:nth-child(n+6) { border-left: 1px solid var(--border-subtle); }
  #rank-buy .rank-row:nth-child(even), #rank-sell .rank-row:nth-child(even) { background: none; }
  #rank-buy .rank-row:is(:nth-child(5n+2), :nth-child(5n+4)),
  #rank-sell .rank-row:is(:nth-child(5n+2), :nth-child(5n+4)) { background: rgba(112, 184, 255, 0.04); }
}
/* 滑過整列提亮：上面的斑馬紋帶 ID（特異度 1,2,0），通用的 .rank-row:hover（0,2,0）蓋不過，
   有底色或被清掉底色的列滑過都不會亮——所以這裡同特異度、排在斑馬紋之後。 */
#rank-buy .rank-row:hover, #rank-sell .rank-row:hover { background: rgba(112, 184, 255, 0.10); }
/* 每日新聞沿用法人排行的欄位與列樣式，只把三市場改成等寬三欄；窄螢幕仍由下方
   .rank-split 的既有規則直排，讓標題不必壓成難讀的三小欄。 */
.news-split .rank-row { align-items: flex-start; }
.news-split .rank-row a { color: var(--text); line-height: 1.5; text-decoration: none; }
.news-split .rank-row a:hover { color: var(--info); text-decoration: underline; }
.news-source { margin-left: auto; }

/* ===== K 線區 ===== */
.chart-row { display: flex; gap: 16px; background: rgba(16, 26, 40, 0.72); border: 1px solid rgba(58, 82, 112, 0.7); border-radius: var(--r-lg); padding: 16px; box-shadow: var(--shadow-card); }
.chart-side { flex: 0 0 110px; display: flex; flex-direction: column; gap: 8px; padding-top: 6px; }
.chart-side-title { font-size: var(--fs-xs); color: var(--muted); }
.chart-side label { font-size: var(--fs-sm); cursor: pointer; display: flex; align-items: center; gap: 6px; }
.chart-side label:hover { color: var(--text-primary); }
.chart-main { flex: 1; min-width: 0; }
.chart-toolbar { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; flex-wrap: wrap; }
/* min-height 統一分段鈕高度：含「▾」這類走後備字型的字元時，行框會被撐高 1px，同一列就高低不齊 */
.tf, .ktf { background: var(--card); color: var(--text); border: 1px solid var(--border); padding: 6px 13px; border-radius: var(--r); cursor: pointer; font-size: var(--fs-sm); min-height: 36px; }
.tf:hover, .ktf:hover { border-color: var(--border-strong); color: var(--text-primary); }
.tf.active, .ktf.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); font-weight: 700; }
.trader-tabs { display: inline-flex; gap: 6px; flex-wrap: wrap; }
.hm-tabs { display: inline-flex; gap: 4px; margin: 0 8px; vertical-align: middle; }
/* 熱力圖的上市／上櫃／全部分頁（.tf.hm-tab）與同一列的「每類股 N 檔」「展開完整熱力圖」
   都是 .tf，尺寸不再另訂（ui79）：原本 .hm-tab 縮成 27px 高、同一列三種尺寸並排。.tf 約 36px，
   已超過 WCAG 2.2 SC 2.5.8 的 24px 下限。.hm-tab 這個 class 仍是總覽熱力圖的點擊掛鉤，不可刪。 */
.ctf, .rkf, .rku { background: var(--card); color: var(--text); border: 1px solid var(--border); padding: 6px 13px; border-radius: var(--r); cursor: pointer; font-size: var(--fs-sm); min-height: 36px; }
/* 停用的 .ctf（例：「重新計算」POST 期間）不吃 hover。用 :where(:not(:disabled))、不用 :not(:disabled)：後者把優先序拉到 (0,3,0)，
   會蓋過下面的 .ctf.active（(0,2,0)，排在後面所以原本贏），滑過已開啟的鈕時深色字會變近白、落在琥珀底上；:where() 不計優先序（瀏覽器實測） */
.ctf:where(:not(:disabled)):hover, .rkf:hover, .rku:hover { border-color: var(--border-strong); color: var(--text-primary); }
.ctf.active, .rkf.active, .rku.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); font-weight: 700; }
/* 「待開發」小標（2026-10-12 使用者：杯柄的回測報告還不完整）。框線與字都跟著按鈕的字色（currentColor），
   平常的深底與按下後的琥珀底都看得清楚；中文不加粗，按鈕 active 的 700 不往下傳。 */
.dev-tag { display: inline-block; margin-left: 6px; padding: 0 5px; border: 1px solid currentColor; border-radius: 4px;
  font-size: 11px; font-weight: 400; line-height: 16px; opacity: .8; vertical-align: 1px; }
.wave-toggle { font-size: var(--fs-data); color: var(--muted); display: flex; align-items: center; gap: 5px; cursor: pointer; margin-left: 6px; min-height: 24px; }
/* 勾選框本身只有 13px，但真正的命中範圍是外面的 <label>（點標籤就會切換），
   所以要撐的是 label 的高度，不是把方框畫大。 */
.wave-toggle input { width: 15px; height: 15px; flex: 0 0 auto; }
.wave-pct { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-xs); color: var(--muted); }

/* 個股查詢的空狀態。刻意做得**安靜**——它是入口不是內容，給它卡片外框或色塊會讓
   空頁面看起來比有資料的頁面還熱鬧。標題一行、說明一行，剩下全是可以直接點的目的地。 */
.stock-empty { padding: 28px 4px; display: flex; flex-direction: column; gap: var(--sp-2); }
.stock-empty.hidden { display: none; }
.se-title { font-size: var(--fs-lg); font-weight: 700; color: var(--text); }
.se-hint { font-size: var(--fs-sm); color: var(--muted); }
.se-group { margin-top: var(--sp-3); }
.se-group.hidden { display: none; }
.se-label { font-size: var(--fs-xs); color: var(--muted); font-weight: 700; letter-spacing: 1px; margin-bottom: 6px; }
.se-chips { display: flex; flex-wrap: wrap; gap: 6px; }
/* 24px 下限同上：這些是主要的點擊目標，不該比工具列的按鈕還難點。 */
.se-chip {
  display: inline-flex; align-items: baseline; gap: 6px; min-height: 30px; padding: 5px 11px;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--r);
  color: var(--text); font-size: var(--fs-sm); cursor: pointer; font-family: inherit;
}
.se-chip:hover { border-color: var(--border-strong); }
.se-chip b { font-weight: 700; }
.se-chip span { color: var(--muted); }
.wave-pct input[type=range] { width: 90px; }
.chart { width: 100%; height: 340px; background: linear-gradient(180deg, rgba(24, 38, 58, 0.55), rgba(11, 18, 29, 0.35)); border: 1px solid rgba(58, 82, 112, 0.62); border-radius: var(--r); }
/* position:relative 讓 #stock-chart 裡的 .lw-legend/.lw-readout（Lightweight Charts
   個股 K 線的疊加層，JS 動態插入）能絕對定位在圖表畫布之上；#cup-chart 仍是 ECharts，
   這個屬性對它無影響（ECharts 本來就自己管內部圖層定位）。 */
.chart-big { width: 100%; flex: 1; min-height: 380px; position: relative; background: linear-gradient(180deg, rgba(24, 38, 58, 0.55), rgba(11, 18, 29, 0.35)); border: 1px solid rgba(58, 82, 112, 0.62); border-radius: var(--r); }
/* **#stock-chart 必須 contain:size，否則高度會無限長大。** .chart-big 是 .view（flex column、
   min-height:100%）裡的 flex:1 項目；個股頁內容超過一個螢幕時 .view 改由內容撐高，而 LWC
   的 autoSize 會把畫布設成容器高度 → 畫布又撐高 .view → flex:1 分到更多 → ResizeObserver
   再放大畫布，實測每 400ms 長 ~35px、停不下來（4,295→4,469px）。ECharts 版本沒有這個迴圈
   只是因為 echarts.init 不監聽尺寸變化。contain:size 讓容器的尺寸不看自己的內容，之前只靠
   min-height 撐開時實測穩定在 378px；只加在 #stock-chart，#cup-chart 仍是 ECharts 不受影響。
   Task 6 加了法人／集保兩個窗格後，總高改成 560px（主圖 300／量能 70／法人 95／集保 95，
   見 initStockChart 的 setStretchFactor 註解）。
   **光寫 height:560px 不夠——實測仍然是 380px。** .chart-big 的 `flex: 1` 是 `flex: 1 1 0%`
   的縮寫，flex-basis 明確是 0%（不是 auto），而 flex 版面配置的規則是：main-size 屬性
   （這裡是 height，因為 .view 是 flex-direction:column）只有在 flex-basis:auto 時才會被
   拿來當 basis 用；flex-basis 一旦是明確的長度／百分比，height 屬性本身完全不參與 main
   size 計算，容器最終大小改由「flex-basis 夾在 min/max 之間」＋ flex-grow 分配剩餘空間
   決定。個股頁內容（籌碼／集保／保證金…）幾乎必然超過一屏，.view 因此改由內容撐高、沒有
   多餘空間可分配給 flex-grow，於是這格的高度會被夾在 flex-basis(0) 與 min-height(380px)
   之間、floor 在 380px——這正是「之前只靠 min-height 撐開時穩定在 378px」的真正原因，
   不是巧合。用瀏覽器 devtools 實測過：只加 height:560px 時 getComputedStyle(...).height
   仍是 "380px"；改成 `flex: 0 0 560px`（grow:0、shrink:0、basis:560px）後才變成
   "560px"——`#stock-chart` 是 ID 選擇器，specificity 高於 `.chart-big` 的 class 選擇器，
   足以覆寫 flex-grow/shrink/basis 三個子屬性，不必動 .chart-big 本身（#cup-chart 等其他
   沿用 .chart-big 的地方不受影響）。 */
#stock-chart { contain: size; flex: 0 0 560px; height: 560px; }
/* 個股 K 線（Lightweight Charts）的 MA 圖例：函式庫沒有內建圖例元件，週期與顏色是
   固定的（MA_DEFS），JS 只畫一次靜態列。pointer-events:none 讓它不擋十字線游標。 */
.lw-legend { position: absolute; top: 6px; left: 12px; z-index: 2; display: flex; gap: 10px; font-size: var(--fs-xs); font-weight: 700; pointer-events: none; }
/* 每個窗格左上角一行讀數（Task 8，取代浮動 tooltip／.lw-tooltip 與其 .ec-tip-* 內容樣式，
   兩者已一併移除——原本只有這張圖在用，拿掉 tooltip 就是死 CSS）。pointer-events:none
   才不會擋住圖表操作；z-index 壓在圖表 canvas 之上、但不蓋住 TradingView 授權標誌
   （它在右下角）。 */
.lw-readout { position: absolute; left: 8px; z-index: 3; pointer-events: none;
  font-size: var(--fs-xs); color: var(--text-secondary); white-space: nowrap;
  text-shadow: 0 1px 2px rgba(0,0,0,.55); }
.lw-readout b { color: var(--text-primary); font-weight: 650; }
.lw-readout .up { color: var(--up); } .lw-readout .down { color: var(--down); }
@media (max-width: 600px) { .lw-readout { font-size: 12px; } }

/* ===== 總覽「大盤 K 線」（Lightweight Charts，ui96）=====
   規格 docs/superpowers/specs/2026-10-07-overview-kline-design.md §4。分段按鈕與勾選膠囊照樣張；
   「加權｜台指期」選中是全站的琥珀「目前選擇」。勾選框是真的 checkbox（appearance:none 自己畫），
   框本身只有 12px，焦點外框畫在整顆膠囊上。
   分段外框不可 overflow:hidden：會把全站的焦點框（外移 2px）一起裁掉，鍵盤看不到焦點在哪顆鈕上；
   改給頭尾兩顆鈕自己的圓角（外框圓角減 1px 邊框），選中的琥珀底照樣貼合圓角。 */
.kl-seg { display: inline-flex; border: 1px solid var(--border-default); border-radius: var(--r-lg); }
.kl-sym { font: inherit; font-size: var(--fs-data); font-weight: 400; color: var(--text-secondary);
  background: transparent; border: 0; padding: 4px 14px; min-height: 28px; cursor: pointer; }
.kl-sym:first-child { border-radius: calc(var(--r-lg) - 1px) 0 0 calc(var(--r-lg) - 1px); }
.kl-sym:last-child { border-radius: 0 calc(var(--r-lg) - 1px) calc(var(--r-lg) - 1px) 0; }
.kl-sym:hover { color: var(--text-primary); }
.kl-sym.active { background: var(--accent); color: var(--on-accent); }
.kl-note { margin-left: auto; font-size: var(--fs-xs); font-weight: 400; }
/* 滾輪提示（總覽的圖要按 Ctrl 才縮放，見 app.js 的 wheel 監聽器）：夾在「加權｜台指期」與說明列之間的普通行內項目，
   桌機一律顯示（2026-10-08 裁定）；標題列放不下時說明列自己換到下一列（加權 1024px、台指期約 1366px 會多一列，
   台指期 1280／1024px 本來就換行）。第一版是「只吃剩下的空白、放不下就整句藏起來」（flex-basis 0＋容器查詢），標題列
   高度完全不變，但最常見的 1366px 筆電選台指期、1024px 選加權就看不到提示，所以拿掉了。≤600px（手機沒有滾輪）不顯示。 */
.kl-hint { margin-left: 6px; font-size: var(--fs-xs); font-weight: 400; white-space: nowrap; }
.kl-panes { display: flex; flex-wrap: wrap; gap: 6px 8px; margin: 0 0 10px; }
.kl-chip { display: inline-flex; align-items: center; gap: 6px; min-height: 26px; padding: 3px 11px 3px 9px;
  font-size: var(--font-size-table); color: var(--text-secondary); border: 1px solid var(--border-default);
  border-radius: var(--r-pill); cursor: pointer; user-select: none; }
.kl-chip:hover { color: var(--text-primary); border-color: var(--border-strong); }
.kl-chip:has(input:checked) { color: var(--text-primary); border-color: var(--border-strong);
  background: color-mix(in srgb, var(--info) 8%, transparent); }
.kl-chip:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.kl-chip input { appearance: none; -webkit-appearance: none; margin: 0; flex: 0 0 auto; width: 12px; height: 12px;
  border: 1.5px solid var(--muted); border-radius: 3px; background: transparent no-repeat center / 10px 10px; cursor: pointer; }
.kl-chip input:focus-visible { outline: none; }
.kl-chip input:checked { border-color: var(--info); background-color: var(--info);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M2 5.2l2 2L8 3' fill='none' stroke='%2308121e' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
/* contain:size：autoSize 的畫布不准反過來撐高容器（個股頁 #stock-chart 記過的無限長大）。高度由 app.js 依
   勾選的窗格寫成 inline height；這裡的值只是第一次畫圖前的預設組合（539＝260＋96＋76＋76＋時間軸 28＋分隔線 3），
   避免載入時版面跳動。 */
.kl-chart { position: relative; width: 100%; height: 539px; contain: size; }
/* 每格左上角一行讀數（個股頁的 .lw-readout 是另一套）：多一層半透明底，資料線經過時字仍清楚。
   max-width 讓開右側 64px 的價格軸；放不下時 app.js 換精簡版（klPaint），仍放不下才截掉。 */
.kl-readout { position: absolute; left: 8px; z-index: 3; pointer-events: none; display: flex; gap: 12px;
  align-items: baseline; max-width: calc(100% - 80px); overflow: hidden; white-space: nowrap; padding: 0 6px;
  border-radius: 4px; background: rgba(13, 21, 33, 0.72); font-size: var(--fs-xs); line-height: 18px;
  color: var(--text-secondary); }
.kl-readout > * { flex: 0 0 auto; }
.kl-readout .t { color: var(--text-primary); font-weight: 600; }
.kl-readout b { color: var(--text-primary); font-weight: 500; }
.kl-readout .up { color: var(--up); }
.kl-readout .down { color: var(--down); }
.kl-readout .dim { color: var(--muted); }
.kl-key { display: inline-block; width: 12px; height: 2px; border-radius: 1px; vertical-align: middle;
  margin-right: 5px; transform: translateY(-2px); }
.kl-sw { display: inline-block; width: 8px; height: 8px; border-radius: 2px; vertical-align: middle;
  margin-right: 5px; transform: translateY(-1px); }
@media (max-width: 600px) {
  .kl-note { flex-basis: 100%; margin-left: 0; }
  .kl-hint { display: none; }
  .kl-chart { height: 459px; }
  /* 手機的讀數列每格只有 190（320px）～245px（375px）寬：字小一級（不低於 11px）、間距與內距收窄 */
  .kl-readout { font-size: 11px; gap: 6px; padding: 0 4px; }
}

/* ===== AI 區 ===== */
/* 操盤手：每個區塊＝標題＋內容；區塊之間 24px、標題到內容 8px（ui79；原本小標緊貼上一張表） */
#trader-sections { display: flex; flex-direction: column; gap: 8px; }
#trader-sections > .ai-head:not(:first-child) { margin-top: 16px; }
/* 檢核表的數值不是資料日當天的（ss_trader 的 asof）：數值後面小字「截至 MM-DD」，與總覽卡片的
   .card-label .asof 同一個樣子。不准從中間折行，免得「截至」和日期分在兩行。 */
#trader-sections .asof { color: var(--muted); font-weight: 400; font-size: var(--fs-xs); white-space: nowrap; }
/* 檢核表的「說明」欄可以換行：全站 td 是 nowrap，資金流向的說明附上快照時間後將近 50 個字，1280px 視窗下
   整張表要橫捲 215px（2026-10 實測）。給最小寬度，手機上才不會被擠成一字一行。 */
#trader-sections .trader-check td:last-child { white-space: normal; min-width: 13em; }
/* 區塊小標（AI 盤勢摘要、設定頁各段、操盤手各段）：同 .group-title 的取捨，標題不拿琥珀裝飾。
   一定要能換行：標題後面常跟一段很長的說明（.muted），單行 flex 會把溢出量依兩邊的內容寬度
   等比例分攤，而中文標題的最小寬度只有一個字，標題就被擠成一字一行（375px 實測設定頁
   「財報新鮮度」48×45px 兩行、「訊號前瞻追蹤」42×68px 三行、操盤手「選股訊號…」三行；1024px
   也有兩行）。flex-wrap 讓放不下的說明整段換到下一行，標題保有自己一行的寬度。刻意不用
   nowrap：比畫面寬的標題（操盤手「K線訊號…（訊號日…）」）照常自己換行、不會溢出。gap 的
   12px 是並排時的最小間距；放得下時位置不變（1280px 三頁的矩形與改前逐一相同）。 */
.ai-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 2px 12px; font-size: var(--fs-sm); color: var(--text-primary); font-weight: 700; }
.ai-btn { background: var(--card); color: var(--text); border: 1px solid var(--border); padding: 5px 12px; border-radius: var(--r); cursor: pointer; font-size: var(--fs-data); }
.ai-btn:hover { border-color: var(--border-strong); }
.ai-box { background: var(--sunken); border: 1px dashed var(--border); border-radius: var(--r-lg); padding: 12px 14px; font-size: var(--fs-base); line-height: 1.75; white-space: pre-wrap; }
.ai-box.disabled { color: var(--muted); }   /* 不用斜體：中文字型沒有斜體，瀏覽器硬斜看起來像壞掉 */
/* .ai-box 的 pre-wrap 是給「純文字 AI 摘要」用的（保留模型自己的換行）。每日財經新聞
   這一格改注入**結構化 HTML**，此時 pre-wrap 會把模板字串裡的縮排與換行也算成真的
   空行——實測每個標籤之間的 "\n      " 都撐出兩個 28.8px 行框，市場標題上下各多 58px
   空白，看起來像「間距沒調好」，其實是空白字元被當成內容。注入 HTML 時務必加 .md。 */
.ai-box.md { white-space: normal; }
/* 摘要放頁尾且全文攤開（使用者決定：不要再按一次才看得到）。曾經放頁首＋收合三行，
   收合是用 max-height 而非 -webkit-line-clamp——.ai-box 是 .view(flex column) 的直接子項，
   flex item 會把 display 區塊化，-webkit-box 變成 flow-root、line-clamp 靜默失效。
   若日後要再做收合，記住這個坑。 */
.ai-box { flex: 0 0 auto; }

/* ===== Daily financial news: larger type and a scan-friendly report hierarchy ===== */
#view-news { gap: var(--space-lg); }
#view-news .ai-head { font-size: var(--font-size-md); }
#view-news .ai-head > span:first-child { color: var(--text-primary); }
#news-summary {
  background: linear-gradient(135deg, rgba(20, 32, 51, .94), rgba(5, 9, 16, .96));
  border-style: solid; border-color: var(--border-default);
  padding: var(--space-xl); font-size: var(--font-size-md); line-height: 1.8;
}
.news-md-title { margin: 0 0 var(--space-lg); color: var(--text-primary); font-size: var(--font-size-lg); line-height: 1.35; }
.news-md-heading { margin: var(--space-xl) 0 var(--space-sm); color: var(--text-primary); font-size: var(--font-size-md); line-height: 1.5; }
.news-md-title + .news-md-heading { margin-top: 0; }
.news-md-item { position: relative; padding: 2px 0 2px var(--space-lg); color: var(--text-primary); }
.news-md-item::before { content: ""; position: absolute; left: 2px; top: .78em; width: 6px; height: 6px; border-radius: 50%; background: var(--info); box-shadow: 0 0 10px rgba(112, 184, 255, .55); }
.news-md-item strong { color: #ffffff; font-weight: 800; }
.news-md-text { margin: var(--space-xs) 0; color: var(--text-secondary); }
.news-md-gap { height: var(--space-sm); }
.news-split .rank-h { padding: var(--space-md) var(--space-lg); font-size: var(--font-size-md); color: var(--text-primary); }
.news-split .rank-row { min-height: 56px; padding: var(--space-sm) var(--space-lg); gap: var(--space-md); }
.news-split .rank-row a { flex: 1; font-size: var(--font-size-sm); font-weight: 650; line-height: 1.6; }
.news-split .news-item-source { align-self: center; color: var(--text-secondary); font-size: var(--font-size-xs); font-weight: 600; }
.news-market-card {
  display: block; margin: var(--space-md) 0; overflow: hidden;
  background: rgba(5, 9, 16, .58); border: 1px solid var(--border-subtle); border-radius: var(--r-lg);
}
/* 三個市場一律全開，所以這是一列標題而不是可點的 summary（見 app.js 的說明）。 */
.news-market-head {
  padding: var(--space-sm) var(--space-lg); color: var(--text-primary);
  font-size: var(--font-size-md); font-weight: 800;
  background: rgba(20, 32, 51, .58); border-bottom: 1px solid var(--border-subtle);
}
.news-market-body { padding: var(--space-sm) var(--space-md) var(--space-sm); }
.news-market-body .news-md-gap { height: var(--space-xs); }
/* 第一則緊貼標題列：段落間距由 .news-story-title 的 margin-top 提供，
   但第一則上面已經有標題列的分隔線了，再留白就是紅框那塊空白。 */
.news-market-body > .news-story-title:first-child { margin-top: 0; }
.news-market-body .news-story-title {
  margin-top: var(--space-sm); padding: var(--space-xs) var(--space-md);
  border: 1px solid rgba(112, 184, 255, .18); border-radius: var(--r);
  background: rgba(112, 184, 255, .055); font-size: var(--font-size-sm); font-weight: 700;
}
.news-market-body .news-story-title::before { display: none; }
.news-market-body .news-story-detail { padding: 1px var(--space-md) 1px 28px; color: var(--text-secondary); font-size: var(--font-size-data); line-height: 1.55; }
.news-market-body .news-story-detail::before { left: var(--space-md); width: 5px; height: 5px; background: var(--text-muted); box-shadow: none; }
.news-market-body .news-story-detail strong { color: var(--info); }
/* 數字：只加亮度與字重，不動色相。紅／綠在本站鎖給行情漲跌，拿來標一般數字會把
   「43,386 點」這種價位讀成「下跌」。數字本來就長得跟中文不一樣，亮度就夠認出來。
   數字走 "Num" 那組 @font-face（M PLUS Rounded 1c），字重拉到 650 仍是同一支字。 */
.nx-num { color: var(--text-primary); font-weight: 650; }
/* 關鍵詞：亮度＋一條紫底線。底線與填色是不同的軸，疊在藍色的條列標籤旁不會打架。
   紫色沿用既有的 --chart-trust，不新增色相；本頁沒有投信序列，不會撞義。 */
.nx-key {
  color: var(--text-primary);
  border-bottom: 1px solid color-mix(in srgb, var(--chart-trust) 55%, transparent);
  padding-bottom: 1px;
}
/* 故事標題本身已是亮字＋粗體，再標一次會變成整行都在喊。只留數字的字重差。 */
.news-story-title .nx-key { border-bottom: none; color: inherit; }
.news-story-title .nx-num { color: inherit; }

@media (max-width: 760px) {
  #news-summary { padding: var(--space-lg); font-size: var(--font-size-sm); }
  .news-md-title { font-size: var(--font-size-md); }
  .news-split .rank-row { min-height: 52px; padding: var(--space-sm) var(--space-md); }
}

/* ===== 工具列 ===== */
.toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; flex: 0 0 auto; }
.file-label { background: var(--card); border: 1px solid var(--border); padding: 8px 16px; border-radius: var(--r); cursor: pointer; font-size: var(--fs-base); color: var(--text); }
.file-label:hover { border-color: var(--border-strong); }
button.file-label { font: inherit; }
a.file-label { text-decoration: none; }   /* 設定頁「下載資料庫」是連結（ui108） */
.date-pick { font-size: var(--fs-sm); color: var(--muted); display: flex; align-items: center; gap: 6px; }
.date-pick select { background: var(--card); color: var(--text); border: 1px solid var(--border); border-radius: var(--r); padding: 7px 10px; font-size: var(--fs-sm); }
.stock-input { background: var(--card); color: var(--text); border: 1px solid var(--border); border-radius: var(--r); padding: 8px 12px; font-size: var(--fs-sm); width: 320px; }
/* 交易帳本：欄位名稱在輸入框上方、一律看得見（不拿提示字當標籤）；按鈕對齊輸入框底線 */
.tr-field { display: flex; flex-direction: column; gap: 4px; }
.tr-field-k { font-size: var(--fs-xs); color: var(--muted); }
#view-trades .picks-command-foot .toolbar { align-items: flex-end; }
/* 日期框天生比文字框高（日曆圖示），統一高度才對得齊；38px＝旁邊「＋記一筆」的高度 */
.tr-field .stock-input { height: 38px; }

/* ===== 籌碼／基本選股工作台 ===== */
.picks-view { gap: 12px; }
.picks-command {
  display: grid; grid-template-columns: minmax(300px, 1fr) auto; gap: 10px 20px;
  padding: 12px 14px; flex: 0 0 auto;
  background: rgba(15, 25, 39, .9); border: 1px solid var(--border-default); border-radius: var(--r-lg);
}
.picks-title-row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 16px; }
.picks-title-row h2 { margin: 0; color: var(--text-primary); font-size: var(--fs-lg); letter-spacing: -.015em; }
.picks-command-copy p { margin: 4px 0 0; color: var(--muted); font-size: var(--fs-xs); line-height: 1.45; }
.picks-actions { justify-content: flex-end; gap: 8px; }
.picks-action { min-height: 38px; padding: 7px 12px; white-space: nowrap; }
.picks-action-meta { margin-left: 3px; color: var(--muted); font-size: var(--fs-xs); }
.picks-label-short { display: none; }
.picks-action-primary { border-color: rgba(112, 184, 255, .62); background: #12263d; font-weight: 750; }
.picks-action-primary:hover { border-color: var(--info); }
.picks-command-foot {
  grid-column: 1 / -1; display: flex; align-items: center; flex-wrap: wrap; gap: 8px 18px;
  padding-top: 9px; border-top: 1px solid var(--border-subtle);
}
/* 自算選股的條件勾選列。label 沿用 .wave-toggle（已含 24px 觸控目標），這裡只管排版。
   放在覆蓋度上方：先看「現在套了哪些條件」，再看「篩出幾檔」，順序與閱讀動線一致。 */
.ss-conds { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 4px; margin-bottom: 6px; }
.ss-conds .wave-toggle { margin-left: 0; }
.picks-summary { display: flex; flex-wrap: wrap; align-items: center; gap: 0; row-gap: 4px; color: var(--muted); font-size: var(--fs-xs); white-space: nowrap; }
.picks-summary span { padding: 0 10px; border-right: 1px solid var(--border-default); }
.picks-summary span:first-child { padding-left: 0; }
.picks-summary span:last-child { border-right: 0; }
.picks-summary b { margin-right: 3px; color: var(--text-primary); font-size: var(--fs-md); font-weight: 850; }
/* 選股自算對照的狀態記號：不用紅綠（鎖給行情）；一致＝中性、有差異＝既有琥珀語彙 */
.sc-badge { display:inline-block; min-width:1.2em; text-align:center; font-weight:700; }
.sc-ok  { color: var(--muted); }
.sc-diff{ color: var(--accent); }         /* 琥珀＝注意這格 */
.sc-na  { color: var(--border-strong); }
/* 選股自算對照：10 欄密集表。單位另置小字副標，欄寬由欄名決定、不被「（張）（%）」撐寬；
   min-width 讓窄視窗乾淨橫捲而非把欄位擠爆／切掉最後一欄。 */
#selfcheck-table table { min-width: 900px; }
/* 排序表頭樣式：selfcheck 與 self-screen 兩張表共用同一份 markup／sort handler，
   樣式必須同時涵蓋兩者，否則其一有游標/hover/雙層單位、另一沒有（實測 self-screen 漏掉過）。 */
#selfcheck-table th, #selfcheck-table td, #self-screen-table th, #self-screen-table td { padding: 8px 10px; }
/* ── 自算選股「新進榜」標籤（判定在後端 signal_ledger，見 app.js::ssNewBadge）──
   使用者退回燙金（「太醜」），要科技感、透明度高一點。改成 HUD 式的半透明晶片：
   · 底色只有 8~10% 的色調，表格底色透得過來；靠 1px 發光細框與字距撐出「儀表」感，不靠實心色塊。
   · NEW（今天才進榜）：冷青色單色框。
   · Week NEW（本集保週期進榜）：青→紫的漸層細框（mask 挖空內部，框是漸層、底仍半透明）＋
     四角的 L 形刻線（HUD 準星語彙），比 NEW 多一層結構而不是多一塊顏色。
   · ✦（今天才進、同時也是本週新進）：CSS clip-path 畫的四芒星，接在 Week NEW 字後面。
   顏色：青用既有外資冰藍 #57c7ff、紫用投信紫 #a78bfa，不碰紅綠（漲跌）與琥珀（注意這格）。 */
.ss-badge {
  position: relative; display: inline-flex; align-items: center; gap: 4px;
  vertical-align: 1px; margin-left: 6px; padding: 3px 6px 3px 7px;
  font-family: "Num", system-ui, sans-serif; font-size: 10.5px; font-weight: 800; line-height: 1;
  letter-spacing: .12em; text-transform: uppercase; white-space: nowrap;
  border-radius: 3px; cursor: help; isolation: isolate; overflow: hidden;
}
.ss-badge-day {
  color: #7fd3ff;
  background: rgba(87, 199, 255, .08);
  box-shadow: inset 0 0 0 1px rgba(87, 199, 255, .42);
}
.ss-badge-week {
  color: #d9d2ff;
  background:
    /* 四角 L 形刻線：每角兩條 1px×5px 的線段 */
    linear-gradient(#8fe0ff, #8fe0ff) left top / 5px 1px no-repeat,
    linear-gradient(#8fe0ff, #8fe0ff) left top / 1px 5px no-repeat,
    linear-gradient(#c7b5ff, #c7b5ff) right bottom / 5px 1px no-repeat,
    linear-gradient(#c7b5ff, #c7b5ff) right bottom / 1px 5px no-repeat,
    linear-gradient(90deg, rgba(87, 199, 255, .10), rgba(167, 139, 250, .12));
}
/* 漸層細框：padding 1px 的漸層層，用 mask 把內容區挖掉，只留框線 */
.ss-badge-week::before {
  content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit; padding: 1px;
  background: linear-gradient(90deg, rgba(87, 199, 255, .55), rgba(167, 139, 250, .55));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  pointer-events: none;
}
/* 四芒星：今天才進榜（同時是本週新進）。畫的而不是 unicode 字元，粗細與顏色才受控 */
.ss-star {
  display: inline-block; width: 8px; height: 8px; flex: none;
  background: #eaf8ff;
  clip-path: polygon(50% 0, 61% 39%, 100% 50%, 61% 61%, 50% 100%, 39% 61%, 0 50%, 39% 39%);
}
/* 掃描線掠過一次：資料剛載入時播，排序／篩選重畫不再播（見 .ss-badge-fresh） */
.ss-badge-week::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 40%; z-index: -1;
  background: linear-gradient(90deg, transparent, rgba(143, 224, 255, .38), transparent);
  transform: translateX(-120%); pointer-events: none;
}
.ss-badge-fresh .ss-badge-week::after { animation: ss-scan 1.1s var(--ease-out) .3s 1 both; }
@keyframes ss-scan { from { transform: translateX(-120%); } to { transform: translateX(280%); } }
.ss-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* 表頭右側的新進榜計數＝篩選鈕（再按一次取消）。沿用標籤的 HUD 語彙：平時只是一行可點的字，
   按下時換成同一套冰藍細框半透明晶片——看起來就是「表格正被這個條件篩著」。
   min-height 24px 是觸控目標下限（WCAG 2.2 SC 2.5.8，同全站其他小控制項）。 */
.ss-new-count {
  display: inline-flex; align-items: center; min-height: 24px; padding: 0 8px; margin: 0;
  font: inherit; font-weight: 700; color: var(--label); white-space: nowrap; cursor: pointer;
  background: transparent; border: 0; border-radius: 3px;
  box-shadow: inset 0 0 0 1px rgba(87, 199, 255, .18);
  transition: color var(--dur-fast, .15s) ease, box-shadow var(--dur-fast, .15s) ease, background var(--dur-fast, .15s) ease;
}
.ss-new-count:hover { color: var(--text-primary); box-shadow: inset 0 0 0 1px rgba(87, 199, 255, .45); }
.ss-new-count:focus-visible { outline: 2px solid var(--info); outline-offset: 2px; }
.ss-new-count.on {
  color: #7fd3ff; background: rgba(87, 199, 255, .10);
  box-shadow: inset 0 0 0 1px rgba(87, 199, 255, .6);
}
/* 手機：第一欄是凍結欄，標籤跟股名同一行會把它撐到 179px（表格可視寬 350px 的一半），
   右邊的數據欄幾乎沒空間。改成換到股名下方一行，凍結欄寬回到只看股名。 */
@media (max-width: 600px) {
  #self-screen-table .ss-badge { display: flex; width: fit-content; margin: 3px 0 0; }
}
/* 排序表頭共用規則（股期概況的原始保證金試算表，review I4；跨週週報的大戶連續加碼表，
   ui75；族群輪動的族群個股表，ui83）——同一種排序表頭樣式現在五張表都在用，選擇器一起列出，避免各自
   漂移（同 selfcheck／自算選股那次的既有教訓；ui83 又漏了一次兩層表頭，手機上單位把欄撐寬、數字跑到
   畫面外）。ssf-margin-table 沒有 sc-h-unit 兩層表頭，故該行不列它。 */
#selfcheck-table th.sc-sort, #self-screen-table th.sc-sort, #ssf-margin-table th.sc-sort, #wk-streaks th.sc-sort, #race-stocks-table th.sc-sort { cursor: pointer; user-select: none; white-space: nowrap; vertical-align: bottom; }
#selfcheck-table th.sc-sort:hover .sc-h-label, #self-screen-table th.sc-sort:hover .sc-h-label, #ssf-margin-table th.sc-sort:hover .sc-h-label, #wk-streaks th.sc-sort:hover .sc-h-label, #race-stocks-table th.sc-sort:hover .sc-h-label { color: var(--text-primary); }
#selfcheck-table th .sc-h-label, #self-screen-table th .sc-h-label, #ssf-margin-table th .sc-h-label, #wk-streaks th .sc-h-label, #race-stocks-table th .sc-h-label { display: block; }
#selfcheck-table th .sc-h-unit, #self-screen-table th .sc-h-unit, #wk-streaks th .sc-h-unit, #race-stocks-table th .sc-h-unit { display: block; margin-top: 1px; font-size: var(--fs-xs); font-weight: 500; color: var(--muted); }
/* 跨週集保週報（#view-weekly）：新進／退出寬螢幕並排、窄螢幕上下疊 */
#view-weekly .wk-two { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; }
/* 並排門檻 1500px 不是 1181px：新進表 6 欄最窄 579px，兩欄要 .wk-two ≥ 2×579＋12 ＝ 1170px；實測 1280px 只有 969px（新進表切掉 102px）、1440px 1126px（切掉 22px）；1481px 剛好放下但只差 1px 就出橫向捲軸，取 1500px 留約 8px 餘裕。退出表 4 欄 477px 起就夠，跟著新進表走。 */
@media (min-width: 1500px) { #view-weekly .wk-two { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
#view-weekly .wk-sub { margin: 0 0 6px; color: var(--text-secondary); font-size: var(--fs-sm); font-weight: 600; }
#view-weekly .wk-reason { color: var(--text-secondary); font-size: var(--fs-xs); }
#view-weekly .wk-empty { padding: 10px 12px; }
#view-weekly .wk-today { margin-left: 4px; color: var(--info); }
/* 排名變化不是行情方向 → 不用紅綠；上升只加亮度與字重（同股期頁未平倉增減的做法） */
#view-weekly .wk-chg { color: var(--text-secondary); white-space: nowrap; }
#view-weekly .wk-chg.wk-up { color: var(--text-primary); font-weight: 600; }
#view-weekly .wk-kept summary { min-height: 24px; cursor: pointer; color: var(--text-secondary); font-size: var(--fs-sm); }
#view-weekly .wk-kept-list { padding: 6px 0; line-height: 1.9; }
/* AI 週報的段落標題：renderAiMarkdown 把 ### 畫成新聞頁的 h3.news-md-title，那是「整頁只出現一次」的
   大標題（上距 0、下距 16px）。週報固定五段 ###，照用的話實測 375px 上一段最後一條 → 下一段標題 0px、
   標題 → 自己的第一條 16px——標題黏在上一段，鄰近關係顛倒。只在 #wk-ai 裡改成上距明顯大於下距；
   新聞頁的 .news-md-title 不動。第一個子元素不留上距（框本身有內距）。 */
#wk-ai .news-md-title { margin: 20px 0 8px; }
#wk-ai > :first-child { margin-top: 0; }
.picks-feedback { flex: 1 1 220px; min-width: 0; color: var(--muted); font-size: var(--fs-xs); text-align: right; }
.picks-split { display: flex; gap: 12px; flex: 1; min-height: 0; }
.picks-side { flex: 0 0 250px; display: flex; flex-direction: column; min-height: 0; }
.picks-main { flex: 1; display: flex; flex-direction: column; min-height: 0; min-width: 0; }
.picks-view .table-wrap { scrollbar-width: thin; scrollbar-color: var(--border-default) transparent; }
.picks-view .table-wrap::-webkit-scrollbar { width: 6px; height: 6px; }
.picks-view .table-wrap::-webkit-scrollbar-track { background: transparent; }
.picks-view .table-wrap::-webkit-scrollbar-thumb { background: var(--border-default); border-radius: 999px; }
.pane-heading { display: flex; align-items: flex-end; justify-content: space-between; gap: 10px; margin-bottom: 7px; min-height: 38px; }
.pane-heading h3 { margin: 0; color: var(--text-primary); font-size: var(--fs-md); }
.pane-heading p { margin: 2px 0 0; color: var(--muted); font-size: var(--fs-xs); }
/* 股期概況版面（2026-09 ui61）：使用者回報「要滑到很下面才看得到」——實測 1904×980
   整頁 17,693px＝19.2 個螢幕，其中保證金試算 342 列全部攤開就佔 14,634px（83%），
   其餘區塊全部單欄堆疊（三張各 1566px 寬只放 20 根 K 棒、6 個短欄的價差表佔滿整列）。
   改成總覽那套 12 欄格線（見 #view-overview.active 的說明：auto-fit 只能「1 欄或整列」、
   區塊沒辦法佔 2 欄），每列 span 相加剛好 12。**第一列是使用者指定的：左邊成交量前 30 股期、
   右邊保證金試算**，之後是三張 K 棒／未平倉／價差＋熱力圖：
     ≥1641px     成交量前 30 佔 7（兩欄 1–15／16–30）＋保證金 5
     1181–1640   成交量前 30 佔 5（單欄 30 列）＋保證金 7
     ≤1180       全部單欄，成交量前 30 在上、保證金在下
   第一列的高度由成交量前 30 決定，保證金表格框填滿到同一條底線（見 #ssf-margin-wrap）。
   - 一定要掛在 `.active` 上：只寫 `#view-ssf { display:grid }` 的話 ID 會壓過
     `.view { display:none }`，這一頁會出現在每一頁底下。
   - 列距大於欄距（20／12）：同一列並排的兩塊要讀成一對，列與列之間才是段落。
   - 預設每塊 1/-1（含 .pane-heading：只分到一欄會被擠成直條），並排的四塊另給 span。 */
#view-ssf.active {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  align-content: start;
  gap: 20px 12px;
}
#view-ssf.active > * { grid-column: 1 / -1; min-width: 0; }
/* 7＋5 不是 6＋6：成交量前 30 要切兩欄，每欄至少約 320px（數字欄 233＋名稱 87），6 欄在
   1641px 只有 633px、切不成兩欄，第一列會變成 30 列單欄 878px 高；7 欄 745px 起就夠。
   保證金 5 欄最窄 521px，放得下整張表（內距 6px、名稱可折，見 #ssf-margin-table）。 */
#view-ssf.active > .ssf-b-hot { grid-column: span 7; }
#view-ssf.active > .ssf-b-margin { grid-column: span 5; }
#view-ssf.active > .ssf-b-basis { grid-column: span 5; }
#view-ssf.active > .ssf-b-hm { grid-column: span 7; }
/* 熱力圖 11 欄（名次＋10 日）約需 730px 才不必橫捲（格子最小 68px，見 .ssf-hm-cell；
   10×68＋名次欄＋框線）。span 7 的內寬＝7 欄＋6 個欄距−26，欄寬由視窗扣掉側欄 240、
   scrollbar-gutter 15、view 內距 56 推得，≥1641px 時內寬約 745px 才放得下。這個寬度以下
   改成價差與熱力圖各佔一整列（第一列仍維持並排）。 */
@media (max-width: 1640px) {
  #view-ssf.active > .ssf-b-basis, #view-ssf.active > .ssf-b-hm { grid-column: 1 / -1; }
  /* 第一列改 5＋7：成交量前 30 在這個寬度切不成兩欄（區塊 330～520px），改單欄 30 列；
     保證金反過來拿 7 欄（內寬 477～744px），靠 #ssf-margin-table 的 6px 內距與名稱可折
     才放得下「N 口合計」。這一列因此約 900px 高——使用者要的是一眼看到 30 檔，保證金表
     順勢拉長、可見列數也跟著變多。 */
  #view-ssf.active > .ssf-b-hot { grid-column: span 5; }
  #view-ssf.active > .ssf-b-margin { grid-column: span 7; }
}
/* 1181–1260：成交量前 30 加了股價欄（ui64）之後每張表至少要 363px（固定欄 280＋名稱 83），
   5 欄在 1260px 以下給不到，名稱會被截。這段改成第一列上下排（成交量前 30 在上、整列寬、
   可以切兩欄），1261px 起才回到左右並排。 */
@media (max-width: 1260px) {
  #view-ssf.active > .ssf-b-hot, #view-ssf.active > .ssf-b-margin { grid-column: 1 / -1; }
}
@media (max-width: 1180px) {
  #view-ssf.active > * { grid-column: 1 / -1 !important; }
}
/* 表格密度：本頁的表都是短欄數字，全站預設 10px 12px 的內距讓一列高 42px；
   熱力圖格子自己的 .ssf-hm-cell 內距（ID＋class）權重較高，不受這條影響。 */
#view-ssf th, #view-ssf td { padding: 6px 10px; }
/* 區塊內的小標（圖表名稱、未平倉的增加／減少）：同一種角色只有一種樣式。 */
.ssf-sub { color: var(--muted); font-size: var(--fs-xs); margin-bottom: 6px; }
/* 三張 K 棒：內層 3 欄一定要 minmax(0,1fr)——ECharts 在容器裡放的是固定像素寬的
   畫布，用 1fr（最小值＝內容寬）的話欄寬會被上一次的畫布撐住、縮不回去。
   三欄並排的條件用容器查詢、不看視窗：座標軸強制畫出 20 個直書名稱（interval:0，
   一字寬 10px），每張圖至少要 44＋8＋20×10＋框線 2＝254px，三張加欄距約 786px。
   原本寫的是視窗 ≤760px 才改單欄，結果 761～1120px（頁面早已單欄）仍三欄並排，
   每格只有 5.6～9.6px，相鄰名稱疊在一起讀不出來（861px 最嚴重）。 */
#view-ssf .ssf-b-charts { container: ssf-charts / inline-size; }
.ssf-charts { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; }
.ssf-chart-cell { min-width: 0; }
.ssf-chart-box { position: relative; }
#view-ssf .ssf-chart { height: 220px; }
@container ssf-charts (min-width: 790px) {
  .ssf-charts { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  #view-ssf .ssf-chart { height: 250px; }
}
/* 「尚無資料」疊在圖表框裡、不放在圖表下方：放下方會讓這一格比同列另外兩張圖高。 */
.ssf-chart-empty { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
/* 成交量前 30 股期（2026-09 取代 10 張大卡片——使用者：「格子放這麼大，資訊看的量就很少」）。
   兩張表（1–15、16–30），區塊內寬 ≥744px（具名容器 ssf-hot）時並排，否則上下疊。
   744＝兩張表各 363px（固定欄 280＋名稱 83）＋欄距 16；ui64 加股價欄前是 660。
   並排或疊起來都要欄位對齊，所以 table-layout:fixed ＋ colgroup 固定數字欄寬、名稱吃剩下的。
   一列 26px（3px 上下內距）：30 檔在 1904px 只要 15 列高，三張 K 棒仍留在第一屏。 */
#view-ssf .ssf-b-hot { container: ssf-hot / inline-size; }
.ssf-top { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0 16px; align-items: start; }
@container ssf-hot (min-width: 744px) {
  .ssf-top { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.ssf-top-t { table-layout: fixed; width: 100%; font-size: var(--fs-sm); }
/* 欄寬是量出來的：最寬內容＋左右內距 8px（ui64 加股價欄時內距 6→4px）。內容 14px 字：
   股價「8,888.5」51px（實際最高 7,160）、漲跌「+10.00%」60px、成交量「888,888」55px、
   較前 3 日「▲888%」52px、名次「30」17px；表頭是 11px 字，比內容窄，不是瓶頸。em 以
   表格的 14px 計，固定欄合計 280px。名稱吃剩下的，最長「小型台積電」75px（＋內距 83），
   放不下時先把後面的代號截成「…」。 */
.ssf-top-t col.c-rk { width: 1.8em; }
.ssf-top-t col.c-px { width: 4.5em; }
.ssf-top-t col.c-chg { width: 4.9em; }
.ssf-top-t col.c-vol { width: 4.5em; }
.ssf-top-t col.c-vchg { width: 4.3em; }
#view-ssf .ssf-top-t th, #view-ssf .ssf-top-t td { padding: 3px 4px; }
#view-ssf .ssf-top-t th { font-size: var(--fs-xs); }
.ssf-top-t .ssf-rk { text-align: right; font-variant-numeric: tabular-nums; }
/* 顏色只給內容格（td）：寫成 .ssf-top-t .ssf-rk 會連表頭 th 一起染（權重 0,2,0 高過全域
   th 的 0,0,1），五個表頭變成三種顏色。 */
.ssf-top-t td.ssf-rk { color: var(--muted); }
.ssf-top-t .ssf-px, .ssf-top-t .ssf-vol, .ssf-top-t .ssf-vchg, .ssf-top-t .ssf-chg { text-align: right; font-variant-numeric: tabular-nums; }
/* 股價不著色：它是水準不是方向，方向交給緊鄰右邊的漲跌欄（紅綠只給那一欄）。 */
.ssf-top-t td.ssf-px { color: var(--text-primary); }
.ssf-top-t td.ssf-nm { overflow: hidden; text-overflow: ellipsis; }
.ssf-top-t .ssf-nm-t { color: var(--text-primary); }
.ssf-top-t .ssf-code { color: var(--muted); font-size: var(--fs-xs); margin-left: 5px; }
/* 成交量長度條＝相對榜首的口數（--w 由 JS 寫進 style）。只寫 background-image 等分項，
   不寫 background 簡寫：全站斑馬紋與滑過高亮用的是 background 簡寫（權重較低），
   簡寫會把那兩者的底色一起蓋掉，分項只疊一層圖、底色照舊。色用中性藍：量不是漲跌。 */
#view-ssf .ssf-top-t td.ssf-vol {
  background-image: linear-gradient(90deg, color-mix(in srgb, var(--info) 22%, transparent) var(--w, 0%), transparent var(--w, 0%));
  background-repeat: no-repeat; background-position: 0 50%; background-size: 100% 64%;
}
/* 量較前 3 日：▲▼＋中性色（量不是價格方向，紅綠只給漲跌那欄）。三階亮度：一般是
   --text-secondary，放大一倍以上提到 --text-primary 並加粗，縮量與算不出（前 3 日湊不齊，
   顯示「—」）降到 --muted。一般狀態不能用 --text：它就是 --text-primary 的別名，
   那樣「提亮」就只剩加粗。 */
.ssf-top-t td.ssf-vchg { color: var(--text-secondary); }
.ssf-top-t td.ssf-vchg.hi { color: var(--text-primary); font-weight: 650; }
.ssf-top-t td.ssf-vchg.lo, .ssf-top-t td.ssf-vchg.na { color: var(--muted); }
/* 這張表不會橫捲，手機的「凍結第一欄」（sticky＋不透明 ::after 底）套上來只會把名次格
   墊成一塊比較暗的底，所以在手機段排除（權重 1,2,2／::after 1,2,3，高過那條的 0,2,3／
   0,2,4）。只能寫在手機段：寫在外面會連桌機的滑過高亮條（box-shadow）一起拿掉，名次格
   就少了那條起頭的藍線。表頭第一格同理拿掉凍結用的右側分隔線，免得只有表頭有、內容沒有。 */
@media (max-width: 600px) {
  /* 手機表格只有約 325px，六欄放不下名稱（只剩 45px）。比照高價股的手機做法拿掉名次欄
     （順序本身就是名次）、字級降到 13px（em 欄寬跟著縮），名稱回到約 88px。名次格留在
     DOM 裡（寬度 0、字 0），讀螢幕的人仍聽得到名次。 */
  .ssf-top-t { font-size: 14px; }
  .ssf-top-t col.c-rk { width: 0; }
  #view-ssf .ssf-top-t .ssf-rk { padding: 0; font-size: 0; }
  #view-ssf .ssf-top-t tbody td:first-child { position: static; box-shadow: none; }
  #view-ssf .ssf-top-t tbody td:first-child::after { content: none; }
  #view-ssf .ssf-top-t thead th:first-child {
    position: sticky; left: auto;
    box-shadow: inset 0 -1px 0 var(--border-strong), 0 4px 9px rgba(0, 0, 0, 0.16);
  }
}
/* 期現價差表：正負是「期貨相對現貨的位置」，不是漲跌方向——全站紅綠鎖給行情漲跌，
   這裡一律中性色，方向改由文字「正價差／逆價差」表達，不套 .up/.down。
   第一欄凍結／斑馬紋／hover 都吃既有的全域 table 規則，不必在此另寫一份。 */
#ssf-basis-table .ssf-basis-dir { color: var(--muted); }
#ssf-basis-table .ssf-code { color: var(--muted); font-size: var(--fs-xs); margin-left: 6px; }
#ssf-basis-table .ssf-late { margin-left: 4px; color: var(--muted); cursor: help; }
/* 排行熱力圖：格子帶白字疊在色塊上，色塊來自 sectorColor 既有的 upFill/downFill
   （不是給小字調亮過的 --up/--down），這裡只管文字排版，不重複調色邏輯。 */
#ssf-hm-table .ssf-hm-cell { color: #fff; text-align: center; padding: 6px 4px; min-width: 68px; }
#ssf-hm-table .ssf-hm-name { display: block; font-size: var(--fs-xs); }
#ssf-hm-table .ssf-hm-pct { display: block; font-weight: 650; }
/* 未平倉增減：兩欄在寬螢幕並排，容器窄到放不下兩個 260px 欄時 auto-fit 自動收成
   一欄，不必另寫 @media（同全站既有的 .cards/.mv-grid 響應式手法）。
   OI 增減不是漲跌方向，delta 一律中性色（不用 --up/--down），方向靠 ▲▼ 字彙表達。
   長合約名稱用 ellipsis 截斷（同 .hm-bar-name/.mv-name 既有慣例）而不是讓它把
   後面的數字擠出這一列——name 用 min-width:0 讓出空間，delta/base 固定不縮。 */
.ssf-oi-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 12px; }
/* 未平倉現在佔一整列，每份清單寬到 700px 以上：一列 10 項會讓名稱與數字隔得很遠、整塊
   又高又空。清單夠寬（≥560px）時折成兩欄（1–5／6–10，由上而下再換欄），高度砍半。
   用容器查詢量清單自己的寬度，不看視窗（具名，免得匹配到其他容器）。
   名次用 CSS 計數器畫：li 是 display:flex、不是 list-item，<ol> 的編號從來沒有顯示過；
   折成兩欄後不標名次，由左往右讀會把第 1 名和第 6 名當成同一列。空狀態那一列（.muted）
   不編號。 */
.ssf-oi-cols > div { min-width: 0; container: ssf-oi / inline-size; }
.ssf-oi-list { margin: 0; padding-left: 0; counter-reset: ssf-oi; }
.ssf-oi-list li { display: flex; gap: 8px; align-items: baseline; padding: 3px 0; break-inside: avoid; }
.ssf-oi-list li:not(.muted) { counter-increment: ssf-oi; }
.ssf-oi-list li:not(.muted)::before {
  content: counter(ssf-oi); flex: 0 0 1.6em; text-align: right;
  color: var(--muted); font-size: var(--fs-xs); font-variant-numeric: tabular-nums;
}
@container ssf-oi (min-width: 560px) {
  .ssf-oi-list { columns: 2; column-gap: 32px; }
}
.ssf-oi-list .ssf-oi-name { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ssf-oi-list .ssf-oi-delta { flex: 0 0 auto; font-weight: 650; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ssf-oi-list .ssf-oi-base { flex: 0 0 auto; color: var(--muted); font-size: var(--fs-xs); white-space: nowrap; }
/* 保證金試算列：口數／搜尋框橫排，容器窄時自動換行（同 .picks-filter 既有手法）。
   兩個 input 沒有既有的 .mini-input/.stock-input 可直接借（寬度不同、且 .mini-input
   是設定頁專屬語境），這裡另外定義深色底樣式（配色抄 .gsearch input），否則會落回
   瀏覽器預設的白底輸入框，在深色介面上非常突兀。
   金額不是漲跌方向，全表不套 .up/.down／C.up/C.down，.ssf-total 只加粗不改色。
   第一欄凍結／斑馬紋／hover 都吃既有的全域 table 規則（手機 ≤600px 的凍結規則是
   `tbody tr td:not([colspan]):first-child`，本表第一欄 `.ssf-name` 沒有 colspan，
   自動套用，不必再寫一條 #ssf-margin-table 專屬選擇器）。 */
.ssf-margin-bar { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-bottom: 8px; }
.ssf-margin-bar label { display: flex; align-items: center; gap: 6px; }
/* 類別切換：沿用站內 .tf.hm-tab 分段鈕（選中＝琥珀底），這裡只管排列與檔數的字重。 */
.ssf-kind { display: inline-flex; flex-wrap: wrap; gap: 4px; }
/* 尺寸自己寫，不借 .hm-tab：總覽熱力圖用 .hm-tab 掛點擊，借了會互相誤觸。
   min-height 24px＝WCAG 2.2 SC 2.5.8 的最小可點尺寸。只在桌機生效：手機另有全站 .tf
   40px 的觸控尺寸（≤600px 段），這條權重 0,2,0 較高，不限寬度的話會把手機壓回 26px。 */
@media (min-width: 601px) {
  .ssf-kind .ssf-kind-btn { padding: 4px 11px; min-height: 24px; font-size: var(--fs-xs); }
}
.ssf-kind .ssf-kind-n { margin-left: 5px; font-variant-numeric: tabular-nums; opacity: .72; }
.ssf-kind .ssf-kind-n:empty { display: none; }
/* 說明列要能換行：它是 .pane-count（全域 nowrap），在 flex 列裡 min-content＝整行
   約 625px。與成交量前 30 並排時保證金只有 477～744px（span 7）或 521～631px（span 5），
   1181～1435px 與 1641～約 1890px 都會撐出卡片右框、結尾的
   「實際以期貨商收取為準　N 檔」被 .content 的 overflow-x:hidden 切掉——那正是畫面上
   必須出現的聲明。放得下時仍跟輸入框同一行。 */
#ssf-margin-note { white-space: normal; min-width: 0; flex: 1 1 260px; }
.ssf-margin-bar input {
  background: var(--sunken); color: var(--text); border: 1px solid var(--border);
  border-radius: var(--r); padding: 6px 10px; font: inherit; font-size: var(--fs-sm);
}
.ssf-margin-bar input:focus { outline: none; border-color: var(--accent); }
.ssf-margin-bar input::placeholder { color: var(--muted); }
.ssf-margin-bar input[type="number"] { width: 72px; }
.ssf-margin-bar input[type="search"] { width: 160px; }
#ssf-margin-table .ssf-total { font-weight: 650; }
#ssf-margin-table .ssf-code { color: var(--muted); font-size: var(--fs-xs); margin-left: 6px; }
/* 保證金表內捲：區塊是直向 flex，表格框限高、表內捲動——限高之後全域 th 的 sticky
   才真的有作用（原本外框從不捲動，表頭其實不會固定）。
     ≥1181px   填滿同一列（flex:1 1 0; min-height:300px）：這一列的高度由左邊的成交量前 30
               決定（實測 1904px 兩欄 15 列 467px、1181–1640 單欄 30 列約 900px）。
     ≤1180     單欄，min(60vh, 560px)。 */
#view-ssf .ssf-b-margin { display: flex; flex-direction: column; }
/* 熱力圖與價差表並排時，價差表 20 列比熱力圖 10 列高約 240px；讓熱力圖的格子長高填滿
   同一列（色塊變大），不要在卡片底部留一塊空白。單欄時卡片就是內容高，這條不起作用。 */
#view-ssf .ssf-b-hm { display: flex; flex-direction: column; }
#ssf-hm-wrap { flex: 1 1 auto; }
#ssf-hm-table { height: 100%; }
#ssf-margin-wrap { flex: 1 1 0; min-height: 300px; }
/* 保證金與成交量前 30 並排時，保證金只分到 5 或 7 欄（內寬 477～744px），但名稱欄被最長的
   「小型元大台灣50ETF」撐到 215px、表頭又全部不換行，整張表要 684px，最右邊的
   「N 口合計」（這個工具真正要看的欄）會被推出框外。讓名稱與表頭在空間不夠時換行，
   寬螢幕上照樣單行（表格自動版面只在放不下時才折）。 */
#ssf-margin-table th, #ssf-margin-table td.ssf-name { white-space: normal; }
/* 可排序表頭另有一條 `… th.sc-sort { white-space: nowrap }`（1,1,1）壓過上一條，
   同權重、寫在後面才蓋得過。 */
#ssf-margin-table th.sc-sort { white-space: normal; }
/* 名稱欄最窄只能縮到 127px：「群益ESG投等債20+ETF」後面緊接代號「00937B」、中間沒有
   斷行點，整段 107px 不能折。anywhere 只影響最窄寬度（min-content），寬螢幕照樣單行。 */
#ssf-margin-table td.ssf-name { min-width: 4.5em; overflow-wrap: anywhere; }
/* 所有寬度左右內距都是 6px：與成交量前 30 並排時保證金只分到 5 或 7 欄（內寬 477～744px），
   整張表最窄 529→約 440px 才放得下「N 口合計」；單欄與手機順帶也變密。 */
#ssf-margin-table th, #ssf-margin-table td { padding-left: 6px; padding-right: 6px; }
@media (max-width: 1180px) {
  #ssf-margin-wrap { flex: none; height: min(60vh, 560px); min-height: 320px; }
}
.pane-count, .all-filter { color: var(--muted); font-size: var(--fs-xs); white-space: nowrap; }
.active-filter { display: inline-flex; align-items: center; gap: 6px; padding: 3px 5px 3px 8px; background: rgba(112, 184, 255, .14); color: #b9d8f8; border-radius: 5px; font-size: var(--fs-xs); }
.active-filter b { color: var(--text-primary); }
.active-filter button { width: 22px; height: 22px; padding: 0; border: 0; border-radius: 4px; background: rgba(112, 184, 255, .12); color: var(--info); cursor: pointer; }
.pane-title { font-size: var(--fs-md); color: var(--label); margin-bottom: 6px; font-weight: 600; }
/* 籌碼選股再篩選列：蘭質/本益比門檻（前端即時收斂檔數） */
.picks-filter { display: flex; align-items: center; gap: 8px 12px; flex-wrap: wrap; }
.picks-filter-label { color: var(--label); font-size: var(--fs-xs); font-weight: 700; }
.picks-filter-item { font-size: var(--fs-sm); color: var(--muted); display: flex; align-items: center; gap: 6px; }
.mini-input { background: var(--sunken); color: var(--text); border: 1px solid var(--border); border-radius: var(--r); padding: 5px 8px; font-size: var(--fs-sm); width: 64px; }
.table-empty { min-height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px; padding: 24px; color: var(--muted); text-align: center; }
.table-empty strong { color: var(--label); font-size: var(--fs-sm); }
.table-empty span { max-width: 48ch; font-size: var(--fs-xs); text-wrap: balance; }   /* balance：避免「…，賣出／時再按該筆…」這種孤行 */

/* ===== 表格 ===== */
/* flex-shrink:0：.view 是 flex column，頁面內容超高時未掛 .fill 的表格（交易帳本未平倉/已平倉）
   會被按比例壓成幾 px 高、只露半行。.fill 要佔滿剩餘空間，覆寫回可收縮。 */
.table-wrap { overflow: auto; border: 1px solid var(--border-strong); border-radius: var(--r-lg); flex-shrink: 0; background: var(--sunken); box-shadow: var(--shadow-card); }
/* 週報下半部的外殼：display:contents 讓裡面的區塊照舊吃 .picks-view 的直排間距；沒有週報時整塊收起 */
.wk-body { display: contents; }
.wk-body[hidden] { display: none; }
/* 週報「尚未產生」的整頁空狀態：外框比照表格的空狀態（.table-wrap 裡的 .table-empty） */
.wk-empty-all { min-height: 0; padding: 32px 24px; border: 1px solid var(--border-strong); border-radius: var(--r-lg); background: var(--sunken); }
.wk-empty-all[hidden] { display: none; }
.table-wrap.fill { flex: 1 1 0; min-height: 0; }
.table-wrap.fill-half { max-height: 55vh; }
/* 大戶買進版圖：真正的面積圖，版塊面積＝buy_value。藍色深淺只建立閱讀層次，不借用行情紅綠。
   每個葉節點都是原生 button；2px 內框同時形成版塊間隙，維持高密度而不糊成一片。 */
.ss-market-map {
  position: relative; width: 100%; height: clamp(420px, 29vw, 470px); min-height: 400px;
  overflow: hidden; background: var(--sunken); border: 1px solid var(--border-strong);
  border-radius: var(--r-lg); box-shadow: var(--shadow-card); isolation: isolate;
}
#ss-hm-note { max-width: 42ch; white-space: normal; text-align: right; line-height: 1.4; }
/* 版塊內容**由上往下排、名稱靠 margin-top:auto 沉到底**，不用 justify-content:flex-end：
   後者放不下時內容往上溢出、撞上左上角的排名數字（實測「08」壓在「光學鏡片／頭」上），而且往上的
   溢出不算進 scrollHeight，JS 量不到。改成往下溢出後，renderSelfScreenBubbles 就能逐格量
   「放不放得下」、逐步收掉副標／排名／縮字，見 fitMarketCells。 */
.ss-market-cell {
  position: absolute; display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start;
  min-width: 24px; min-height: 24px; margin: 0; padding: 12px 13px; overflow: hidden;
  border: 2px solid var(--sunken); border-radius: 7px; color: #edf7ff; text-align: left;
  font: inherit; cursor: pointer; box-shadow: inset 0 1px 0 rgba(255, 255, 255, .08);
  transition: filter var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
/* **子元素一律不准被 flex 壓縮**：flex 項目預設 flex-shrink:1，而名稱有 overflow:hidden（最小高度因此
   變成 0），放不下時瀏覽器會先把名稱壓扁——內容沒有溢出、scrollHeight 量不到，fitMarketCells 以為放得下，
   畫面上卻是被切掉半行的名稱（實測 1600px「半導體」名稱被壓成 0px、「光學鏡片／頭」18px<行高 21px）。
   不准壓縮之後，放不下就是真的溢出，才量得到、才會逐級收合。 */
.ss-market-cell > * { flex-shrink: 0; }
.ss-market-cell.tone-1 { background: linear-gradient(145deg, #397fba, #1d527f); }
.ss-market-cell.tone-2 { background: linear-gradient(145deg, #2d6da4, #194467); }
.ss-market-cell.tone-3 { background: linear-gradient(145deg, #285d89, #173751); }
.ss-market-cell.tone-4 { background: linear-gradient(145deg, #214b6e, #132d43); }
.ss-market-cell:hover { filter: brightness(1.14); z-index: 2; }
.ss-market-cell:focus-visible {
  z-index: 4; outline: 3px solid #d8efff; outline-offset: -5px;
  box-shadow: inset 0 0 0 2px #183b59, 0 5px 16px rgba(0, 0, 0, .3);
}
.ss-market-cell.active {
  z-index: 3; box-shadow: inset 0 0 0 3px #b9e1ff, inset 0 1px 0 rgba(255, 255, 255, .18);
  filter: brightness(1.12);
}
.ss-market-map.has-selection .ss-market-cell:not(.active) { opacity: .5; }
.ss-cell-rank { flex: none; color: #c8e6fb; font-size: 12px; font-weight: 800; line-height: 1; letter-spacing: .05em; }
.ss-cell-name {
  margin-top: auto;
  display: -webkit-box; overflow: hidden; color: #f6fbff; font-size: clamp(13px, 1.12vw, 17px);
  font-weight: 800; line-height: 1.22; letter-spacing: -.01em; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
}
.ss-cell-value { margin-top: 5px; font-size: clamp(19px, 1.75vw, 28px); font-weight: 800; line-height: 1; letter-spacing: -.025em; }
.ss-cell-value small { margin-left: 3px; color: #d5ebfa; font-size: .55em; font-weight: 700; letter-spacing: 0; }
.ss-cell-meta { margin-top: 6px; color: #c8e0f2; font-size: 12px; font-weight: 600; line-height: 1.2; white-space: nowrap; }
.ss-market-cell.is-compact { padding: 8px 9px; }
.ss-market-cell.is-compact .ss-cell-rank, .ss-market-cell.is-compact .ss-cell-meta { display: none; }
.ss-market-cell.is-compact .ss-cell-name { font-size: 13px; }
.ss-market-cell.is-compact .ss-cell-value { margin-top: 3px; font-size: 18px; }
.ss-market-cell.is-micro { padding: 5px 6px; }
.ss-market-cell.is-micro .ss-cell-name { margin-block: auto; font-size: 11px; line-height: 1.15; -webkit-line-clamp: 3; }
.ss-market-cell.is-micro .ss-cell-value { display: none; }
/* fitMarketCells 量過放不下時逐步加上的收合層級（每一級只多收一樣東西） */
.ss-market-cell.no-meta .ss-cell-meta, .ss-market-cell.no-rank .ss-cell-rank { display: none; }
.ss-market-cell.is-tiny .ss-cell-name { -webkit-line-clamp: 1; word-break: break-all; }
.ss-market-empty {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 5px; padding: 24px; color: var(--muted); text-align: center;
}
.ss-market-empty strong { color: var(--label); font-size: var(--fs-sm); }
.ss-market-empty span { max-width: 42ch; font-size: var(--fs-xs); line-height: 1.5; }

/* ===== 法人資料研究室 =====
   這一頁是研究工作台，不沿用選股頁的卡片與紅綠訊號語彙。單一狀態帶交代結論，
   其下只分成「資料準備」與「證據畫布」兩個工作面。 */
.inst-research-view { gap: var(--space-lg); }
.ir-status-band {
  display: grid; grid-template-columns: minmax(300px, .9fr) minmax(480px, 1.1fr);
  gap: var(--space-xl); align-items: center; padding: 18px 20px;
  background: linear-gradient(118deg, var(--bg-card), var(--bg-panel));
  border: 1px solid var(--border-strong); border-radius: var(--r-lg); box-shadow: var(--shadow-panel);
}
.ir-title-row { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-sm); }
.ir-title-row h2 { margin: 0; font-size: var(--fs-lg); letter-spacing: -.01em; }
.ir-mode-tag {
  display: inline-flex; align-items: center; min-height: 24px; padding: 2px 9px;
  border: 1px solid var(--border-strong); border-radius: var(--r-pill);
  color: var(--label); background: var(--sunken); font-size: var(--fs-xs); font-weight: 700;
}
.ir-verdict { margin: var(--space-sm) 0; color: var(--label); font-size: var(--fs-base); line-height: 1.55; }
.ir-status-band[data-state="insufficient_data"] .ir-verdict,
.ir-status-band[data-state="too_broad"] .ir-verdict { color: var(--warn-text); }
.ir-status-band[data-state="candidate_for_prospective"] .ir-verdict { color: var(--info); }
.ir-meta { display: flex; flex-wrap: wrap; gap: 6px 16px; color: var(--muted); font-size: var(--fs-data); }
.ir-meta b { color: var(--text); font-weight: 600; }
.ir-readiness { margin: 0; padding: 0; list-style: none; display: grid; grid-template-columns: repeat(3, 1fr); }
.ir-readiness li { position: relative; min-width: 0; padding: 0 16px 0 24px; color: var(--muted); }
.ir-readiness li::before {
  content: ""; position: absolute; left: 0; top: 5px; width: 10px; height: 10px;
  border-radius: 50%; border: 2px solid var(--border-strong); background: var(--bg-panel);
}
.ir-readiness li:not(:last-child)::after {
  content: ""; position: absolute; left: 12px; right: -4px; top: 10px; height: 1px;
  background: var(--border-strong); z-index: 0;
}
.ir-readiness li span, .ir-readiness li small { position: relative; z-index: 1; display: block; }
.ir-readiness li span { color: var(--label); font-size: var(--fs-sm); font-weight: 700; }
.ir-readiness li small { margin-top: 4px; font-size: var(--fs-xs); line-height: 1.4; }
.ir-readiness li.ready::before { border-color: var(--info); background: var(--info); }
.ir-readiness li.ready span { color: var(--text); }
.ir-readiness li.blocked::before { border-color: var(--warning); background: var(--warning); }
.ir-readiness li.blocked span { color: var(--warn-text); }
.ir-stale, .ir-candidate-warning {
  padding: 10px 14px; border-radius: var(--r); font-size: var(--fs-sm); line-height: 1.55;
}
.ir-stale { background: var(--warn-surface); color: var(--warn-text); border: 1px solid rgba(242, 166, 75, .45); }
.ir-candidate-warning { background: #10243a; color: #b8d9ff; border: 1px solid rgba(112, 184, 255, .48); }
.ir-stale[hidden], .ir-candidate-warning[hidden], #ir-report[hidden], #ir-empty[hidden], .ir-actions [hidden] { display: none; }
.ir-workspace { display: grid; grid-template-columns: minmax(270px, 330px) minmax(0, 1fr); gap: var(--space-lg); align-items: start; }
.ir-coverage-panel, .ir-evidence {
  min-width: 0; background: rgba(15, 25, 39, .84); border: 1px solid var(--border-default);
  border-radius: var(--r-lg); box-shadow: var(--shadow-card);
}
.ir-coverage-panel { padding: 16px; }
.ir-evidence { padding: 18px 20px; }
.ir-section-head, .ir-evidence-head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-md); }
.ir-section-head h3, .ir-evidence-head h3 { margin: 0 0 5px; font-size: var(--fs-md); }
.ir-section-head p, .ir-evidence-head p { margin: 0; color: var(--muted); font-size: var(--fs-sm); line-height: 1.55; max-width: 72ch; }
.ir-estimate { flex: 0 0 auto; color: var(--info); font-size: var(--fs-xs); text-align: right; max-width: 110px; line-height: 1.4; }
.ir-market { margin-top: var(--space-lg); }
.ir-market-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-sm); padding-bottom: 7px; border-bottom: 1px solid var(--border-subtle); }
.ir-market-head strong { font-size: var(--fs-sm); }
.ir-market-head span { color: var(--muted); font-size: var(--fs-xs); }
.ir-source { padding: 9px 0; border-bottom: 1px solid var(--border-subtle); }
.ir-source-label { display: flex; justify-content: space-between; gap: var(--space-sm); margin-bottom: 6px; font-size: var(--fs-data); }
.ir-source-label span:last-child { color: var(--muted); }
.ir-source progress { display: block; width: 100%; height: 7px; border: 0; border-radius: var(--r-pill); overflow: hidden; accent-color: var(--info); background: var(--sunken); }
.ir-source progress::-webkit-progress-bar { background: var(--sunken); border-radius: var(--r-pill); }
.ir-source progress::-webkit-progress-value { background: var(--info); border-radius: var(--r-pill); }
.ir-source progress::-moz-progress-bar { background: var(--info); border-radius: var(--r-pill); }
.ir-source-failure { margin-top: 5px; color: var(--warn-text); font-size: var(--fs-xs); line-height: 1.4; }
.ir-actions { display: flex; flex-wrap: wrap; gap: var(--space-sm); margin-top: var(--space-lg); }
.ir-primary, .top3-primary { border-color: rgba(112, 184, 255, .62); background: #12263d; }
.ir-primary:hover, .top3-primary:hover { border-color: var(--info); }
.ir-primary:disabled, .ir-actions button:disabled, #ir-run:disabled { color: var(--text-disabled); border-color: var(--border-subtle); cursor: wait; opacity: .8; }
.ir-live { min-height: 20px; margin-top: 7px; line-height: 1.45; }
.ir-errors { margin-top: var(--space-sm); }
.ir-error-row { padding: 7px 9px; background: var(--warn-surface); color: var(--warn-text); border-radius: var(--r); font-size: var(--fs-xs); line-height: 1.45; }
.ir-error-row + .ir-error-row { margin-top: 5px; }
.ir-empty-state { min-height: 250px; display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 7px; text-align: center; color: var(--muted); }
.ir-empty-state strong { color: var(--label); font-size: var(--fs-md); }
.ir-empty-state span { max-width: 55ch; line-height: 1.55; }
.ir-segments { display: inline-flex; gap: 4px; margin-top: var(--space-lg); }
.ir-core { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: var(--space-lg) 0 var(--space-xl); border-block: 1px solid var(--border-subtle); }
.ir-core-item { padding: 12px 14px; min-width: 0; }
.ir-core-item + .ir-core-item { border-left: 1px solid var(--border-subtle); }
.ir-core-item span { display: block; color: var(--muted); font-size: var(--fs-xs); line-height: 1.35; }
.ir-core-item b { display: block; margin-top: 4px; color: var(--text); font-size: var(--fs-lg); font-weight: 700; }
.ir-chart-section { margin-top: var(--space-xl); }
.ir-chart-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-md); margin-bottom: var(--space-sm); }
.ir-chart-head h4 { margin: 0; font-size: var(--fs-base); }
.ir-chart-head span { color: var(--muted); font-size: var(--fs-xs); text-align: right; }
.ir-chart { width: 100%; height: 280px; min-width: 0; }
.ir-alpha-chart { height: 240px; }
.ir-details { margin-top: var(--space-xl); border-top: 1px solid var(--border-subtle); padding-top: var(--space-md); }
.ir-details summary { width: fit-content; min-height: 24px; color: var(--info); cursor: pointer; font-size: var(--fs-sm); }
.ir-details[open] summary { margin-bottom: var(--space-md); }
.ir-details table { min-width: 660px; }
.ir-disclaimer { margin: 0; color: var(--muted); font-size: var(--fs-xs); line-height: 1.55; }

@media (max-width: 900px) {
  .ir-status-band { grid-template-columns: 1fr; gap: var(--space-lg); }
  .ir-workspace { grid-template-columns: 1fr; }
  .ir-coverage-panel { max-width: none; }
}

@media (max-width: 600px) {
  .inst-research-view { gap: var(--space-md); }
  .ir-status-band, .ir-evidence, .ir-coverage-panel { padding: 14px; }
  .ir-readiness { grid-template-columns: 1fr; gap: var(--space-sm); }
  .ir-readiness li { min-height: 48px; padding-left: 26px; }
  .ir-readiness li:not(:last-child)::after { left: 5px; right: auto; top: 15px; bottom: -13px; width: 1px; height: auto; }
  .ir-section-head, .ir-evidence-head, .ir-chart-head { flex-direction: column; align-items: stretch; }
  .ir-estimate, .ir-chart-head span { max-width: none; text-align: left; }
  .ir-actions, .ir-actions button, #ir-run { width: 100%; }
  .ir-core { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ir-core-item:nth-child(3) { border-left: 0; }
  .ir-core-item:nth-child(n+3) { border-top: 1px solid var(--border-subtle); }
  .ir-core-item b { font-size: var(--fs-md); }
  .ir-chart { height: 260px; }
  .ir-alpha-chart { height: 240px; }
  .ir-details .table-wrap th:first-child, .ir-details .table-wrap td:first-child {
    position: sticky; left: 0; z-index: 2;
  }
  .ir-details .table-wrap th:first-child::after, .ir-details .table-wrap td:first-child::after {
    content: ""; position: absolute; inset: 0; z-index: -1; background: var(--sunken);
  }
}
table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--fs-base); }
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid rgba(58, 82, 112, 0.52); white-space: nowrap; }
tbody tr:nth-child(even) td { background: rgba(115, 142, 176, 0.055); }
th { position: sticky; top: 0; background: #1b2a3f; color: var(--label); font-weight: 700; z-index: 1; box-shadow: inset 0 -1px 0 var(--border-strong), 0 4px 9px rgba(0, 0, 0, 0.16); }
th.sortable { cursor: pointer; user-select: none; }
th.sortable:hover { color: var(--text-primary); }
tbody tr:hover td { background: rgba(108, 182, 255, 0.12); box-shadow: inset 3px 0 0 var(--info); }
tbody tr:last-child td { border-bottom: 0; }
tr.clickrow { cursor: pointer; }
.rank-quote { min-width: 180px; }
.rank-quote strong { color: var(--text-primary); }
.rank-quote span { margin-left: 5px; font-size: var(--font-size-xs); font-weight: 700; }
/* 量的增減不是價格方向：照股期頁「量較前 3 日」的三階亮度——≥100%（附 🔥）亮＋粗、>0 一般、
   ≤0 暗。原本放量紅底、縮量綠字，LINE 版早就改成金／灰。 */
.rank-amount-delta { min-width: 168px; font-weight: 700; color: var(--text-secondary); }
.rank-amount-delta.hi { color: var(--text-primary); font-weight: 800; }
.rank-amount-delta.lo { color: var(--muted); }
.rank-surge { margin-right: 3px; filter: saturate(1.15); }
.rank-inst { font-weight: 700; cursor: help; }
.capital-signal { display: inline-block; padding: 4px 9px; border-radius: var(--r-pill); font-size: var(--font-size-base); font-weight: 700; line-height: 1.2; white-space: nowrap; }
/* 資金訊號是資料分類、不是買進訊號：「法人承接／低周轉偏多」用藍底＋細框（比「放量觀察」
   的藍底多一層強調），不用行情紅。 */
.capital-signal.up { color: var(--info); background: rgba(112, 184, 255, .16); box-shadow: inset 0 0 0 1px rgba(112, 184, 255, .45); }
.capital-signal.warn { color: var(--accent); background: rgba(255, 183, 77, .12); }
.capital-signal.caution { color: var(--text-secondary); background: rgba(115, 142, 176, .16); }
.capital-signal.neutral { color: var(--info); background: rgba(108, 182, 255, .12); }

/* 高密度選股清單：範圍只限 #daily / #industry，其他表格暫不改動。 */
#daily table, #industry table { font-size: var(--font-size-table); }
#daily table { min-width: 930px; }
#daily th, #industry th {
  position: sticky; top: 0; z-index: 2;
  padding: var(--space-sm) var(--space-md);
  background: var(--bg-panel); border-bottom-color: var(--border-strong);
  font-size: var(--font-size-data); letter-spacing: .01em;
}
#daily td, #industry td {
  padding: 6px var(--space-md);
  border-bottom-color: var(--border-subtle);
  line-height: 1.35;
}
#daily tbody tr:nth-child(even) td, #industry tbody tr:nth-child(even) td { background: rgba(112, 184, 255, 0.04); }
#daily tbody tr:hover td, #industry tbody tr:hover td {
  background: rgba(112, 184, 255, 0.10); box-shadow: none;
}
#daily tbody tr:hover td:first-child, #industry tbody tr:hover td:first-child { box-shadow: inset 3px 0 0 var(--info); }
#daily .col-primary { background: rgba(112, 184, 255, .075); font-weight: 850; }
#daily .col-support { color: var(--text-primary); font-weight: 700; }
#daily th.col-primary { color: #c5e1ff; }
#daily th.is-sorted { color: var(--info); box-shadow: inset 0 -2px 0 var(--info); }
#daily th[data-col="mu_value"], #daily td[data-col="mu_value"],
#daily th[data-col="mu_score"], #daily td[data-col="mu_score"] { border-right: 1px solid var(--border-default); }
.sort-mark { margin-left: 5px; color: var(--info); font-size: 10px; }
#daily a.stock { font-size: var(--fs-sm); font-weight: 750; }
#industry tr.is-active td { background: rgba(112, 184, 255, .16); color: var(--text-primary); font-weight: 750; }
#industry tr.is-active td:first-child { box-shadow: inset 3px 0 0 var(--info); }

/* 漲跌欄位優先保留台股語意；支援值直接放 td 或包在子元素兩種既有輸出。 */
#daily td.up, #daily td.down, #daily td .up, #daily td .down {
  border-radius: 3px; font-weight: 700;
}
#daily td.up, #daily td .up { background: var(--up-soft); }
#daily td.down, #daily td .down { background: var(--down-soft); }
#daily tbody tr:hover td.up, #daily tbody tr:hover td.down { background: inherit; }
#daily tbody tr:hover td.up { background: var(--up-soft); }
#daily tbody tr:hover td.down { background: var(--down-soft); }
a.stock { color: var(--info); text-decoration: none; font-size: var(--fs-md); font-weight: 600; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45); }
a.stock:hover { text-decoration: underline; }
#sub-filter b { color: var(--accent); }

.status { font-size: var(--fs-xs); padding: 2px 8px; border-radius: var(--r-pill); }
.status.new { background: rgba(112, 184, 255, .16); color: var(--info); }   /* 在榜：狀態不用行情綠 */
.status.out { background: var(--neutral-surface); color: var(--text-secondary); }

/* ===== 個股詳情卡 ===== */
.kline-profile { display: flex; flex-wrap: wrap; gap: 8px 14px; font-size: var(--fs-xs); flex: 0 0 auto; }
.pf-group { background: var(--card); border: 1px solid var(--border); border-radius: var(--r); padding: 6px 10px; }
.pf-title { color: var(--label); font-weight: 700; margin-right: 8px; }
.pf-item { color: var(--muted); margin-right: 10px; white-space: nowrap; }
.pf-item b { color: var(--text); font-weight: 600; }

/* 股期原始保證金（1 口）：沒有股期的個股整行不出現，標題也由 JS 產生，
   否則沒有股期的日子會留一個空框（用 :empty 收起整塊）。 */
#stock-ssf-margin:empty { display: none; }
#stock-ssf-margin { display: flex; flex-wrap: wrap; gap: 10px; align-items: baseline; padding: 8px 0; }
#stock-ssf-margin .ssm-item { font-weight: 650; }   /* 金額不是漲跌方向，不套 .up/.down */

/* ===== 自選股：輸入預估面板 ===== */
/* → Top 3 沒設顏色時吃瀏覽器預設的紫色＋底線（href="#"，按過一次就算「已造訪」），比照估價用 --info */
.watch-est, .watch-top3-add { color: var(--info); text-decoration: none; }
.est-panel { background: var(--bg); border: 1px solid var(--border); border-radius: var(--r); padding: 14px 16px; margin: 4px 0; }
.est-panel .settings-grid { max-width: 560px; }
.est-panel .set-row > span { color: var(--text); font-size: var(--fs-sm); }
.est-panel input[type=number] { background: var(--card); color: var(--text); border: 1px solid var(--border); border-radius: var(--r); padding: 6px 10px; font-size: var(--fs-sm); width: 120px; }
.est-panel .set-row label { width: 140px; }
.est-save { align-self: flex-start; }
/* 財報四格的來源說明（ui111）：0,3,0 蓋過上面 .set-row > span 的正文色。固定自成一行、對齊輸入框（標籤 140＋gap 12）：
   跟在輸入框後面的話，毛利率那句約 316px，560 的格線放不下會折到標籤底下；格線也不能放寬——
   601–1000px 的視窗裡面板沒有限寬（手機才有 88vw），表格橫捲的可視範圍約 640px，放寬會切到估價區間條 */
.est-panel .set-row > .est-hint { color: var(--muted); font-size: var(--fs-xs); flex: 0 0 calc(100% - 152px); margin: -8px 0 0 152px;
  white-space: normal; }   /* 表格每欄 nowrap，會繼承進整列展開的面板：不解開的話窄螢幕上這句不折行、伸出面板外 */
.est-refill { background: transparent; color: var(--text-secondary); }
.est-range { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }
.est-range-track { position: relative; height: 8px; border-radius: var(--r-pill); background: var(--border); }
.est-range-mid { position: absolute; top: -3px; width: 2px; height: 14px; background: var(--muted); transform: translateX(-1px); }
.est-range-marker { position: absolute; top: -4px; width: 10px; height: 16px; border-radius: 3px; transform: translateX(-5px); }
.est-range-labels { display: flex; gap: 14px; flex-wrap: wrap; font-size: var(--fs-xs); color: var(--muted); }

/* ===== 設定頁 ===== */
.settings-grid { display: flex; flex-direction: column; gap: 14px; max-width: 640px; }
.set-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.set-row > label { width: 170px; color: var(--muted); font-size: var(--fs-sm); }
/* 設定頁表單（ui79）：標籤一欄、控制項與說明一欄——說明對齊控制項，不再從標籤欄底下起排。
   只套 .set-field：.set-row 還被自選股估價面板與「財報新鮮度」列共用。 */
#view-settings .set-field, #view-account .set-field { display: grid; grid-template-columns: 170px minmax(0, 1fr); align-items: center; column-gap: 12px; row-gap: 4px; }
#view-settings .set-field > label, #view-account .set-field > label { width: auto; grid-column: 1; }
/* 控制項欄裡的連接字（「日均成交額 ≥」「億元」「份」）用次要色＋sm：原本繼承成 16px 主文字色，比左邊的標籤還搶眼 */
.set-ctl { grid-column: 2; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; min-width: 0;
  color: var(--label); font-size: var(--fs-sm); }
.set-help { grid-column: 2; margin: 0; color: var(--muted); font-size: var(--fs-xs); line-height: 1.5; }
.set-row input[type=time] { background: var(--card); color: var(--text); border: 1px solid var(--border); border-radius: var(--r); padding: 8px 10px; font-size: var(--fs-sm); }
/* 已設定/未設定是系統狀態：走藍/琥珀（同 .status-bar），不得用市場紅綠 */
.set-badge { font-size: var(--fs-data); padding: 3px 10px; border-radius: var(--r-pill); }
.set-badge.ok { background: var(--info-surface); color: var(--info); }
.set-badge.no { background: var(--warn-surface); color: var(--warn-text); }
.stats-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; max-width: 640px; }
.stat { background: var(--card); border: 1px solid var(--border); border-radius: var(--r-lg); padding: 10px 12px; }
.stat-k { font-size: var(--fs-sm); color: var(--muted); }
.stat-v { font-size: var(--fs-lg); font-weight: 700; margin-top: 3px; }
/* 交易帳本績效 9 張卡（ui79）：寬螢幕一列 9 張、一般 3×3、手機 2 欄且最後一張跨兩欄——不留空格
   （原本 auto-fill 排成 7＋2）。一列 9 張時每張內容只剩約 100px，數字降到 18px，
   七位數損益（+1,234,567 約 90px）才放得下。 */
#tr-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (min-width: 1500px) {
  #tr-stats { grid-template-columns: repeat(9, minmax(0, 1fr)); }
  #tr-stats .stat-v { font-size: var(--fs-md); }
}
@media (max-width: 600px) {
  #tr-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  #tr-stats > :last-child { grid-column: span 2; }
}

/* 訊號前瞻追蹤（唯讀）。與法人研究實驗室同一類的統計讀數 → **不用紅綠也不用琥珀**：
   任何暖色在「這個訊號有沒有用」的畫面上都會被讀成「可以買」。平均報酬雖是真的報酬%，
   但這裡量的是訊號品質而非今天的行情。層級只靠亮度與字級。 */
.sig-perf { max-width: 900px; container: sigperf / inline-size; }
.sp-table { width: 100%; border-collapse: collapse; font-size: var(--fs-data); }
.sp-table th { text-align: left; font-size: var(--fs-xs); font-weight: 600; color: var(--muted);
  padding: 4px 10px 6px; border-bottom: 1px solid var(--border); white-space: nowrap; }
.sp-table td { padding: 8px 10px; border-bottom: 1px solid var(--border-subtle); vertical-align: top; }
.sp-table tr:last-child td { border-bottom: 0; }
.sp-src { color: var(--text); font-weight: 600; white-space: nowrap; }
.sp-main { color: var(--text); white-space: nowrap; }
.sp-meta { font-size: var(--fs-xs); color: var(--muted); font-weight: 400; margin-top: 2px; }
/* 「還沒到期」與「沒在記」是兩件事，不可都顯示成破折號 */
.sp-wait { color: var(--label); font-size: var(--fs-xs); }
.sp-none { color: var(--text-disabled); }
.sp-weak .sp-main { color: var(--label); }
.sp-note { font-size: var(--fs-xs); color: var(--muted); margin-top: 8px; max-width: 640px; line-height: 1.6; }
/* 「來源」是唯一能折行的欄（其餘是 nowrap 的數字），表格放不下時由它先讓，但不小於 5 個字：
   不設下限時，表格自動版面在「各欄最小寬度總和 > 容器」時只給每欄最小寬度，它就縮到最窄的
   不可斷詞「CSV」＝44px、一字一行。寬螢幕放得下整行，不會折（最大寬度不變，桌機欄寬照舊）。
   全站 border-box，min-width 連左右各 10px 內距一起算，所以是 5em＋20px。
   .sp-src 掛在 td 自己身上，寫成後代選擇器會靜默不生效。 */
.sp-table td.sp-src:first-child { white-space: normal; min-width: calc(5em + 20px); }
/* 窄容器改上下堆疊（2026-10）：每個來源一個區塊，名稱＋訊號數一行、5／10／20 日三格在下。
   ui49 記的「375px 只讓來源欄換行就剛好 350/350」是三個橫幅都還是「尚未到期」（4 個字）時量的；
   09-16 報酬回填修好後每格變成 nowrap 的「勝率／平均」兩行（80～88px），訊號＋三個橫幅四欄
   本身就要 376px（最寬資料 407px），超過 350px 的容器——五欄放不下手機，與字級無關
   （ui68 只再加寬約 6%：換回舊字級仍是 396px）。看容器不看視窗：601px（側欄收成圖示）的
   容器也只有 489px，同樣溢出。門檻 549：五欄整行放得下最寬資料要 548px；861px（側欄展開、
   容器 550）維持五欄。比這更寬的資料（例如三位數的平均報酬，實測五欄最小 521px）出現時，
   來源欄先折行（不小於 5 個字），不會橫捲。 */
@container sigperf (max-width: 548px) {
  .sp-table, .sp-table thead, .sp-table tbody { display: block; }
  .sp-table thead { position: relative; }
  /* 每一列各是一個 grid：三欄一律等分、不看內容（minmax(0,1fr)），各列的欄位才對得齊 */
  .sp-table tr { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
  /* 「來源」「訊號」表頭只在視覺上藏起來，不可 display:none：只剩三個表頭的話，讀屏會依位置
     把來源格對到「5 日」。 */
  .sp-table thead th:nth-child(-n+2) {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
    overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  .sp-table tbody td:nth-child(1) { grid-column: 1 / 3; }
  .sp-table tbody td:nth-child(2) { grid-column: 3; text-align: right; }
  /* 表頭「訊號」藏起來了，數字前補小標；第二個 content 的 "" 是給讀屏的替代文字（表頭已念過），
     不支援這個寫法的瀏覽器會整條略過、沿用第一條 */
  .sp-table tbody td:nth-child(2)::before {
    content: "訊號 "; content: "訊號 " / "";
    font-size: var(--fs-xs); font-weight: 400; color: var(--muted);
  }
  .sp-table tbody td:nth-child(-n+2) { border-bottom: 0; padding-bottom: 0; }
  /* 手機全域的「凍結第一欄」這裡用不到（不橫捲），它的右邊線與不透明底會在標題列中間畫一條直線、
     蓋掉第二列的斑馬紋——關掉。特異度要高過那條 (0,2,3) 的全域規則。 */
  .sp-table tbody td.sp-src:first-child { position: static; box-shadow: none; }
  .sp-table tbody td.sp-src:first-child::after { content: none; }
}

/* 評分規則（木質/木率，唯讀）。分數不是漲跌 → 中性藍，不碰紅綠/琥珀。 */
.scoring-rules { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 10px; max-width: 900px; }
.sr-card { background: var(--card); border: 1px solid var(--border); border-left: 3px solid var(--info); border-radius: var(--r-lg); padding: 10px 12px; }
.sr-card.sr-pending { border-left-color: var(--muted); opacity: .85; }
.sr-h { display: flex; align-items: center; gap: 8px; font-weight: 700; color: var(--info); }
.sr-card.sr-pending .sr-h { color: var(--label); }
.sr-max, .sr-tag { font-size: var(--fs-xs); font-weight: 600; color: var(--muted); }
.sr-tag { background: var(--info-surface); padding: 1px 7px; border-radius: var(--r-pill); }
.sr-desc { font-size: var(--fs-sm); color: var(--label); margin-top: 4px; }
.sr-sub { font-size: var(--fs-xs); color: var(--muted); margin-top: 6px; }
.sr-list { list-style: none; margin: 4px 0 0; padding: 0; font-size: var(--fs-xs); color: var(--text); }
.sr-list li { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }
.sr-list.sr-cols { columns: 2; column-gap: 16px; }
.sr-list.sr-cols li { break-inside: avoid; }
.sr-pt { color: var(--info); font-weight: 700; }

/* 選股頁在窄桌機先切成單欄，避免 240px 側欄把候選表壓成不足 500px。 */
@media (max-width: 1100px) {
  .picks-command { grid-template-columns: 1fr; }
  /* 放不下就換行，不單列橫捲：杯柄頁 7 顆按鈕在 1024px 寬 720px、可用只有 683px，橫捲會把最右邊的
     「重新篩選」藏在一條細捲軸後面，滑鼠使用者幾乎看不到。換行後多一列，但每顆都看得到。 */
  .picks-actions { justify-content: flex-start; flex-wrap: wrap; }
  /* 按鈕不縮、字不折行：flex 項目預設會先被壓到 min-content，中文按鈕就被擠成兩行
     （杯柄頁 768px 實測「寬鬆／50」73px 高）。放不下的整顆換到下一列。 */
  .picks-actions > * { flex-shrink: 0; }
  .picks-actions button { white-space: nowrap; }
  .picks-command-foot { grid-column: 1; }
  .picks-feedback { flex-basis: 100%; text-align: left; }
  .picks-split { flex-direction: column; }
  .picks-side { flex: 0 0 auto; max-height: none; }
  .picks-side .pane-heading { min-height: 0; margin-bottom: 5px; }
  .picks-side .pane-heading p { display: none; }
  #industry.table-wrap { flex: 0 0 auto; overflow-x: auto; border: 0; border-radius: 0; background: transparent; box-shadow: none; }
  #industry table { display: block; width: max-content; min-width: 100%; }
  #industry thead { display: none; }
  #industry tbody { display: flex; gap: 6px; padding: 1px 0 5px; }
  #industry tbody tr { display: flex; flex: 0 0 auto; overflow: hidden; border: 1px solid var(--border-default); border-radius: 5px; background: var(--bg-card); }
  #industry tbody td { display: block; padding: 6px 7px; border: 0; background: transparent; }
  #industry tbody td + td { padding-left: 2px; color: var(--muted); }
  #industry tbody tr:hover td, #industry tbody tr:nth-child(even) td { background: transparent; }
  #industry tbody tr.is-active td { background: rgba(112, 184, 255, .16); }
}

/* 平板：側欄收成圖示、並排改直排 */
@media (max-width: 860px) {
  .app { grid-template-columns: 56px 1fr; }
  .nav { justify-content: center; padding: 12px 0; position: relative; }
  /* 標籤只在視覺上藏起來、留在無障礙樹裡：圖示是 aria-hidden，display:none 會讓導覽項沒有名稱
     （ui79 換線條圖示前，emoji 字元至少還算一個名稱）。手機（≤600px）另有規則，由 ::after 短標籤提供名稱。 */
  .nav .lbl { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
  /* 56px 圖示模式放不下文字標題 → 收起，群組間改細分隔線區隔 */
  .nav-group-title { display: none; }
  .nav-group + .nav-group { margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--border); }
  .chart-row { flex-direction: column; }
  .chart-side { flex-direction: row; flex-wrap: wrap; flex-basis: auto; }
}

/* 「更多」鈕：只在手機出現，桌機/平板由完整側欄取代。 */
.nav-more { display: none; }
.nav-scrim { display: none; }

/* 窄桌面／平板（601–860px）：頂欄**自己的控制項**維持放大前的尺寸。
   這一段的頂欄本來就排不下「標題＋搜尋＋資料日」而折成 3 列（改動前實測 601px 82px、
   768px 76px），字級整體 +1 級後 3 列一起長高，601px 直接撐到 125px。頁面內容照樣放大，
   只把常駐 chrome 收回去：實測 601px 125→85（基準 82）、768px 81→60（比基準還矮）。
   ≤600px 另有更緊的一組（搜尋收成圖示），寫在下面、順序在後所以會蓋過這裡。 */
@media (max-width: 860px) {
  .topbar h1 { font-size: 18px; }
  .topbar h1 .zh { font-size: 13px; }
  .topbar .gsearch input { font-size: 15px; padding: 7px 10px; }
  .topbar .muted, .topbar .data-date { font-size: 12px; }
}

/* 手機：導覽改「4 個常駐分頁 ＋ 更多面板」，版面單欄；寬表格沿用 .table-wrap 的左右滑動 */
@media (max-width: 600px) {
  .app { display: flex; flex-direction: column; height: 100vh; height: 100dvh; }

  .topbar { padding: 8px 12px; flex-wrap: wrap; gap: 2px 8px; }
  .topbar h1 { font-size: 16px; letter-spacing: 0; }
  /* 搜尋框自己佔一整列：375px 的頂欄放不下「標題＋搜尋＋資料日」三塊，擠在同一行
     會讓搜尋框窄到只剩兩三個字寬。但常駐一整列＝頂欄 56→101px，**永久**吃掉 45px
     （812px 螢幕的 5.5%），而搜尋不是每次進站都要用的東西。所以預設收成一顆圖示，
     按下才展開；order 讓展開時排在最後一列。 */
  /* **絕對定位，不參與 flex 排版。** 頂欄在 375px 會折成兩列（標題 193px ＋ 資料日那組
     270px 放不進 351px），標題那列右側整片空著、只有 22px 高。把 34px 的鈕放進流裡
     會把那一列撐成 34px、整個頂欄 56→71px；浮在右上角則完全不佔高度，頂欄維持 56px。 */
  .topbar { position: relative; }
  .gs-toggle {
    position: absolute; top: 8px; right: 12px;
    display: inline-flex; align-items: center; justify-content: center;
    width: 34px; height: 34px; flex: 0 0 auto;
    background: var(--sunken); color: var(--text); border: 1px solid var(--border);
    border-radius: var(--r); font-size: 16px; line-height: 1; cursor: pointer;
  }
  .gs-toggle[aria-expanded="true"] { border-color: var(--accent); }
  .gsearch { display: none; }
  .topbar.search-open .gsearch { display: block; flex: 1 0 100%; order: 3; margin-top: 4px; }
  .gsearch input { padding: 9px 12px; font-size: 15px; }   /* 手機可點高度 */
  .topbar h1 .zh { font-size: 12px; margin-left: 5px; }
  .actions { gap: 8px; }
  .muted, .data-date { font-size: 13px; }
  /* 但頂欄自己的那兩串維持 12px：它們橫向排在同一列、13px 就會折行（見上面 .topbar
     的 line-height 註解）。**只縮頂欄內的**，頁面其他地方的 .muted 照樣是放大後的 13px。 */
  .topbar .muted, .topbar .data-date { font-size: 12px; }
  /* 更新時間藏年份：資料日期就在同一列，年份重複；省下的寬度讓它不鑽到搜尋鈕底下 */
  .topbar .lu-y { display: none; }

  .content { flex: 1; min-height: 0; }
  /* 底部留白 = 分頁列高度 ＋ iPhone 底部指示條（safe-area），不夠的話最後一列會被蓋住 */
  .view { padding: 12px 12px calc(62px + env(safe-area-inset-bottom, 0px)); }
  /* 報告視窗（報告模式）藏了底部分頁列，不必再為它留 62px */
  body.report-mode .view { padding-bottom: calc(24px + env(safe-area-inset-bottom)); }
  .picks-view { gap: 9px; }
  .picks-command { padding: 10px; gap: 8px; }
  .picks-title-row { justify-content: space-between; gap: 6px; }
  .picks-title-row h2 { font-size: var(--fs-md); }
  .picks-command-copy p { line-height: 1.4; }
  .picks-action { min-height: 40px; }
  .picks-label-full { display: none; }
  .picks-label-short { display: inline; }
  .picks-action-meta { display: none; }
  .picks-command-foot { gap: 8px 12px; padding-top: 8px; }
  .picks-summary { flex: 1 1 100%; }
  .picks-summary span { flex: 1; padding-inline: 7px; text-align: center; }
  .picks-summary span:first-child { padding-left: 0; }
  .picks-filter { flex-wrap: nowrap; overflow-x: auto; width: 100%; padding-bottom: 2px; }
  .picks-filter-item { white-space: nowrap; }
  .pane-heading { min-height: 34px; }
  /* 股期概況的保證金說明列（#ssf-margin-note，.pane-count 預設 nowrap）在手機要能換行；
     頁首改 .picks-command（ui79）後，原本那條 .pane-heading 換行規則已不需要。 */
  #view-ssf .pane-count { white-space: normal; }
  /* 跨週週報「大戶連續加碼」的 pane-count（列前 N、排除 M 檔成交額不足…）比標題長很多，
     同一個 h3 被壓成一字一行的直條（CJK 逐字都是斷行點，min-content 只剩一個字寬）。
     同上面 #view-ssf 的既有修法：手機上讓它換到下一行。 */
  #view-weekly .pane-heading { flex-wrap: wrap; align-items: flex-start; }
  #view-weekly .pane-heading > div { flex: 1 1 100%; min-width: 0; }
  #view-weekly .pane-count { white-space: normal; }
  /* .ai-head 的說明在手機一律放到標題下一行（放得下的「評分規則」也一樣），整頁同一種排法。
     總覽「AI 盤勢摘要」的說明包在標題 span 裡、不是直接子項，不受影響。 */
  .ai-head > .muted { flex-basis: 100%; }
  .table-wrap.fill-half { max-height: 60vh; }
  .ss-market-map { height: 620px; min-height: 620px; border-radius: var(--r); }
  .ss-map-heading { display: grid; grid-template-columns: minmax(0, 1fr); align-items: start; gap: 5px; }
  #ss-hm-note { text-align: left; }
  .ss-market-cell { padding: 7px 8px; border-width: 1.5px; border-radius: 5px; }
  /* .ss-cell-rank 已改成排在文字流裡（不再絕對定位），手機不必另調 top/left */
  .ss-cell-name { font-size: 13px; }
  .ss-cell-value { font-size: 20px; }

  /* 左側欄 → 固定底部分頁列。
     **`display: flex` 這行不能省。** `.sidebar` 本身沒有宣告 display（桌機的 flex 容器
     是它裡面的 `.sidebar-nav`），而這裡把 `.sidebar-nav` 與 `.nav-group` 都設成
     `display: contents` 讓 13 個 .nav 直接成為 `.sidebar` 的子項——少了這行，`.sidebar`
     仍是 block，13 個 .nav 就一個一個往下疊：實測底部列高 **648px**、每個 .nav 寬 375px，
     等於一塊固定面板蓋掉 80% 的畫面，整個 App 在手機上不能用。只設 flex-direction 不會
     有任何作用也不會報錯，是那種「看 CSS 看不出來、要量才知道」的失效。 */
  .sidebar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
    display: flex; flex-direction: row; align-items: stretch; gap: 0;
    padding: 0 0 env(safe-area-inset-bottom, 0px); overflow: visible;
    background: var(--panel); border-right: none; border-top: 1px solid var(--border);
    box-shadow: 0 -6px 20px rgba(0, 0, 0, 0.35);
  }
  .sidebar-nav { display: contents; }
  .nav-group { display: contents; }
  .nav-group-title { display: none; }

  /* 13 個目的地擠不進 375px：一格只有 28.8px，圖示放得下、標籤只剩一個字，
     等於有跟沒有一樣。改成 iOS/Android 都通用的「4 常駐 ＋ 更多」——4+1 格各 75px，
     圖示與短標籤都放得下，其餘 9 個進「更多」面板（3 欄、每格 ~105px）。 */
  .nav, .nav-more {
    flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 2px;
    align-items: center; justify-content: center; padding: 7px 1px;
    border: 1px solid transparent; border-radius: 0; min-height: 56px;
    background: transparent; color: var(--text); font: inherit; cursor: pointer;
  }
  .nav:not([data-primary]) { display: none; }
  .nav-more { display: flex; }
  .nav .ico, .nav-more .ico { font-size: 22px; line-height: 1; }
  /* 手機一律用 data-short：底部列與面板的格寬都容不下「籌碼/基本選股」這種長度，
     兩種狀態共用一條規則比各寫一條不容易漂移。 */
  .nav .lbl { display: none; }
  .nav::after {
    content: attr(data-short); font-size: 11px; line-height: 1;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
  }
  .nav-more .lbl { font-size: 11px; line-height: 1; }
  .nav:hover, .nav-more:hover { background: transparent; border-color: transparent; }
  .nav.active { background: transparent; border-color: transparent; box-shadow: none;
                color: var(--accent); font-weight: 700; }

  /* 展開的「更多」面板：沿用同一批 .nav 節點，改為右側分組抽屜；不另外複製 DOM，
     否則會有兩套 active 狀態要同步。 */
  .sidebar.open {
    position: fixed; top: 0; right: 0; bottom: 0; left: auto;
    width: min(84vw, 300px); max-height: none;
    display: flex; flex-direction: column; align-items: stretch; gap: 0;
    padding: calc(10px + env(safe-area-inset-top, 0px)) 10px calc(10px + env(safe-area-inset-bottom, 0px));
    overflow-y: auto; border-radius: var(--r-lg) 0 0 var(--r-lg);
    transform: translateX(0); animation: nav-drawer-in var(--dur-base) var(--ease-out);
  }
  .sidebar.open .sidebar-nav { display: flex; flex-direction: column; gap: 2px; min-height: 0; }
  .sidebar.open .nav-group { display: block; }
  .sidebar.open .nav-group + .nav-group { margin-top: var(--space-sm); padding-top: 0; border-top: 0; }
  .sidebar.open .sidebar-nav .nav-group-sys { margin-top: var(--space-sm); }
  .sidebar.open .nav-group-title { display: block; }
  /* 抽屜內的小標題只是分組標籤，不能讓手機點擊改寫桌機的收合偏好。 */
  .sidebar.open .nav-group-title { pointer-events: none; }
  .sidebar.open .nav {
    flex: 0 0 auto; display: flex; flex-direction: row; justify-content: flex-start;
    gap: 10px; min-height: 44px; padding: 8px 14px;
  }
  .sidebar.open .nav::after { content: none; }
  /* ≤860px 的 .nav .lbl 是「只在視覺上隱藏」（1px＋裁切，見圖示軌那段），抽屜要把那組屬性全部還原——
     只改 display 的話標籤仍被裁成 1px，抽屜只剩圖示（ui79 上線後就是這樣，ui81 修正）。 */
  .sidebar.open .nav .lbl { display: inline; font-size: 15px; position: static; width: auto; height: auto;
    margin: 0; overflow: visible; clip: auto; clip-path: none; }
  .sidebar.open .nav-more { order: -1; flex: 0 0 auto; }
  .sidebar.open .nav-more .lbl::before { content: "關閉"; }
  .sidebar.open .nav-more .lbl { font-size: 0; }
  .sidebar.open .nav-more .lbl::before { font-size: 11px; }
  /* 抽屜打開時「更多」換成 X 圖示（原本把 ☰ 轉 45° 充當關閉，看起來只是斜線） */
  .sidebar.open .nav-more .i-closed { display: none; }
  .sidebar.open .nav-more .i-open { display: block; }

  @keyframes nav-drawer-in {
    from { transform: translateX(100%); }
    to { transform: translateX(0); }
  }
  /* reduced-motion 現由全站的單一閘門收斂（見 :root 後的全域 media query），
     不在此重複判斷——兩份會漂移，之前這裡各自寫一份正是要收斂的那個問題。 */

  /* 面板外的遮罩：點一下關閉。hidden 屬性由 app.js 切換，這裡只管長相。 */
  .nav-scrim { display: block; position: fixed; inset: 0; z-index: 49;
               background: rgba(0, 0, 0, 0.45); }
  .nav-scrim[hidden] { display: none; }

  /* 置頂 KPI 條：手機用 3 欄 × 2 列而不是 2 欄 × 3 列。實測 2 欄時整條高 132px，
     加上頂列 56px 等於固定吃掉 188/812＝23% 的畫面；3 欄壓到 ~78px。 */
  .kpi-sticky { grid-template-columns: repeat(3, minmax(0, 1fr));
                gap: 4px 8px; padding: 6px 10px; }
  .kpi-k { font-size: 11px; }
  .kpi-v { font-size: 14px; }

  /* 上方那一帶：判讀在上、數字在下。標籤改兩欄、只寫名稱與偏多空（名稱｜判定｜名稱｜判定），
     數值在依據說明與桌機版裡——使用者看過三種手機版後選的（spec §6.2） */
  .dir-panel { padding: 12px 14px; gap: 8px; }
  .dir-lead { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px; }
  .dir-main { margin-top: 0; gap: 8px; }
  .dir-verdict { font-size: 28px; }
  .dir-tags { display: grid; grid-template-columns: auto minmax(0, 1fr) auto minmax(0, 1fr); column-gap: 10px; row-gap: 3px; }
  .dir-tag { display: contents; }
  .dir-tag > span { font-size: var(--fs-sm); line-height: 1.5; }
  .dir-tag .v { display: none; }
  .market-strip { padding: 12px 14px; }
  .ms-index .ms-chg { font-size: var(--fs-base); }
  .ms-inst { gap: var(--sp-3); }
  .section-head { margin-top: var(--sp-4); }
  .sh-title { font-size: var(--fs-md); }
  .card-group { padding: 12px; }

  /* 寬表格在 350px 的視窗裡只露得出三成（自選股實測 1,153px、高價股 991px），橫向捲到
     一半就不知道這一列是哪一檔了，而且畫面上**沒有任何跡象**顯示右邊還有東西。兩個處理：
     (1) 第一欄凍住，代號/名稱一直在；(2) 右緣加陰影當「還有內容」的提示。
     陰影用 background-attachment local/scroll 疊圖：`local` 那層跟著內容捲、捲到底時
     剛好蓋掉固定在邊緣的 `scroll` 陰影層，所以不必用 JS 監聽 scroll 事件。
     `:not([colspan])` 是必要的——自選股的估價面板是一整列 `<td colspan="13">`，
     把它凍在左邊會變成整個面板浮在表格上。 */
  .table-wrap {
    background:
      linear-gradient(to left, var(--sunken), rgba(0, 0, 0, 0)) right center / 22px 100% no-repeat local,
      radial-gradient(farthest-side at 100% 50%, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)) right center / 12px 100% no-repeat scroll,
      var(--sunken);
  }
  /* 凍哪一欄：實測 6 張寬表裡有 5 張的第 1 欄就是識別欄（股票／個股／檢核項），
     只有高價股例外——它第 1 欄是名次「#」（41px）。凍一個序號沒有意義又吃掉一成螢幕寬，
     所以手機上把名次欄收起來（名次由列序表達），凍結改套在「股票」欄。 */
  #view-hiprice th:first-child,
  #view-hiprice td:first-child { display: none; }

  tbody tr td:not([colspan]):first-child,
  #view-hiprice tbody tr td:nth-child(2) {
    position: sticky; left: 0; z-index: 2;
    box-shadow: inset -1px 0 0 var(--border-strong);
  }
  /* **凍住的欄位一定要不透明**，否則捲過去的內容會從半透明的斑馬紋／hover 底色下面透出來，
     看起來像疊字。直接改 `background` 會陷入特異度戰爭——`#daily`／`#industry` 各有自己
     帶 ID 前綴的斑馬紋與 hover 規則（(1,1,2)），權重高過任何不含 ID 的選擇器，而且以後
     新增一張表就要再補一條。改用 `::after` 疊一層不透明底，跟那些規則完全不衝突：
     sticky 已經建立堆疊脈絡，`z-index:-1` 只會壓在這一格自己的背景之上、文字之下。 */
  tbody tr td:not([colspan]):first-child::after,
  #view-hiprice tbody tr td:nth-child(2)::after {
    content: ""; position: absolute; inset: 0; z-index: -1; background: var(--sunken);
  }
  /* 整列展開的面板（自選股估價）寬度會跟著整張 1,153px 的表格走，在 350px 的視窗裡
     等於要一邊橫捲一邊填表。把**面板本身**黏在左緣並限寬到可視範圍（td 不能黏，理由見上）——
     max-width 用 vw 而不是 %：% 是相對那 1,129px 的 td，限不出東西來。 */
  td[colspan] > * { position: sticky; left: 0; max-width: 88vw; }
  /* 面板只剩約 296px，標籤就佔 140：估價說明不再縮排對齊輸入框，改用整行寬（ui111） */
  .est-panel .set-row > .est-hint { flex-basis: 100%; margin-left: 0; }

  /* th 本來就是 sticky top:0，補上 left:0 即成雙軸凍結；box-shadow 要把原本的下緣線一起帶著寫，
     否則整條寫掉只剩右緣線，表頭下方的分隔就在第一欄斷掉。 */
  th:not([colspan]):first-child,
  #view-hiprice th:nth-child(2) {
    left: 0; z-index: 3;
    box-shadow: inset -1px 0 0 var(--border-strong), inset 0 -1px 0 var(--border-strong),
                0 4px 9px rgba(0, 0, 0, 0.16);
  }
  #industry tbody tr td:first-child { position: static; box-shadow: none; }
  #industry tbody tr td:first-child::after { content: none; }

  /* 排行熱力圖第一欄在 tbody 是 th[scope="row"]（名次），不是 td——上面的通用凍結
     規則只認 `td:not([colspan]):first-child`，選擇器對不上 th，蓋不到它，因此另外
     寫一條，手法沿用同一套技巧：sticky + 正 z-index 疊在儲存格本身，`::after` 補一層
     不透明底、z-index:-1 讓它壓在儲存格自己的背景之上、文字之下。th 全域已有
     `background:#1b2a3f`（見上方 `th{...}`）本身就不透明，這裡仍用 ::after 而非直接
     覆寫 background，是為了與全站凍結第一欄的既有寫法保持一致。色值用 --card
     （#142033，最接近 th 現有的 #1b2a3f 深藍）而非其餘凍結規則慣用的 --sunken
     （#050910 太暗，疊上去會讓凍結欄與同一列的日期表頭色差過大）。 */
  #ssf-hm-table tbody th[scope="row"] {
    position: sticky; left: 0; z-index: 1;
    box-shadow: inset -1px 0 0 var(--border-strong);
  }
  #ssf-hm-table tbody th[scope="row"]::after {
    content: ""; position: absolute; inset: 0; z-index: -1; background: var(--card);
  }

  /* 卡片固定 2 欄；並排區塊一律直排 */
  .card-grid, .cards { grid-template-columns: repeat(2, 1fr); gap: 7px; }
  .stat-board { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px; }
  .card-val { font-size: 21px; }
  .stat-board .card-metric { flex-direction: column; align-items: stretch; gap: 4px; }
  .stat-board .card-metric .card-chg { align-self: flex-end; }
  .rank-split { flex-direction: column; gap: 4px; }
  .chart-side { gap: 12px; }

  /* 個股 K 線圖（四窗格）手機段整體縮到 460px（同比例縮小），避免在小螢幕佔掉過多首屏高度。
     同上面桌機規則的理由，必須連 flex-basis 一起覆寫（`flex: 0 0 460px`），否則個股頁在
     手機上內容一樣超過一屏、.chart-big 的 flex-basis:0% 會讓這條 height 被忽略、floor 回
     min-height:380px。 */
  #stock-chart { flex: 0 0 460px; height: 460px; }

  /* 輸入框滿版、按鈕加大好點（44px 是 iOS 建議的最小可點高度） */
  .stock-input { width: 100%; }
  .tf, .ktf, .ctf, .rkf, .rku, .ai-btn, .file-label { padding: 10px 14px; font-size: 15px; min-height: 40px; }
  /* 分段鈕群組在手機收窄左右內距、不准折行：杯柄的四顆門檻鈕原本被擠成「寬鬆／50」兩行（60px 高） */
  .hm-tabs .tf, .hm-tabs .ctf { padding-left: 8px; padding-right: 8px; white-space: nowrap; }
  .set-row > label { width: auto; }
  /* 手機：設定頁改成標籤在上、控制項在中、說明在下（單欄） */
  #view-settings .set-field, #view-account .set-field { grid-template-columns: minmax(0, 1fr); }
  #view-settings .set-field > label, #view-account .set-field > label, .set-ctl, .set-help { grid-column: 1; }
}

/* ===== 自選股 Top 5 股池（2026-09 起是 Top 3，ui88 改 5 格）=====
   不新增顏色：拖曳中的格子用中性 --info 外框；琥珀只給「報告失敗」（沿用「注意這格」語彙）。 */
.watch-top3-wrap { flex-shrink: 0; display: flex; flex-direction: column; gap: 8px; margin: 12px 0; }
.watch-top3-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; }
.watch-top3-head h3 { margin: 0; font-size: var(--fs-md); color: var(--text-primary); white-space: nowrap; }
/* 今日報告額度徽章（ui78）：平常中性；用完才用琥珀（同設定頁 LINE 額度用完的語彙）。
   還沒拿到 quota 時是空的，整顆不顯示（不寫假數字）。 */
.top3-quota { font-size: var(--fs-xs); color: var(--text-secondary); padding: 2px 8px;
  border: 1px solid var(--border-strong); border-radius: var(--r-pill); white-space: nowrap; }
/* 這個 live region 可以用 :empty 藏起來（只有載入後第一次填字不會被念出來，無傷）；#watch-top3-msg 的訊息一定要念出來，所以刻意不用。 */
.top3-quota:empty { display: none; }
.top3-quota.full { color: var(--accent); border-color: var(--accent); }
/* 股池訊息沿用 .picks-feedback 的字級與顏色，但那條的 flex: 1 1 220px 是給橫排頁首列的（最少 220px 寬）；
   .watch-top3-wrap 是直排，flex-basis 變成 220px 高，股池下方憑空空出一大塊（ui77 修，同「flex-basis 在
   方向翻轉時從寬度變成高度」那條）。不用 :empty 藏起來：它是 aria-live，display:none 會退出無障礙樹。 */
#watch-top3-msg { flex: none; text-align: left; }
/* ===== 一行五張（ui88）=====
   #watch-top3 定義 6 列（股名／分數／狀態／四個數字／一句結論／按鈕），有報告的卡用 subgrid 跨全部列：
   某張卡的狀態折成兩行時，其他卡的同一列跟著往下，五張橫著看仍是同一列。
   股池 ≥950px 五張平均分：一張卡最窄要放得下按鈕列（實測「報告 ↗」74px＋兩顆 34px 圖示鈕＋兩個 6px 間距
   ＝154px）加左右內距 24px 與外框 2px＝180px，五張＋四個 10px 欄距＝940px，取 950 讓每張多 2px 餘裕。
   實測（2026-10-03）：視窗 1,280px 股池 975px、卡片 186px，一行五張不用滑；1,240px 股池 935px 放不下，改橫捲。
   不到 950px 仍排一行、左右滑（使用者選的）：一次 3.5 張，右邊永遠露出半張，看得出還有；股池不到 660px
   （3.5×180＋3×10）時 3.5 張會小於 180px，改一次 2.5 張。停在卡片邊界。
   3px 內距（配 -3px 外距）：橫捲時卡片的 outline（拖曳中、失敗）不被捲動框切掉。 */
.watch-top3-wrap { container: top3pool / inline-size; }
.watch-top3 { display: grid; grid-auto-flow: column; grid-template-rows: repeat(6, auto);
  grid-auto-columns: calc((100% - 30px) / 3.5); gap: 6px 10px;
  overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory; padding: 3px; margin: -3px; }
@container top3pool (max-width: 659px) {
  .watch-top3 { grid-auto-columns: calc((100% - 20px) / 2.5); }
}
@container top3pool (min-width: 950px) {
  .watch-top3 { grid-auto-columns: minmax(0, 1fr); overflow-x: visible; scroll-snap-type: none; }
}
.top3-slot { grid-row: 1 / -1; min-width: 0; min-height: 112px; padding: 10px 12px; border: 1px dashed var(--border-strong);
  border-radius: var(--r-lg); background: var(--sunken); scroll-snap-align: start; }
.top3-slot.filled { display: grid; grid-template-rows: subgrid; border-style: solid; background: var(--card); box-shadow: var(--shadow-card); }
/* 空格維持自然高度：跟有報告的卡撐成一樣高，會變成一大塊虛線空框 */
.top3-slot:not(.filled) { align-self: start; display: flex; align-items: baseline; gap: 8px; }
.top3-slot.drop-over { outline: 2px solid var(--info); outline-offset: 2px; }
.top3-slot.alert { outline: 2px solid var(--accent); outline-offset: 2px; }
/* 子項逐列放：沒有報告的卡少了分數與結論，其餘仍在自己的列上，不會往上補位 */
.top3-head { grid-row: 1; display: flex; align-items: baseline; gap: 6px; min-width: 0; }
.top3-scoreline { grid-row: 2; }
.top3-status { grid-row: 3; text-wrap: balance; font-size: var(--fs-sm); color: var(--text-secondary); }
.top3-body { grid-row: 4; display: block; min-width: 0; }
.top3-leadrow { grid-row: 5; display: block; min-width: 0; }
.top3-actions { grid-row: 6; display: flex; gap: 6px; margin-top: 2px; }
.top3-actions button { min-height: 28px; }
.top3-no { font-size: var(--fs-md); font-weight: 700; color: var(--muted); }
/* 股名放不下截成省略號，title 是全名 */
.top3-id { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.top3-empty { color: var(--muted); font-size: var(--fs-sm); }
.top3-score { display: inline-flex; align-items: baseline; gap: 3px; white-space: nowrap; color: var(--info); }
.top3-score b { font-size: var(--fs-xl); line-height: 1; font-variant-numeric: tabular-nums; }
.top3-of { color: var(--muted); font-size: var(--fs-sm); }
.top3-stance { margin-left: 6px; font-size: var(--fs-sm); font-weight: 700; }
/* 四個數字：標籤左、值右。標籤用表格字級（13px）：窄卡片的值那一欄才放得下「+108.55%」 */
.top3-metrics { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: baseline; gap: 4px 8px; margin: 2px 0 0; }
.top3-metrics > div { display: contents; }
.top3-metrics dt { color: var(--text-secondary); font-size: var(--font-size-table); }
.top3-metrics dd { margin: 0; text-align: right; color: var(--text-primary); font-weight: 600;
  font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 「較前四季」與護城河都跨兩欄：塞在值那一欄只剩約 64px，會被截掉 */
.top3-metrics .top3-eps-sub { grid-column: 1 / -1; margin-top: -2px; font-size: var(--fs-xs); font-weight: 400; color: var(--text-secondary); }
.top3-metrics .top3-moat dt, .top3-metrics .top3-moat dd { grid-column: 1 / -1; text-align: left; }
.top3-metrics .top3-moat dd { white-space: normal; }
/* line-clamp 要包在一般區塊裡：grid／flex 的直接子項會被 blockify，-webkit-box 靜靜失效（.top3-lead 同理包在 .top3-leadrow） */
.top3-clamp2 { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }
.top3-lead { margin: 0; color: var(--text-primary); font-size: var(--fs-sm); line-height: 1.5;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }
.top3-hint { margin: 0; color: var(--muted); font-size: var(--fs-sm); }
.top3-actions a.top3-open { display: inline-flex; align-items: center; text-decoration: none; padding-left: 12px; padding-right: 12px; white-space: nowrap; }
.top3-sub { background: transparent; border-color: transparent; color: var(--text-secondary); padding-left: 8px; padding-right: 8px; }
/* ↻ 重新產生、✕ 移出：圖示鈕（文字在 aria-label 與 title），高度跟著按鈕列撐開 */
.top3-actions .top3-icon { width: 34px; padding: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 18px; }
/* 琥珀只給失敗：次要鈕不吃 .file-label:hover 的琥珀框（0,2,0；這條 0,4,0 蓋過它）。停用的鈕排除在外，
   免得停用中的「重新產生」滑過去反而亮起來（停用外觀由下一條 :disabled 管） */
.top3-actions .top3-sub:not(:disabled):hover { border-color: var(--border-strong); color: var(--text-primary); }
.top3-actions button:disabled { color: var(--text-disabled); border-color: var(--border-subtle); cursor: not-allowed; }
/* 次要鈕是無框的，停用時也不該冒出淡框（上一條的 border-color 0,2,1；這條 0,3,0 蓋過它，文字色與游標沿用上一條） */
.top3-actions .top3-sub:disabled { border-color: transparent; }
#watch-table tr[draggable="true"] { cursor: grab; }
#watch-table .watch-top3-add { white-space: nowrap; }
/* 自選股表格在 1536px 視窗要橫捲（ui77 修）：13 欄全部 nowrap，其中 9 欄的表頭比資料寬（「營收年增%」
   表頭 77px、資料 18px），ui76 又在最後一欄加了「→ Top 3 ／」，整張表 1,309px、框只有 1,223px，
   最右欄被切掉 86px。表頭只准在 app.js 的 <wbr> 處折行（keep-all：中文不在任意字之間斷），
   放不下時才折成兩行、寬螢幕照樣單行（同 #ssf-margin-table）；資料格維持 nowrap，數字不會被拆開。
   單行的表頭貼底，跟折成兩行的第二行對齊。 */
#watch-table th { white-space: normal; word-break: keep-all; vertical-align: bottom; }
/* 操作欄用間距分開，不用「 ／ 」（每個約 25px）；兩個藍色連結靠「→」與中英文字形區分 */
#watch-table .watch-acts { display: inline-flex; align-items: baseline; gap: 14px; }
@media (max-width: 600px) {
  /* 手機一次一張、露出下一張的邊；圖示鈕補到 40px 寬（觸控目標） */
  .watch-top3 { grid-auto-columns: calc(100% - 48px); }
  .top3-actions .top3-icon { width: 40px; }
}

/* ===== 個股分析報告面板 =====
   分數與偏多空標籤用中性 --info（不是行情方向）；財務數字不著色，情緒交給表情。 */
/* ===== 報告視窗（/?report=代號，ui77）=====
   同一份前端的「報告模式」：藏起頂欄、側欄與狀態列，.app 改單欄，只留 #view-report。
   權重要壓過 .app 在 ≤860px（56px 圖示軌）與 ≤600px（flex 直排）的兩套版面。 */
body.report-mode .topbar, body.report-mode .sidebar, body.report-mode #update-status { display: none; }
body.report-mode .app { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); }
body.report-mode .content { grid-column: 1; grid-row: 1; }
#view-report { width: 100%; max-width: 1080px; margin: 0 auto; }
.report-page { display: flex; flex-direction: column; gap: 12px; }
/* 工具列捲動時固定在上方（捲動容器是 .content）：讀到新聞時仍按得到下載 */
.report-page .report-toolbar { position: sticky; top: 0; z-index: 3; margin: 0; padding: 10px 0;
  background: var(--bg-workspace); border-bottom: 1px solid var(--border-subtle); }
.rp-bar-title { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.rp-bar-title b { font-size: var(--fs-md); color: var(--text-primary); white-space: nowrap; }
.rp-bar-actions { margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.rp-page-note { font-size: var(--fs-sm); color: var(--info); }
.rp-page-note.warn { color: var(--accent); }
.rp-page-msg { margin: 48px 0; text-align: center; color: var(--text-secondary); font-size: var(--fs-md); }
.report-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 8px; }
.report-sheet { background: var(--card); border: 1px solid var(--border-strong); border-radius: var(--r-lg);
  padding: 16px 20px; display: flex; flex-direction: column; gap: 14px; color: var(--text-primary); }
.report-sheet h4 { margin: 0 0 6px; font-size: var(--fs-md); }
.report-sheet p { margin: 4px 0; line-height: 1.6; }
.rp-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.rp-head h3 { margin: 0; font-size: var(--fs-lg); }
.rp-score { display: flex; align-items: baseline; gap: 6px; padding: 6px 12px; border: 1px solid var(--info); border-radius: var(--r-lg); }
.rp-score-num { font-size: var(--fs-xl); font-weight: 700; color: var(--info); }
.rp-score-of { color: var(--muted); }
.rp-stance { font-weight: 700; color: var(--info); margin-left: 6px; }
.rp-warn { outline: 1px solid var(--accent); padding: 6px 10px; border-radius: var(--r-lg); }
.rp-cards { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
.rp-card { min-width: 0; border: 1px solid var(--border-strong); border-radius: var(--r-lg); padding: 10px 12px; background: var(--sunken); }
.rp-card-label { color: var(--text-secondary); font-size: var(--fs-sm); }
.rp-card-val { font-size: var(--fs-xl); font-weight: 700; white-space: nowrap; }
.rp-unit { font-size: var(--fs-sm); font-weight: 400; color: var(--muted); margin-left: 4px; }
.rp-card-sub { font-size: var(--fs-xs); color: var(--text-secondary); }
.rp-mini { height: 56px; margin: 6px 0; }
.rp-kv { border-collapse: collapse; width: 100%; }
.rp-kv th { text-align: left; font-weight: 400; color: var(--text-secondary); padding: 3px 12px 3px 0; white-space: nowrap; vertical-align: top; }
.rp-kv td { padding: 3px 0; }
.rp-fin th, .rp-fin td { white-space: nowrap; }
.rp-note { color: var(--text-secondary); font-size: var(--fs-sm); }
.rp-news li { margin: 4px 0; }
.rp-news a { color: var(--info); }
.rp-foot { border-top: 1px solid var(--border-strong); padding-top: 8px; line-height: 1.6; }
@media (max-width: 1100px) { .rp-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px) {
  .report-sheet { padding: 12px; }
  .rp-cards { grid-template-columns: 1fr; }
  /* 鍵值表在手機上改成上下堆疊：th 的 min-width 加上 td 的長串數字，實測會把表撐到 368px（卡片內寬 326px）。
     overflow-wrap:anywhere 對表格儲存格的最小寬度無效，只能拆掉 table 排版。 */
  .rp-kv, .rp-kv tbody, .rp-kv tr, .rp-kv th, .rp-kv td { display: block; }
  .rp-kv th { padding: 6px 0 0; white-space: normal; }
}

/* 匯出時固定 800px 寬：手機與電腦產出的 PNG／PDF 長得一樣（見 exportReport）。 */
.report-sheet.exporting { width: 800px; max-width: none; }
.report-sheet.exporting .rp-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.report-sheet.exporting .table-wrap { overflow: visible; }
.report-sheet.exporting th, .report-sheet.exporting td { white-space: normal; overflow-wrap: break-word; }
.report-sheet.exporting .rp-kv td { overflow-wrap: anywhere; }
.report-sheet.exporting .num { white-space: nowrap; overflow-wrap: normal; }
/* 八季表 12 欄（科目＋8 季＋推估＋表情＋評語）。真實數字是七位數百萬元（2330 營收「1,425,065.85」），
   原本 6px 5px／12px 時表格要 835px、匯出寬度只有 756px，最右的表情與評語兩欄在 PNG／PDF 裡被切掉。
   只在匯出時收窄：內距 4px 3px、數字 10.5px、其餘 11px，評語欄留至少 7em 換行。
   實測 2330（全市場營收最大、數字最長）最窄 737px，表格填滿 756px；螢幕上的表格不受影響（仍是框內橫捲）。 */
.report-sheet.exporting .rp-fin th, .report-sheet.exporting .rp-fin td { padding: 4px 3px; font-size: 11px; }
.report-sheet.exporting .rp-fin td.num { font-size: 10.5px; }
.report-sheet.exporting .rp-fin td:last-child { min-width: 7em; }
/* 科目後面的單位（.small＝15px）比 11px 的科目還大，會把「（百萬元）」撐成三行、整列變高。 */
.report-sheet.exporting .rp-fin th .small { font-size: 10px; }
/* 科目欄留 4 個字寬，「合約負債」「營運現金」才不會被拆成「合約負／債」。 */
.report-sheet.exporting .rp-fin tbody th { min-width: 5em; }
.report-sheet.exporting table { max-width: 100%; }
/* 手機斷點（≤600px）的樣式不可漏進匯出：內距與鍵值表都還原成桌機版排法。 */
.report-sheet.exporting { padding: 16px 20px; }
.report-sheet.exporting .rp-kv { display: table; }
.report-sheet.exporting .rp-kv tbody { display: table-row-group; }
.report-sheet.exporting .rp-kv tr { display: table-row; }
.report-sheet.exporting .rp-kv th, .report-sheet.exporting .rp-kv td { display: table-cell; }
.report-sheet.exporting .rp-kv th { padding: 3px 12px 3px 0; white-space: nowrap; }

/* 登入頁（登入系統第一期，規格 §4.6）——2026-10 改成 SpaceX 式：全幅太空（login.html 裡自己畫的 SVG 地球邊緣＋星空）、
   細線。2026-10-12 使用者改：品牌字＋表單整塊放在畫面正中央（欄裡的字靠左），英文副標拿掉；時鐘仍在右下。字型：英文大寫用 DIN 系（Windows 內建 Bahnschrift，蘋果是 DIN Alternate）當點綴；DIN 沒有中文字，
   中文落到下一個有字的粉圓（Huninn），跟內頁一致——試過系統黑體，進站時中文從直筆畫跳成圓體，使用者覺得怪。
   **顏色維持站內暗色系**（使用者指定）：只借 SpaceX 的版面與字體，顏色全接 :root 的 token，SVG 的顏色也由這裡給；
   唯一的字面色是全站主要按鈕的藍框＋深藍底（同 .ir-primary／.top3-primary）。
   中文不加粗：標題明寫 400——粉圓宣告 100–900 不會被合成粗體，但粉圓沒載入時會落到系統黑體，它有真的粗體字面。
   琥珀只留給警示（.login-msg）與全站的 :focus-visible 焦點框。表單元件至少 44px 高：手機觸控目標；字 16px：iOS 不放大。 */
.login-page {
  --login-ink: var(--text-primary); --login-dim: var(--text-secondary); --login-faint: var(--text-muted);
  --login-line: var(--border-strong); --login-void: var(--bg);
  --login-gutter: clamp(20px, 6vw, 88px);
  /* height: auto 蓋掉全站的 html, body { height: 100% }：表單比螢幕高時 body 要跟著長高，以 body 底部定位的時鐘才不會壓在表單上 */
  position: relative; height: auto; min-height: 100dvh; margin: 0; overflow-x: hidden; color: var(--login-ink); background: var(--login-void);
  font-family: "Bahnschrift", "DIN Alternate", "D-DIN", "Huninn", "Microsoft JhengHei", "PingFang TC", "Noto Sans TC", sans-serif;
}
.login-sky { position: fixed; inset: 0; width: 100%; height: 100%; pointer-events: none; animation: login-fade 1.8s ease both; }
.login-sky .sky-earth-top { stop-color: var(--bg-card); }
.login-sky .sky-earth-mid { stop-color: var(--bg-workspace); }
.login-sky .sky-earth-low { stop-color: var(--bg-primary); }
.login-sky .sky-atmo { stop-color: var(--info); }
.login-sky .sky-glint { stop-color: var(--text-primary); }
.login-sky .login-rim { stroke: var(--info); stroke-opacity: .5; }
.login-stars { fill: var(--text-primary); animation: login-drift 150s linear infinite alternate; }
.login-stars .tw { animation: login-twinkle 4.6s ease-in-out infinite; }
.login-stars .tw:nth-of-type(3n) { animation-duration: 6.2s; animation-delay: -1.7s; }
.login-stars .tw:nth-of-type(3n+1) { animation-duration: 5.3s; animation-delay: -3.1s; }
.login-brand { width: min(100%, 380px); display: grid; gap: 6px; margin-bottom: 36px; }
.login-brand-en { font-size: 14px; letter-spacing: .38em; }
.login-brand-zh { font-size: 12px; letter-spacing: .6em; color: var(--login-dim); }
.login-wrap { position: relative; min-height: 100dvh; box-sizing: border-box; display: flex; flex-direction: column;
  justify-content: center; align-items: center; padding: 72px var(--login-gutter); }
.login-card { width: min(100%, 380px); display: grid; gap: 20px; }
.login-title { margin: 0 0 4px; font-size: clamp(32px, 4vw, 46px); font-weight: 400; letter-spacing: .14em; line-height: 1.05; }
.login-step { display: grid; gap: 18px; }
.login-step[hidden] { display: none; }
.login-field[hidden] { display: none; }
.login-field { display: grid; gap: 6px; font-size: 12px; letter-spacing: .2em; color: var(--login-dim); }
.login-en { margin-left: .8em; font-size: 10px; letter-spacing: .3em; opacity: .75; }
.login-field input { width: 100%; box-sizing: border-box; min-height: 44px; padding: 8px 0; font: inherit; font-size: 16px;
  letter-spacing: .04em; color: var(--login-ink); background: transparent; border: 0; border-bottom: 1px solid var(--login-line);
  border-radius: 0; transition: border-color .3s cubic-bezier(.16, 1, .3, 1); }
.login-field input:focus { border-bottom-color: var(--login-ink); }
.login-field input:-webkit-autofill { -webkit-text-fill-color: var(--login-ink); box-shadow: 0 0 0 40px var(--login-void) inset; }
.login-remember { display: flex; align-items: center; gap: 10px; min-height: 44px; width: max-content; font-size: 13px;
  letter-spacing: .1em; color: var(--login-dim); cursor: pointer; }
.login-remember input { appearance: none; display: grid; place-items: center; width: 16px; height: 16px; margin: 0;
  border: 1px solid var(--login-line); border-radius: 0; background: transparent; cursor: pointer; }
.login-remember input:checked { background: var(--info); border-color: var(--info); }
.login-remember input:checked::after { content: ""; width: 8px; height: 4px; margin-top: -2px; border: solid var(--login-void);
  border-width: 0 0 2px 2px; transform: rotate(-45deg); }
.login-hint { margin: 0; font-size: 13px; line-height: 1.7; letter-spacing: .04em; color: var(--login-dim); }
.login-msg { margin: 0; font-size: 13px; letter-spacing: .04em; color: var(--warn-text); }
.login-msg:empty { display: none; }
/* SpaceX 式的細框長方鈕，配色用全站主要按鈕（藍框＋深藍底，同 .ir-primary／.top3-primary）：平常透明底，
   滑過時深藍底從左邊掃進來（只動 transform）、框線轉成 --info；按下沿用全站 button:active 的下沉 1px。 */
.login-submit { position: relative; isolation: isolate; overflow: hidden; width: 100%; min-height: 48px; padding: 0 20px; font: inherit;
  font-size: 13px; letter-spacing: .34em; color: var(--login-ink); background: transparent; border: 1px solid rgba(112, 184, 255, .62);
  border-radius: 0; cursor: pointer; transition: border-color .45s cubic-bezier(.16, 1, .3, 1); }
.login-submit::before { content: ""; position: absolute; inset: 0; z-index: -1; background: #12263d; transform: scaleX(0);
  transform-origin: left; transition: transform .45s cubic-bezier(.16, 1, .3, 1); }
.login-submit:hover:not(:disabled) { border-color: var(--info); }
.login-submit:hover:not(:disabled)::before { transform: scaleX(1); }
.login-submit:disabled { opacity: .5; cursor: progress; }
.login-submit .login-en { font-size: 11px; opacity: 1; }
.login-telemetry { position: absolute; right: var(--login-gutter); bottom: 30px; margin: 0; font-size: 11px; letter-spacing: .3em;
  color: var(--login-dim); font-variant-numeric: tabular-nums; }
.login-reveal { animation: login-rise .9s cubic-bezier(.16, 1, .3, 1) both; animation-delay: calc(var(--i, 0) * 90ms + 150ms); }
@keyframes login-rise { from { opacity: 0; transform: translateY(14px); } }
@keyframes login-fade { from { opacity: 0; } }
@keyframes login-drift { to { transform: translate(-28px, 12px); } }
@keyframes login-twinkle { 50% { opacity: .15; } }
@media (max-width: 600px) {
  /* 左右留白 20px（375px 寬時表單與左下的時鐘同一條左邊界）；上下留白收小，矮螢幕（470×560）不多出捲動。 */
  .login-page { --login-gutter: 20px; }
  .login-brand { margin-bottom: 28px; }
  .login-wrap { padding: 56px var(--login-gutter) 64px; }
  .login-telemetry { left: var(--login-gutter); right: auto; bottom: 22px; letter-spacing: .2em; }
}
@media (prefers-reduced-motion: reduce) {
  .login-sky, .login-stars, .login-stars .tw, .login-reveal { animation: none; }
  .login-submit, .login-submit::before, .login-field input { transition: none; }
}
#auth-warn { text-decoration: none; cursor: pointer; }
/* 頂欄登出（2026-10-10，使用者要求）：跟頂欄讀數同一層級的安靜小鈕。登出失敗時框線轉警示色（琥珀的「警示」用途）。
   手機（≤600px）頂欄放不下，改在「更多」面板裡的 #nav-logout；其他寬度那顆一律不顯示。 */
.auth-logout { font: inherit; font-size: var(--fs-xs); color: var(--label); background: transparent; border: 1px solid var(--border);
  border-radius: var(--r); padding: 3px 10px; cursor: pointer; white-space: nowrap; }
.auth-logout:hover { color: var(--text-primary); border-color: var(--border-strong); }
.auth-logout-err, .auth-logout-err:hover { color: var(--warn-text); border-color: var(--warn-text); }
.nav-logout { display: none; }
@media (max-width: 600px) {
  #auth-warn, .auth-logout { display: none; }
  .sidebar.open .nav-logout:not([hidden]) { display: flex; align-items: center; width: 100%; min-height: 44px; margin-top: var(--space-sm);
    padding: 8px 14px; font: inherit; font-size: 15px; color: var(--text); background: transparent; border: 1px solid var(--border);
    border-radius: var(--r); cursor: pointer; }
}
/* 每頁標題左邊的「← 返回」（app.js 插在每頁第一個 h2 前面；總覽與報告視窗沒有）。次要鈕，不搶標題。 */
.view-back { flex: 0 0 auto; align-self: center; margin-right: 10px; padding: 3px 10px; font: inherit; font-size: var(--fs-xs);
  line-height: 1.4; color: var(--label); background: transparent; border: 1px solid var(--border); border-radius: var(--r);
  cursor: pointer; white-space: nowrap; }
.view-back:hover { color: var(--text-primary); border-color: var(--border-strong); }
/* 手機：觸控目標加大；.picks-title-row 在手機是 space-between（原本讓右側小標籤靠右），返回鈕在最左時
   標題會被推到右邊（375px 實測 h2 跑到 x=246）——讓標題緊跟返回鈕、剩下的空間由它右邊吃掉。桌機是靠左排，不動。 */
@media (max-width: 600px) { .view-back { padding: 6px 12px; } .view-back + h2 { margin-right: auto; } }

/* 設定頁「帳號與安全」（登入系統第一期）。琥珀只給警示（.acct-warn／.acct-bad）；中文不加粗。 */
#acct-head[hidden], .acct[hidden], .acct-col[hidden], .acct-backup[hidden] { display: none; }
.acct { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; margin-bottom: 8px; }
.acct-form { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.acct-form .stock-input { width: min(100%, 220px); }
.acct-form .acct-code { width: 140px; }
.acct-col { display: grid; gap: 8px; justify-items: start; }
.acct-user { overflow-wrap: anywhere; }
.acct-sub { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px 12px;
  margin-top: 6px; font-size: var(--fs-sm); color: var(--label); }
.acct-table td, .acct-table th { white-space: nowrap; }
.acct-btn { min-height: 28px; padding: 4px 10px; font: inherit; font-size: var(--fs-xs); color: var(--text-secondary); cursor: pointer;
  background: transparent; border: 1px solid var(--border); border-radius: var(--r); }
.acct-btn:hover { color: var(--text-primary); border-color: var(--border-strong); }
.acct-warn { margin: 0; padding: 8px 12px; font-size: var(--fs-sm); color: var(--warn-text);
  background: var(--warn-surface); border: 1px solid var(--accent); border-radius: var(--r); }
.acct-qr { width: 200px; padding: 6px; background: #fff; border-radius: var(--r); } /* QR 需要白底才掃得到 */
.acct-qr svg { display: block; width: 100%; height: auto; }
.acct-secret { word-break: break-all; font-size: var(--fs-sm); }
.acct-backup ol { margin: 0; padding-left: 1.5em; columns: 2; font-size: var(--fs-sm); }
.acct-bad { color: var(--warn-text); }

/* ===== 登入系統第二期（ui100）：會員的側欄、上鎖頁 ===== */
/* 會員看不到的頁：hidden 屬性。.nav／.nav-group 在各種寬度都有自己的 display，要寫得比它們具體才蓋得過 */
.nav[hidden], .sidebar.open .nav[hidden], .nav-group[hidden], .sidebar.open .nav-group[hidden] { display: none; }
.nav-locked .lbl { color: var(--text-muted); }
.nav-lock-i { flex: 0 0 auto; width: 14px; height: 14px; margin-left: auto; color: var(--text-muted); }
/* 860px 以下側欄是 56px 圖示軌、手機是底部列：鎖頭改成圖示右上角的小角標；手機抽屜裡又是一整列，回到列尾 */
@media (max-width: 860px) {
  .nav-locked { position: relative; }
  .nav-locked .nav-lock-i { position: absolute; top: 4px; right: 4px; width: 10px; height: 10px; margin: 0; }
}
@media (max-width: 600px) {
  .sidebar.open .nav-locked .nav-lock-i { position: static; width: 14px; height: 14px; margin-left: auto; }
}
/* 會改資料或強制重抓的按鈕：會員不顯示（後端閘門也擋） */
body.is-member :is(#btn-news-refresh, #btn-osfut-refresh, #btn-latest, #csv-label, #btn-cup-recompute) { display: none; }
.locked-card { display: flex; align-items: center; gap: var(--space-md); max-width: 560px; padding: var(--space-md);
  border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--panel); color: var(--text-secondary); }
.locked-card p { margin: 0; }
.locked-card-i { flex: 0 0 auto; width: 22px; height: 22px; color: var(--text-muted); }
/* 設定頁的帳號管理（管理者；第二期 §3.3）：#users 沿用 .acct 的格線，表格欄位不折行（.acct-table），窄螢幕靠 .table-wrap 橫捲 */
#users-head[hidden] { display: none; }
.users-table select, .users-table .users-exp { font: inherit; font-size: var(--fs-xs); }
/* 到期日欄位借了 .mini-input（64px，給數字篩選用）：放不下 YYYY-MM-DD 加日期選擇圖示。
   兩條選擇器都比 .mini-input 具體（兩個 class／id），不必動 .mini-input 本身。 */
.users-table .users-exp, #users-new-exp { width: auto; min-width: 10.5em; }
.users-acts { white-space: nowrap; }

/* ===== 平均週線選股（view-weekly-ha）===== */
.wha-lookup { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 0; }
.wha-lookup .stock-input { width: 11em; }
.wha-result { border: 1px solid var(--border-strong); border-radius: var(--r-lg); background: var(--sunken); padding: 12px 16px; }
.wha-checks { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 6px; }
.wha-check { display: grid; grid-template-columns: 1.5em minmax(0, 1fr) minmax(0, 1.2fr); gap: 8px; align-items: baseline; }
.wha-mark { text-align: center; }
.wha-check.ok .wha-mark { color: var(--info); }                 /* 分數不是漲跌：不用紅綠 */
.wha-check.ng .wha-mark, .wha-check.ng .wha-check-val { color: var(--muted); }
.wha-explain ol { margin: 8px 0; padding-left: 1.5em; }
@media (max-width: 600px) {
  .wha-check { grid-template-columns: 1.5em minmax(0, 1fr); }
  .wha-check-val { grid-column: 2; }
  .wha-lookup .stock-input { flex: 1 1 8em; width: auto; }
}

/* ===== 台指 3C 策略（view-tx3c）=====
   方案 B（使用者 2026-10-24「狀態列要清楚表達」「一頁就可呈現，可參考用書簽方式」）：一行標題、部位表（三個週期一列
   一個）、書籤（走勢與績效／交易明細／規則；情境參考是走勢那頁圖下面一行）。紅綠只給多空與損益，3C 線、停損線、出場標記用中性色；
   琥珀只給目前選取（部位表的週期鈕、書籤、區間鈕）、缺日警示與焦點框。桌機夠寬夠高時整頁剛好一個畫面（最後面那段
   @media）：書籤那塊吃剩下的高度、圖再吃書籤裡剩下的，明細與規則在書籤裡自己捲；其他尺寸照常往下捲，圖給固定高度。 */
#view-tx3c { gap: 10px; }
/* 標題一行：名稱、資料時間、重新計算（原本的策略說明搬到「規則」書籤）；放不下時資料時間換到第二行、按鈕留在右上 */
.tx3c-top { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 16px; flex-shrink: 0; padding: 8px 14px;
  background: rgba(15, 25, 39, .9); border: 1px solid var(--border-default); border-radius: var(--r-lg); }
.tx3c-top #tx3c-status { flex: 1 1 22em; min-width: 0; }
.tx3c-top #tx3c-recompute { margin-left: auto; }
@media (max-width: 1100px) { .tx3c-top #tx3c-status { order: 1; flex-basis: 100%; } }
.tx3c-warn { padding: 10px 14px; border-radius: var(--r-lg); background: var(--warn-surface); color: var(--warn-text); border: 1px solid rgba(242, 166, 75, .45); font-size: var(--fs-data); line-height: 1.6; flex-shrink: 0; }
.tx3c-warn[hidden] { display: none; }
#tx3c-status { white-space: normal; }      /* 失敗訊息可能很長：段內也要能換行（.picks-summary 預設 nowrap） */
/* 部位表：三個週期一列一個、欄位上下對齊（取代 ui126 的三張狀態卡與 15／30／60 鈕），點哪一列書籤裡就看哪個週期。
   一格裡的片段各包 .nw，放不下時在片段之間換行、不從數字中間折。三列不要斑馬紋，滑過與選中都只換整列的底色
   （不畫左側彩色線）；選中的週期鈕琥珀實心，同原本的週期鈕。依表的寬度排（container query，不看視窗）：窄時
   （手機）一列變成一張卡，第一行週期與狀態，下面「標籤左、數值右」（標籤是每格的 data-label） */
.tx3c-pos { container: tx3c-pos / inline-size; flex-shrink: 0; overflow: hidden; border: 1px solid var(--border-strong); border-radius: var(--r-lg); background: var(--sunken); box-shadow: var(--shadow-card); }
.tx3c-pos table { font-size: var(--fs-data); }
.tx3c-pos th { position: static; padding: 6px 10px; font-size: var(--fs-xs); }
.tx3c-pos td { padding: 5px 10px; white-space: normal; }
.tx3c-pos tbody tr td { background: transparent; box-shadow: none; }
.tx3c-pos .nw { white-space: nowrap; }
.tx3c-row { cursor: pointer; transition: background-color var(--dur-fast) var(--ease-out); }
.tx3c-row:hover { background: var(--panel); }
.tx3c-row.is-sel { background: var(--card); }
.tx3c-pos td.tx3c-pick-cell { white-space: nowrap; }      /* 蓋過上面 td 的 normal：週期鈕和「盤中」不拆開 */
.tx3c-pick { min-height: 28px; padding: 2px 10px; background: var(--card); color: var(--text); border: 1px solid var(--border); border-radius: var(--r); font: inherit; font-size: var(--fs-sm); line-height: 22px; white-space: nowrap; cursor: pointer; }
.tx3c-pick:hover { border-color: var(--border-strong); color: var(--text-primary); }
.tx3c-pick[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
/* 60 分 K 用了盤中資料（行情網）：滑過看得到算到幾點；藍是「資訊」，不是多空 */
.tx3c-live-tag { display: inline-block; margin-left: 8px; padding: 1px 6px; white-space: nowrap; border: 1px solid rgba(112, 184, 255, .45); border-radius: var(--r); color: var(--info); font-size: var(--fs-xs); cursor: help; }
/* 狀態：白字色塊（白字底下用 upFill／downFill），空手中性 */
.tx3c-pill { display: inline-block; padding: 1px 10px; border-radius: var(--r); font-size: var(--fs-sm); line-height: 20px; white-space: nowrap; }
.tx3c-pill.long { background: var(--up-fill); color: #fff; }
.tx3c-pill.short { background: var(--down-fill); color: #fff; }
.tx3c-pill.flat { background: var(--panel); color: var(--label); box-shadow: inset 0 0 0 1px var(--border); }
/* 同樣的色塊也用在圖下面的情境參考（ui131）：紅綠照下一週平均的正負（賺／賠），沒有樣本用上面的 flat */
.tx3c-pill.gain { background: var(--up-fill); color: #fff; }
.tx3c-pill.loss { background: var(--down-fill); color: #fff; }
/* 每一格（ui130，使用者 2026-10-24「排版很亂，指數我說過要明顯一點」）：先寫指數，放大一級、中等粗細、最亮（數字的
   Num 有 500）；時間、距離、小台跟在後面、小字淡色，放不下時從它前面換行。空手那列的「—」與「等下一個進場訊號」
   淡色，最近出場是狀態色塊底下的一行小字。列高不變（週期鈕 28px＋上下 5px）：色塊 22px＋小字 14px，狀態那格上下
   只留 1px，空手那列才不會比持有的高（ui130 實測：多一行時三列都 +16px、圖矮一截） */
.tx3c-px { color: var(--text-primary); font-size: var(--fs-md); font-weight: 500; line-height: 24px; white-space: nowrap; }
.tx3c-px.up { color: var(--up); }
.tx3c-px.down { color: var(--down); }
.tx3c-unit { margin-left: 3px; color: var(--muted); font-size: var(--fs-xs); }
.tx3c-aux { color: var(--muted); font-size: var(--fs-xs); white-space: nowrap; }
.tx3c-pos td.tx3c-state { padding-block: 1px; }
.tx3c-state-sub { display: block; color: var(--muted); font-size: var(--fs-xs); line-height: 14px; white-space: nowrap; }
.tx3c-pos td.tx3c-na, .tx3c-pos td.tx3c-wait { color: var(--muted); }
/* 表格模式表窄、指數後面的小字換到第二行時，第二行只佔 16px（不寫的話是格子 14px 字的 1.5 倍、21px）：1263 寬實測
   三列各 56→52px、圖 349→361px（ui129 是 53px、359px）。「等下一個進場訊號」照常行高、平均分行（1180 寬時換行，
   不平均的話第二行只剩一個「號」）；卡片模式不收，否則左邊的標籤會比數字高 */
@container tx3c-pos (min-width: 760px) {
  .tx3c-pos td[data-label]:not(.tx3c-wait) { line-height: 16px; }
  .tx3c-pos td.tx3c-wait { text-wrap: balance; }
}
.tx3c-pos-msg { color: var(--muted); }
.tx3c-pos-msg .table-empty { padding: 14px; }
/* 盤中失敗原因（例外名稱、代號、網址）可能沒有空白可斷：一定要能在任何地方換行，否則撐破表格 */
.tx3c-pos tbody tr.tx3c-row-err td { padding: 6px 12px; background: var(--warn-surface); color: var(--warn-text); font-size: var(--fs-xs); line-height: 1.5; overflow-wrap: anywhere; }
.tx3c-pos-note { flex-shrink: 0; margin: -4px 0 0; color: var(--muted); font-size: var(--fs-xs); line-height: 1.5; }
/* 附註裡的「規則」：點了切到規則書籤（app.js 委派在 #tx3c-pos-note）；長得像連結、是按鈕 */
.tx3c-link { padding: 0; margin: 0 1px; background: none; border: 0; color: var(--info); font: inherit; text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }
.tx3c-link:hover { color: var(--text-primary); }
@container tx3c-pos (max-width: 759px) {
  .tx3c-pos table, .tx3c-pos tbody { display: block; }
  .tx3c-pos thead { display: none; }
  .tx3c-pos tbody tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 4px 12px; padding: 10px 12px; }
  .tx3c-pos tbody tr + tr { border-top: 1px solid var(--border-default); }
  .tx3c-pos tbody tr td { display: block; padding: 0; border: 0; }
  .tx3c-pos td[data-label] { grid-column: 1 / -1; text-align: right; }
  .tx3c-pos td[data-label]::before { content: attr(data-label); float: left; margin-right: 12px; color: var(--muted); }
  /* 卡片（空手）：沒有值的那三格不列；「等下一個進場訊號」自己一行、靠左、不掛標籤；狀態色塊與底下的最近出場靠右 */
  .tx3c-pos td.tx3c-na { display: none; }
  .tx3c-pos td.tx3c-wait { text-align: left; }
  .tx3c-pos td.tx3c-wait::before { content: none; }
  .tx3c-pos td.tx3c-state { text-align: right; }
  .tx3c-pos .tx3c-pos-msg, .tx3c-pos tbody tr.tx3c-row-err td { grid-column: 1 / -1; }
  .tx3c-pos tbody tr.tx3c-row-err { padding: 0; }
  /* 全站手機版把表格第一欄凍在左邊（sticky＋::after 不透明底）、整列展開的格子裡的東西也黏左：卡片不橫捲，關掉
     （同 #industry 的寫法；寫在那段後面，同權重後者勝），否則選中那張卡的週期那格會多一塊深色底 */
  .tx3c-pos tbody tr td:first-child { position: static; box-shadow: none; }
  .tx3c-pos tbody tr td:first-child::after { content: none; }
  .tx3c-pos td[colspan] > * { position: static; max-width: none; }
}
/* 書籤列：書籤（全站分段鈕的樣子：有底色的軌道，選中的琥珀實心、深字）＋回測區間，放不下時區間換到下一行；
   切到規則時區間收起來（app.js 的 tx3cTabSelect） */
.tx3c-tabbar { container: tx3c-bar / inline-size; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 12px; flex-shrink: 0; }
.tx3c-tabs { display: grid; grid-auto-flow: column; grid-auto-columns: auto; gap: 4px; padding: 3px; background: var(--sunken); border: 1px solid var(--border); border-radius: var(--r-lg); }
.tx3c-tab { padding: 4px 11px; background: none; border: 0; border-radius: var(--r); color: var(--label); font: inherit; font-size: var(--fs-sm); line-height: 24px; text-align: center; white-space: nowrap; cursor: pointer; transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); }
.tx3c-tab:hover { background: var(--card-hover); color: var(--text-primary); }
.tx3c-tab[aria-selected="true"] { background: var(--accent); color: var(--on-accent); }
.tx3c-tab-count { margin-left: 6px; color: var(--muted); font-size: var(--fs-xs); }
.tx3c-tab[aria-selected="true"] .tx3c-tab-count { color: inherit; }
/* 回測區間（2026-10-21）：快捷鈕＋起訖日期，窄螢幕換行。日期框天生比文字框高（日曆圖示），高度對齊旁邊的 .tf */
/* 和書籤排在同一行：1536 寬的筆電排得下（快捷鈕、日期框比全站的窄一點），再窄就整組換到書籤下面 */
.tx3c-range { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
.tx3c-range[hidden] { display: none; }
.tx3c-range-quick { display: flex; flex-wrap: wrap; gap: 6px; }
.tx3c-range-quick .tf { padding-inline: 9px; }
.tx3c-range-dates { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.tx3c-range-field { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-size: var(--fs-sm); }
/* 9em：日期框是 15px 等寬字，「2026/09/21」加日曆圖示與內距約 124px，再窄最後一位會被圖示蓋住 */
.tx3c-range-field .stock-input { width: 9em; height: 36px; padding: 6px 10px; }
.tx3c-range-sep { color: var(--muted); }
/* 書籤列一行放不下時（ui129，約 1180～1300 寬的筆電）：書籤換成短名、日期框前的「開始」「結束」藏起來（螢幕上不佔位，
   念得出來），書籤和區間才排得進同一行，圖多拿一行的高度。手機（書籤列不到 600px）照下面那段，標籤移到框上面 */
@container tx3c-bar (max-width: 1099px) {
  .tx3c-tabbar .picks-label-full { display: none; }
  .tx3c-tabbar .picks-label-short { display: inline; }
}
@container tx3c-bar (min-width: 600px) and (max-width: 979px) {
  .tx3c-range-field > span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
}
/* 書籤的面板：三個都在頁面上，只有選中的那個顯示。走勢與績效＝成績單＋圖＋圖下面的情境參考：書籤那塊寬的時候成績單
   放在圖的右邊（疊在上面的話，一個畫面裡圖只剩兩百多 px 高），窄的時候成績單在上、圖在下；跟著面板寬度，不跟視窗。
   換區間要等後端時，舊的圖與數字淡掉留著（is-stale，app.js 的 tx3cStale），不閃成空白 */
.tx3c-panels { container: tx3c-panels / inline-size; display: flex; flex-direction: column; min-height: 0; }
.tx3c-panel { display: flex; flex-direction: column; gap: 8px; min-height: 0; transition: opacity var(--dur-fast) var(--ease-out); }
.tx3c-panel[hidden] { display: none; }
.tx3c-panel.is-stale { opacity: .55; }
.tx3c-chart-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; }
/* 成績單（ui129，使用者 2026-10-24 選「成績單欄」，取代六個一樣大的方框）：一張卡、一個主角數字（淨損益，紅綠照
   損益）；標題列是週期、區間、交易日與筆數；同期買進持有緊跟在主角下面、上下細線隔開；PF、勝率、逐筆 MDD 是一排三格
   小字、不著色；樣本少時的提醒用警示色；樣本說明釘在卡片底部。卡片寬的時候（成績單在圖上面、佔整行）主角在左、
   其他在右 */
.tx3c-card { container: tx3c-card / inline-size; display: flex; flex-direction: column; min-width: 0; min-height: 0; padding: 12px 14px; background: var(--card); border: 1px solid var(--border); border-radius: var(--r-lg); }
.tx3c-sc { display: flex; flex-direction: column; }
.tx3c-sc .table-empty { padding: 18px 0; }
.tx3c-sc-head { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0 10px; color: var(--muted); font-size: var(--fs-xs); line-height: 1.5; }
.tx3c-sc-hero { margin-top: 8px; }
.tx3c-sc-k-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.tx3c-sc-k { color: var(--label); font-size: var(--fs-sm); }
.tx3c-sc-cnt { color: var(--muted); font-size: var(--fs-xs); white-space: nowrap; }
.tx3c-sc-big { margin-top: 2px; color: var(--text-primary); font-size: var(--fs-xl); font-weight: 500; line-height: 1.15; white-space: nowrap; }
.tx3c-sc-big.up { color: var(--up); }
.tx3c-sc-big.down { color: var(--down); }
.tx3c-sc-unit { margin-left: 4px; color: var(--muted); font-size: var(--fs-xs); font-weight: 400; }
.tx3c-sc-sub { margin-top: 2px; color: var(--muted); font-size: var(--fs-xs); }
.tx3c-sc-none { margin-top: 4px; color: var(--muted); font-size: var(--fs-sm); }
.tx3c-sc-bh { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-top: 8px; padding: 6px 0; border-block: 1px solid var(--border); }
.tx3c-sc-bh-v { font-size: var(--fs-md); white-space: nowrap; }
.tx3c-sc-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px 12px; margin: 8px 0 0; }
.tx3c-sc-grid dt { color: var(--muted); font-size: var(--fs-xs); }
.tx3c-sc-grid dd { margin: 1px 0 0; color: var(--text-primary); font-size: var(--fs-md); white-space: nowrap; }
.tx3c-sc-grid dt[title] { cursor: help; }
.tx3c-sc-warn { margin: 6px 0 0; color: var(--warn-text); font-size: var(--fs-xs); line-height: 1.5; }
/* 細項：卡片在圖的右邊、又夠高時才出現（下面那段 container query），矮的時候不擠 */
.tx3c-sc-more { display: none; grid-template-columns: minmax(0, 1fr) auto; gap: 7px 12px; margin: 12px 0 0; padding-top: 10px; border-top: 1px solid var(--border); }
.tx3c-sc-more dt { color: var(--muted); font-size: var(--fs-xs); }
.tx3c-sc-more dd { margin: 0; color: var(--text-primary); font-size: var(--fs-data); text-align: right; white-space: nowrap; }
@container tx3c-card (min-height: 500px) { .tx3c-sc-more { display: grid; } }
.tx3c-card .tx3c-sample { margin-top: auto; padding-top: 10px; }
@container tx3c-card (min-width: 600px) {
  .tx3c-sc { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr); column-gap: 28px; }
  .tx3c-sc-head { grid-column: 1 / -1; }
  .tx3c-sc-hero { grid-column: 1; grid-row: 2 / span 3; }
  .tx3c-sc > :not(.tx3c-sc-head, .tx3c-sc-hero) { grid-column: 2; }
}
/* 高度一律由外面給（固定高或格線的那一列）：contain:size 讓畫布不會反過來撐高容器（同個股頁 #stock-chart） */
.tx3c-chart { contain: size; flex: 0 0 auto; height: 440px; min-height: 0; overflow: hidden; }
/* 報價列（ui129，使用者選的「圖左上一行報價」）：一行，平常是最新一根、游標在哪一根就是那一根；半透明底（同下窗格的
   說明），線經過時字仍清楚。收盤紅綠照漲跌，其他字中性；3C、停損同時是那兩條線的圖例。放不下時 app.js 依序拿掉項目
   （tx3cPaintQuote），仍放不下才截掉尾巴。max-width 讓開右邊的價格軸（五位數的台指實測 58px，加左邊 8px 與間隔）；
   它蓋住的範圍，K 棒與進出場標籤會讓開（setObstacle） */
.tx3c-quote { position: absolute; top: 6px; left: 8px; z-index: 3; pointer-events: none; display: flex; align-items: baseline; gap: 10px; max-width: calc(100% - 76px); overflow: hidden; white-space: nowrap; padding: 3px 8px; border-radius: 6px; background: color-mix(in srgb, var(--bg-panel) 78%, transparent); color: var(--text-secondary); font-size: var(--fs-xs); line-height: 20px; }
.tx3c-quote > * { flex: 0 0 auto; }
.tx3c-quote b { font-weight: 500; color: var(--text-primary); }
.tx3c-q-px { font-size: var(--fs-md); }
.tx3c-quote .up { color: var(--up); }
.tx3c-quote .down { color: var(--down); }
.tx3c-quote .dim, .tx3c-q-when { color: var(--muted); }
.tx3c-quote .tx3c-live-tag { margin-left: 0; line-height: 16px; }     /* 不撐高這一行（它蓋住的高度，K 棒都要讓） */
.tx3c-key { display: inline-block; width: 12px; height: 2px; margin-right: 5px; border-radius: 1px; vertical-align: middle; transform: translateY(-2px); }
.tx3c-key.dashed { height: 0; border-top: 2px dashed; border-radius: 0; }
.tx3c-key.dotted { height: 0; border-top: 2px dotted; border-radius: 0; }
/* 下窗格左上角（ui132，使用者 2026-10-25 選「同一格切換，預設單次」）：「單次｜累積」切換鈕＋說明＋圖例，掛在下窗格
   自己那一格裡（tx3cInitChart）。半透明底同報價列；只有按鈕吃滑鼠，其餘讓給圖（游標、拖曳照常）。按鈕的樣子同書籤的
   分段鈕（凹下去的底、選中＝琥珀），小一號：下窗格很矮（1536×735 約 68px），整條和 ui130 的說明一樣約 20px 高，點擊
   範圍用 ::after 往上下撐，不撐高這一行；焦點框往內畫（外面那圈會被 overflow 切掉）。放不下時 app.js 從後面拿掉圖例與
   說明（tx3cPaintPnlCap） */
.tx3c-pnl-cap { position: absolute; top: 2px; left: 4px; z-index: 3; pointer-events: none; display: flex; align-items: center; gap: 10px; max-width: calc(100% - 12px); overflow: hidden; white-space: nowrap; padding-right: 8px; border-radius: 6px; background: color-mix(in srgb, var(--bg-panel) 78%, transparent); color: var(--label); font-size: var(--fs-xs); line-height: 16px; }
.tx3c-pnl-seg { flex: 0 0 auto; display: inline-flex; gap: 2px; padding: 1px; background: var(--sunken); border: 1px solid var(--border); border-radius: 5px; pointer-events: auto; }
.tx3c-pnl-seg button { position: relative; padding: 0 8px; background: none; border: 0; border-radius: 4px; color: var(--label); font: inherit; line-height: 16px; cursor: pointer; transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); }
.tx3c-pnl-seg button::after { content: ""; position: absolute; inset: -5px -1px; }
.tx3c-pnl-seg button:hover { background: var(--card-hover); color: var(--text-primary); }
.tx3c-pnl-seg button[aria-pressed="true"] { background: var(--accent); color: var(--on-accent); }
.tx3c-pnl-seg button:focus-visible { outline-offset: -2px; }
.tx3c-pnl-desc { display: flex; gap: 10px; }
.tx3c-pnl-desc > span { display: inline-flex; align-items: center; gap: 4px; }
.tx3c-pnl-desc .tx3c-key { margin-right: 0; transform: none; }
.tx3c-pk-tri { width: 0; height: 0; border: 4px solid transparent; }
.tx3c-pk-tri.up { border-top-width: 0; border-bottom: 7px solid var(--up); }
.tx3c-pk-tri.down { border-bottom-width: 0; border-top: 7px solid var(--down); }
.tx3c-pk-exit { width: 5px; height: 11px; border-radius: 1px; background: var(--label); }
.tx3c-pk-bars { display: inline-flex; gap: 2px; height: 12px; }
.tx3c-pk-bars > span { width: 5px; height: 6px; }
.tx3c-pk-bars > span:first-child { background: var(--up-fill); }
.tx3c-pk-bars > span:last-child { align-self: flex-end; background: var(--down-fill); }
/* 情境參考（ui131，使用者 2026-10-25「寫白話一些，呈現方式更簡單、好理解」「不想放書簽內，要跟走勢那頁一起放」）：
   圖下面一行。開頭與「不看情況」淡色小字，因子是部位表那種色塊（上面的 .tx3c-pill.gain／loss）；細節在滑過的提示裡
   （title），滑得到的換成問號游標。手機放不下時換行；還沒有資料時也先佔好一行，資料到了圖不會再跳一次。放在格線的
   哪一格見下面 tx3c-panels 那兩段 container query */
.tx3c-cx { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; min-width: 0; min-height: 22px; color: var(--muted); font-size: var(--fs-xs); line-height: 22px; }
.tx3c-cx [title] { cursor: help; }
/* 「不看情況」和前面的色塊多隔一點：空白加在最後一個色塊的右邊，換行時落在上一行行尾，下一行不會縮排或掛一條線 */
.tx3c-cx .tx3c-pill:has(+ .tx3c-cx-base) { margin-right: 8px; }
.tx3c-cx-base { white-space: nowrap; }
.tx3c-cx-base .num { font-size: var(--fs-sm); line-height: 1; }     /* 大一號的數字不撐高這一行（不寫是 23.3px） */
.tx3c-sample { flex-shrink: 0; margin: 0; color: var(--muted); font-size: var(--fs-xs); line-height: 1.5; }
#tx3c-trades td { white-space: nowrap; }
.tx3c-rules-box { flex-shrink: 0; padding: 12px 16px; background: var(--card); border: 1px solid var(--border); border-radius: var(--r-lg); line-height: 1.7; }
.tx3c-about { margin: 0 0 10px; color: var(--label); }
.tx3c-rules-box p { margin: 0; }
.tx3c-rules-box ol { margin: 4px 0 12px; padding-left: 1.5em; }
.tx3c-rules-box ol:last-child { margin-bottom: 0; }
.tx3c-rules-h { color: var(--text-primary); }
/* 一個畫面：視窗要寬到統計放得進圖的右邊（書籤那塊 ≥820px，視窗 1180px 以上都夠）、又夠高，書籤那塊吃剩下的高度、
   圖再吃書籤裡剩下的；明細、規則在書籤裡自己捲（明細的表頭黏在上面）。整頁不寫死 height: 100%，靠 .view
   原本的 min-height: 100% 撐滿：放得下時剛好一個畫面；放不下（1280×720 這種部位表、附註、書籤列都換行的）時書籤
   那塊停在 260px、整頁照常變長往下捲，底部留白還在（寫死的話內容會溢出 .view、底部留白被吃掉）。條件以外（平板、
   矮視窗、手機）照常往下捲，圖用上面的固定高度 */
@media (min-width: 1180px) and (min-height: 640px) {
  #view-tx3c.active { padding-top: 16px; padding-bottom: 16px; }
  .tx3c-panels { flex: 1 1 0; min-height: 260px; }
  .tx3c-panel { flex: 1 1 0; }
  .tx3c-panel[data-tab="rules"] { overflow-y: auto; }
  .tx3c-chart-row { flex: 1 1 0; min-height: 0; grid-template-rows: auto minmax(0, 1fr) auto; }
  .tx3c-chart { height: auto; min-height: 240px; }     /* 成績單疊在上面時（書籤那塊比下面的門檻窄）圖也不會被壓扁 */
  #tx3c-trades.table-wrap { flex: 0 1 auto; min-height: 0; }
}
/* 寫在一個畫面那段後面：成績單在右邊時只有一列，要蓋過那段「成績單在上、圖在下」的兩列。門檻要比「一個畫面」那段
   的視窗寬度（1180px）對應的書籤寬度低，否則 1180～1190 寬時成績單疊在圖上面、圖被擠扁（ui129 修） */
@container tx3c-panels (min-width: 820px) {
  .tx3c-chart-row { grid-template-columns: minmax(0, 1fr) 19em; grid-template-rows: minmax(0, 1fr) auto; }
  /* 圖跟成績單一樣高、吃那一列剩下的（書籤那塊最矮 260px，圖最矮也有 228px）；不收掉一個畫面那段的 240px 的話，
     1180×700 時圖比那一列高 3px、底邊和成績單對不齊 */
  .tx3c-chart-row > .tx3c-chart { grid-column: 1; grid-row: 1; min-height: 0; }
  /* size container：高度是那一列給的（跟圖一樣高），卡片夠高時才列細項（上面的 .tx3c-sc-more） */
  .tx3c-chart-row > .tx3c-card { grid-column: 2; grid-row: 1; overflow-y: auto; container-type: size; }
  /* 情境參考：圖那一欄放不下一行時（1180～1366 寬的筆電）橫跨到成績單底下，不擠成兩行讓圖再矮一截 */
  .tx3c-chart-row > .tx3c-cx { grid-column: 1 / -1; grid-row: 2; }
}
/* 圖那一欄放得下一行時（1536 寬以上）只在圖的下面，成績單照舊佔滿整個高度（矮的視窗才不會被擠出捲軸） */
@container tx3c-panels (min-width: 1180px) {
  .tx3c-chart-row > .tx3c-card { grid-row: 1 / -1; }
  .tx3c-chart-row > .tx3c-cx { grid-column: 1; }
}
@media (max-width: 600px) {
  .tx3c-chart { height: 400px; }
  .tx3c-rules-box { padding: 10px 12px; }
  .tx3c-tabs { flex: 1 1 100%; grid-auto-columns: minmax(0, 1fr); }     /* 手機：三個書籤等寬、佔滿一行 */
  .tx3c-tab { padding: 4px 6px; }
  /* 手機：開始、結束兩欄並排，標籤移到框上面（同一行放不下「開始 [日期] ～ 結束 [日期]」，折行時「～」會掛在第一行尾巴） */
  .tx3c-range-dates { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: end; width: 100%; }
  .tx3c-range-field { flex-direction: column; align-items: stretch; gap: 2px; font-size: var(--fs-xs); }
  .tx3c-range-field .stock-input { width: 100%; height: 40px; }   /* 全站手機版 .tf 是 40px */
  .tx3c-range-sep { padding-bottom: 9px; }
}
