/* SN 評價工具 — 條款書（term sheet）視覺系統
   tokens：冷調帳票紙＋墨藍結構；行情紅（KO 向上出場）／沉青（KI 下檔保護）
   type：中文 微軟正黑；價格數字一律 Consolas 等寬 */
:root {
  --paper: #F3F5F2;
  --card: #FFFFFF;
  --ink: #17263B;
  --spine: #29527E;
  --spine-soft: #DCE4EC;
  --meta: #5F6F80;
  --line: #DBE0DA;
  --ko: #C8342B;
  --ki: #1E6F63;
  --warn-bg: #FBEFEA;
  --warn-line: #E4BDB3;
  --verify-bg: #FBF3D3;
  --verify-line: #E3C766;
  --verify-ink: #7A5A0C;
  --mono: Consolas, "Cascadia Mono", monospace;
  /* 導覽外殼（spec 2026-11-08 §2）：青色＝導覽的「選中／新增」；側欄文字與選中底色 */
  --accent-teal: #16707A;
  --side-fg: #A9BBD1;
  --side-sel: #24364B;
  --side-line: #2C405A;
  --teal-on-ink: #5BB8C0;
  --bottomnav-h: 56px;
  /* 持倉監控（spec 2026-11-08 §3）：狀態三級＝底色＋文字（不靠顏色單獨表達）；
     障礙尺：粉色＝KI 以下、藍線＝KO、墨色點＝最新收盤。文字色對底色皆 ≥ 4.5:1。 */
  --st-near: #8A5606; --st-near-bg: #FCF0DA; --st-near-line: #EBCF9A;
  --st-watch: #1F5F99; --st-watch-bg: #E6EDF5;
  --st-safe: #1E6F63; --st-safe-bg: #E1F1EC;
  --st-neutral: #4E5B69; --st-neutral-bg: #ECEFF2;
  --gauge-track: #EEF1F4; --gauge-ki: #F6D9D3; --gauge-ki-edge: #D59A8E;
  --gauge-ko: #1F5F99;
  /* 障礙尺的兩條線：KI 線＝實線（粉色區邊緣，比區塊深一階的陶土色）、執行價線＝虛線（深灰）。
     兩者還有線型之分，重合時不靠顏色單獨辨認。 */
  --gauge-ki-line: #B5604E; --gauge-strike: #4B5563;
  /* 紅色政策唯一的標的例外（使用者 2026-11-09）：低於 KI（沒有 KI 就低於執行價）的標的
     代號與百分比。政策本文在 docs/superpowers/specs/2026-11-08-ui-redesign-sn-design.md §3。 */
  --below-barrier: var(--ko);
  /* 風險分數四段色（使用者 2026-11-09）：[0,25] 綠、[26,50] 黃、[51,70] 橙、[71,100] 紅。
     紅色政策的第二條例外（政策本文同上 §3）；字色對各自底色 ≥ 4.5:1，黃段用深一階的字色。 */
  --score-low: #1E6F4A; --score-low-bg: #E3F2EA;
  --score-mid: #7A5C00; --score-mid-bg: #FBF1C7;
  --score-high: #A44A06; --score-high-bg: #FCE6D2;
  --score-max: #B42318; --score-max-bg: #FBE3E0;

  /* 視窗單位要先除掉縮放。html 上的 zoom 會把整棵樹放大，但 vw／vh 仍解析成
     **未縮放**的視窗 px（實測 zoom=1.5、視窗 1400：calc(100vw - 48px) 算出
     1322px，渲染成 1983px，比視窗寬 613px——候選券表格因此左右都溢出到視窗
     外，且左側捲不回來；terms 對話框的 max-height 同理超出畫面底部）。
     --z 由 base.html 的 <style>html{...}</style> 隨 zoom 一起注入。
     **fallback 寫在 var() 裡、不在這裡給 --z 預設值**：這裡是 :root
     （特異度 0,1,0），會蓋過 base.html 的 html（0,0,1），縮放注入就永遠
     生效不了——實測過，--z 讀回來一直是 1。var(--z, 1) 沒有這個競爭，
     模板沒注入時（例如單獨開啟 CSS 的測試頁）自動退回 1。
     zoom=1 時 calc(100vw / 1) 就是 100vw，未縮放的版面逐像素不變。 */
  --vw: calc(100vw / var(--z, 1));
  --vh: calc(100vh / var(--z, 1));
}
/* 手機上 100vh 是「網址列收起後」的最大高度：網址列還在時，吃 --vh 的對話框
   max-height 會比可視區高出一條網址列，底部被切掉。dvh 跟著網址列伸縮。
   放 @supports 裡：自訂屬性不在宣告時驗證，不支援 dvh 的瀏覽器會在使用處
   才變成無效值，連 vh 的 fallback 都拿不到。 */
@supports (height: 100dvh) {
  :root { --vh: calc(100dvh / var(--z, 1)); }
}

/* ---- 觸控平台層（手機上「像網站而不像 app」的幾個破綻）----
   - tap highlight：iOS／Android 點任何有 click handler 的元素都會閃一塊灰藍，
     蓋掉我們自己的 :active 回饋。
   - text-size-adjust：iOS 橫放時會自行放大字級，表格欄寬跟著亂。
   - overscroll contain：拉過頂端會觸發下拉重新整理——表單頁的輸入、進行中
     job 的追蹤都在記憶體裡，一拉就沒了。contain 只擋「傳出去」的效果
     （重新整理、上一頁），保留本身的回彈。 */
html {
  -webkit-tap-highlight-color: transparent;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  overscroll-behavior-y: contain;
}
/* manipulation：告訴瀏覽器這些元素不會被雙擊縮放，click 不必等 300ms 看
   有沒有第二下。只關掉雙擊縮放，雙指縮放照常（無障礙需求）。 */
a, button, summary, label, select, [role="button"],
input[type="checkbox"], input[type="radio"], input[type="file"] {
  touch-action: manipulation;
}
/* 長按控制項會選取按鈕字或跳出連結的分享選單；控制項不是內容，不該可選。
   一般連結與內文仍可選取（使用者會複製票券代號、錯誤訊息）。 */
.btn, .nav-item, .nav-add, .bn-item, .more-item, .crumb a, summary, .u-del, .toast-x {
  -webkit-user-select: none; user-select: none;
  -webkit-touch-callout: none;
}
/* iOS Safari 聚焦字級 < 16px 的輸入框會整頁放大、離開後不縮回。以觸控能力
   而不是寬度判斷：平板一樣會放大。不用 maximum-scale=1——那會順便禁止
   使用者自己縮放。!important：media query 不加特異度，`table.sel-rows input`
   （13px）、`.sel-mkt-line input`（12px）這類元件規則都會蓋過一條裸的
   `input`；這是平台底線，必須壓過所有元件字級。表格內的最小欄寬以 ch／em
   訂（見盤前比價段），會隨字級一起放大，不會被切字。 */
@media (pointer: coarse) {
  input, select, textarea { font-size: 16px !important; }
}

* { box-sizing: border-box; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: "Microsoft JhengHei", "PingFang TC", "Noto Sans TC", sans-serif;
  font-size: 14px;
  line-height: 1.55;
}
a { color: var(--spine); }
.mono, input.mono { font-family: var(--mono); }

.wrap { max-width: 1180px; margin: 0 auto; padding: 30px 24px 72px; }

/* ---- 頁首 ---- */
.eyebrow {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .18em;
  color: var(--meta); margin: 0 0 2px; text-transform: uppercase;
}
.page-head { margin: 0 0 20px; }
.page-head h1 { font-size: 26px; margin: 0; letter-spacing: .01em; }
.crumb { margin: 0 0 10px; font-size: 13px; }
.section-label { font-size: 12px; color: var(--meta); letter-spacing: .12em; margin: 26px 0 8px; text-transform: uppercase; font-family: var(--mono); }
/* 頁面層級的說明段落限行長（spec 2026-11-08 §7）：寬螢幕上一行拉到上千 px 讀不下去。
   只選頁面層級（.wrap 的直接子項、頁面卡片、使用說明）；表格格子與表單格線裡的提示
   不在此列。.lede 與 p.note 在各自的規則裡寫同一個值。 */
.page-head p,
.wrap > p.hint,
.wrap > .card > p.hint,
.wrap > details > p.hint,
.guide-body > p.hint { max-width: 40em; }

/* ---- 票券列表 ---- */
/* min(300px, 100%)：版面寬不足 300px（320px 手機扣掉內距、或手機縮放到上限——
   80%～螢幕短邊 ÷ 320，可用寬最少 320px）時卡片縮到容器寬，而不是撐出視窗外造成整頁橫捲。 */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); gap: 18px; }
/* 票券卡片多了籃子與條款兩行，最小寬放到 320 才不會把四檔籃子擠成兩行。 */
.ticket-grid { grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr)); }
.card {
  background: var(--card); border: 1px solid var(--line); border-top: 3px solid var(--spine);
  padding: 18px 20px;
}
.card h2 { font-size: 17px; margin: 0 0 4px; }
.card h2 a { text-decoration: none; }
.meta { color: var(--meta); font-size: 12px; margin: 0 0 12px; }
.empty-state { padding: 56px 0; text-align: center; color: var(--meta); }
.empty-state p { margin: 4px 0 16px; }

/* ---- 生成中票券卡片 ---- */
.active-jobs { margin: 0 0 22px; }
.active-jobs .eyebrow { margin-bottom: 8px; }
.gen-card { border-left: 3px solid var(--spine); }
.gen-status { color: var(--spine); }

/* ---- 按鈕 ---- */
.btn {
  display: inline-block; border: 1px solid var(--spine); background: var(--card); color: var(--spine);
  padding: 8px 14px; font-size: 13px; cursor: pointer;
  text-decoration: none; margin: 2px 8px 2px 0; font-family: inherit;
}
/* 全檔的 :hover 一律包在 (hover: hover) and (pointer: fine) 裡：觸控沒有
   hover，瀏覽器會把第一下點擊當成 hover 並一直掛著——按過的鈕維持抬起＋
   陰影、表格列維持反白，直到點別處。觸控的按下回饋改由 :active 提供。 */
@media (hover: hover) and (pointer: fine) {
  .btn:hover { background: #EAF0F6; }
}
.btn:focus-visible, input:focus-visible, select:focus-visible, summary:focus-visible, a:focus-visible {
  outline: 2px solid var(--spine); outline-offset: 2px;
}
.btn.primary { background: var(--spine); color: #fff; }
@media (hover: hover) and (pointer: fine) {
  .btn.primary:hover { background: #1E3F63; }
}
.btn.ghost { border-color: #C3CCC1; color: #46566A; }
@media (hover: hover) and (pointer: fine) {
  .btn.ghost:hover { background: #ECEFEA; }
}
.btn.small { padding: 5px 10px; font-size: 12px; }
.big-btn { font-size: 15px; padding: 11px 20px; }

/* ---- 使用說明（填表資料來源指引）---- */
.guide {
  background: var(--card); border: 1px solid var(--line); border-left: 4px solid var(--spine);
  margin: 0 0 18px;
}
.guide summary {
  cursor: pointer; padding: 12px 18px; font-size: 13.5px; font-weight: 600; color: var(--spine);
}
.guide-eyebrow {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .18em; color: var(--meta);
  margin-right: 12px;
}
.guide-body { padding: 2px 18px 14px; border-top: 1px solid var(--line); }
table.guide-table { border-collapse: collapse; font-size: 12.5px; line-height: 1.6; }
.guide-table th {
  text-align: left; white-space: nowrap; color: #46566A; padding: 7px 18px 7px 0;
  vertical-align: top; font-weight: 600;
}
.guide-table td { padding: 7px 0; color: var(--ink); }
.guide-table tr + tr th, .guide-table tr + tr td { border-top: 1px solid var(--line); }

/* ---- 表單版面：左條款、右摘要 ---- */
.form-layout { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 22px; align-items: start; }
/* minmax(0, 1fr) 而不是 1fr：1fr 的下限是內容的 min-content，標的表／市場
   參數表的最小寬（手機上約 455px）會把整欄、連同整頁撐寬到視窗外——表格
   自己的橫向捲動就失去作用（375px 寬實測整頁可左右拖 92px）。 */
/* 單欄的分水嶺看**內容區**寬（.page 容器＝視窗扣掉側欄、再除以縮放），不看視窗：
   media query 看不到側欄與 html{zoom}。1080 沿用舊門檻（舊版沒有側欄，內容區≈視窗）。 */
@container page (max-width: 1079.98px) { .form-layout { grid-template-columns: minmax(0, 1fr); } }

form.termsheet { background: var(--card); border: 1px solid var(--line); padding: 4px 26px 22px; }

/* 條款節：左側 spine 章名（壹貳參…）＝條款書語彙 */
.clause { border: none; border-top: 1px solid var(--line); margin: 0; padding: 16px 0 10px; }
.clause:first-of-type { border-top: none; }
.clause-head { display: flex; align-items: baseline; gap: 10px; margin: 0 0 6px; }
.clause-no {
  font-size: 13px; color: #fff; background: var(--spine);
  width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; flex: none; transform: translateY(3px);
}
.clause-head h2 { font-size: 15px; margin: 0; letter-spacing: .02em; }
/* 步驟（spec 2026-11-08 §4）：標題列整條是切換鈕（app.js initFormSteps）。完成度靠右；
   ▾ 指出可收合。收合＝body 的 hidden（欄位留在表單裡）。 */
.clause-head h2.clause-title { flex: 1 1 auto; min-width: 0; }
.clause-toggle {
  display: flex; align-items: center; gap: 10px; width: 100%; min-height: 44px;
  margin: 0; padding: 0; border: 0; background: none; color: inherit;
  font: inherit; letter-spacing: inherit; text-align: left; cursor: pointer;
}
.clause-toggle .clause-no { transform: none; }
.clause-name { min-width: 0; }
.step-status { margin-left: auto; font-size: 12px; font-weight: 400; color: var(--meta); white-space: nowrap; }
.step-status.is-done { color: var(--st-safe); font-weight: 600; }
.step-status.is-missing { color: var(--st-near); font-weight: 600; }
.clause-toggle::after {
  content: ""; flex: none; width: 7px; height: 7px; margin: 0 4px 3px 0;
  border-right: 2px solid var(--meta); border-bottom: 2px solid var(--meta);
  transform: rotate(45deg); transition: transform .15s ease;
}
.clause-toggle[aria-expanded="false"]::after { transform: rotate(-45deg); margin-bottom: 0; }
.clause-toggle:focus-visible { outline: 2px solid var(--spine); outline-offset: 2px; }
.clause-body[hidden] { display: none; }

/* 「預設」標記：模板預設值旁的小標籤（app.js refreshDefaultTags 決定顯示） */
.dflt-field { display: flex; align-items: center; gap: 6px; min-width: 0; }
.dflt-field input { flex: 1 1 auto; }
.dflt-tag {
  flex: none; font-style: normal; font-size: 10.5px; color: var(--meta); white-space: nowrap;
  background: var(--paper); border: 1px solid var(--line); border-radius: 4px; padding: 1px 5px;
}
.dflt-tag[hidden] { display: none; }
.clause-foot { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }

.row { display: flex; flex-wrap: wrap; gap: 14px; }
label { display: flex; flex-direction: column; font-size: 12px; color: #46566A; gap: 4px; margin: 6px 0; }
label.check { flex-direction: row; align-items: center; gap: 8px; }
input, select {
  font-family: inherit; font-size: 14px; padding: 7px 9px;
  border: 1px solid #C4CDC3; background: #FDFEFC; min-width: 150px;
  border-radius: 0;
}
/* placeholder＝範例（「例：…」）或留白的意思，用提示色（與 .hint 同色）；opacity:1 是因為
   Firefox 會再把 placeholder 調淡一次。 */
input::placeholder { color: var(--meta); opacity: 1; }
input[type=checkbox] { min-width: 0; }
/* 自動推算的唯讀輸出：與 input 同尺寸對齊，但底色與虛線邊框明示「不可編輯」 */
output.derived {
  font-size: 14px; padding: 7px 9px; min-width: 150px;
  border: 1px dashed #C4CDC3; background: #F3F5F1; color: #46566A;
}
output.derived.bad { border-color: #B4483C; color: #B4483C; }
label > small.hint { margin: 0; }
.hint { font-size: 12px; color: var(--meta); margin: 8px 0 2px; }

/* 條款價位三欄：與價格梯同色鏈 */
.lv-ko input { border-left: 3px solid var(--ko); }
.lv-strike input { border-left: 3px solid var(--spine); }
.lv-ki input { border-left: 3px solid var(--ki); }
.lv-kistyle select { border-left: 3px solid var(--ki); }
.ki-toggle { align-self: flex-end; margin-bottom: 12px; color: var(--ki); font-weight: 600; }
.ladder.no-ki .ladder-rung.ki { display: none; }
/* DACN 沒有 KI：價格梯拿掉 KI 橫檔（app.js::applyProductMode 掛 .dacn） */
.ladder.dacn .ladder-rung.ki { display: none; }
.product-switch { border: 0; padding: 0; margin: 0 0 12px; display: flex; gap: 18px; align-items: center; }
.product-switch legend { float: left; margin-right: 12px; font-weight: 600; }
/* 通用 input 的 min-width:150px 會把 radio 撐寬（同 checkbox 的處理） */
.product-switch input[type=radio] { min-width: 0; }

/* 標的表 */
table#underlyings { border-collapse: collapse; width: 100%; margin: 4px 0 8px; }
#underlyings th {
  text-align: left; font-size: 11px; color: var(--meta); padding: 4px 6px;
  border-bottom: 1px solid var(--line); font-weight: 600;
}
#underlyings td { padding: 3px 6px 3px 0; }
#underlyings input { min-width: 100px; width: 100%; }
.u-del { border: none; background: none; color: var(--ko); cursor: pointer; font-size: 14px; padding: 4px 6px; }

.advanced { margin-top: 12px; border-top: 1px solid var(--line); }
.advanced summary { cursor: pointer; font-size: 13px; color: var(--spine); padding: 12px 0 6px; }

/* ---- 伍 市場參數（兩段式表單，2026-08-20）----
   表格樣式與 #underlyings 共用同一組值，避免兩段在視覺上像兩個不同的東西。
   舊的 .skew-col 收放規則已隨進階區的 skew 開關一起移除——skew 現在是這一
   段的常駐欄位。 */
table#market-rows { border-collapse: collapse; width: 100%; margin: 4px 0 8px; }
#market-rows th {
  text-align: left; font-size: 11px; color: var(--meta); padding: 4px 6px;
  border-bottom: 1px solid var(--line); font-weight: 600;
}
#market-rows td { padding: 3px 6px 3px 0; }
/* 第五欄（52 週 HV 區間）加入後首欄被擠到逐字直排；代碼欄只放短文字，不換行。 */
#market-rows th:first-child { white-space: nowrap; }
#market-rows input { min-width: 100px; width: 100%; }
/* skew 格的 placeholder「留白＝標準評價」是 7 個全形字（1 字＝1em）：100px 的
   min-width 在 zoom 1.5 起欄被擠到底時只剩約 80px 內容寬，句尾被切掉。給
   7em ＋ 36px（padding 18 ＋ 邊框 2 ＋ number spinner 16）。specificity 高於手機
   斷點的 `#market-rows input { min-width: calc(9ch + 36px) }`。 */
#market-rows input[name=u_skew] { min-width: calc(7em + 36px); }
/* 52 週 HV 區間兩格下方的唯讀 rank（app.js 即時算、不送出）。空字串時整個
   收起來，沒有 HV 的列高度不變；放 block 才不會把表格撐寬到卡片外。 */
.hv-rank { display: block; font-size: 11px; line-height: 1.3; color: var(--meta); font-family: var(--mono); padding: 2px 0 0 2px; }
.hv-rank:empty { display: none; }
.m-ticker { font-size: 13px; color: var(--spine); }

/* ---- 值的來源標記（左邊界三色）與偏離警示 ----
   使用者裁決 2026-08-20：自動＝原色、手動＝綠、與自動值差距過大＝紅＋驚嘆號。

   三者是**同一個維度**（這個值從哪來、可不可信），所以共用左邊界這一個
   視覺通道；is-warn（黃底，「抓不到、請你處理」）是另一個維度，可同時出現。

   驚嘆號用 background-image 而非 ::before：這些是 <input>，偽元素放不進去，
   而包一層 wrapper 會動到動態生成列的結構。padding-left 讓數字讓開位置。 */
input[data-fill="auto"] {
  border-left: 3px solid var(--verify-line);
}
input[data-fill="manual"], input[data-fill="imported"] {
  border-left: 3px solid #2E7D52;   /* 綠：人填的 */
}
input[data-deviates="true"] {
  border-left-color: #B4483C;        /* 紅：偏離自動值超過門檻 */
  padding-left: 18px;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='14'%3E%3Ctext x='0' y='11' font-family='sans-serif' font-size='12' font-weight='700' fill='%23B4483C'%3E!%3C/text%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: 5px center;
}

.hint.bad { color: #8C2B1E; }
/* 狀態列分兩色（notice.js 的 paintSeverity）：.bad＝程式／連線問題（紅），
   .warn＝有逃生門的降級（部分未取得、取舊資料、請手填幾格）。沿用既有色票，
   不新造色。 */
.hint.warn { color: var(--verify-ink); }
/* 手機自動帶入的三格嘗試條（app.js sayAuto）：進行中藍、失敗琥珀、成功綠；紅只給價格上漲 */
.atts { display: flex; gap: 4px; margin: 6px 0 0; max-width: 132px; }
.att { flex: 1 1 0; height: 4px; border-radius: 2px; background: var(--line); }
.att.r { background: var(--st-watch); }
.att.f { background: var(--st-near); }
.att.ok { background: var(--st-safe); }
#mkt-auto-retry { margin: 4px 0 6px; }

/* 行內「進行中」orb（G2 球面點陣，orb.js 產幾何）：放在「查詢中…」前面當心跳。
   顏色吃 currentColor，所以在 .hint 裡就是同一個灰。動畫只是 8 幀的位置／透明度
   內插；reduced-motion 時停在第 0 幀——仍看得到一顆球，只是不轉。 */
.orb {
  position: relative; display: inline-block; overflow: hidden;
  vertical-align: -0.22em; margin-right: 0.35em; color: currentColor;
}
.orb-dot {
  position: absolute; left: 50%; top: 50%;
  width: var(--orb-dot, 1.4px); height: var(--orb-dot, 1.4px);
  margin: calc(var(--orb-dot, 1.4px) / -2) 0 0 calc(var(--orb-dot, 1.4px) / -2);
  border-radius: 50%; background: currentColor;
  transform: translate(var(--g0x), var(--g0y)); opacity: var(--g0o);
  will-change: transform, opacity;
}
.orb-g2 .orb-dot { animation: orb-globe-spin 3.6s linear infinite both; }
@keyframes orb-globe-spin {
  0%, 100% { transform: translate(var(--g0x), var(--g0y)); opacity: var(--g0o); }
  12.5% { transform: translate(var(--g1x), var(--g1y)); opacity: var(--g1o); }
  25% { transform: translate(var(--g2x), var(--g2y)); opacity: var(--g2o); }
  37.5% { transform: translate(var(--g3x), var(--g3y)); opacity: var(--g3o); }
  50% { transform: translate(var(--g4x), var(--g4y)); opacity: var(--g4o); }
  62.5% { transform: translate(var(--g5x), var(--g5y)); opacity: var(--g5o); }
  75% { transform: translate(var(--g6x), var(--g6y)); opacity: var(--g6o); }
  87.5% { transform: translate(var(--g7x), var(--g7y)); opacity: var(--g7o); }
}
/* B2（Lens drift）：三顆圓繞軌道，前清晰後模糊；整個 28px 舞台用 --orb-k
   縮放（blur 的 px 跟著縮）。儀表板「載入中」標題用它取代舊的轉圈圈。 */
.orb-lens {
  position: absolute; left: 0; top: 0; width: 28px; height: 28px;
  transform-origin: 0 0; transform: scale(var(--orb-k, 1));
}
.orb-shape {
  position: absolute; left: 50%; top: 50%; width: 7px; height: 7px;
  margin: -3.5px 0 0 -3.5px; border-radius: 50%; background: currentColor;
  animation: orb-revolve 3.3s linear infinite both;
}
.orb-shape-a { animation-delay: 0s; }
.orb-shape-b { animation-delay: -1.1s; }
.orb-shape-c { animation-delay: -2.2s; }
@keyframes orb-revolve {
  0%   { opacity: 1;    filter: blur(0);     transform: rotate(0deg)   translateY(6.5px) scale(1); }
  25%  { opacity: 0.55; filter: blur(1.3px); transform: rotate(90deg)  translateY(6.5px) scale(0.82); }
  50%  { opacity: 0.28; filter: blur(2.4px); transform: rotate(180deg) translateY(6.5px) scale(0.66); }
  75%  { opacity: 0.55; filter: blur(1.3px); transform: rotate(270deg) translateY(6.5px) scale(0.82); }
  100% { opacity: 1;    filter: blur(0);     transform: rotate(360deg) translateY(6.5px) scale(1); }
}
/* 載入標題裡的 orb 吃品牌色（舊 spinner 也是 --spine），且不要文字那側的外距。 */
.dash-loading-head .orb { color: var(--spine); margin-right: 0; }
@media (prefers-reduced-motion: reduce) {
  .orb-g2 .orb-dot { animation: none; }
  .orb-shape { animation: none; opacity: 0.3; filter: blur(1.4px); transform: none; }
  .orb-shape-a { opacity: 1; filter: blur(0); }
}

/* 由勾選狀態決定、使用者不該直接改的下拉（下檔觸價方式選到 NA 時）。
   不是 disabled——disabled 的欄位不會隨表單送出。 */
select.is-locked {
  background: var(--paper-2, #F3F4F6);
  color: var(--meta);
}

/* 自動帶入進行中：變灰＋擋輸入（專案紀律）。
   用原生 disabled 而非只改樣式——只改樣式擋不住鍵盤輸入，使用者在等待期間
   打的字會在結果回來的瞬間被覆蓋掉，那是最難察覺的一種資料遺失。 */
input:disabled, select:disabled {
  background: var(--paper-2, #F3F4F6);
  color: var(--meta);
  cursor: progress;
  opacity: .75;
}

/* ---- 右欄摘要 rail ---- */
/* top＝「頂端列高度＋20px」：--fcn-topbar-h 由 app.js 以 ResizeObserver 量頂端列
   （手機的 .appbar）寫入；600 以上導覽在左側、頂端沒有列，量到 0。沒有該變數
   （JS 未跑）時 fallback 0px＝導覽在左側時的正確值。 */
.rail { position: sticky; top: calc(var(--fcn-topbar-h, 0px) + 20px); display: flex; flex-direction: column; gap: 16px; }
@container page (max-width: 1079.98px) { .rail { position: static; } }
.panel { background: var(--card); border: 1px solid var(--line); padding: 14px 18px 16px; }
.panel h2 { font-size: 15px; margin: 0 0 10px; }

/* Signature：障礙價格梯 */
.ladder-panel { border-top: 3px solid var(--spine); }
.ladder { position: relative; height: 210px; margin: 6px 4px 4px; }
.ladder-rail { position: absolute; left: 12px; top: 4px; bottom: 4px; width: 2px; background: var(--spine-soft); }
.ladder-ref {
  position: absolute; left: 0; right: 0; border-top: 1px dashed #B7C2CE;
  transition: top .25s ease;
}
.ladder-ref span {
  position: absolute; right: 0; top: -9px; font-size: 10.5px; color: var(--meta);
  background: var(--card); padding: 0 2px; font-family: var(--mono);
}
.ladder-rung { position: absolute; left: 0; right: 0; height: 0; transition: top .25s ease; }
.ladder-rung i {
  position: absolute; left: 6px; top: -7px; width: 14px; height: 14px; border-radius: 50%;
  border: 3px solid currentColor; background: var(--card);
}
.ladder-rung b { position: absolute; left: 28px; top: -10px; font-size: 12px; font-weight: 600; }
.ladder-rung span { position: absolute; right: 0; top: -10px; font-size: 12.5px; }
.ladder-rung.ko { color: var(--ko); }
.ladder-rung.strike { color: var(--spine); }
.ladder-rung.ki { color: var(--ki); }
.ladder.empty .ladder-rung, .ladder.empty .ladder-ref { visibility: hidden; }
/* 還沒填齊時的示意（各欄「例：」的數字；app.js paintLadder）：淡色畫出，註記標「示意（例：…）」 */
.ladder.demo .ladder-rung { visibility: visible; opacity: .42; }
.ladder.demo .ladder-ref { opacity: .42; }
.ladder-note-bad { color: var(--ko); }

.actions { display: flex; flex-direction: column; gap: 8px; }
.actions .btn { margin: 0; text-align: center; padding: 10px 14px; font-size: 14px; }
.actions .btn small { display: block; font-size: 11px; opacity: .75; font-weight: 400; margin-top: 1px; }
/* 「更多動作」（加入持倉、匯出設定檔）：寬版在右欄內往下展開；窄版的底部動作列改成往上
   浮出的小選單（下方 @container shell 段）。開關與鍵盤由 app.js initMoreActions。 */
.submit-lock-hint {
  margin: 0; padding: 6px 10px; font-size: 12.5px; line-height: 1.45;
  color: var(--ink); background: var(--verify-bg); border: 1px solid var(--verify-line); border-radius: 6px;
}
.submit-lock-hint a { white-space: nowrap; font-weight: 600; }
#doc-fill { scroll-margin-top: calc(var(--fcn-topbar-h, 0px) + 12px); }
.more-actions { position: relative; display: flex; flex-direction: column; gap: 8px; }
.more-actions-menu { display: flex; flex-direction: column; gap: 8px; }
.more-actions-menu[hidden] { display: none; }
.more-actions-toggle::after {
  content: ""; display: inline-block; width: 6px; height: 6px; margin: 0 0 3px 8px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(45deg);
}
.more-actions-toggle[aria-expanded="true"]::after { transform: rotate(-135deg); margin-bottom: -1px; }

/* 可用寬 < 800：動作列貼在畫面底部（底部導覽之上、含 iPhone 手勢條安全區），表單底部讓出
   它的高度。只留快速預覽／產生完整報告／更多動作三顆，副標收起。 */
@container shell (max-width: 799.98px) {
  .rail .actions {
    position: fixed; z-index: 60; left: 0; right: 0;
    bottom: calc(var(--bottomnav-h) + env(safe-area-inset-bottom, 0px));
    flex-direction: row; align-items: stretch; gap: 8px; margin: 0;
    padding: 8px max(12px, env(safe-area-inset-right, 0px)) 8px max(12px, env(safe-area-inset-left, 0px));
    border: 0; border-top: 1px solid var(--line); box-shadow: 0 -2px 10px rgba(23, 38, 59, .08);
  }
  .rail .actions { flex-wrap: wrap; }
  .rail .actions .submit-lock-hint { flex: 1 0 100%; }
  .form-layout:has(#submit-lock-hint:not([hidden])) { padding-bottom: 112px; }
  .rail .actions .btn { padding: 10px 8px; font-size: 13.5px; min-height: 44px; }
  .rail .actions .btn small { display: none; }
  .rail .actions > .btn { flex: 1 1 0; min-width: 0; }
  .rail .actions .more-actions { flex: 0 0 auto; }
  .rail .actions .more-actions-menu {
    position: absolute; right: 0; bottom: calc(100% + 8px); width: max-content; min-width: 200px;
    background: var(--card); border: 1px solid var(--line); border-radius: 8px; padding: 8px;
    box-shadow: 0 8px 24px rgba(23, 38, 59, .18);
  }
  .rail .actions .more-actions-menu .btn { text-align: left; }
  .form-layout { padding-bottom: 72px; }
  /* 手機打字時（軟鍵盤縮小版面）動作列會浮在鍵盤上方：收起（nav.js 掛 html.kb-open） */
  html.is-phone.kb-open .rail .actions { display: none; }
}
/* 600–799：沒有底部導覽，左側是 76px 直式導覽列 */
@container shell (min-width: 600px) and (max-width: 799.98px) {
  .rail .actions {
    left: calc(76px + env(safe-area-inset-left, 0px));
    bottom: env(safe-area-inset-bottom, 0px);
  }
}

/* 匯入設定檔：整塊可點的選檔區＋醒目的「帶入表單」動作鈕 */
.file-drop {
  display: flex; align-items: center; gap: 10px; cursor: pointer;
  border: 1.5px dashed #B4BFB2; background: #FBFCFA;
  padding: 12px 14px; margin: 2px 0 10px; font-size: 12.5px; color: #46566A;
  transition: border-color .15s ease, background .15s ease;
}
@media (hover: hover) and (pointer: fine) {
  .file-drop:hover { border-color: var(--spine); background: #F2F5F8; }
}
.file-drop input[type=file] {
  position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden;
}
.file-drop:focus-within { outline: 2px solid var(--spine); outline-offset: 2px; }
.file-drop-icon { font-size: 17px; color: var(--spine); flex: none; }
.file-drop-text { overflow-wrap: anywhere; }
.file-drop.has-file { border-style: solid; border-color: var(--spine); }
.file-drop.has-file .file-drop-text { color: var(--ink); font-family: var(--mono); font-size: 12px; }

/* 從既有設定檔帶入：下拉＋按鈕，全寬堆疊比照 .actions 的 flex-column 慣例 */
.existing-config-row { display: flex; flex-direction: column; gap: 8px; margin: 0 0 10px; }
.existing-config-row select { min-width: 0; font-size: 12.5px; }
.existing-config-row .btn { margin: 0; text-align: center; }

.import-btn {
  display: block; width: 100%; margin: 0; text-align: center;
  background: var(--spine); color: #fff; border-color: var(--spine);
  padding: 11px 14px; font-size: 14px; font-weight: 600;
  box-shadow: 0 1px 2px rgba(23, 38, 59, .18), 0 4px 12px rgba(41, 82, 126, .22);
  transition: transform .15s ease, box-shadow .15s ease, background .15s ease;
}
@media (hover: hover) and (pointer: fine) {
  .import-btn:hover {
    background: #1E3F63; transform: translateY(-1px);
    box-shadow: 0 2px 4px rgba(23, 38, 59, .2), 0 8px 20px rgba(41, 82, 126, .3);
  }
}
.import-btn:active {
  transform: translateY(0);
  box-shadow: 0 1px 2px rgba(23, 38, 59, .22);
}

/* ---- 錯誤與預覽 ---- */
.errors {
  background: var(--warn-bg); border: 1px solid var(--warn-line); color: #8C2B1E;
  padding: 12px 16px; margin: 4px 0; font-size: 13px;
}
.errors ul { margin: 6px 0 0; padding-left: 18px; }
.errors code { background: #fff; padding: 1px 5px; font-family: var(--mono); font-size: 12px; }

.preview { margin: 0; }
.preview-card { background: var(--card); border: 1px solid var(--line); border-left: 4px solid var(--spine); padding: 14px 18px; }
.badge {
  display: inline-block; background: #F4EBD6; color: #77571A; font-size: 11.5px;
  padding: 3px 10px; font-family: var(--mono); letter-spacing: .03em;
}
.badge .spinner { display: inline-block; vertical-align: -2px; margin-right: 6px; }
.preview .big { font-size: 24px; margin: 12px 0 8px; font-family: var(--mono); }
.preview .big small { font-size: 12.5px; color: var(--meta); font-weight: normal; font-family: var(--mono); }
ul.probs { list-style: none; padding: 0; margin: 8px 0 0; display: flex; gap: 18px; flex-wrap: wrap; }
ul.probs li { font-size: 13.5px; }

/* ---- 成品清單 ---- */
ul.file-list { list-style: none; padding: 0; margin: 4px 0; max-width: 560px; }
ul.file-list li {
  padding: 7px 2px; display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; border-bottom: 1px solid var(--line);
}
ul.file-list .size { color: var(--meta); font-size: 12px; flex: none; }

/* ---- 生成進度 ---- */
ol.stages { list-style: none; padding: 0; margin: 20px 0; max-width: 560px; }
ol.stages li {
  padding: 11px 16px 11px 42px; margin: 6px 0; background: var(--card);
  border: 1px solid var(--line); color: var(--meta); position: relative;
}
ol.stages li::before { content: "○"; position: absolute; left: 16px; color: #C4CDC3; font-family: var(--mono); }
ol.stages li.done { color: var(--ki); }
ol.stages li.done::before { content: "✓"; color: var(--ki); }
ol.stages li.active { color: var(--spine); border-color: var(--spine); font-weight: 600; }
ol.stages li.active::before { content: "▶"; color: var(--spine); }
.progress-msg { font-size: 14px; color: #46566A; }
.empty { color: var(--meta); padding: 40px 0; }
.hidden { display: none; }

@media (prefers-reduced-motion: reduce) {
  .ladder-rung, .ladder-ref, .import-btn, .file-drop { transition: none; }
  .import-btn:hover { transform: none; }
}

.ivi-open {
  margin-left: auto; text-align: center;
  background: var(--spine); color: #fff; border-color: var(--spine);
  padding: 7px 14px; font-size: 12.5px; font-weight: 600;
  box-shadow: 0 1px 2px rgba(23, 38, 59, .18), 0 4px 12px rgba(41, 82, 126, .22);
  transition: transform .15s ease, box-shadow .15s ease, background .15s ease;
}
@media (hover: hover) and (pointer: fine) {
  .ivi-open:hover {
    background: #1E3F63; transform: translateY(-1px);
    box-shadow: 0 2px 4px rgba(23, 38, 59, .2), 0 8px 20px rgba(41, 82, 126, .3);
  }
}
.ivi-open:active { transform: translateY(0); box-shadow: 0 1px 2px rgba(23, 38, 59, .22); }
/* 相關係數欄＋「從歷史計算」工具鈕：置於市場假設無風險利率下方，欄在左
   （名稱在上）、鈕沿用 IV 觸發鈕外觀（同設計模式）並排在右、上下較窄 */
#corr-calc { margin: 0; padding-top: 4px; padding-bottom: 4px; }
.corr-tool { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; margin: 4px 0 0; }
.corr-tool label { margin: 0; }
#corr-note { display: block; margin: 5px 0 0; }
.ivi-dialog {
  position: fixed; margin: 0;
  /* top 吃頂欄實高變數（見 .rail 註解），避免頂欄折成多行時蓋住對話框頂緣 */
  top: calc(var(--fcn-topbar-h, 0px) + 85px);
  left: max(24px, calc(50% - 280px));
  z-index: 70; border: 1px solid var(--line); border-top: 4px solid var(--spine);
  padding: 0 24px 20px; max-width: 560px;
  /* 高度上限＝可視高度扣掉 top（上面那個 calc）再留 12px：position:fixed 的對話框
     沒有上限時，鍵盤彈出或手機橫放的可視高度只剩幾百 px，底部「插值／關閉」落到
     畫面外、而它不在文件流裡，頁面捲動也救不了。超出時在框內捲動。 */
  max-height: calc(var(--vh) - var(--fcn-topbar-h, 0px) - 85px - 12px); overflow: auto;
  box-shadow: 0 6px 16px rgba(23, 38, 59, .18), 0 16px 40px rgba(41, 82, 126, .22);
}
.ivi-drag {
  display: flex; align-items: baseline; gap: 10px;
  margin: 0 -24px 10px; padding: 14px 24px 10px;
  cursor: move; user-select: none; touch-action: none;
  border-bottom: 1px solid var(--line); background: var(--card);
}
.ivi-drag h2 { margin: 0; font-size: 15px; }
.ivi-drag-hint { margin-left: auto; font-size: 11px; color: var(--meta); }
.ivi-actions { justify-content: flex-end; }
.ivi-result {
  margin: 8px 0 2px; min-height: 32px;
  font-family: var(--mono); font-size: 24px; font-weight: 700; color: var(--ink);
}
.ivi-result.bad { color: #8C2B1E; }  /* 與 .errors 表單驗證警示同紅 */

/* ---- 列表管理模式（個別／批量刪除）---- */
.manage-bar { display: flex; align-items: center; margin: 0 0 14px; min-height: 36px; }
.manage-tools { display: flex; align-items: center; }
.btn.danger { border-color: var(--ko); color: var(--ko); }
@media (hover: hover) and (pointer: fine) {
  .btn.danger:hover { background: var(--warn-bg); }
}
.btn.danger:disabled { opacity: .45; cursor: not-allowed; }
@media (hover: hover) and (pointer: fine) {
  .btn.danger:disabled:hover { background: var(--card); }
}
.ticket-card { position: relative; }
.ticket-check { display: none; position: absolute; top: 12px; right: 14px; pointer-events: none; margin: 0; }
/* 配色（spec 2026-11-08）：紅只給價格上漲、破壞性動作的確認鈕（刪除）與程式錯誤。
   管理模式的選取是一般的選中狀態＝墨藍；過期是「請你留意」＝警示黃。 */
.ticket-check input { width: 16px; height: 16px; accent-color: var(--spine); }
body.managing .ticket-check { display: block; }
body.managing .ticket-card { cursor: pointer; user-select: none; }
body.managing .ticket-card a,
body.managing .ticket-card .note-link,
body.managing .ticket-card .regen-btn { pointer-events: none; }
body.managing .ticket-card.selected { border-color: var(--spine); border-top-color: var(--spine); background: #F2F6FA; }
/* 過期票券（config 比報告新）：卡片頂框轉黃＋提示＋重新生成鈕 */
.ticket-card.is-stale { border-top-color: var(--verify-line); }
.stale-note { margin: 2px 0 8px; font-size: 12px; font-weight: 600; color: var(--verify-ink); }
.regen-btn { border-color: var(--verify-line); color: var(--verify-ink); }
@media (hover: hover) and (pointer: fine) {
  .regen-btn:hover { background: var(--verify-bg); }
}
.regen-btn:disabled { opacity: .55; cursor: not-allowed; }
/* 卡片的條款摘要（讀自設定檔；讀不到就整段不出現）：產品＋已在持倉、籃子、年化配息與天期。
   兩個標籤都是中性資訊，不用行情紅／警示黃。 */
.tk-tags { display: flex; flex-wrap: wrap; gap: 4px 6px; margin: 2px 0 6px; }
.tk-product, .tk-held {
  display: inline-block; padding: 0 6px; border-radius: 4px;
  font-size: 11px; font-weight: 600; line-height: 1.7; white-space: nowrap;
}
.tk-product { background: var(--st-neutral-bg); color: var(--st-neutral); font-family: var(--mono); }
.tk-held { background: var(--spine-soft); color: var(--spine); }
.tk-basket { margin: 0 0 4px; font-size: 13.5px; color: var(--ink); overflow-wrap: anywhere; }
.tk-terms {
  display: grid; grid-template-columns: auto auto auto auto; justify-content: start;
  column-gap: 6px; margin: 0 0 8px; font-size: 12.5px;
}
.tk-terms dt { color: var(--meta); }
.tk-terms dd { margin: 0 14px 0 0; color: var(--ink); }
.delete-dialog {
  border: 1px solid var(--line); border-top: 4px solid var(--ko);
  padding: 18px 26px 20px; max-width: 480px;
  box-shadow: 0 6px 16px rgba(23, 38, 59, .18), 0 16px 40px rgba(41, 82, 126, .22);
}
.delete-dialog::backdrop { background: rgba(23, 38, 59, .45); }
.delete-dialog h2 { font-size: 16px; margin: 0 0 10px; }
.delete-dialog ul { margin: 6px 0; padding-left: 20px; }
.delete-warn { color: #8C2B1E; font-weight: 600; }
.dialog-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 14px; }
/* 對話框高度上限：手機橫放或鍵盤彈出時可視高度只剩三四百 px，沒有上限的
   對話框底部（確定鈕）會落到畫面外、而 modal 又擋住背後捲動，就按不到了。 */
.delete-dialog, .hold-dialog, .pf-dialog, .settings-dialog {
  max-height: calc(var(--vh) - 32px); overflow: auto;
}
.dialog-actions .btn { margin: 0; }
.manage-tools.hidden { display: none; }  /* .manage-tools 宣告晚於 .hidden，需更高 specificity 才藏得住 */

/* ===== 持倉監控儀表板（SPEC_dashboard.md §7）===== */
.dash-head { display: flex; justify-content: space-between; align-items: baseline;
  gap: 12px; flex-wrap: wrap; }
.dash-actions { display: flex; align-items: baseline; gap: 12px; }
.dash-meta { color: var(--meta); font-size: 0.85rem; }
.dash-muted { color: var(--meta); font-size: 0.85em; }
/* 拆股調整過的期初價格下方那行「已依拆股調整，原 Y」：中性灰、自成一行 */
.dash-split-note { display: block; }
/* 窄欄要換行時斷在逗號後，「原 247.02」不拆開 */
.dash-split-orig { white-space: nowrap; }
.dash-dim { opacity: 0.55; }
.dash-foot { border-top: 1px solid var(--line); padding-top: 10px; margin-top: 28px; }
/* 持倉檔路徑（絕對路徑，沒有空白可斷行）：不加這條時長路徑把 min-content 撐寬，
   320 寬的 layout viewport 被撐到 519px、整頁橫向溢出。anywhere 會計入 min-content，
   break-word 不會，所以不能用後者。 */
.dash-meta code { overflow-wrap: anywhere; }

.dash-errors { background: var(--warn-bg); border: 1px solid var(--warn-line);
  border-radius: 6px; padding: 10px 14px; margin: 12px 0; }
.dash-errors p { margin: 2px 0; }

/* 黃框（payload 的 warnings）：資料退了一步但逃生門接住了——用 D-1 的 IV、
   用開倉 vol、拆股序列沒驗證。沿用既有的 --verify-* 語彙（.dash-iv-fallback／
   p.note[role="note"] 同一套），**不新造顏色**；與紅框只差色票，版面一致。 */
.dash-warnings { background: var(--verify-bg); border: 1px solid var(--verify-line);
  color: var(--verify-ink); border-radius: 6px; padding: 10px 14px; margin: 12px 0; }
.dash-warnings p { margin: 2px 0; }

.dash-setup pre { background: var(--card); border: 1px solid var(--line);
  border-radius: 6px; padding: 12px; font-family: var(--mono); font-size: 0.85rem;
  overflow-x: auto; }

.dash-cards { display: grid; gap: 8px; margin: 16px 0 12px;
  grid-template-columns: repeat(auto-fit, minmax(min(124px, 100%), 1fr)); }
.dash-card { background: var(--card); border: 1px solid var(--line);
  border-radius: 8px; padding: 10px 12px; min-width: 0; }
.dash-card-v { font-family: var(--mono); font-size: 1.5rem; color: var(--ink); }
.dash-card-l { color: var(--spine); font-size: 0.85rem; }
.dash-card-note { color: var(--meta); font-size: 0.78rem; margin-top: 2px; }
/* 加權平均主數字：繼承 .dash-card-v 的字級／字形（與左右兩張卡一致），一般墨色。
   紅色只留給「價格上漲」（台股紅漲綠跌），分數不是價格。 */
.dash-wscore { color: var(--ink); }
/* 「／ 最高 N」是次要參考值：等寬數字、縮小、灰字，以字級與顏色區分主次。 */
.dash-card-sub { font-family: var(--mono); font-size: 0.9rem; color: var(--meta); }
/* 最高那張的分數：粗體＋色階，在灰色次要字裡也看得出警示（使用者 2026-11-09） */
.dash-card-max { font-weight: 700; }

.dash-loading-head { display: flex; align-items: center; gap: 8px; margin: 0 0 4px; }
/* 已等待秒數：等寬數字，秒數跳動時不讓行內其他東西左右抖。 */
.dash-elapsed { font-variant-numeric: tabular-nums; }
.dash-loading-note { margin: 0 0 4px; }
.spinner {
  width: 13px; height: 13px; border: 2px solid var(--spine-soft);
  border-top-color: var(--spine); border-radius: 50%;
  animation: fcn-spin .7s linear infinite;
}
@keyframes fcn-spin { to { transform: rotate(360deg); } }

/* ---- 票券列表首屏載入（仿桌面 splash：spinner＋狀態文字）---- */
.tickets-loading { padding: 44px 0; color: var(--meta); }
.tickets-loading .dash-loading-head { justify-content: center; }

.dash-scroll { overflow-x: auto; }
/* 右緣捲動提示（spec 2026-11-08 §7）：右邊還有欄位沒露出來時，框的右緣淡出。
   has-more-right 由共用的 scrollhint.js 依 scrollLeft／scrollWidth 切換（捲到底、
   或表格放得下就拿掉）。用 mask 而不是陰影：mask 套在框本身、不隨內容捲動，
   也不會被格子的不透明底色蓋住。 */
.dash-scroll.has-more-right {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 36px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 36px), transparent);
}
/* 橫捲到底時不把手勢傳給整頁：觸控板／手機的水平滑動在頁面層會被當成
   「上一頁」，捲表格捲過頭就整頁跳走。 */
.dash-scroll, .guide-table { overscroll-behavior-x: contain; }
/* 對話框自己捲到底時，不帶動背後的頁面一起捲。 */
dialog { overscroll-behavior: contain; }
.dash-table { border-collapse: collapse; width: 100%; margin: 8px 0;
  font-size: 0.9rem; }
.dash-table th, .dash-table td { border-bottom: 1px solid var(--line);
  padding: 7px 10px; text-align: left; vertical-align: top; }
.dash-table thead th { color: var(--meta); font-weight: 600; white-space: nowrap; }
.dash-score { font-family: var(--mono); font-size: 1.05rem; color: var(--ink); }
/* 分數格帶 score_note 時才上這個：tooltip 是「存在但看不見」的 affordance，
   一條虛線底線讓它可被發現（游標也換成 help）。**沒有 note 的分數格不加**
   ——無故變樣式會讓使用者以為那一列有事。底線色沿用 --verify-* 語彙，與
   「開倉IV」標記、頁首「IV=開倉值」徽章同一套：要留意輸入口徑，不是錯誤。 */
.dash-score-noted { border-bottom: 1px dashed var(--verify-line); cursor: help; }
/* 風險分數色階：字色給所有帶色階的數字（含摘要卡的加權與「最高」），
   明細的分數格再加淡底成一顆小標籤，黃段才讀得清楚 */
.dash-score-low { color: var(--score-low); }
.dash-score-mid { color: var(--score-mid); }
.dash-score-high { color: var(--score-high); }
.dash-score-max { color: var(--score-max); }
.dash-score.dash-score-low, .dash-score.dash-score-mid,
.dash-score.dash-score-high, .dash-score.dash-score-max { padding: 0 6px; border-radius: 4px; }
.dash-score.dash-score-low { background: var(--score-low-bg); }
.dash-score.dash-score-mid { background: var(--score-mid-bg); }
.dash-score.dash-score-high { background: var(--score-high-bg); }
.dash-score.dash-score-max { background: var(--score-max-bg); }

/* 需留意事項：待辦＝琥珀底＋字；已結束的事實（已 KO）＝中性灰。紅色只給價格上漲
   （tests/test_dashboard_layout_css.py 守）。 */
.dash-flag { display: inline-block; background: var(--st-near-bg);
  border: 1px solid var(--st-near-line); color: var(--st-near); border-radius: 4px;
  padding: 0 6px; margin: 1px 4px 1px 0; font-size: 0.8rem; white-space: nowrap; }
.dash-flag.is-ended { background: var(--st-neutral-bg); border-color: var(--line);
  color: var(--st-neutral); }

.dash-iv-fallback { display: inline-block; background: var(--verify-bg);
  border: 1px solid var(--verify-line); color: var(--verify-ink); border-radius: 4px;
  padding: 0 6px; margin-right: 8px; font-size: 0.8rem; white-space: nowrap; }
/* author-origin display 會蓋過 UA 的 [hidden]{display:none}，須顯式補回 */
.dash-iv-fallback[hidden] { display: none; }

.dash-grid { display: grid; gap: 24px; margin-top: 8px;
  grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); }
/* 票券數少時不要撐滿全寬，否則兩三格會拉成巨大色塊 */
.dash-heat { width: auto; }
.dash-heat td { font-family: var(--mono); text-align: center; min-width: 76px; }
.dash-heat th { white-space: nowrap; }

h2 small { display: block; color: var(--meta); font-size: 0.78rem;
  font-weight: 400; line-height: 1.5; margin-top: 3px; }

/* ---- 全域氣泡訊息（toast）---- */
/* 容器不吃點擊，只有氣泡本體吃——蓋住的按鈕照樣按得到 */
.toast-host {
  position: fixed; left: 50%; transform: translateX(-50%);
  /* top 吃頂欄實高變數（見 .rail 註解），避免頂欄折成多行時蓋住第一顆 toast */
  top: calc(var(--fcn-topbar-h, 0px) + 21px);
  z-index: 90; width: min(560px, calc(var(--vw) - 32px));
  display: flex; flex-direction: column; gap: 8px; pointer-events: none;
}
.toast {
  pointer-events: auto; position: relative;
  background: var(--card); border: 1px solid var(--line); border-left: 4px solid var(--ki);
  padding: 10px 34px 10px 14px; font-size: 13px;
  box-shadow: 0 2px 6px rgba(23, 38, 59, .14), 0 10px 28px rgba(41, 82, 126, .18);
  animation: toast-in .18s ease-out;
}
.toast p { margin: 2px 0; }
.toast-warn { border-left-color: #B8791F; }
.toast-error { border-left-color: var(--ko); }
.toast-x {
  position: absolute; top: 4px; right: 6px; border: 0; background: none;
  color: var(--meta); font-size: 17px; line-height: 1; cursor: pointer; padding: 2px 4px;
}
@media (hover: hover) and (pointer: fine) {
  .toast-x:hover { color: var(--ink); }
}
.toast-out { opacity: 0; transform: translateY(-6px); transition: opacity .2s, transform .2s; }
@keyframes toast-in { from { opacity: 0; transform: translateY(-8px); } }
@media (prefers-reduced-motion: reduce) {
  .toast { animation: none; }
  .toast-out { transition: none; }
  .spinner { animation: none; }
}

/* ---- 加入持倉／管理持倉 ---- */
.btn.success { border-color: var(--ki); color: var(--ki); }
@media (hover: hover) and (pointer: fine) {
  .btn.success:hover { background: #E8F1EF; }
}
.btn.success:disabled { opacity: .45; cursor: not-allowed; }
@media (hover: hover) and (pointer: fine) {
  .btn.success:disabled:hover { background: var(--card); }
}

.hold-dialog, .pf-dialog {
  border: 1px solid var(--line); border-top: 4px solid var(--ki);
  padding: 18px 26px 20px; max-width: 880px; width: min(880px, calc(var(--vw) - 40px));
  box-shadow: 0 6px 16px rgba(23, 38, 59, .18), 0 16px 40px rgba(41, 82, 126, .22);
}
.hold-dialog::backdrop, .pf-dialog::backdrop { background: rgba(23, 38, 59, .45); }
.hold-dialog { max-width: 620px; width: min(620px, calc(var(--vw) - 40px)); }
.hold-dialog h2, .pf-dialog h2 { font-size: 16px; margin: 0 0 8px; }
.hold-dialog input { width: 100%; }
/* 固定欄寬：input 吃 width:100% 會把表格撐爆對話框，最後一欄被切掉 */
.pf-dialog input[type="text"] { width: 150px; }
.pf-dialog input[type="number"] { width: 120px; }
.pf-dialog select { width: 108px; }
.hold-dialog .dash-table td, .pf-dialog .dash-table td { vertical-align: middle; }
.pf-dialog .dash-table th, .pf-dialog .dash-table td { padding: 6px 8px; }
.pf-dialog .pf-noreport { color: var(--meta); font-size: .8em; white-space: nowrap; }
.pf-check { width: 16px; height: 16px; accent-color: var(--ki); }

/* ---- 儀表板：標的價格、旗標圖例 ---- */
.dash-px { font-family: var(--mono); }
.dash-up { color: var(--ko); }      /* 台股慣例：紅漲 */
.dash-down { color: var(--ki); }    /* 綠跌 */
.dash-below { color: var(--below-barrier); }  /* 低於 KI（無 KI 則低於執行價）：政策例外，見 --below-barrier */
.dash-below .dash-tag { color: var(--below-barrier); }  /* 明細表的代號帶自己的藍色，要蓋回紅字 */
.dash-gauge-p.dash-below { font-weight: 700; }
.dash-arrow { color: var(--meta); }
/* 漲跌幅寬度隨數字位數變動（▲1.4% vs ▼10.5%），不鎖寬度就會把左邊的
   最新收盤價一起推得參差不齊——整欄右對齊時特別明顯 */
.dash-chg {
  font-size: .82em; display: inline-block; min-width: 3.9em; text-align: left;
  margin-left: 5px;  /* 取代字面空白，見 dashboard.js changeHtml 的註解 */
}
.dash-noreport { color: var(--meta); font-size: .85rem; }
.dash-generating { color: var(--spine); font-size: .85rem; font-weight: 600; }
/* 標的代碼：等寬字（與價格數字同一套 --mono，即 VS Code 在 Windows 的預設
   Consolas／Cascadia）＋墨藍字色。**純文字、不加底色與外框**——底色框會帶進
   邊框與內距，代號文字就比表頭往右縮排，怎麼調都對不齊；純文字則兩者同起
   於儲存格內容左緣，天然切齊。字級刻意不動，放大會與同列價格數字錯位。 */
.dash-tag { font-family: var(--mono); color: var(--spine); letter-spacing: .02em; }

/* 代號旁的小字標記（目前只有 IV 來源）：小字、緊貼代號右側。 */
.dash-hit { font-size: .78em; margin-left: 5px; font-weight: 600; white-space: nowrap; }
/* 標的欄的「開倉IV」標記（該檔的 IV 退回開倉值）。用 --verify-* 語彙、
   與頁首的 .dash-iv-fallback 同一套：這是「要留意輸入口徑」不是錯誤，
   故刻意不用 --warn-*／--ko 那組警示色，也不新造顏色。 */
.dash-hit-iv { color: var(--verify-ink); }

/* .dash-table th/td 的 text-align:left 是「類別＋元素」，這裡要同等特異性才蓋得過 */
.dash-table th.dash-num, .dash-table td.dash-num { text-align: right; }
.dash-legend { margin: 14px 0 26px; }
.dash-legend summary { cursor: pointer; color: var(--spine); font-size: .9rem; }
/* 標記說明（使用者 2026-11-09：每項 30 字內，標籤＋範例）：標籤欄｜說明＋一行範例。
   窄時標籤欄縮到內容寬，說明照樣折行；不左右橫移。 */
.dash-legend dl {
  margin: 6px 0 0; display: grid; grid-template-columns: max-content minmax(0, 1fr);
  gap: 8px 12px; align-items: baseline;
}
.dash-legend dt { margin: 0; }
.dash-legend dd { margin: 0; color: #3C4B5C; font-size: .88rem; }
.dash-legend-h { margin: 14px 0 0; font-size: .82rem; color: var(--meta); font-weight: 600; letter-spacing: .02em; }
.dash-legend-eg { display: block; margin-top: 1px; font-size: .8rem; color: var(--meta); }
.dash-legend-eg .dash-score { font-size: .78rem; }
.dash-legend-tag {
  display: inline-block; padding: 0 6px; border: 1px solid var(--line); border-radius: 4px;
  font-size: .78rem; color: var(--spine); white-space: nowrap;
}

/* ---- 設定（縮放、token）---- */
.settings-dialog {
  border: 1px solid var(--line); border-top: 4px solid var(--spine);
  padding: 18px 26px 20px; max-width: 480px; width: min(480px, calc(var(--vw) - 40px));
  box-shadow: 0 6px 16px rgba(23, 38, 59, .18), 0 16px 40px rgba(41, 82, 126, .22);
}
.settings-dialog::backdrop { background: rgba(23, 38, 59, .45); }
.settings-dialog h2 { font-size: 16px; margin: 0 0 8px; }
.settings-dialog h3 { font-size: 13px; margin: 14px 0 4px; }
.settings-dialog input { width: 110px; }
/* token 是一長串字元，110px 只看得到開頭幾個字，要吃滿整行。 */
.settings-dialog input#settings-token { width: 100%; }
.settings-presets { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 12px; }
.settings-presets .btn { margin: 0; }
.settings-presets .btn:disabled { opacity: .4; cursor: not-allowed; }
/* 縮放控制：滑桿吃掉剩餘寬度，數字框固定寬。數字框 16px：iOS 聚焦小於 16px 的輸入框
   會整頁放大（tests/test_app_settings.py 守）。 */
.zoom-ctl { display: flex; align-items: center; gap: 10px; margin: 8px 0 4px; }
.settings-dialog .zoom-ctl input[type="range"] {
  flex: 1 1 auto; width: auto; min-width: 0; min-height: 32px; padding: 0; border: 0;
  background: none; accent-color: var(--spine);
}
.zoom-num { display: inline-flex; align-items: center; gap: 4px; flex: none; }
/* 76px 放得下「200」＋數字框箭頭；全域 input 的 min-width:150px 要歸零才吃得到。 */
.settings-dialog #settings-zoom-num { width: 76px; min-width: 0; font-size: 16px; padding: 6px 8px; }
#settings-zoom-cap { margin: 0 0 8px; }
/* 對應票券：代號用等寬字（與票券欄同調），但不做成 tag——一格可能列好幾張，
   全都套 tag 會變成色塊牆，反而蓋過旁邊的金額 */
.dash-notes { font-family: var(--mono); font-size: .9em; }

/* ===========================================================================
   外觀收斂（2026-07-21）— 純樣式，未動任何行為、參數或版面結構。

   目標三條：可點的一眼看得出可點、資訊一眼分得出層級、載入即全貌（不做
   進場動畫）。動作回饋沿用本專案既有的手勢（見 .ivi-open）：hover 抬起
   1px＋陰影綻開、active 壓回，.15s ease——不新造動態語彙。
   =========================================================================== */

/* ---- 1. 可點性：所有按鈕共用同一組回饋 ---- */
.btn {
  transition: background-color .15s ease, border-color .15s ease,
              transform .15s ease, box-shadow .15s ease;
}
@media (hover: hover) and (pointer: fine) {
  .btn:hover { transform: translateY(-1px); box-shadow: 0 2px 6px rgba(23, 38, 59, .12); }
}
.btn:active { transform: translateY(0); box-shadow: inset 0 1px 2px rgba(23, 38, 59, .16); }
@media (hover: hover) and (pointer: fine) {
  .btn.primary:hover { box-shadow: 0 2px 8px rgba(41, 82, 126, .32); }
  .btn.danger:hover { box-shadow: 0 2px 6px rgba(200, 52, 43, .2); }
  .btn.success:hover { box-shadow: 0 2px 6px rgba(30, 111, 99, .2); }
}
/* 按下的瞬間就要有變化：觸控沒有 hover，原本唯一的回饋是 inset 陰影，在
   手機上幾乎看不見——放開手指才換頁，讀起來就是「卡了一下」。底色沿用
   各自 hover 的色票，不新造顏色。 */
.btn:active { background-color: #E1E9F2; }
.btn.primary:active, .btn.cta:active, .import-btn:active, .ivi-open:active { background-color: #1E3F63; }
.btn.primary.success:active, .btn.done:active { background-color: #17564C; }
.btn.success:active { background-color: #E8F1EF; }
.btn.danger:active { background-color: var(--warn-bg); }
.regen-btn:active { background-color: var(--verify-bg); }
.btn.dash-regen:active { background-color: #F6E2BD; }
.crumb a:active { background: #EAF0F6; border-color: var(--spine); }
.file-drop:active { border-color: var(--spine); background: #F2F5F8; }
ul.file-list li:active { background: #EDF1F6; }
/* 停用的按鈕不得有任何「可按」的暗示 */
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn:disabled:active { transform: none; box-shadow: none; background: var(--card); }
@media (hover: hover) and (pointer: fine) {
  .btn:disabled:hover {
    transform: none; box-shadow: none; background: var(--card);
  }
  .btn.primary:disabled:hover { background: var(--spine); }
}
@media (prefers-reduced-motion: reduce) {
  .btn, .btn:hover, .btn:active { transition: none; transform: none; }
}

/* 收合區塊原本只有系統預設的小三角，幾乎看不出可點。給它按鈕般的外框與
   會轉向的箭頭——這是全頁最容易被錯過的可點元素。 */
/* 圖例浮在紙底上、四周無容器，做成卡片才有邊界；表單裡的「進階」是條款
   之間的分隔式揭露，做成卡片會打斷細線節奏，故只給它箭頭與 hover。 */
.dash-legend {
  background: var(--card); border: 1px solid var(--line);
  border-left: 3px solid var(--spine);
}
.dash-legend summary, .advanced summary {
  list-style: none; display: flex; align-items: center; gap: 9px;
  padding: 10px 14px; color: var(--spine); font-weight: 600;
  transition: background-color .15s ease;
}
.dash-legend summary::-webkit-details-marker,
.advanced summary::-webkit-details-marker { display: none; }
.dash-legend summary::before, .advanced summary::before {
  content: ""; flex: none; width: 0; height: 0;
  border: 5px solid transparent; border-left-color: var(--spine);
  margin-left: 2px; transition: transform .15s ease;
}
.dash-legend[open] summary::before, .advanced[open] summary::before {
  transform: rotate(90deg);
}
@media (hover: hover) and (pointer: fine) {
  .dash-legend summary:hover, .advanced summary:hover { background: #EAF0F6; }
}
.dash-legend dl, .dash-legend > .dash-meta, .dash-legend > .dash-legend-h { padding: 0 14px; }
.dash-legend > dl:last-child { padding-bottom: 14px; }
.dash-legend dl { padding-bottom: 12px; }
.advanced summary { padding: 12px 0 8px; font-size: 13px; }
@media (hover: hover) and (pointer: fine) {
  .advanced summary:hover { background: transparent; color: #1E3F63; }
}
@media (prefers-reduced-motion: reduce) {
  .dash-legend summary::before, .advanced summary::before { transition: none; }
}

/* 報告過期的「重新生成」：與「已過期」同一組琥珀色（要人看一眼，不是價格） */
.dash-stale { color: var(--st-near); font-weight: 600; font-size: 0.85rem; }
.btn.dash-regen { border-color: var(--st-near-line); color: var(--st-near); background: var(--st-near-bg); }
@media (hover: hover) and (pointer: fine) {
  .btn.dash-regen:hover { background: #F6E2BD; }
}

.toast-x { border-radius: 50%; transition: background-color .15s ease, color .15s ease; }
@media (hover: hover) and (pointer: fine) {
  .toast-x:hover { background: #EAEEF3; }
}

/* ---- 2. 一目了然：讀行線 ---- */
/* 十二欄寬表格橫向掃視極易跳行。滑過的列上底色並在左緣落一道墨藍記號，
   像拿尺壓在帳票上——同時也讓列內的「生成報告」鈕更容易被發現。
   熱力圖除外：它的格子有行內背景色，列底色會蓋不過去而顯得半殘。 */
.dash-table:not(.dash-heat) tbody tr { transition: background-color .12s ease; }
@media (hover: hover) and (pointer: fine) {
  .dash-table:not(.dash-heat) tbody tr:hover { background: #EDF1F6; }
  /* 第一欄在 .dash-scroll 裡是 sticky、自帶不透明底色，hover 的列底色要另外蓋到它 */
  .dash-table:not(.dash-heat) tbody tr:hover > td:first-child {
    background: #EDF1F6; box-shadow: inset 3px 0 0 var(--spine);
  }
  /* 明細的標的區是疊排（代號格內距 0）：記號會壓在代號字上，只留整塊的底色 */
  .dash-table.dash-utable:not(.dash-heat) tbody tr:hover > td:first-child {
    background: none; box-shadow: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .dash-table:not(.dash-heat) tbody tr { transition: none; }
}

/* 表頭與資料的分界要比列間分隔線重，眼睛才知道哪裡是欄名 */
.dash-table thead th {
  color: #46566A; letter-spacing: .04em; vertical-align: bottom;
  border-bottom: 2px solid var(--spine-soft); padding-bottom: 6px;
}

/* 風險分數是這一頁存在的理由，字級與字重要壓過同列其他數字 */
.dash-score { font-size: 1.3rem; font-weight: 700; letter-spacing: -.01em; }

/* 非存續的列用淡化表示，但別淡到讀不出來（原 .55 在細明體上偏糊） */
.dash-dim { opacity: .62; }

/* ---- 3. 與其他頁面同一套識別：方角＋墨藍頂線 ---- */
/* 儀表板原本用 6–8px 圓角，與表單／票券列表的方角系統不一致，看起來像
   另一個產品。卡片頂線沿用 .card 既有的墨藍規格。 */
.dash-card {
  border-radius: 0; border-top: 2px solid var(--spine);
  transition: box-shadow .15s ease;
}
.dash-card-v { font-size: 1.4rem; line-height: 1.2; letter-spacing: -.01em; }
.dash-card-l { letter-spacing: .04em; }
.dash-errors { border-radius: 0; border-left: 3px solid var(--ko); }
.dash-warnings { border-radius: 0; border-left: 3px solid var(--verify-line); }
.dash-setup pre { border-radius: 0; }
.dash-flag { border-radius: 2px; }

/* ---- 持倉監控：清單＋明細（spec 2026-11-08-ui-redesign-sn §3）----
   版面全由這裡決定，dashboard.js 只記 #dash-split 的 data-view（list｜detail）與網址的
   ?t=<note_id>，不量寬度。基底＝手機（單欄、點卡片推入明細），往上疊：
     @container shell ≥ 600   卡片兩欄（仍是推入）
     @container shell ≥ 800   清單（300–340px）＋明細雙欄；返回鈕藏起來
     @container shell ≥ 1200  清單 360px
   雙欄規則一律帶 html:not(.is-phone)：手機橫放（寬度可能 ≥ 800）維持推入
   （tests/test_dashboard_layout_css.py 守）。 */
.dash-basis-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 0 0 12px; }
.dash-basis-btn {
  display: inline-flex; align-items: center; gap: 7px; min-height: 36px; padding: 0 14px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--card);
  font: inherit; font-size: 13px; color: var(--spine); cursor: pointer;
}
.dash-basis-btn::after {
  content: ""; width: 0; height: 0; border: 4px solid transparent; border-top-color: currentColor;
  margin-top: 4px; transition: transform .15s ease;
}
.dash-basis-btn[aria-expanded="true"]::after { transform: rotate(180deg); margin-top: -4px; }
.dash-basis-btn:focus-visible { outline: 2px solid var(--accent-teal); outline-offset: 2px; }
@media (hover: hover) and (pointer: fine) {
  .dash-basis-btn:hover { background: #EAF0F6; }
}
.dash-basis {
  max-width: 40em; margin: 0 0 16px; padding: 10px 14px; background: var(--card);
  border: 1px solid var(--line); border-left: 3px solid var(--spine);
  font-size: .88rem; color: #3C4B5C;
}
.dash-basis p { margin: 4px 0; }
.dash-basis[hidden] { display: none; }

.dash-split { display: grid; grid-template-columns: minmax(0, 1fr); gap: 20px; align-items: start; }
.dash-split > * { min-width: 0; }
.dash-split[data-view="list"] > .dash-detail-pane,
.dash-split[data-view="detail"] > .dash-list-pane { display: none; }

.dash-listhead { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.dash-listhead h2 { margin: 0; font-size: 18px; }
.dash-listhead .btn { margin: 0; }
.dash-count { font-family: var(--mono); font-weight: 400; color: var(--meta); }
.dash-filters { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 12px; }
.dash-filter {
  min-height: 36px; padding: 0 12px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--card); color: var(--meta); font: inherit; font-size: 13px; cursor: pointer;
}
.dash-filter[aria-pressed="true"] { border-color: var(--ink); color: var(--ink); font-weight: 700; }
.dash-filter-n { font-family: var(--mono); }
.dash-filter:focus-visible { outline: 2px solid var(--accent-teal); outline-offset: 2px; }

.dash-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px;
  grid-template-columns: minmax(0, 1fr); }
.dash-li { display: grid; min-width: 0; }
.dash-empty { color: var(--meta); font-size: .9rem; padding: 12px 2px; }
.dash-item {
  display: grid; gap: 10px; align-content: start; min-width: 0; padding: 14px 14px 12px;
  background: var(--card); border: 1px solid var(--line); border-radius: 8px;
  color: var(--ink); text-decoration: none;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.dash-item:focus-visible { outline: 2px solid var(--accent-teal); outline-offset: 2px; }
.dash-item:active { background: #F4F7FA; }
@media (hover: hover) and (pointer: fine) {
  .dash-item:hover { border-color: #B9C4CF; box-shadow: 0 2px 8px rgba(23, 38, 59, .08); }
}
@media (prefers-reduced-motion: reduce) {
  .dash-item { transition: none; }
}
.dash-item.is-dim { opacity: .62; }
.dash-item-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; }
.dash-item-id { font-weight: 700; font-size: 16px; overflow-wrap: anywhere; }
.dash-item-meta { margin-top: -6px; font-size: 12px; color: var(--meta); overflow-wrap: anywhere; }

/* 狀態標籤：底色＋文字。已 KO／已到期／無行情是中性灰（不是警示）。 */
.dash-st {
  display: inline-block; padding: 2px 9px; border-radius: 999px; white-space: nowrap;
  font-family: inherit; font-size: 12px; font-weight: 700; line-height: 1.5;
}
.dash-st-near { background: var(--st-near-bg); color: var(--st-near); }
.dash-st-ki { background: var(--st-near-bg); color: var(--st-near); box-shadow: inset 0 0 0 1px var(--st-near-line); }
.dash-st-watch { background: var(--st-watch-bg); color: var(--st-watch); }
.dash-st-safe { background: var(--st-safe-bg); color: var(--st-safe); }
.dash-st-ko, .dash-st-matured, .dash-st-none { background: var(--st-neutral-bg); color: var(--st-neutral); }

.dash-big { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; }
.dash-big-v { font-family: var(--mono); font-size: 28px; line-height: 1; }
.dash-big-l { font-size: 12px; color: var(--meta); }

/* 障礙尺：40%–120%，期初＝100%。位置由 dashboard.js 的 gaugeOf 算成百分比寫進 style。 */
.dash-gauges { display: grid; gap: 8px; }
.dash-grow {
  display: grid; grid-template-columns: 78px minmax(0, 1fr) 50px; gap: 10px; align-items: center;
  font-size: 12px;
}
.dash-gauge-name { display: flex; align-items: center; gap: 4px; min-width: 0; white-space: nowrap; }
.dash-gauge-t { font-family: var(--mono); font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
.dash-gauge-p { font-family: var(--mono); text-align: right; white-space: nowrap; }
.dash-worst {
  flex: none; padding: 0 5px; border-radius: 4px; background: var(--gauge-ki); color: #8E2F21;
  font-size: 10.5px; font-weight: 600; line-height: 1.6; white-space: nowrap;
}
.dash-gauge {
  position: relative; display: block; height: 14px; min-width: 0;
  background: var(--gauge-track); border-radius: 4px;
}
.dash-gauge-mini { height: 8px; min-width: 120px; }
.dash-gauge-ki {
  position: absolute; left: 0; top: 0; bottom: 0; background: var(--gauge-ki);
  border-radius: 4px 0 0 4px;
}
.dash-gauge-ko {
  position: absolute; top: -3px; bottom: -3px; width: 2px; margin-left: -1px;
  background: var(--gauge-ko); color: var(--gauge-ko);
}
/* KI 線：實線，貼著粉色區的右緣，高度與尺同高（不外伸，與 KO 線、執行價線的外伸區隔） */
.dash-gauge-kiline {
  position: absolute; top: 0; bottom: 0; width: 2px; margin-left: -1px;
  background: var(--gauge-ki-line); color: var(--gauge-ki-line);
}
/* 執行價線：虛線，上下各外伸 3px；畫在 KI 線之上，兩者重合時仍看得出一條實線一條虛線 */
.dash-gauge-strike {
  position: absolute; top: -3px; bottom: -3px; width: 2px; margin-left: -1px;
  background: repeating-linear-gradient(to bottom, var(--gauge-strike) 0 3px, transparent 3px 5px);
  color: var(--gauge-strike);
}
.dash-gauge-dot {
  position: absolute; top: 50%; width: 12px; height: 12px; margin: -6px 0 0 -6px;
  box-sizing: border-box; border-radius: 50%; background: var(--ink); color: var(--ink);
  border: 2px solid #fff; box-shadow: 0 0 0 1px var(--ink);
}
.dash-gauge-mini .dash-gauge-dot { width: 10px; height: 10px; margin: -5px 0 0 -5px; }
/* 超出 40%–120% 的值夾在端點，再加一個朝外的三角：夾了不說，KO 130% 會看起來像 120%。 */
.dash-gauge .is-clip-lo::after, .dash-gauge .is-clip-hi::after {
  content: ""; position: absolute; top: 50%; margin-top: -4px; width: 0; height: 0;
  border: 4px solid transparent;
}
.dash-gauge .is-clip-hi::after { left: calc(100% + 3px); border-right-width: 0; border-left: 5px solid currentColor; }
.dash-gauge .is-clip-lo::after { right: calc(100% + 3px); border-left-width: 0; border-right: 5px solid currentColor; }
.dash-gauge-ki.is-clip-lo { color: var(--gauge-ki-edge); }
.dash-gauge-ki.is-clip-lo::after { right: auto; left: -9px; }
.dash-gauge-kiline.is-clip-lo::after { display: none; }  /* 粉色區那顆箭頭已經說了 */

.dash-gauge-legend {
  display: flex; flex-wrap: wrap; gap: 4px 14px; margin: 10px 2px 0;
  font-size: 11.5px; color: var(--meta);
}
.dash-lg { display: inline-block; width: 10px; height: 10px; margin-right: 4px; vertical-align: -1px; border-radius: 2px; }
.dash-lg-ki { background: var(--gauge-ki); }
.dash-lg-ko { width: 3px; background: var(--gauge-ko); }
.dash-lg-kiline { width: 3px; background: var(--gauge-ki-line); }
.dash-lg-strike {
  width: 3px; background: repeating-linear-gradient(to bottom, var(--gauge-strike) 0 3px, transparent 3px 5px);
}
.dash-lg-below { font-style: normal; width: auto; height: auto; margin-right: 0; font-weight: 700; color: var(--below-barrier); }
.dash-lg-dot { border-radius: 50%; background: var(--ink); }

/* 曝險：收在清單下方，點開才看三張表 */
.dash-expo { margin-top: 16px; background: var(--card); border: 1px solid var(--line); }
.dash-expo summary {
  list-style: none; display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 9px;
  min-height: 44px; box-sizing: border-box; padding: 11px 14px; cursor: pointer;
  color: var(--spine); font-weight: 600;
}
.dash-expo summary::-webkit-details-marker { display: none; }
.dash-expo summary::before {
  content: ""; flex: none; align-self: center; width: 0; height: 0;
  border: 5px solid transparent; border-left-color: var(--spine); transition: transform .15s ease;
}
.dash-expo[open] summary::before { transform: rotate(90deg); }
.dash-expo summary small { color: var(--meta); font-weight: 400; font-size: .8rem; }
.dash-expo > h3, .dash-expo > .dash-scroll { margin-left: 14px; margin-right: 14px; }
.dash-expo > h3 { font-size: 14px; margin-top: 14px; margin-bottom: 0; }
.dash-expo > h3 small { display: block; max-width: 40em; color: var(--meta); font-size: .78rem; font-weight: 400; margin-top: 2px; }
.dash-expo > .dash-scroll:last-child { margin-bottom: 12px; }
@media (hover: hover) and (pointer: fine) {
  .dash-expo summary:hover { background: #EAF0F6; }
}
@media (prefers-reduced-motion: reduce) {
  .dash-expo summary::before { transition: none; }
}

/* 明細 */
/* 單一欄要寫成 minmax(0, 1fr)：隱含的 auto 欄下限是 min-content，標的表（nowrap）會把
   整個明細撐出視窗，裡面那層 .dash-scroll 的橫捲就失效了。 */
.dash-detail-pane {
  display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; align-content: start;
  padding: 12px 14px 16px;
  background: var(--card); border: 1px solid var(--line); border-top: 2px solid var(--spine);
}
.dash-detail-pane > *, #dash-detail-body { min-width: 0; }
/* 明細本身是容器：資訊格的欄數看明細寬（雙欄窗格 280–750px、手機 260–370px），不看 shell */
#dash-detail-body {
  display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px;
  container: detail / inline-size;
}
/* 實心綠底白字（使用者 2026-11-10：只有綠字太不明顯；字不能細） */
.dash-back {
  justify-self: start; display: inline-flex; align-items: center; min-height: 44px;
  margin: -4px 0 0; padding: 0 18px 0 14px; border: 0; border-radius: 6px;
  background-color: var(--accent-teal);
  font: inherit; font-size: 15px; font-weight: 700; color: #fff; cursor: pointer;
  /* 進明細時 focus() 它來捲到明細頂端：手機頂端小標題列是 sticky，要讓出它的高度 */
  scroll-margin-top: calc(var(--fcn-topbar-h, 0px) + 8px);
  transition: background-color .15s ease;
}
.dash-back:active { background-color: #0F5960; }
@media (hover: hover) and (pointer: fine) {
  .dash-back:hover { background-color: #0F5960; }
}
.dash-back:focus-visible { outline: 2px solid var(--accent-teal); outline-offset: 2px; }
.dash-dh-title { margin: 0; font-size: 22px; line-height: 1.3; }
.dash-dh-title .note-link { font-weight: 800; }
.dash-dh-sub { margin: 2px 0 0; font-size: 13px; color: var(--meta); overflow-wrap: anywhere; }
/* 格線：每格畫自己的右、下 1px 線，最外圈那半條被 overflow 裁掉、外框由容器 border 給。
   往左上畫的話，最後一列沒排滿時空位上方那條線沒有格子負責畫，格線就斷了（2026-11-09
   使用者標出）；用 gap＋底色畫線則空位會露出一塊灰底。
   欄數：9 格（FCN／DACN 都是）寬時 3×3 剛好填滿，窄時 2 欄。 */
.dash-facts {
  margin: 0; display: grid; overflow: hidden; background: var(--card); border: 1px solid var(--line);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@container detail (min-width: 420px) {
  .dash-facts { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* 右線、下線各一道且只往一個方向偏移（寫成 1px 1px 會讓直線整條往下錯 1px） */
.dash-facts > div { min-width: 0; padding: 8px 12px; box-shadow: 1px 0 0 var(--line), 0 1px 0 var(--line); }
.dash-facts dt { font-size: 11.5px; color: var(--meta); }
.dash-facts dd {
  margin: 2px 0 0; font-size: 16px; font-weight: 700; font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.dash-facts .dash-sub { font-size: 12px; font-weight: 400; color: var(--meta); white-space: nowrap; }
.dash-facts dd .dash-score { font-size: 1.1rem; }
.dash-flags { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 0; }
.dash-flags p { flex-basis: 100%; margin: 2px 0; }
.dash-utable { margin: 0; }
.dash-utable td { vertical-align: middle; white-space: nowrap; }
.dash-chip {
  display: inline-block; margin-left: 5px; padding: 0 5px; border-radius: 4px;
  font-family: inherit; font-size: 10.5px; font-weight: 600; line-height: 1.6; white-space: nowrap;
  vertical-align: 1px;
}
.dash-chip-ko { background: var(--st-neutral-bg); color: var(--st-neutral); }
.dash-chip-ki { background: var(--st-near-bg); color: var(--st-near); }
.dash-urow .dash-worst { margin-left: 6px; }
.dash-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; }
.dash-acts .btn { margin: 0; }
.dash-detail-pane .dash-legend { margin: 4px 0 0; }

/* 寬表在自己的框裡橫捲，第一欄黏住（spec §7）：明細的標的表、曝險表、重疊矩陣、管理面板 */
.dash-scroll > .dash-table th:first-child,
.dash-scroll > .dash-table td:first-child { position: sticky; left: 0; z-index: 1; background: var(--card); }
/* 空狀態那種跨整列的一格（colspan）不黏：它本身就和表一樣寬 */
.dash-scroll > .dash-table td[colspan] { position: static; background: none; }
/* 表單的標的表／市場參數表（表單卡片上）：第一欄（代碼）黏住 */
.dash-scroll > table#underlyings th:first-child,
.dash-scroll > table#underlyings td:first-child,
.dash-scroll > table#market-rows th:first-child,
.dash-scroll > table#market-rows td:first-child { position: sticky; left: 0; z-index: 1; background: var(--card); }

/* 明細的標的區：每個寬度都是每檔一塊、兩行（使用者 2026-11-09：明細不要左右橫移——
   表格版在雙欄明細窗格放不下，只能橫捲；KI·期初·KO 那行灰字太複雜，價格直接標在線旁）：
     1 代號＋最弱＋已觸標籤｜最新收盤＋漲跌
     2 大障礙尺，KI（實線）、執行價（虛線）、KO（藍線）的價格標在各自的線下方，
       太近的換到上方（dashboard.js assignLabelSides／layoutGaugeLabels）
   仍是 detailHtml 產出的表格 DOM，只在這裡換版面；表頭藏起來（各線比例由尺的 aria-label 念，
   線旁數字 aria-hidden 不重念）。
   寬時各塊以格線並排（tests/test_dashboard_layout_css.py 守）。 */
.dash-utable { display: block; }
/* 各檔一塊、自動欄數並排；下限 min(300px, 100%)：窗格比 300 窄時退成一欄而不是溢出。
   auto-fit：只有一檔時佔滿整寬，不在右邊留一條空軌道 */
.dash-utable tbody {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  column-gap: 28px;
}
.dash-utable thead { display: none; }
.dash-utable tr.dash-urow {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-areas: "t t last" "g g g";
  /* baseline：同一行的格子高度不同（折行、已觸 chip）時，置中會讓數字上下錯開 */
  gap: 8px 10px; align-items: baseline; padding: 10px 2px 12px; border-bottom: 1px solid var(--line);
  /* 並排時 tbody 會把較矮的塊拉成同高；內容貼頂，否則多出的高度分到三行、兩塊的尺錯位 */
  align-content: start;
}
.dash-scroll > .dash-utable .dash-urow > td {
  display: block; position: static; padding: 0; border: 0; background: none; white-space: nowrap;
}
.dash-utable .dash-u-t { grid-area: t; }
.dash-utable .dash-u-last { grid-area: last; text-align: right; }
/* 第一行只有 2/3＋1/3 寬（塊最窄 300px）：大價位＋漲跌、長代號＋「最弱」放不下就折行，
   不能 nowrap 溢出窗格右緣（那會讓 .dash-uscroll 又出現橫捲） */
.dash-scroll > .dash-utable .dash-urow > td.dash-u-t,
.dash-scroll > .dash-utable .dash-urow > td.dash-u-last {
  display: flex; flex-wrap: wrap; align-items: baseline; row-gap: 2px;
}
.dash-scroll > .dash-utable .dash-urow > td.dash-u-last { justify-content: flex-end; }
.dash-utable .dash-u-g { grid-area: g; }
/* 超出 120%／低於 40% 的圓點夾在端點、再加朝外三角，從尺端伸出約 14px：尺左右內縮，
   否則 .dash-uscroll 會多出橫捲（實測 ratio 2.2 時 10px，右緣數字被淡出遮罩吃掉） */
.dash-scroll > .dash-utable .dash-urow > td.dash-u-g { padding-left: 14px; padding-right: 14px; }
/* 線旁價格：上下各留一列標籤高（17px），標籤絕對定位在線的位置；尺端 8% 以內靠邊對齊
   （is-start／is-end），置中會凸出尺外。字色＝線色，title 說是哪條線。 */
.dash-glab-wrap { position: relative; display: block; padding: 17px 0; }
.dash-glab {
  position: absolute; transform: translateX(-50%); white-space: nowrap;
  font-family: var(--mono); font-size: 11px; line-height: 14px; font-variant-numeric: tabular-nums;
}
.dash-glab.is-start { transform: none; }
.dash-glab.is-end { transform: translateX(-100%); }
.dash-glab[data-side="below"] { bottom: 0; }
.dash-glab[data-side="above"] { top: 0; }
.dash-glab-ki { color: var(--gauge-ki-line); }
.dash-glab-strike { color: var(--gauge-strike); }
.dash-glab-ko { color: var(--gauge-ko); }
.dash-scroll > .dash-utable .dash-urow > td.dash-u-g .dash-split-note {
  display: block; margin-top: 2px; font-size: 11.5px; white-space: normal;
}
.dash-utable .dash-gauge-mini { height: 16px; min-width: 0; }
.dash-utable .dash-gauge-mini .dash-gauge-dot { width: 12px; height: 12px; margin: -6px 0 0 -6px; }
.dash-utable .dash-chg { min-width: 0; }
@container shell (min-width: 600px) {
  .dash-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dash-cards { gap: 12px; margin-bottom: 16px; }
  .dash-card { padding: 12px 16px; }
  .dash-card-v { font-size: 1.7rem; }
  .dash-detail-pane { padding: 16px 20px 20px; }
}
@container shell (min-width: 800px) {
  html:not(.is-phone) .dash-split { grid-template-columns: minmax(300px, 340px) minmax(0, 1fr); }
  html:not(.is-phone) .dash-split[data-view] > .dash-list-pane { display: block; }
  html:not(.is-phone) .dash-split[data-view] > .dash-detail-pane { display: grid; }
  /* 明細黏在頂端、比視窗高時自己捲：往下捲清單點別張，明細仍在眼前（換票時
     dashboard.js 把捲軸歸零）。高度用 --vh，裸 vh 在 html{zoom} 下會超出視窗。 */
  html:not(.is-phone) .dash-detail-pane {
    position: sticky; top: calc(var(--fcn-topbar-h, 0px) + 16px);
    max-height: calc(var(--vh) - var(--fcn-topbar-h, 0px) - 32px);
    overflow-y: auto;
  }
  /* sticky 只在自己的格線區內移動：格線區之後剩多少高度，捲到頁底時明細就被頂上去多少
     （減掉 top）。頁尾說明已放進清單欄；頁面底部留白（.wrap 72px）在這裡收到 16px。 */
  html:not(.is-phone) .dash { margin-bottom: -56px; }
  html:not(.is-phone) .dash-list { grid-template-columns: minmax(0, 1fr); }
  html:not(.is-phone) .dash-back { display: none; }
  /* 選取只在雙欄時標出來（推入版的清單畫面沒有「目前這張」可言） */
  html:not(.is-phone) .dash-item[aria-current="true"] {
    border-color: var(--spine); box-shadow: inset 3px 0 0 var(--spine); background: #F6F9FC;
  }
}
@container shell (min-width: 1200px) {
  html:not(.is-phone) .dash-split { grid-template-columns: 360px minmax(0, 1fr); }
  html:not(.is-phone) .dash-detail-pane { padding: 18px 24px 22px; }
}

/* ---- 4. 導覽外殼：側欄／直式圖示導覽列／底部導覽（spec 2026-11-08-ui-redesign-sn §2）----
   版型看「可用寬＝視窗寬 ÷ 介面縮放」，所以一律用 .shell 上的 container query。
   media query 看不到 html{zoom}（實測 zoom 200%、視窗 1178：media query 看到 1178，
   container query 看到 589），用它切導覽，放大後的桌機就會拿到塞不下的側欄。
     < 600     底部導覽 5 格＋頂端小標題列（.appbar）；側欄隱藏。
     600–1199  76px 直式圖示導覽列（白底、青色選中）；自製SN／設定／說明收進「更多」選單。
     ≥ 1200    完整側欄（墨藍底、含文字）；「＋ 新增票券」是長方形按鈕；底部說明／設定／版本。
   基底規則＝手機版（行動優先），兩個 @container 往上疊。
   **手機橫放不進雙欄**：html.is-phone 仍是「是不是手機」的唯一來源。導覽本身只看寬度
   （橫放手機 844 寬→直式導覽列，正確），但之後的「清單＋明細雙欄」規則不能只看寬度——
   一律讀 --fcn-two-pane（.page 上；只在可用寬 ≥ 800 且非手機時為 1），或在選擇器前
   加 html:not(.is-phone)。JS 用 getComputedStyle(.page).getPropertyValue("--fcn-two-pane")。 */
.shell { container: shell / inline-size; display: flex; align-items: flex-start; }
/* .page 也是容器：內容區（扣掉側欄後）的寬度給頁內元件用（cqw，例如候選券寬表）。 */
.page {
  container: page / inline-size; flex: 1 1 auto; min-width: 0;
  --fcn-two-pane: 0;
  /* 新增票券的步驟：可用寬 < 800 開頁只展開第一個未完成段（app.js stepperIsNarrow） */
  --fcn-stepper: 1;
  /* 底部導覽是 fixed，內容底部要讓出它的高度（含 iPhone 底部手勢條的安全區） */
  padding-bottom: calc(var(--bottomnav-h) + env(safe-area-inset-bottom, 0px));
}

.brand { display: flex; align-items: center; gap: 12px; color: #fff; cursor: default; }
.brand-mark {
  font-family: var(--mono); font-weight: 700; font-size: 18px; letter-spacing: .08em;
  background: #fff; color: var(--ink); padding: 2px 8px;
}
.brand-text { font-size: 15px; font-weight: 600; display: flex; flex-direction: column; line-height: 1.25; }
.brand-text small { font-size: 10.5px; font-weight: 400; color: #9FB0C4; letter-spacing: .06em; }

/* 手機頂端小標題列：識別＋所在頁名稱。sticky；實高由 app.js 寫進 --fcn-topbar-h。
   viewport-fit=cover（base.html）讓底色延伸到瀏海後面，內容用安全區內距推回來。 */
.appbar {
  position: sticky; top: 0; z-index: 60;
  display: flex; align-items: center; gap: 10px; min-height: 46px;
  background: var(--ink); color: #fff; border-bottom: 3px solid var(--spine);
  padding: env(safe-area-inset-top, 0px) max(12px, env(safe-area-inset-right, 0px)) 0 max(12px, env(safe-area-inset-left, 0px));
}
.appbar .brand-mark { font-size: 14px; padding: 2px 6px; }
.appbar-title {
  min-width: 0; font-size: 15px; font-weight: 600; letter-spacing: .02em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* PWA（pwa.js）：重新整理鈕只在主畫面 app 顯示；橫幅是 fixed，不佔版面流。
   登入過期／新版橫幅貼在標題列下（--fcn-topbar-h 由 app.js 量）；安裝引導浮在底部導覽上，
   層級 66：高於底部導覽（65）與標題列（60）、低於對話框與 toast（90）。 */
.appbar-refresh { display: none; }
html.is-app .appbar-refresh {
  display: inline-flex; align-items: center; justify-content: center;
  margin-left: auto; min-width: 44px; min-height: 44px; padding: 0;
  background: var(--ink); color: #fff; border: 0; cursor: pointer;
}
.pwa-bar {
  /* 平板寬度的主畫面 app 沒有手機標題列（--fcn-topbar-h＝0），要自己讓開狀態列。 */
  position: fixed; z-index: 66; left: 0; right: 0;
  top: max(var(--fcn-topbar-h, 0px), env(safe-area-inset-top, 0px));
  display: flex; align-items: center; gap: 10px; font-size: 14px;
  padding: 8px max(12px, env(safe-area-inset-right, 0px)) 8px max(12px, env(safe-area-inset-left, 0px));
  background: var(--card); color: var(--ink); border-bottom: 1px solid var(--line);
}
/* 登入過期有逃生門（重新登入）→ 黃，不是紅（頁級提示分色規則）。 */
.pwa-bar-warn { background: var(--verify-bg); border-bottom-color: var(--verify-line); }
.pwa-bar-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.pwa-btn {
  flex: none; min-height: 44px; padding: 0 16px; font-size: 14px; font-weight: 600;
  background: var(--accent-teal); color: #fff; border: 0; border-radius: 8px; cursor: pointer;
}
/* 安裝引導卡片在 main 最上方、跟著版面捲動（pwa.js）；浮動會蓋住新增票券頁的固定送出列。 */
.pwa-install {
  display: flex; align-items: flex-start; gap: 8px; margin: 12px 0;
  padding: 12px 8px 12px 14px; background: var(--card); color: var(--ink);
  border: 1px solid var(--line); border-left: 4px solid var(--accent-teal); border-radius: 10px;
}
.pwa-install-text { flex: 1 1 auto; min-width: 0; font-size: 14px; }
.pwa-install-text p { margin: 4px 0 0; }
.pwa-install .pwa-sub { color: var(--meta); }
.pwa-slot { margin-top: 8px; }
.pwa-x {
  flex: none; min-width: 44px; min-height: 44px; font-size: 22px; line-height: 1;
  background: transparent; color: var(--meta); border: 0; cursor: pointer;
}
html.kb-open .pwa-install { display: none; }

/* 側欄（600 以上才顯示）。sticky 滿高、自己可捲（矮視窗時底部的帳號仍拿得到）；
   高度用 --vh（已除掉 zoom；支援時是 dvh），裸 100vh 在 zoom 下會被再放大一次。 */
.sidenav {
  display: none; position: sticky; top: 0; z-index: 50; flex: none;
  height: 100vh; height: var(--vh);
  flex-direction: column; gap: 2px;
  overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none;
}
.nav-list { display: flex; flex-direction: column; gap: 2px; }
.nav-icon { width: 20px; height: 20px; flex: none; }
.nav-long { display: none; }
/* 直式導覽列的項目：圖示在上、10.5px 短標籤在下，整格 ≥ 44px 可點 */
.nav-item, .nav-add {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  min-height: 58px; width: 100%; padding: 4px 2px; margin: 0;
  font: inherit; font-size: 10.5px; line-height: 1.2; text-align: center;
  color: var(--meta); text-decoration: none; background: none; border: 0; border-radius: 6px;
  cursor: pointer;
  transition: color .15s ease, background-color .15s ease;
}
.nav-item.is-active { color: var(--accent-teal); font-weight: 700; }
.nav-item:focus-visible, .nav-add:focus-visible, .bn-item:focus-visible, .more-item:focus-visible,
.nav-more-menu a:focus-visible, .nav-more-menu button:focus-visible {
  outline: 2px solid var(--accent-teal); outline-offset: -2px;
}
/* ＋新增：青色正圓＋「新增」，緊貼「更多」下方 6px（不推到底） */
/* 與「更多」之間總共 6px：.nav-list 的 gap 2px ＋ 這裡 4px */
.nav-add { margin-top: 4px; color: var(--accent-teal); font-weight: 700; }
.nav-plus {
  width: 40px; height: 40px; border-radius: 50%; flex: none;
  display: grid; place-items: center; background: var(--accent-teal); color: #fff;
}
.nav-foot { display: none; }
.nav-meta {
  display: flex; flex-wrap: wrap; align-items: center; column-gap: 4px;
  margin: 2px 0 0; padding: 0 10px; font-size: 11px; line-height: 1.3; color: var(--side-fg);
}
/* 連結本身撐到 44px 高才點得到（觸控平板也會落到 ≥ 1200 的完整側欄） */
.nav-meta a { display: inline-flex; align-items: center; min-height: 44px; color: var(--side-fg); }
.nav-account { flex: none; }
@media (hover: hover) and (pointer: fine) {
  .nav-item:hover { color: var(--ink); background: var(--paper); }
  .nav-item.is-active:hover { color: var(--accent-teal); }
  .nav-add:hover .nav-plus { background: #105A62; }
}
.nav-item:active { background: var(--spine-soft); }
.nav-add:active .nav-plus { background: #105A62; }

/* 「更多」選單：fixed（側欄可捲，absolute 會被它的 overflow 裁掉），top 由 nav.js
   對齊「更多」那一格；left 貼在 76px 導覽列右邊。 */
.nav-more-menu {
  position: fixed; z-index: 80;
  left: calc(76px + env(safe-area-inset-left, 0px) + 6px);
  min-width: 210px; display: grid; gap: 2px; padding: 6px;
  background: var(--card); border: 1px solid var(--line); border-radius: 8px;
  box-shadow: 0 6px 16px rgba(23, 38, 59, .16), 0 16px 40px rgba(41, 82, 126, .18);
}
.nav-more-menu[hidden] { display: none; }
.nav-more-menu a, .nav-more-menu button {
  display: flex; align-items: center; gap: 10px; min-height: 44px; width: 100%;
  padding: 0 12px; margin: 0; border: 0; border-radius: 6px; background: none;
  font: inherit; font-size: 14px; color: var(--ink); text-align: left; text-decoration: none;
  cursor: pointer;
}
.nav-more-menu .nav-icon { color: var(--meta); }
@media (hover: hover) and (pointer: fine) {
  .nav-more-menu a:hover, .nav-more-menu button:hover { background: var(--paper); }
}
.nav-more-menu a:active, .nav-more-menu button:active { background: var(--spine-soft); }

/* 底部導覽（< 600）：5 格均分，正中的「新增」是青色正圓。fixed（.shell 的
   container-type 不建立 fixed 的包含區塊，實測 Chromium 捲動 500px 後仍貼視窗）。 */
.bottomnav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 65;
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  background: var(--card); border-top: 1px solid var(--line);
  box-shadow: 0 -2px 10px rgba(23, 38, 59, .06);
  padding: 4px max(4px, env(safe-area-inset-right, 0px)) 0 max(4px, env(safe-area-inset-left, 0px));
  padding-bottom: env(safe-area-inset-bottom, 0px);
}
.bn-item {
  display: grid; justify-items: center; align-content: center; gap: 2px;
  min-height: 52px; min-width: 0; font-size: 10.5px; line-height: 1.2;
  color: var(--meta); text-decoration: none; border-radius: 6px;
}
.bn-item .nav-icon { width: 22px; height: 22px; }
.bn-item.is-active { color: var(--accent-teal); font-weight: 700; }
.bn-add { color: var(--accent-teal); font-weight: 700; }
.bn-plus {
  width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center; background: var(--accent-teal); color: #fff;
}
.bn-plus .nav-icon { width: 20px; height: 20px; }
.bn-item:active { background: var(--paper); }
/* 手機打字時（nav.js：焦點在 main 裡的文字輸入框就掛 html.kb-open）收起底部導覽：
   interactive-widget=resizes-content 讓版面縮到鍵盤上緣，fixed 的導覽會浮在鍵盤上、吃掉
   打字時本來就不多的高度。 */
html.is-phone.kb-open .bottomnav { display: none; }

/* 600–1199：直式圖示導覽列 */
@container shell (min-width: 600px) {
  .appbar { display: none; }
  .bottomnav { display: none; }
  /* padding-top：主畫面 app 的狀態列是 black-translucent（base.html），標題列藏起來後
     內容會鑽到狀態列底下；瀏覽器分頁裡這個 inset 是 0。 */
  .page { padding-top: env(safe-area-inset-top, 0px); padding-bottom: 0; padding-right: env(safe-area-inset-right, 0px); }
  .sidenav {
    display: flex; width: calc(76px + env(safe-area-inset-left, 0px));
    padding: max(10px, env(safe-area-inset-top, 0px)) 4px 12px calc(4px + env(safe-area-inset-left, 0px));
    background: var(--card); border-right: 1px solid var(--line);
  }
  .sidenav .brand { justify-content: center; padding: 2px 0 12px; }
  .sidenav .brand-mark { background: var(--ink); color: #fff; font-size: 14px; padding: 2px 6px; }
  .sidenav .brand-text { display: none; }
  .nav-sn { display: none; }
  /* 帳號在最底；其餘（含＋新增）照順序由上往下排 */
  .nav-account { margin-top: auto; }
}

/* 可用寬 ≥ 800 且不是手機：才可以走「清單＋明細」雙欄（手機橫放維持單欄推入）。 */
@container shell (min-width: 800px) {
  html:not(.is-phone) .page { --fcn-two-pane: 1; }
  /* 新增票券的步驟：≥ 800 開頁全部展開（app.js 讀 --fcn-stepper；基底 1＝窄版） */
  .page { --fcn-stepper: 0; }
}

/* ≥ 1200：完整側欄 */
@container shell (min-width: 1200px) {
  .sidenav {
    width: calc(212px + env(safe-area-inset-left, 0px));
    padding: max(16px, env(safe-area-inset-top, 0px)) 12px 14px calc(12px + env(safe-area-inset-left, 0px));
    background: var(--ink); color: var(--side-fg); border-right: 0;
  }
  .sidenav .brand { justify-content: flex-start; padding: 0 6px 18px; }
  .sidenav .brand-mark { background: #fff; color: var(--ink); font-size: 16px; padding: 2px 7px; }
  .sidenav .brand-text { display: flex; color: #fff; }
  .nav-item {
    flex-direction: row; justify-content: flex-start; gap: 10px;
    min-height: 44px; padding: 0 10px; font-size: 14px; text-align: left; color: var(--side-fg);
  }
  .nav-item .nav-icon { width: 18px; height: 18px; }
  .nav-item.is-active { color: #fff; background: var(--side-sel); }
  .nav-long { display: inline; }
  .nav-short { display: none; }
  .nav-sn { display: flex; }
  .nav-more { display: none; }
  .nav-add {
    flex-direction: row; gap: 6px; min-height: 44px; margin-top: 12px; padding: 0 12px;
    background: var(--accent-teal); color: #fff; font-size: 14px; border-radius: 6px;
  }
  .nav-add .nav-plus { width: auto; height: auto; background: none; }
  .nav-add .nav-plus .nav-icon { width: 16px; height: 16px; }
  .nav-foot { display: flex; flex-direction: column; gap: 2px; margin-top: auto; padding-top: 12px; }
  .nav-foot .nav-item { min-height: 44px; font-size: 13px; }
  .nav-account { margin-top: 8px; }
  @media (hover: hover) and (pointer: fine) {
    .nav-item:hover { color: #fff; background: #22334C; }
    .nav-item.is-active:hover { color: #fff; background: var(--side-sel); }
    .nav-add:hover { background: #105A62; }
    .nav-add:hover .nav-plus { background: none; }
  }
  .nav-item:active { background: #22334C; }
  .nav-add:active { background: #105A62; }
  .nav-add:active .nav-plus { background: none; }
}
@media (prefers-reduced-motion: reduce) {
  .nav-item, .nav-add { transition: none; }
}

/* 「更多」頁（手機底部導覽第五格；也是「更多」選單沒有 JS 時的去處） */
.more-group { margin: 0 0 22px; }
.more-group h2 { font-size: 13px; color: var(--meta); font-weight: 600; letter-spacing: .04em; margin: 0 0 6px; }
.more-list { list-style: none; margin: 0; padding: 0; background: var(--card); border: 1px solid var(--line); }
.more-list li + li { border-top: 1px solid var(--line); }
.more-item {
  display: flex; align-items: center; gap: 12px; width: 100%; min-height: 48px;
  padding: 0 16px; margin: 0; border: 0; background: none; cursor: pointer;
  font: inherit; font-size: 15px; color: var(--ink); text-align: left; text-decoration: none;
}
.more-item .nav-icon { color: var(--meta); }
.more-item:active { background: var(--paper); }
@media (hover: hover) and (pointer: fine) {
  .more-item:hover { background: #F7F9F6; }
}
.more-meta { color: var(--meta); font-size: 12px; }

/* 使用說明頁（/help）：目錄＋各主題的圖解截圖。截圖裡的標籤是琥珀色（--st-near
   系列），與「需要注意」同一個語意色；圖下說明的編號對應圖上的圓標。 */
.help-key { background: #FFF4D6; border: 1px solid #D9A21B; color: #4A3500; padding: 0 4px; }
.help-toc { background: var(--card); border: 1px solid var(--line); border-left: 4px solid var(--spine); padding: 10px 18px; margin: 0 0 26px; }
.help-toc ol { margin: 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: 6px 22px; }
.help-toc a { font-size: 14px; font-weight: 600; color: var(--spine); }
.help-topic { margin: 0 0 40px; scroll-margin-top: calc(var(--fcn-topbar-h, 0px) + 12px); }
.help-topic h2 { font-size: 20px; margin: 0 0 6px; padding-bottom: 6px; border-bottom: 1px solid var(--line); }
.help-topic > p { font-size: 14px; line-height: 1.7; max-width: 46em; margin: 8px 0 12px; }
.help-topic h3 { font-size: 15px; margin: 18px 0 6px; }
.help-steps { font-size: 14px; line-height: 1.75; max-width: 46em; margin: 0; padding-left: 1.6em; }
.help-shot { margin: 14px 0 18px; }
.help-shot img {
  display: block; max-width: 100%; height: auto; background: var(--card);
  border: 1px solid var(--line); box-shadow: 0 1px 3px rgba(23, 38, 59, .08);
}
.help-cap { font-size: 12.5px; color: var(--meta); margin: 6px 0 0; }
/* 桌機版／手機版截圖二選一：手機判定只看 html.is-phone（base.html head script 設的唯一來源）。
   「點圖可放大」只在手機出現（桌機的圖本來就是原尺寸）。 */
.help-v-phone, .help-tap { display: none; }
html.is-phone .help-shot.has-phone .help-v-desk { display: none; }
html.is-phone .help-v-phone, html.is-phone .help-tap { display: block; }
.help-tap { font-size: 12px; color: var(--meta); margin: 4px 0 0; }
.help-group { font-size: 13px; color: var(--meta); font-weight: 600; letter-spacing: .04em; margin: 8px 0 10px; }
.help-topic .help-title { font-size: 18px; margin: 0 0 6px; padding-bottom: 6px; border-bottom: 1px solid var(--line); }
.help-topic h4 { font-size: 14px; margin: 16px 0 6px; }
.help-callouts { font-size: 13.5px; line-height: 1.65; max-width: 46em; margin: 6px 0 0; padding-left: 1.8em; }
.help-callouts li::marker { font-weight: 700; color: var(--st-near); }
.guide-more { font-size: 13px; margin: 10px 0 4px; }
.guide-more a { font-weight: 600; }

/* ---- 帳號選單（spec 2026-11-08 §6；只在雲端有登入身分時渲染）----
   同一顆 .acct-btn：直式導覽列（600–1199）只剩頭像；完整側欄（≥ 1200）是
   頭像＋email（截斷）＋角色。選單 fixed（側欄可捲，absolute 會被裁掉）；水平位置在
   這裡、垂直位置由 account.js 依 --acct-menu-place 對齊（up＝按鈕上方，side＝按鈕右邊、
   底緣對齊）。
   **層級**：選單在 .sidenav 裡，而 .sidenav（sticky＋z-index 50）自成堆疊脈絡，所以
   下面的 z-index 只在側欄內排序，對頁面其他部分的有效層級是 50。選單只在 ≥ 600 出現，
   那時比 50 高的外殼層（.appbar 60、.bottomnav 65）都是 display:none。比 50 高的只剩
   暫時浮層（.ivi-dialog 70、.toast-host 90、.sn-pop 120），它們本來就該蓋在選單上；
   <dialog> 走 top layer（tests/test_account_menu.py 守外殼這幾點）。 */
.acct-av {
  width: 36px; height: 36px; border-radius: 50%; flex: none;
  display: grid; place-items: center; background: var(--accent-teal); color: #fff;
  font-weight: 700; font-size: 15px; line-height: 1;
}
.acct-btn {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; min-height: 52px; padding: 6px 4px; margin: 0;
  border: 0; border-radius: 8px; background: none; color: inherit;
  font: inherit; text-align: left; cursor: pointer;
}
.acct-btn:focus-visible { outline: 2px solid var(--accent-teal); outline-offset: -2px; }
.acct-text, .acct-chev { display: none; }
.acct-menu {
  position: fixed; z-index: 80; --acct-menu-place: up;
  left: calc(76px + env(safe-area-inset-left, 0px) + 6px);
  width: 260px; display: grid; gap: 2px; padding: 6px;
  background: var(--card); color: var(--ink); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 6px 16px rgba(23, 38, 59, .16), 0 16px 40px rgba(41, 82, 126, .18);
}
.acct-menu[hidden] { display: none; }
.acct-menu-head {
  display: flex; align-items: center; gap: 10px; padding: 8px;
  border-bottom: 1px solid var(--line); margin-bottom: 4px;
}
.acct-menu-head .acct-av { width: 32px; height: 32px; font-size: 14px; }
.acct-menu-id { display: grid; min-width: 0; line-height: 1.35; }
.acct-menu-id b { font-size: 13px; overflow-wrap: anywhere; }
.acct-menu-id small { color: var(--meta); font-size: 11px; }
.acct-menu [role="menuitem"] {
  display: flex; align-items: center; gap: 10px; min-height: 44px; width: 100%;
  padding: 0 10px; margin: 0; border: 0; border-radius: 6px; background: none;
  font: inherit; font-size: 14px; color: var(--ink); text-align: left; text-decoration: none;
  cursor: pointer;
}
.acct-menu [role="menuitem"] .nav-icon { width: 18px; height: 18px; color: var(--meta); }
.acct-menu [role="menuitem"]:focus-visible { outline: 2px solid var(--accent-teal); outline-offset: -2px; }
.acct-sep { border-top: 1px solid var(--line); margin: 4px 0; }
@media (hover: hover) and (pointer: fine) {
  .acct-btn:hover { background: var(--paper); }
  .acct-menu [role="menuitem"]:hover { background: var(--paper); }
}
.acct-menu [role="menuitem"]:active { background: var(--spine-soft); }
@container shell (min-width: 600px) {
  .acct-menu { --acct-menu-place: side; }
}
@container shell (min-width: 1200px) {
  .acct-btn {
    justify-content: flex-start; min-height: 52px; padding: 8px;
    border: 1px solid var(--side-line); color: #fff;
  }
  .acct-btn .acct-av { width: 30px; height: 30px; font-size: 13px; }
  .acct-text { display: grid; flex: 1; min-width: 0; line-height: 1.3; }
  .acct-email { font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .acct-role { color: var(--side-fg); font-size: 11px; }
  .acct-chev { display: inline; color: var(--side-fg); font-size: 11px; }
  .acct-btn[aria-expanded="true"] .acct-chev { transform: rotate(180deg); }
  .acct-menu {
    --acct-menu-place: up;
    /* 比側欄寬一點（email 少斷行）；蓋到內容左緣幾十 px，選單本來就浮在內容上 */
    left: calc(12px + env(safe-area-inset-left, 0px)); width: 260px;
  }
  .acct-btn:focus-visible { outline-color: #8DB4DC; outline-offset: 2px; }
  @media (hover: hover) and (pointer: fine) {
    .acct-btn:hover { background: #22334C; }
  }
}

/* 「更多」頁最上面的帳號卡（< 600 唯一看得到目前帳號的地方） */
.acct-card {
  display: flex; align-items: center; gap: 12px; margin: 0 0 18px; padding: 14px;
  background: var(--card); border: 1px solid var(--line);
}
.acct-card .acct-av { width: 44px; height: 44px; font-size: 18px; }
.acct-card-id { display: grid; flex: 1; min-width: 0; line-height: 1.4; }
.acct-card-id small { color: var(--meta); font-size: 11.5px; }
.acct-card-id b { font-size: 14px; overflow-wrap: anywhere; }
.acct-role-tag {
  flex: none; font-size: 11px; font-weight: 700; color: var(--accent-teal);
  background: #E3F1EE; padding: 2px 8px; border-radius: 999px; white-space: nowrap;
}

/* 本機預覽帳號介面（FCN_PREVIEW_ACCOUNT）的「預覽」標記：顏色跟著所在文字（側欄淺灰、
   選單與卡片灰），只用外框區分——不是警示，不用紅、不用黃。 */
.acct-preview-tag {
  display: inline-block; margin-left: 6px; padding: 0 6px; border: 1px solid currentColor;
  border-radius: 999px; font-size: 10.5px; font-weight: 700; line-height: 1.5;
  vertical-align: 1px; white-space: nowrap;
}
.acct-card .acct-preview-tag { flex: none; margin-left: 0; color: var(--meta); }

/* 成員管理（唯讀） */
.members-email { overflow-wrap: anywhere; }
.members-flag { color: var(--verify-ink); background: var(--verify-bg); padding: 1px 6px; font-size: 12px; }

/* ---- 入口頁 /welcome（spec 2026-11-08 §6）----
   獨立頁、不套導覽外殼，也不套帳號縮放（沒有租戶 context），所以 --z 恆為 1。
   版型照樣看自己的容器寬：< 900 上下疊（說明縮成頂部一段、登入卡接在下面），
   ≥ 900 左右分欄（左說明、右登入）。 */
.welcome-body { background: var(--paper); }
.wl-root { container: welcome / inline-size; }
.wl { display: grid; grid-template-columns: minmax(0, 1fr); align-content: start; min-height: 100vh; min-height: var(--vh); }
.wl-brand {
  display: grid; gap: 14px; align-content: start; background: var(--ink); color: #fff;
  padding: max(28px, env(safe-area-inset-top, 0px)) max(20px, env(safe-area-inset-right, 0px)) 22px max(20px, env(safe-area-inset-left, 0px));
}
.wl-lock { display: flex; align-items: center; gap: 12px; }
.wl-pitch { display: grid; gap: 12px; }
/* keep-all：只在標點與空白處換行（「比價與持／倉監控」那種斷在詞中間的換行不會出現） */
.wl-brand h1 { margin: 0; font-size: 21px; font-weight: 900; line-height: 1.35; word-break: keep-all; overflow-wrap: anywhere; }
.wl-points { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; font-size: 14px; color: #DCE5EE; }
.wl-points li { display: flex; align-items: center; gap: 10px; }
.wl-points li::before { content: ""; width: 8px; height: 8px; border-radius: 2px; background: var(--teal-on-ink); flex: none; }
.wl-prev {
  display: none; gap: 9px; margin: 0; max-width: 360px; padding: 14px 16px; font-size: 12px;
  border: 1px solid rgba(255, 255, 255, .14); background: rgba(255, 255, 255, .05); border-radius: 12px;
}
.wl-prev figcaption { display: flex; align-items: baseline; gap: 8px; }
.wl-prev figcaption b { font-size: 14px; }
.wl-prev figcaption small { margin-left: auto; color: var(--side-fg); }
.wl-tag { font-size: 10.5px; color: #DCE5EE; border: 1px solid rgba(255, 255, 255, .35); border-radius: 4px; padding: 0 5px; }
.wl-gr { display: grid; grid-template-columns: 46px minmax(0, 1fr) 48px; gap: 8px; align-items: center; font-family: var(--mono); }
.wl-gr span:last-child { text-align: right; color: #DCE5EE; }
.wl-tr { position: relative; height: 10px; background: rgba(255, 255, 255, .1); border-radius: 4px; }
.wl-tr i { position: absolute; inset: 0 auto 0 0; background: rgba(240, 138, 120, .5); border-radius: 4px 0 0 4px; }
.wl-tr b { position: absolute; top: -3px; bottom: -3px; width: 2px; background: #8DB4DC; }
.wl-tr em { position: absolute; top: 50%; width: 10px; height: 10px; margin: -5px 0 0 -5px; border-radius: 50%; background: #fff; }
.wl-side { padding: 14px max(14px, env(safe-area-inset-right, 0px)) max(18px, env(safe-area-inset-bottom, 0px)) max(14px, env(safe-area-inset-left, 0px)); }
.wl-col { display: grid; gap: 14px; width: 100%; }
.wl-card {
  display: grid; gap: 14px; padding: 20px;
  background: var(--card); border: 1px solid var(--line); border-radius: 16px;
}
.wl-card h2 { margin: 0; font-size: 22px; font-weight: 900; }
.wl-sub { margin: -6px 0 0; color: var(--meta); font-size: 14px; }
.gbtn {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 48px; padding: 0 18px; margin: 0;
  border: 1px solid #C3CCD6; border-radius: 999px; background: #fff; color: var(--ink);
  font: inherit; font-size: 15px; font-weight: 700; text-decoration: none; cursor: pointer;
}
.gbtn .gm {
  width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--meta); flex: none;
  display: grid; place-items: center; font: 700 11px var(--mono); color: var(--meta);
}
.gbtn .nav-icon { width: 18px; height: 18px; color: var(--meta); }
.gbtn.sec { min-height: 44px; border-radius: 10px; font-size: 14px; }
.gbtn:focus-visible { outline: 2px solid var(--accent-teal); outline-offset: 2px; }
.gbtn:active { background: var(--paper); }
@media (hover: hover) and (pointer: fine) {
  .gbtn:hover { background: #F7F9F6; }
}
.wl-note { margin: 0; font-size: 12.5px; line-height: 1.65; color: var(--meta); }
.wl-note:empty { display: none; }
.wl-msg { padding: 10px 12px; border-radius: 10px; font-size: 13px; line-height: 1.6; overflow-wrap: anywhere; }
.wl-msg.warn { background: var(--verify-bg); color: var(--verify-ink); }
.wl-msg.ok { background: #E3F1EE; color: var(--ki); }
.wl-msg code { font-family: var(--mono); font-size: 12px; }
.wl-foot { margin: 0; text-align: center; font-size: 12px; color: var(--meta); }
@container welcome (min-width: 900px) {
  .wl { grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); align-content: stretch; }
  .wl-brand { align-content: space-between; gap: 28px; padding: 40px 44px; }
  .wl-pitch { gap: 16px; }
  .wl-brand h1 { font-size: 32px; max-width: 18em; }
  .wl-points { gap: 10px; }
  .wl-prev { display: grid; }
  .wl-side { display: grid; place-items: center; padding: 40px; }
  .wl-col { width: min(400px, 100%); justify-items: stretch; }
  .wl-card { gap: 16px; padding: 28px; box-shadow: 0 10px 30px rgba(20, 30, 40, .08); }
}

.btn.cta {
  background: var(--spine); color: #fff; border-color: #4E7FB8;
  font-weight: 600; margin: 0;
}
@media (hover: hover) and (pointer: fine) {
  .btn.cta:hover { background: #35659B; box-shadow: 0 2px 10px rgba(41, 82, 126, .55); }
}

/* 掃描完成後才出現的匯出鈕。綠色沿用既有的 --ki（沉青）而不另立新色——
   本系統的紅／青已分配給「向上出場」與「下檔保護」，但那是**資料編碼**的
   語義；這裡是 chrome（操作介面）層的「完成／可取用」，兩者不在同一個
   閱讀平面上，不會讓人把按鈕誤讀成障礙方向。 */
.btn.done { background: var(--ki); color: #fff; border-color: #17564C; font-weight: 600; }
@media (hover: hover) and (pointer: fine) {
  .btn.done:hover { background: #17564C; }
}
/* `.btn` 的 display: inline-block（特異度 0,1,0）會蓋掉瀏覽器對 `[hidden]`
   的預設 display: none（UA 樣式表的屬性選擇器輸給作者樣式），於是 HTML 的
   hidden 屬性完全失效——實測匯出鈕從頁面載入就可見。這條把它補回來。
   （斷言「HTML 裡有 hidden」的測試會綠著而按鈕仍可見，所以另加了一條斷言
   本規則存在的 CSS 守衛。） */
.btn[hidden] { display: none; }

/* ---- 5. 返回：從一行小字改成看得出可按的控制項 ---- */
/* 箭頭由 ::before 提供而非寫在文字裡，才能在 hover 時單獨往回位移——
   「往回」這個動作本身就是提示，比任何說明文字都直接。 */
.crumb { margin: 0 0 16px; }
.crumb a {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 14px 7px 11px; text-decoration: none;
  border: 1px solid var(--spine-soft); background: var(--card);
  color: var(--spine); font-size: 13px; font-weight: 600;
  transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.crumb a::before { content: "←"; transition: transform .15s ease; }
@media (hover: hover) and (pointer: fine) {
  .crumb a:hover {
    background: #EAF0F6; border-color: var(--spine);
    box-shadow: 0 2px 6px rgba(23, 38, 59, .12);
  }
  .crumb a:hover::before { transform: translateX(-3px); }
}
@media (prefers-reduced-motion: reduce) {
  .crumb a, .crumb a::before, .crumb a:hover::before { transition: none; transform: none; }
}

/* 實心綠＝「加入持倉」作為所在情境的主要行動（新增票券頁與加入持倉視窗），
   與實心藍的「產生完整報告」同等分量。票券列表工具列那顆維持外框——它是
   全選／刪除選取的同儕，不是那一區的主要行動，實心會壓過旁邊的刪除。
   0,3,0 才蓋得過 .btn.primary 與 .btn.success 各自的 0,2,0。 */
.btn.primary.success { background: var(--ki); border-color: var(--ki); color: #fff; }
@media (hover: hover) and (pointer: fine) {
  .btn.primary.success:hover { background: #17564D; box-shadow: 0 2px 8px rgba(30, 111, 99, .34); }
  .btn.primary.success:disabled:hover { background: var(--ki); }
}

/* ---- 條款規格 popup（共用：票券列表、儀表板皆由 data-terms-note 觸發）---- */
.terms-dialog { border: none; border-radius: 10px; padding: 20px 22px; width: min(560px, calc(var(--vw) - 32px)); max-height: calc(var(--vh) - 64px); overflow: auto; box-shadow: 0 12px 40px rgba(23, 38, 59, .22); }
.terms-dialog::backdrop { background: rgba(23, 38, 59, .32); }
.terms-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.terms-head h2 { margin: 0; font-size: 18px; }
.terms-sec { margin-top: 14px; }
.terms-sec h3 { margin: 0 0 6px; font-size: 13px; color: var(--meta); }
.terms-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.terms-table th, .terms-table td { text-align: left; padding: 4px 8px; border-bottom: 1px solid var(--line); vertical-align: top; }
.terms-table th { color: var(--meta); font-weight: 500; white-space: nowrap; }
.terms-under th { color: var(--meta); }
.terms-loading, .terms-error { color: var(--meta); padding: 12px 0; }
.terms-error { color: var(--ko); }
.note-link { background: none; border: none; padding: 0; font: inherit; color: var(--spine); cursor: pointer; text-decoration: underline; }
@media (hover: hover) and (pointer: fine) {
  .note-link:hover { color: #1E3F63; }
}

/* ---- 手機判定（縮放套用）：<html class="is-phone">，由 base.html <head> 設定 ----
   「手機」＝主指標觸控（pointer: coarse）且（screen 短邊 ≤ 500 或 viewport 寬 ≤ 640）——**不看 viewport 高度**。viewport 條件（舊的 max-height: 500px 分支）會被軟鍵盤命中：base.html 的
   interactive-widget=resizes-content 讓 Android 鍵盤縮小 layout viewport 高度，平板橫放
   （約 1138×712）聚焦輸入框時高度 < 500，html zoom 在打字時從帳號桌機值跳成 --phone-z、
   整頁重排。判定由 base.html <head> 的 inline script 在首次繪製前做，之後在螢幕尺寸
   變動時重判（摺疊機展開／收合；旋轉與軟鍵盤不動 screen，不會重判）；
   settings.js、app.js 讀同一個 class（tests/test_phone_condition_sync.py 守）。
   版面類規則仍用下方純寬度斷點。 */
html.is-phone {
  /* 租戶的 zoom 桌機／手機共用一份（雲端同一個 settings.json）；手機不吃它，
     否則 390px 的版面被壓進 390/zoom px、字與格子全縮、頂欄折行變成左側
     一條深色塊。手機改吃自己的 --phone-z（settings.js 存在這支手機的
     localStorage、base.html <head> 注入；沒設＝1）。!important 同時壓過
     base.html 的 inline <style> 與桌機路徑寫進 element.style 的 zoom。 */
  zoom: var(--phone-z, 1) !important; --z: var(--phone-z, 1) !important;
}
/* IV 計算機的拖曳在手機停用（app.js；窄視窗的同一組撤銷在下方 max-width 斷點），
   標題列不必再宣告「可拖」、也不顯示「拖曳此列移動」。 */
html.is-phone .ivi-drag { cursor: auto; touch-action: auto; }
html.is-phone .ivi-drag-hint { display: none; }
/* 手機的市場參數全自動帶入（app.js；spec 2026-11-08 §4），不需要帶入鈕。桌機照舊按鈕。 */
html.is-phone #mkt-fetch { display: none; }

/* ---- RWD：~640px 手機斷點（app 頁面；看＋生成為主，表單求可用不亂版；
   寬表維持 .dash-scroll 橫捲不重排——spec §6）---- */
@media (max-width: 640px) {
  /* 寬表格在自己的框內橫向捲，不撐寬整頁（撐寬 → iOS 整頁縮小去塞）。表單的標的表與
     市場參數表改放在 .dash-scroll 框裡（任何寬度都在框內捲、共用右緣提示），不在這裡。 */
  .guide-table { display: block; overflow-x: auto; max-width: 100%; }
  /* 底部留白只要一點：底部導覽的高度由 .page 的 padding-bottom 讓出（導覽外殼段） */
  .wrap {
    padding: 16px max(12px, env(safe-area-inset-right, 0px)) 24px max(12px, env(safe-area-inset-left, 0px));
  }
  .page-head h1 { font-size: 21px; }
  .btn { padding: 10px 12px; }            /* tap target 加大 */
  .manage-bar, .manage-tools { flex-wrap: wrap; gap: 8px; }
  form.termsheet { padding: 4px 14px 18px; }
  /* 章節標題右側的工具鈕（IV 月份插值計算機／帶入市場參數）在窄寬下把標題
     擠成一行四五個字。改成折到標題下一行、與標題文字左緣切齊（章號 26＋
     間距 10）。h2 的 flex-basis 吃滿剩餘寬，按鈕才一定會折行。 */
  .clause-head { flex-wrap: wrap; row-gap: 8px; }
  .clause-head h2 { flex: 1 1 calc(100% - 36px); min-width: 0; }
  .clause-head > .ivi-open { margin-left: 36px; }
  /* 產品類型：legend 原本 float 在左，窄寬下標籤與兩個選項都被逐字折行。
     legend 佔一整行、選項並排且不拆字。legend 保持 float——浮動的 legend
     才是一般 flex 子項，width:100% 與折行才有效。 */
  .product-switch { flex-wrap: wrap; gap: 2px 20px; }
  .product-switch legend { width: 100%; margin: 0; }
  .product-switch label { white-space: nowrap; }
  .row { gap: 8px; }
  .row label { flex: 1 1 100%; }          /* 表單單欄直式 */
  input, select, output.derived { min-width: 0; width: 100%; }
  input[type=checkbox], input[type=radio] { width: auto; }
  /* 表格 input 的最小寬以 ch 訂、不歸零：min-width:0 ＋ width:100% 會讓 auto 版面
     先把欄擠到約 38px（「38.25」只剩「38」、「0.45」只剩「0.」），而表格的
     橫向捲動只在欄有最小寬時才發生。coarse 的 16px 字級下，ch 跟著字級放大；
     9ch ＝ 放得下 8 位數的期初價「247.0230」，36px ＝ 箭頭 16 ＋ padding 18 ＋
     邊框 2（見 test_zoom_input_min_width 的 test_phone_*）。u_skew 另有更高
     特異度的 7em 規則，HV 低／高兩格自帶行內 style。 */
  #underlyings input, #market-rows input { min-width: calc(9ch + 36px); }

  /* 成品清單：連結只有字那麼高（約 22px），手指點不準。讓連結吃滿整列的
     高度與剩餘寬度，整列都是點擊區。 */
  ul.file-list li { padding: 0 2px; align-items: center; }
  ul.file-list li a { flex: 1 1 auto; padding: 11px 0; min-width: 0; overflow-wrap: anywhere; }

  /* 對話框：桌機的 26px 內距在 375px 寬上吃掉太多；內容超過一屏時在框內
     捲動，不要把背後的頁面一起帶著跑。 */
  .hold-dialog, .pf-dialog, .settings-dialog, .delete-dialog {
    padding: 16px 14px 14px; width: calc(var(--vw) - 24px);
  }
  .settings-dialog .row label { flex: 1 1 0; }
  .ivi-dialog {
    left: 12px; right: 12px; max-width: none; padding: 0 14px 16px;
  }
  .ivi-drag { margin: 0 -14px 10px; padding: 12px 14px 10px; cursor: auto; touch-action: auto; }
  /* 「可拖曳」是滑鼠的提示，窄視窗上整個框已貼齊兩側；app.js 的拖曳停用條件同為
     「max-width:640px 或 html.is-phone」（上方 html.is-phone 規則涵蓋橫放手機）。 */
  .ivi-drag-hint { display: none; }
}

/* ===== 盤前比價（Task 8，2026-08-07-pretrade-scan-design.md §5.3）===== */

/* 排程落後（收在「進階」摺疊裡）：與表單既有 .row 同一套水平 label 排列，
   換個 class 名是因為這一區的 label 語意與票券表單條款不同，但視覺慣例
   （generic label／input 規則已提供字級與邊框）完全沿用不重寫。 */
.sel-globals { display: flex; flex-wrap: wrap; gap: 14px; }

/* 交易日：v3 起這是**唯一**的全表共用市場輸入（天期／IV／HV／r 全部逐
   列），所以不再需要一整個「全域設定」區塊——一行字就夠。 */
.sel-tradedate {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 12px;
  margin: 0 0 10px;
}
.sel-tradedate label {
  display: flex; flex-direction: row; align-items: baseline; gap: 6px; margin: 0;
}
.sel-tradedate input { width: auto; }

/* 進階摺疊：純視覺沿用 details 的既有慣例，只收斂間距。 */
.sel-advanced { margin: 0 0 18px; }
.sel-advanced > summary {
  cursor: pointer; font-size: 12.5px; color: var(--meta); padding: 2px 0;
}
.sel-advanced .sel-globals { padding: 8px 0 2px; }

/* 頁面說明段落：沿用既有段落慣例（無現成 class 可套），字級比正文小、
   顏色比純黑淡，與 .hint／.dash-meta 同一套灰階語彙。 */
.lede { font-size: 13.5px; color: #3C4B5C; max-width: 40em; margin: 0 0 14px; }

/* 平坦 IV 偏誤提醒：語意等同「近似值、需要人工留意」，套用既有
   --verify-* 語彙（.dash-iv-fallback 同一套），不是錯誤（不用 --warn-*）。 */
p.note[role="note"] {
  font-size: 12.5px; color: var(--verify-ink); background: var(--verify-bg);
  border: 1px solid var(--verify-line); padding: 10px 14px; margin: 0 0 20px;
  max-width: 40em;
}

/* 掃描表（資料密集，欄位多）：橫向可捲，沿用既有 .dash-scroll 容器。 */
table.sel-tickers, table.sel-rows {
  border-collapse: collapse; width: 100%; margin: 8px 0; font-size: 13px;
}
table.sel-rows { min-width: 1180px; }  /* 14 欄含多個輸入框，強制可捲而非擠壓到不能填 */

/* 候選券是**橫向比較**表：欄多，放不進 .wrap 的 1180px。寬視窗時讓這一區
   突破內容欄寬（兩側各借用原本空著的 margin），窄視窗維持原樣＋橫向捲動。
   只動這一區，不改 .wrap 本身，其他頁面完全不受影響。 */
/* 看內容區寬（.page 容器，已扣掉側欄、已除掉 zoom）而非視窗寬：用 --vw 會把側欄
   的寬度也算進來，表格左緣借到側欄底下去。 */
@container page (min-width: 1320px) {
  .sel-rows-wide {
    width: min(1560px, calc(100cqw - 48px));
    margin-inline: calc((100% - min(1560px, calc(100cqw - 48px))) / 2);
  }
}
table.sel-tickers th, table.sel-tickers td,
table.sel-rows th, table.sel-rows td {
  border-bottom: 1px solid var(--line); padding: 6px 8px;
  text-align: left; vertical-align: middle;
}
table.sel-tickers thead th, table.sel-rows thead th {
  color: var(--meta); font-weight: 600; font-size: 11.5px; white-space: nowrap;
  border-bottom: 2px solid var(--spine-soft);
}
/* 橫捲時第一欄（編號／代號）黏住（spec 2026-11-08 §7），底色要不透明，捲過去的格子
   才不會從底下透出來。盤前比價只黏表頭與條款層：結果層是一格跨整列的 <td>，黏住它
   等於整條結果帶不能捲。右側一條細線標出黏住的邊界。 */
table.sel-rows thead th:first-child,
table.sel-rows tbody.sel-note > tr.sel-in > td:first-child,
table.sel-tickers thead th:first-child,
table.sel-tickers tbody td:first-child {
  position: sticky; left: 0; z-index: 1; background: var(--paper);
  box-shadow: inset -1px 0 0 var(--line);
}
table.sel-tickers input, table.sel-rows input, table.sel-rows select {
  min-width: 0; width: 100%; padding: 5px 7px; font-size: 13px;
}
/* KI 型態：選單至少要放得下「AKI」／「EKI」＋箭頭。欄頭「KI 型態」只有約
   52px 寬，width:100% 的 select 會把選項文字截成「A」「EI」——而 AKI vs EKI
   正是這頁最常被橫向比較的條款之一，看不清等於比不出來。 */
table.sel-rows select.sel-row-kistyle { min-width: 66px; }
/* 產品：同理，至少放得下「DACN」＋箭頭（實測沒這條只剩約 36px，只看得到箭頭）。 */
table.sel-rows select.sel-row-notetype { min-width: 72px; }
/* KI 型態選 NA（無下檔保護）時，同列的 KI 欄不適用：用**原生 disabled**
   擋住輸入（只改樣式擋不住鍵盤），變灰沿用上面 input:disabled 那條。游標
   要覆寫——那條的 cursor: progress 講的是「自動帶入中，請稍候」，這裡不是
   等待而是這一格對這種條款根本不存在。 */
table.sel-rows input.sel-row-ki:disabled { cursor: not-allowed; }
/* 條款輸入欄位收窄：值都是 1～3 位數（保息 1、KO 100、KI 55、票息 9.9），
   不需要與其他表格同寬。收窄後把右側的輸出欄（含 HV 情境價差與接股平均
   損失）拉進預設可見範圍，減少橫向捲動——這頁的用途就是橫向比較。 */
table.sel-rows td.sel-term input { max-width: 76px; }
/* 各數字格的**可讀最小寬**（放大倍率 0.75～2.0 都要能填）。
   機制：自動表格版面裡，width:100%＋min-width:0 的 input 對欄寬的 min-content
   貢獻是 0（百分比寬的 input 被當成可壓縮）；html{zoom} 放大後內容區的 CSS 寬
   變窄，表格就從這些欄開始擠，擠到只剩 padding（實測 zoom 1.25 起 KO／KI／ρ
   可用寬約 2px）。欄擠不下時整張表改由外層容器橫向捲動，而不是把格子壓扁。
   算式（通則）：min-width = max(最長合理值 Nch, 最長全形 placeholder Kem)
   ＋ 34px。
   - Nch：mono 字型下 1ch ＝ 1 個數字／小數點／負號，精確。
   - Kem：全形字 1 字 ＝ 1em，**不是** 1ch（「不適用」≈ 3em，用 ch 會少算一半）。
     K 對應的字串：必填警示 REQUIRED_SHORT「必填」、DACN／NA 時 disabled 的 KI
     「不適用」、ρ 解鎖時的 RHO_OPEN_HINT「可手填」（都在 selection.js）。
   - 34px ＝ number spinner 16 ＋ 左右 padding 14 ＋ 邊框 2 ＋ 次像素餘裕 2。
     type=number 的 input，Chrome **不論是值還是 placeholder、disabled 與否**
     都會預留 spinner 的寬，所以 placeholder 也要加這 16。
   N／K 的依據與守衛見 tests/test_zoom_input_min_width.py。 */
table.sel-rows input.sel-row-tenor { min-width: calc(max(3ch, 2em) + 34px); }       /* 120／必填 */
table.sel-rows input.sel-row-guaranteed { min-width: calc(max(2ch, 2em) + 34px); }  /* 12／必填 */
table.sel-rows input.sel-row-ko { min-width: calc(max(5ch, 2em) + 34px); }          /* 105.5 */
table.sel-rows input.sel-row-ki { min-width: calc(max(4ch, 3em) + 34px); }          /* 55.5／不適用 */
table.sel-rows input.sel-row-strike { min-width: calc(max(5ch, 2em) + 34px); }      /* 100.5 */
table.sel-rows input.sel-row-coupon { min-width: calc(max(5ch, 2em) + 34px); }      /* 12.25 */
table.sel-rows input.sel-row-rho { min-width: calc(max(6ch, 3em) + 34px); }         /* -0.583／可手填 */
table.sel-rows input.sel-row-r { min-width: calc(max(5ch, 2em) + 34px); }           /* 4.125 */
/* IV／HV 逐檔格：padding 4px 5px（見下方 .sel-mkt-line input）→ 16 ＋ 10 ＋ 2 ＝
   28px；不加餘裕是因為 max-width 72px 的上限（fallback 字型下已約 71px）。 */
table.sel-rows input.sel-row-iv { min-width: calc(max(6ch, 2em) + 28px); }          /* 120.55 */
table.sel-rows input.sel-row-hv { min-width: calc(6ch + 28px); }                    /* 120.55 */

/* ---- 候選券的雙層列（一張券 ＝ 一個 <tbody>，上半條款、下半結果）----
   為什麼不是一整列 23 欄：輸入 14 欄、結果 9 欄，硬排成同一條格線會逼出
   2000px 以上的表寬，兩邊都被擠到不能讀。拆成上下兩層之後，兩層各自有自己
   的欄寬邏輯——上半沿用 <table> 的欄（跨列對齊，才比得出條款差異），下半是
   固定 9 欄的 grid（同樣跨列對齊，因為每一列的下半都是整寬的一格；欄數 ＝
   selection.js 的 OUT_FIELDS 長度，v7 多了 DACN 的「計息比率」）。 */
tbody.sel-note > tr.sel-in > td { border-bottom: none; }
tbody.sel-note > tr.sel-out > td {
  padding-top: 2px; padding-bottom: 10px;
  border-bottom: 2px solid var(--spine-soft);   /* 分隔線畫在「一列」的底部 */
}
.sel-outband {
  display: grid; grid-template-columns: repeat(9, minmax(88px, 1fr)); gap: 2px 12px;
}
.sel-outitem { display: flex; flex-direction: column; min-width: 0; }
.sel-outlabel { font-size: 10.5px; color: var(--meta); white-space: nowrap; }
.sel-outitem > span.mono { font-size: 13px; white-space: nowrap; }
.sel-outsub {
  display: flex; gap: 4px; font-size: 10.5px; color: var(--meta); white-space: nowrap;
}
.sel-outsub .mono { font-size: 11px; }
/* 溢折價為負時標黃（沿用 --verify-*「請你留意」語彙；<span> 不吃上面那條
   td.is-warn／input.is-warn 規則，要自己一條）。 */
.sel-outband .is-warn {
  background: var(--verify-bg); color: var(--verify-ink);
  border-radius: 2px; padding: 0 3px; align-self: flex-start;
}
/* 該列的錯誤訊息：整寬一行，:empty 時完全不佔位（否則每列都留一道空白）。*/
.sel-out-error { margin: 6px 0 0; font-size: 12px; color: #8C2B1E; }
.sel-out-error:empty { display: none; }
/* 掃描失敗的列：整列（兩層）都要看得出來，class 掛在 <tbody> 上。 */
tbody.sel-note.is-error > tr { background: var(--warn-bg); }
tbody.sel-note.is-error > tr > td { color: #8C2B1E; }
/* 黏住的編號格自帶不透明底色，要跟著整列換，不然失敗列左端會留一塊頁面底色 */
table.sel-rows tbody.sel-note.is-error > tr.sel-in > td:first-child { background: var(--warn-bg); }

/* IV／HV：**欄位**，欄內逐標的堆疊（使用者裁決 2026-08-20）——5 檔標的時
   這一列變高、不變寬，右邊的條款欄不會被推出視野。 */
/* 寬度：代號 40px ＋ 輸入 72px。輸入不能再窄——number input 會替上下箭頭
   留約 13px，58px 時「40.07」這種五位數就被切掉最後一位（實測）。 */
table.sel-rows td.sel-mkt-cell { min-width: 118px; }
/* flex-direction／margin 是**明寫的覆蓋**，不是多餘：基準 label 規則是
   `display:flex; flex-direction:column; margin:6px 0`（給表單用的直式標籤），
   照單全收會讓每一檔佔掉兩倍高度——5 檔標的的列會高到看不完。 */
.sel-mkt-line {
  display: flex; flex-direction: row; align-items: center; gap: 4px; margin: 0;
}
.sel-mkt-line + .sel-mkt-line { margin-top: 3px; }
.sel-mkt-tk {
  font-family: var(--mono); font-size: 10.5px; color: var(--meta);
  width: 40px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
table.sel-rows .sel-mkt-line input { max-width: 72px; padding: 4px 5px; font-size: 12px; }
.sel-mkt-empty { color: var(--meta); }

/* ρ：**預設鎖住的輸入格**（2026-08-22 推翻 R10）。值平時由「開始掃描」自動算
   出，只有那一列算不出來時才解鎖成手填的逃生門。
   鎖住的樣子要一眼看得出「這不是你要填的東西」——無邊框、灰字、與唯讀顯示格
   無異；解鎖時（data-rho-state="open"）才長回一般輸入格的邊框。 */
table.sel-rows td.sel-row-rho-cell { text-align: right; white-space: nowrap; }
/* max-width 84：上面的 min-width 是 max(6ch, 3em)＋34px，Consolas 下約 77px，但非
   Consolas 的等寬 fallback（1ch≈0.6em）會到約 81px，max 要蓋得住 min。 */
table.sel-rows input.sel-row-rho {
  max-width: 84px; text-align: right; color: var(--meta);
}
table.sel-rows input.sel-row-rho[readonly] {
  border-color: transparent; background: transparent; cursor: default;
}
table.sel-rows input.sel-row-rho[data-rho-state="open"] { color: var(--ink); }

/* 列尾三顆按鈕（移除／複製／帶入）：窄視窗時允許換行，不硬撐欄寬。 */
table.sel-rows td.sel-row-actions { white-space: nowrap; }

/* 進階覆寫區（股利率）：代號／來源／下次財報三欄都是唯讀文字，只有股利率是
   輸入格。
   **表寬 auto、輸入格約 177～180px**：`table.sel-tickers` 的基準是 `width: 100%`
   （那是給欄位多的掃描表用的），照收會讓唯一的輸入格被撐到整頁寬——一個只放
   1~2 位數的格子沒有理由這麼寬，而且來源與財報日會被推到視野右緣。寬度由
   `input.is-error::placeholder` 的警示句決定：最長的「Yahoo 未提供，請自行填入」
   在 11.5px 約需 141px，數字框另替上下箭頭保留約 15px（2026-10-02 實測；原本
   150px 扣 padding 與箭頭只剩約 120px，zoom 1 就截掉「填入」）。
   **min-width 不可省**：`table.sel-tickers` 通則是 `min-width: 0`，放大倍率下
   auto layout 會把這格擠到約 66px（2026-10-02 實測 900px 視窗、zoom 1.8～2）；
   11em＋34px（箭頭＋padding＋邊框）在 13px 字級約 177px，扣完約剩 146px。 */
table#sel-override-table { width: auto; }
table#sel-override-table input.sel-override-div {
  min-width: calc(max(5ch, 11em) + 34px); max-width: 180px;
}
table.sel-tickers td.sel-override-ticker,
table.sel-tickers td.sel-override-basis,
table.sel-tickers td.sel-override-earnings {
  white-space: nowrap; color: var(--meta);
}

/* is-error（紅，實際問題）／is-warn（黃，需要人工判斷、非錯誤）：沿用
   既有 --warn-*／--verify-* 兩套語彙，不新造顏色。 */
tr.is-error { background: var(--warn-bg); }
tr.is-error td { color: #8C2B1E; }
td.is-warn, input.is-warn {
  background: var(--verify-bg); border-color: var(--verify-line); color: var(--verify-ink);
}

/* Task 8 review Important #1：tr.is-error／tr.is-error td 只套用在 <tr>
   本身或其後代 <td>，「無風險利率必填未填」與前檢標記的欄位都是獨立的
   <input>（不在 <tr> 裡），先前完全沒有對應規則、視覺上跟合法欄位一樣。
   這裡補一條通用規則，讓任何直接標 is-error 的表單控制項都看得出來。 */
input.is-error, select.is-error {
  background: var(--warn-bg); border-color: var(--warn-line); color: #8C2B1E;
}

/* 輸入類提示的 placeholder（見 selection.js 的 armInputNotice）：這裡的
   placeholder 是**警告文字本身**，不是範例值——瀏覽器預設的淡灰會讓它讀起來
   像「這格可以填 2.5」而不是「這格有問題」，所以套上與 is-error 同一套紅。
   字級收小一級：股利率格只有約 180px，13px 放不下一句話。 */
input.is-error::placeholder { color: #A8452F; opacity: 1; font-size: 11.5px; }

/* v3：r 從全域單一欄位改為逐列一格，必填提示改走 input 自己的紅框＋
   placeholder（見 selection.js 的 markRowRequiredR）；原本掛在全域欄位下方
   的 .sel-r-hint 沒有對應的元素了，故一併移除。 */

/* 候選券的「標的」格＝worst-of 籃子。
   問題：原本是單一個 <input>，逗號分隔；<input> 不換行，填到 4~5 檔就只能在
   窄格子裡橫向捲，看不到全部。
   做法：晶片留在 flow 裡（由它定義格高，才撐得出第二行），input 絕對定位疊在
   同一個盒子上、平時 opacity:0。這樣 input 始終在 tab order 裡、點格子任何位置
   都會聚焦它（定位元素疊在上層，點擊自然落到 input），聚焦後才顯形讓人編輯
   ——不用 display:none，否則鍵盤使用者永遠到不了這一欄。
   配色刻意不動 --ko／--ki：那兩色在本系統已是「向上出場」與「下檔保護」的
   語義，「主導檔」不是兩者任一，借色會汙染整頁賴以判讀的障礙語義。 */
table.sel-rows td.sel-basket-cell { width: 156px; }
/* 標的格的最小寬：放得下三檔四字母代號「NVDA,AVGO,MSFT」（14 字）＋ padding 14
   ＋ 邊框 2。掛在流內的 .sel-basket 而不是 input——input 是 absolute 疊在晶片
   上，不貢獻欄寬；沒有這條時，上面條款格的最小寬會把這一欄擠到約 22px，晶片被
   迫逐檔直排、聚焦時連 placeholder 都放不下。.sel-basket 繼承內文字型（不是
   mono），ch 不準，改用等寬字寬的上界 0.62em／字（Consolas 0.55、DejaVu Sans
   Mono 0.602；em 與 input 同為 13px）。 */
.sel-basket { position: relative; min-width: calc(14 * 0.62em + 16px); }
/* 觸控裝置上輸入框被平台底線拉到 16px（見檔頭 pointer: coarse），聚焦時疊上來
   的 input 比 13px 算出的格寬大一截、尾字被捲走；以 16px 重算同一條式子。 */
@media (pointer: coarse) {
  .sel-basket { min-width: calc(14 * 0.62 * 16px + 16px); }
}
.sel-basket .sel-basket-chips { display: flex; flex-wrap: wrap; gap: 3px; }
.sel-basket .sel-row-tickers {
  position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0;
}
.sel-basket:focus-within .sel-row-tickers { opacity: 1; }
/* visibility 而非 display：保留晶片的版面盒，編輯時格高不跳動 */
.sel-basket:focus-within .sel-basket-chips { visibility: hidden; }
.sel-chip {
  font-family: var(--mono); font-size: 12px; line-height: 1.5;
  padding: 1px 5px; border: 1px solid var(--spine-soft); border-radius: 2px;
  background: var(--card); color: var(--ink); white-space: nowrap;
}
/* 主導檔（最高 IV，worst-of 結構裡最可能決定結局的那檔）。用墨藍結構線而非
   換色：這是「哪個名字說話最大聲」的資訊，不是障礙方向。 */
.sel-chip.is-dominant { border-bottom: 2px solid var(--spine); font-weight: 600; }
/* 空狀態是行動邀請，不是空白；中文提示用一般字體（等寬只給資料） */
.sel-chip.is-empty {
  font-family: inherit; border-style: dashed; background: transparent; color: var(--meta);
}
/* input.is-error 的底色在 opacity:0 時看不見，用兄弟選擇器把錯誤狀態帶到
   可見的晶片上（DOM 順序是 input 在前、chips 在後）。 */
.sel-basket .sel-row-tickers.is-error ~ .sel-basket-chips .sel-chip {
  border-color: var(--warn-line); background: var(--warn-bg); color: #8C2B1E;
}

#sel-status { color: var(--meta); font-size: 13px; margin: 10px 0 0; }
#sel-status.bad { color: #8C2B1E; }
#sel-status.warn { color: var(--verify-ink); }

@media (max-width: 640px) {
  .sel-globals { gap: 8px; }
  .sel-globals label { flex: 1 1 100%; }
}

/* 自製SN機會掃描：詢價紀錄（spec 2026-10-03-sn-quote-log §6） */
.sn-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.sn-table td.sn-note { min-width: 8em; max-width: 24em; white-space: normal; word-break: break-word; }
.sn-table td.sn-actions { white-space: nowrap; }
.sn-table td:nth-child(-n+3) { white-space: nowrap; }
.sn-table td.sn-actions .btn { margin: 0 4px 0 0; }
.sn-table td.sn-empty { color: var(--meta); text-align: center; }
.sn-dialog .row label { min-width: 0; }
.sn-dialog input[type="number"] { width: 9em; }
.sn-dialog label.sn-wide { flex: 1 1 100%; }
.sn-dialog label.sn-wide input { width: 100%; box-sizing: border-box; }

/* 自製SN機會掃描：機會搜尋（spec 2026-10-08-sn-search-tab §8）。
   版面沿用 .row（flex wrap），不另開 grid；數字框與表格數字欄的最小寬以
   ch 訂，放大倍率下不會被 auto layout 擠到看不見。 */
.sn-universe { margin: 10px 0 14px; padding: 8px 12px; border: 1px solid var(--line); background: var(--paper); }
/* 清單全站共用：一行摘要＋「看完整清單」（不得連結／非美灰字）＋可收合的「更新清單」區 */
.sn-universe > p { margin: 2px 0; }
.sn-uni-head { font-size: 13px; color: #46566A; }
.sn-muted { color: var(--meta); word-break: break-word; }
.sn-uni-nonus { font-size: 12px; margin: 4px 0; }
/* 非美清單動輒數百檔：展開後在自己的框裡捲，捲到底不帶動整頁 */
#sn-uni-nonus { max-height: 12em; overflow-y: auto; overscroll-behavior: contain; }
.sn-uni-lists { margin-top: 2px; }
.sn-uni-update { margin-top: 6px; }
.sn-uni-lists > summary,
.sn-uni-update > summary { cursor: pointer; font-size: 13px; color: #46566A; }
.sn-uni-row { align-items: flex-end; }
.sn-uni-row .btn { margin: 6px 0; }
#sn-search textarea {
  font-family: var(--mono); font-size: 14px; padding: 7px 9px; width: 100%;
  border: 1px solid #C4CDC3; background: #FDFEFC; border-radius: 0; resize: vertical;
}
#sn-search textarea.is-error {
  background: var(--warn-bg); border-color: var(--warn-line); color: #8C2B1E;
}
#sn-search textarea:disabled, #sn-search input:disabled, #sn-search select:disabled {
  opacity: .6; cursor: not-allowed;
}
#sn-search label.sn-wide { flex: 1 1 100%; min-width: 0; }
#sn-search .row label { min-width: 0; }
#sn-search input[type="number"] { min-width: 0; width: calc(9ch + 34px); }
#sn-search .field-error { color: #8C2B1E; font-size: 12px; white-space: normal; }
#sn-search label > small.hint { max-width: 32ch; }
.sn-run-row { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; margin: 10px 0 4px; }
.sn-run-row .hint { margin: 0; }
.sn-history-row { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 8px; }
#sn-history { min-width: 0; max-width: 100%; }
.sn-summary .sn-sum-line { font-size: 13px; margin: 4px 0; }
.sn-notes { font-size: 12px; color: var(--meta); margin: 4px 0 8px; padding-left: 1.4em; }
.sn-sub { font-size: 14px; margin: 14px 0 0; }
.sn-rank th, .sn-tick th { white-space: nowrap; }
.sn-rank td.dash-num, .sn-tick td.dash-num { min-width: 9ch; white-space: nowrap; }
.sn-rank td.sn-basket { min-width: 14ch; white-space: nowrap; }
.sn-rank td.sn-main { font-weight: 600; }
.sn-rank td.sn-ref { color: var(--meta); }
.sn-rank td.sn-empty { color: var(--meta); text-align: center; }
.sn-flagged { white-space: nowrap; }
.sn-flag-cell { min-width: 6ch; }
/* 懸浮說明（欄位說明、旗標）：原生 popover 進 top layer，位置由 sn_search.js 依觸發鈕
   擺放；寬度照 --vw（html zoom 不修正 vw）。不支援 popover 時以 .is-open 開關。
   :popover-open 另寫一條規則：不認得它的瀏覽器只丟那一條，不連帶丟 .is-open。 */
.sn-pop-btn {
  font: inherit; font-size: 12px; color: #2F5D8A; background: none; border: 0; padding: 2px 4px;
  cursor: pointer; text-decoration: underline dotted; white-space: nowrap;
}
.sn-pop-btn[aria-expanded="true"] { color: #17263B; }
.sn-rank-title { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 12px; }
.sn-pop {
  display: none; position: fixed; inset: auto; margin: 0; z-index: 120;
  width: min(28rem, calc(var(--vw) - 24px)); max-height: calc(var(--vh) * .6); overflow: auto;
  padding: 10px 12px; background: #fff; border: 1px solid var(--line); border-radius: 6px;
  box-shadow: 0 8px 24px rgba(23, 38, 59, .18); font-size: 13px; line-height: 1.55; color: #17263B;
}
.sn-pop.is-open { display: block; }
.sn-pop:popover-open { display: block; }
.sn-pop p { margin: 0 0 6px; }
.sn-pop ul { margin: 0; padding-left: 1.2em; }
button.sn-sort {
  background: none; border: 0; padding: 0; font: inherit; color: inherit;
  cursor: pointer; white-space: nowrap; text-align: inherit;
}
button.sn-sort:hover { text-decoration: underline; }
tr.sn-row-off td { color: var(--meta); }

/* 列印：--vh＝紙高、overflow:auto 會把超過一頁的明細裁掉，還原成一般區塊 */
@media print {
  /* 橫印（或帳號縮放 0.75）時 shell 仍 ≥ 800，雙欄的 sticky 規則照樣成立：選擇器要與它
     同特異度，靠檔尾的順序勝出 */
  .dash-detail-pane, html:not(.is-phone) .dash-detail-pane {
    position: static; max-height: none; overflow-y: visible;
  }
}

/* ---- 從文件帶入（任務 7）----
   視覺隱藏而非 display:none／hidden：file input 要保留鍵盤與輔助技術可達性。 */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.doc-fill { margin: 14px 0 6px; padding: 12px 16px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 4px; }
.doc-fill .eyebrow { margin: 0; }
.doc-fill .doc-src { justify-self: start; cursor: pointer; }
.doc-fill .doc-src:focus-within { outline: 2px solid var(--spine); outline-offset: 2px; }
/* 上傳鈕被鎖（市場帶入中／解析中）時外層 label 也要看得出來，不然點了沒反應又沒說明。 */
.doc-fill .doc-src:has(input:disabled) { opacity: .55; cursor: progress; }
/* 觸控裝置的候選鈕／確認鈕至少 44px，避免點錯候選值。 */
@media (pointer: coarse) {
  .doc-fill .btn, .doc-cands .btn, .doc-actions .btn {
    min-height: 44px; display: inline-flex; align-items: center;
  }
}
.doc-fill .doc-row-pick { display: grid; gap: 4px; max-width: 100%; }
.doc-fill .doc-row-pick select { max-width: 100%; min-width: 0; }
.doc-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.doc-tag {
  display: inline-block; width: auto; align-self: flex-start; max-width: 100%;
  margin: 2px 0 0 6px; line-height: 1.5; font-size: 11px; font-weight: 400; color: var(--meta);
  background: var(--paper); border: 1px solid var(--line); border-radius: 4px; padding: 1px 5px;
}
.doc-cands { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
