:root {
  color-scheme: dark;

  /* ── 型階（type scale）·modern-design 1.25 比 ────────────────────────────
     ⛔ 最小就是 --fs-sm(14px)：站長 2026-08-22「字體最小有下限，部分字體還是過小」。
        原本全站有 106 處低於 14px（80 條 font-size ＋ 26 條藏在 font: 簡寫裡），本輪全部拉到 14。
     ⭐ 新寫的碼一律用這組 token，⛔ 不要再硬寫 px —— 硬寫的值會散開，
        下次要調整就得再掃一次 106 個地方。
     ⚠ 舊碼目前是直接改成 14px 的數值而非 var()：這份檔有 5 個 :root，
        且 `font:` 簡寫一旦 var 未定義會讓整條宣告失效（字體全掉）⇒ 分兩步走比較安全。 */
  --fs-sm: 14px;    /* 下限。標籤、輔助說明 */
  --fs-base: 16px;  /* 內文 */
  --fs-md: 18px;
  --fs-lg: 20px;
  --fs-xl: 24px;
  --fs-2xl: 30px;
  --fs-3xl: 36px;
  --fs-4xl: 48px;
  --lh-body: 1.5;
  --lh-heading: 1.2;
  --bg: #07111f;
  --surface: #0e1c2e;
  --surface-2: #14243a;
  --border: #243b56;
  --text: #eef6ff;
  --muted: #8fa9c4;
  --blue: #35a4ff;
  --violet: #9d7cff;
  --cyan: #3ed7d0;
  --green: #48d597;
  --red: #ff6279;
  --amber: #f2b84b;
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;
  /* ⛔ 單一真相源：**每個分頁**的標題到頂部那排常駐按鈕的距離，只由這一個值決定。
     站長 2026-08-20：「標題距離長方常駐按紐 要單一真相源的距離 不是忽高忽低」。
     ⚠ 根因不是 .section-title 的 margin（它在 60/507/1148 三處都寫 48px），而是
       **各 panel 的首元素結構不同** ⇒ 實測 73（交易紀錄・設定）/ 114（總覽）/
       121（Webhook・API Key）三種值，落差 48px。
     ⇒ 改成「panel 自己負責頂端距離、首元素一律不帶自己的上邊距」，
       任何新分頁不必再各自調 margin 就自動對齊。 */
  --panel-title-gap: var(--space-7);
  /* 選中態單一真相源（Fluent/Material 選中＝實心底＋對比邊，WCAG 1.4.11 ≥3:1）。
     機器人頁籤／紀錄頁籤／頂欄分頁都讀這三個，⛔ 不要各寫一套藍。
     26% 太淡：站長「我都看不出來我選哪個」。42% 才是分段控制的實心底。 */
  --sel-bg: color-mix(in srgb, var(--blue) 42%, transparent);
  --sel-border: var(--blue);
  --sel-ink: var(--text);
  --text-xs: 12px;
  --text-sm: 14px;
  --text-lg: 18px;
  --text-base: 16px;
  --text-xl: 20px;
  --text-2xl: 24px;
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 16px;
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
}
* { box-sizing: border-box; }
/* Keep the viewport content width stable when a page gains or loses its
   vertical scrollbar. Grid workbench deliberately disables page scrolling;
   without a stable root gutter its top-row controls shift horizontally. */
@media (min-width: 761px) {
  html { scrollbar-gutter: stable; }
}
body { margin: 0; min-width: 360px; background: radial-gradient(circle at 85% -10%, #12345d 0, transparent 32%), var(--bg); color: var(--text); }
button, input, select { font: inherit; }
button, .button { cursor: pointer; }
/* ⛔ .app-header 的基底定義只有一處（見檔尾 0.3.20 區塊）。0.3.35 之前這裡、290、651、
   856、2297 共五處各寫一份，全部被檔尾那份完整覆蓋 ⇒ 誰贏誰輸只看行序，改上面幾處
   完全沒有效果卻看起來像有改。要改 header 幾何請改檔尾那一處。 */
.brand { display: flex; align-items: center; gap: 16px; }
.brand-mark { width: 44px; height: 52px; display: grid; place-items: center; border-radius: 14px; font-size: 30px; font-weight: 900; background: linear-gradient(135deg, #2a74ff, #39d7d3); box-shadow: 0 8px 30px #1b8ee655; }
.brand strong { display: block; font-size: 24px; letter-spacing: .2px; }
.brand small { display: block; margin-top: 4px; color: var(--muted); }
.mode-badge { color: #98f1c8; padding: 12px 16px; border: 1px solid #2f765d; border-radius: 999px; font-size: 14px; font-weight: 800; letter-spacing: .6px; background: #13382d80; }
.mode-badge span { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 8px; background: var(--green); box-shadow: 0 0 12px var(--green); }
.tabs { height: 72px; display: flex; align-items: end; gap: 4px; padding: 0 48px; border-bottom: 1px solid var(--border); background: #0a1626; }
.tab { height: 72px; padding: 0 24px; border: 0; border-bottom: 3px solid transparent; background: transparent; color: var(--muted); font-weight: 700; }
.tab:hover, .tab.active { color: var(--text); border-bottom-color: var(--blue); }
.status-link { margin: auto 0 auto auto; color: var(--cyan); text-decoration: none; font-size: 14px; }
main { padding: 48px 48px 60px; max-width: 1500px; margin: auto; }
.panel { display: none; }
/* ⭐ 距離由 --panel-title-gap 一處決定（見 :root 的說明）。
   ⛔ 首元素的自帶上邊距一律歸零 —— 不歸零的話它會疊在 padding 上，
   而每個 panel 的首元素又不一樣 ⇒ 又回到忽高忽低。 */
.panel { padding-top: var(--panel-title-gap); }
.panel > *:first-child { margin-top: 0; }
.panel.active { display: block; animation: appear .18s ease-out; }
@keyframes appear { from { opacity: 0; transform: translateY(4px); } }
.metric-grid { display: grid; grid-template-columns: repeat(4, minmax(180px, 1fr)); gap: 24px; }
.metric { position: relative; min-height: 164px; padding: 32px; overflow: hidden; border: 1px solid var(--border); border-radius: 18px; background: linear-gradient(145deg, #14263c, #0d1a2b); box-shadow: 0 16px 45px #0003; }
.metric::after { content: ''; position: absolute; right: -25px; bottom: -40px; width: 110px; height: 128px; border-radius: 50%; background: #238cff18; }
.metric span { display: block; color: var(--muted); font-weight: 700; }
.metric strong { display: inline-block; margin-top: 24px; font-size: 48px; letter-spacing: -1px; }
.metric small { margin-left: 12px; color: var(--muted); }
.section-title { display: flex; align-items: center; justify-content: space-between; gap: 32px; margin: 48px 0 24px; }
h1, h2, h3, p { margin-top: 0; }
.section-title h1 { margin-bottom: 8px; }
.section-title p, .muted { margin-bottom: 0; }
button, .button { min-height: 48px; padding: 0 24px; border: 1px solid transparent; color: white; text-decoration: none; display: inline-flex; align-items: center; justify-content: center; }
.primary { background: linear-gradient(135deg, #2579ef, #25a5e8); box-shadow: 0 8px 22px #167bdd35; }
.secondary { background: #152a42; border-color: #31506f; }
.danger { background: #4a1e2b; border-color: #783044; color: #ffb1bf; }
.warning { background: #493817; border-color: #7d6128; color: #ffd77b; }
button:hover, .button:hover { filter: brightness(1.12); }
button:focus-visible, .button:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; }
.actions { display: flex; gap: 12px; }
/* ⛔ auto-fill 不是 auto-fit —— 差一個字，差在「卡片寬度跟不跟數量走」。
   站長 2026-08-21（第二次講同一件事）：「機器人我說 不要一個 兩個 三個 不同寬 統一卡片大小 就好」

   auto-fit 會把【空的欄摺疊掉】，然後把剩下的空間分給有內容的那幾欄
   ⇒ 卡片越少、每張越寬。auto-fill 保留空欄 ⇒ 欄寬只跟容器寬度有關。

   2026-08-21 用真瀏覽器量的（chromium，viewport 1600、容器 1504px）：
     auto-fit ：1 張 = 1504px｜2 張 = 740px｜3 張 = 485px｜4 張 = 358px  ← 站長看到的
     auto-fill：1 張 = 358px｜2 張 = 358px｜3 張 = 358px｜4 張 = 358px  ← 要的
   ⚠ 這條規則是欄數與欄寬的【唯一】真相源；:607 那條只設 gap，⛔ 別去那裡找欄數。
   ⛔ 也不要改成寫死欄數（例 repeat(4, ...)）：視窗縮小時會橫向溢出，
      而 minmax 的 340px 下限本來就在替小視窗兜底。 */
.bot-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 24px; }
.card, .bot-card { border: 1px solid var(--border); background: linear-gradient(145deg, #102037, #0c192a); box-shadow: 0 14px 42px #0002; }
.bot-card { padding: 24px; }
.bot-head { display: flex; align-items: start; justify-content: space-between; gap: 24px; }
.bot-head h3 { margin-bottom: 4px; }
.bot-head p { font-size: 14px; }
.badge { display: inline-flex; align-items: center; padding: 8px 12px; text-transform: uppercase; }
.badge.running { color: #9af0c8; background: #173b31; }
.badge.stopped { color: #b5c5d7; background: #263546; }
.badge.connecting { color: #8bd8ff; background: #173650; }
.badge.awaiting_verification { color: #ffd77b; background: #493817; }
.badge.error { color: #ff9daf; background: #4a1e2b; }
/* ⛔ 2026-08-18：3 欄 -> 2 欄。這裡本來就是錯的 —— renderBots 產生的是**四個**欄位
   （指定商品／持倉／最新價格／未實現），四個塞進 3 欄，第四個會孤零零掉到第二列。
   字級放大後欄寬不足，中文開始**逐字換行**（一個字一行），這個既有錯誤才被看見。
   四個項目排 2x2 才對稱，且每欄可用寬度直接加倍（340px 卡片內約 90px -> 約 140px）。
   ⛔ 別改回 3 欄再靠縮小字級塞進去 —— 那是拿可讀性換排版。
   min-width:0 是 grid 子項的必備保險：預設 min-width:auto 會讓長字串撐破欄位。 */
.bot-stats { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin: 24px 0; }
.bot-stats > div { min-width: 0; }
.bot-stats div { padding: 16px; border-radius: 11px; background: #071420; }
.bot-stats span { display: block; }
.bot-stats strong { display: block; margin-top: 4px; font-size: 18px; }
.positive { color: var(--green) !important; }.negative { color: var(--red) !important; }
.bot-actions { display: flex; flex-wrap: wrap; gap: 12px; }
.bot-actions button { min-height: 40px; padding: 0 16px; font-size: 14px; }
.webhook { margin-top: 24px; padding: 16px; background: #07131f; }
.webhook span { display: block; margin-bottom: 8px; }
.webhook code { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #76d8ff; font-size: 14px; cursor: pointer; }
.empty { padding: 64px 32px; text-align: center; border: 1px dashed #31506f; border-radius: 17px; color: var(--muted); }
.filters { display: grid; grid-template-columns: 180px 180px 1fr; gap: 12px; margin-bottom: 16px; }
input, select { width: 100%; min-height: 48px; padding: 0 16px; border: 1px solid var(--border); background: #081727; outline: none; }
input:focus, select:focus { border-color: var(--blue); box-shadow: 0 0 0 3px #248af522; }
.table-wrap { overflow: auto; border: 1px solid var(--border); border-radius: 15px; background: #0b1929; }
table { width: 100%; border-collapse: collapse; min-width: 900px; }
th, td { padding: 16px 24px; text-align: left; border-bottom: 1px solid #1d344c; font-size: 14px; }
th { position: sticky; top: 0; color: var(--muted); background: #102238; }
td { color: #d9e7f5; }
.media-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 24px; }
.media-card { overflow: hidden; border: 1px solid var(--border); border-radius: 15px; background: #0c1b2c; }
.media-card img { width: 100%; aspect-ratio: 16/10; display: block; object-fit: cover; background: #06101b; }
.media-info { padding: 16px; }
.media-info strong { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.media-info small { display: block; color: var(--muted); margin: 8px 0 12px; }
.settings-grid { display: grid; grid-template-columns: repeat(2, minmax(320px, 1fr)); gap: 24px; }
.form-card { padding: 32px; }
label { display: block; margin: 24px 0; }
label input, label select { margin-top: 8px; }
.switch-row { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding: 16px 0; border-bottom: 1px solid #1d344c; }
.switch-row span small { display: block; margin-top: 4px; color: var(--muted); font-weight: 400; }
.switch-row input { width: 20px; height: 20px; }
.inline-form { display: grid; grid-template-columns: 1fr 140px auto; gap: 12px; align-items: center; }
.credential-form { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; }
.credential-form button { align-self: end; margin-bottom: 24px; }
.list { margin-top: 16px; }
.list-item { display: flex; align-items: center; justify-content: space-between; padding: 16px 0; border-bottom: 1px solid #1d344c; }
.account-manager { grid-column: 1 / -1; padding: 0; overflow: hidden; background: linear-gradient(145deg, #102239, #0a1728); }
.account-manager::before { content: ''; display: block; height: 5px; background: linear-gradient(90deg, var(--green), var(--cyan), var(--blue)); }
.account-manager-head { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding: 32px 32px 24px; border-bottom: 1px solid #203a55; }
.account-manager-title { display: flex; align-items: center; gap: 24px; }
.account-manager-title h2 { margin-bottom: 8px; font-size: 30px; }
.account-manager-title p { margin-bottom: 0; color: var(--muted); font-size: 14px; }
.account-title-icon { width: 50px; height: 56px; display: grid; flex: 0 0 50px; place-items: center; border: 1px solid #2b6d70; border-radius: 15px; color: #86f1cd; background: linear-gradient(145deg, #173a36, #102c34); box-shadow: 0 10px 26px #0004; }
.account-title-icon svg { width: 29px; height: 29px; fill: currentColor; }
.simulation-chip, .secure-chip, .account-count, .account-status, .account-demo { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; border-radius: 999px; font-weight: 800; }
.simulation-chip { padding: 12px 16px; border: 1px solid #2d6d5a; color: #9af0c8; background: #14362d; font-size: 14px; letter-spacing: .45px; }
.simulation-chip span, .account-status span { width: 8px; height: 8px; border-radius: 50%; background: var(--green); box-shadow: 0 0 10px #48d59799; }
.account-manager-grid { display: grid; grid-template-columns: minmax(360px, .85fr) minmax(480px, 1.15fr); min-height: 484px; }
.credential-panel, .account-list-panel { padding: 32px 32px 32px; }
.credential-panel { border-right: 1px solid #203a55; background: #0a1829aa; }
.subsection-heading { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 8px; }
.subsection-heading h3 { margin: 2px 0 0; }
.eyebrow { display: block; color: #63bee9; font-size: 14px; font-weight: 900; letter-spacing: 1.2px; text-transform: uppercase; }
.secure-chip { padding: 8px 12px; color: #9af0c8; background: #173b31; font-size: 14px; }
.account-form-hint { margin: 0 0 24px; color: var(--muted); font-size: 14px; line-height: 1.55; }
/* ⛔ 這裡本來是 float: right，而 float **不會**把 grid 容器推開 —— 它只影響行盒。
   於是「指定商品」的說明文字直接壓在 .symbol-picker（display:grid）的搜尋框上，
   兩段字疊在一起（站長 2026-08-18 截圖指出）。其他欄位看起來沒事，只是因為它們
   後面接的是普通的 input/select。
   ⇒ 改成獨立一行的 block：說明本來就該在標題底下，而且這樣每個欄位的排法一致
   （站長第一句話就是「畫面對齊」）。間距取 8px 網格上的 4，⛔ 不要臨時填一個數字。 */
.field-help { display: block; margin-top: 4px; color: #6f8da8; font-size: 14px; font-weight: 500; line-height: 1.5; }
.account-form-actions { grid-column: 1 / -1; display: flex; justify-content: flex-end; gap: 12px; padding-top: 8px; }
.account-form-actions button { min-width: 136px; margin-bottom: 0; }
.account-list-heading { margin-bottom: 24px; }
.account-count { padding: 8px 12px; color: #9cc7e8; background: #132a40; font-size: 14px; }
.account-list { display: grid; gap: 16px; }
.account-card { position: relative; overflow: hidden; border: 1px solid #29445f; border-radius: 15px; background: linear-gradient(145deg, #12253c, #0c1b2d); box-shadow: 0 12px 30px #0002; }
.account-card::before { content: ''; position: absolute; inset: 0 auto 0 0; width: 4px; background: linear-gradient(var(--blue), var(--cyan)); }
.account-card-head { display: flex; align-items: center; gap: 16px; padding: 24px 24px 16px 32px; border-bottom: 1px solid #213a52; }
.account-avatar { width: 42px; height: 48px; display: grid; flex: 0 0 42px; place-items: center; border-radius: 12px; color: #9edcff; background: #173b57; font-size: 16px; font-weight: 900; letter-spacing: .6px; }
.account-card-title { min-width: 0; flex: 1; }
.account-card-title h4 { margin: 0 0 4px; overflow: hidden; color: var(--text); font-size: 20px; text-overflow: ellipsis; white-space: nowrap; }
.account-card-title p { margin: 0; color: var(--muted); font-size: 14px; }
.account-status { padding: 8px 12px; color: #9af0c8; background: #173b31; font-size: 14px; }
.account-details { margin: 0; padding: 4px 24px 4px 32px; }
.account-detail-row { display: grid; grid-template-columns: 120px minmax(0, 1fr); align-items: center; min-height: 60px; border-bottom: 1px solid #1b3248; }
.account-detail-row:last-child { border-bottom: 0; }
.account-detail-row dt { color: var(--muted); font-size: 14px; font-weight: 700; }
.account-detail-row dd { min-width: 0; margin: 0; color: #e7f2fb; font-size: 14px; }
.account-username { font: 700 14px/1.3 ui-monospace, SFMono-Regular, Consolas, monospace; letter-spacing: .55px; }
.account-demo { padding: 8px 12px; border: 1px solid #315d7c; color: #a9ddff; background: #132d45; font-size: 14px; }
/* Environment badge on a saved account. Colour carries the ONE thing that
   matters at a glance — can this account send orders — and it is never the
   only carrier: the badge always spells out 可送單 / 演練 / 已鎖定 in words,
   so the distinction survives a colour-blind reader and a greyscale screenshot. */
.account-env { display: inline-flex; align-items: center; gap: 8px; padding: 8px 12px; border-radius: 999px; font-size: 14px; font-weight: 800; white-space: nowrap; }
.account-env-demo { border: 1px solid #315d7c; color: #a9ddff; background: #132d45; }
.account-env-rehearsal { border: 1px solid #7d6320; color: #ffdf9b; background: #3a2f10; }
.account-env-locked { border: 1px solid #7d3030; color: #ffb4b4; background: #3a1414; }
.account-env-unknown { border: 1px dashed #5a6f85; color: #b9cbdb; background: #16232f; }
/* The sentence under the environment picker. Same three-way tone, and it is
   the only place the operator is told what the choice actually does. */
.account-env-note { margin: -8px 0 14px; grid-column: 1 / -1; color: var(--muted); font-size: 14px; line-height: 1.55; min-height: 17px; }
.account-env-note.is-rehearsal { color: #ffdf9b; }
.account-env-note.is-locked { color: #ffb4b4; }
.broker-account-field { grid-column: 1 / -1; }
/* ⛔ 明寫，不要倚賴「.broker-account-field 剛好沒設 display」：這一格預設帶 hidden
   （站長 2026-08-19「不要給用戶填寫」），而任何一條之後加上的 display 規則都會蓋掉
   瀏覽器內建的 [hidden]{display:none} —— 屬性照樣是對的、畫面上卻看得見，而每一個
   「它藏起來了嗎」的檢查都會回答藏起來了。 */
.broker-account-field[hidden] { display: none; }
/* 0.4.5 欄位重排（平台｜類別 ／ 帳號｜密碼 ／ 帳號代碼 ／ 帳號名稱）後，帳號名稱是
   奇數欄位的落點 ⇒ 半格會在它右邊留一個空格子。⛔ 不要為了「看起來成對」把別的欄位
   搬回來湊 —— 帳號代碼那格是 1/-1（test/broker-account-ui.test.js 釘住），搬過來會紅。 */
.account-name-field { grid-column: 1 / -1; }
.broker-account-note { min-height: 18px; margin: -8px 0 4px; grid-column: 1 / -1; color: var(--muted); font-size: 14px; line-height: 1.55; }
.broker-account-note.is-bound, .account-binding-state.is-bound { color: #9af0c8; }
.broker-account-note.is-unbound, .account-binding-state.is-unbound { color: #ffdf9b; }
.account-binding-state { display: inline-block; font-weight: 750; line-height: 1.45; }
.account-encrypted { color: #86dcb9; }
.account-card-actions { display: flex; justify-content: flex-end; gap: 12px; padding: 16px 24px 24px 32px; border-top: 1px solid #213a52; background: #07152288; }
.account-card-actions button { min-height: 44px; font-size: 14px; }
.account-card-actions .danger { margin-left: 8px; }
.account-list-empty { padding: 64px 32px; text-align: center; border: 1px dashed #31506f; border-radius: 14px; color: var(--muted); background: #08172788; }
.account-list-empty strong { display: block; margin-bottom: 8px; color: #cce0f2; font-size: 16px; }
.safety-card li { margin: 16px 0; color: #aac0d5; }
.connection-line { display: flex; align-items: center; gap: 12px; margin: 16px 0 4px; padding: 12px 16px; border-radius: 9px; color: var(--muted); background: #071522; font-size: 14px; }
.connection-line.connected { color: #98f1c8; border: 1px solid #285f4c; }
.connection-line.error { color: #ff9daf; border: 1px solid #743447; }
.connection-line.awaiting_verification { color: #ffd77b; border: 1px solid #735a25; }
/* ⛔ 欄數**不在這裡**決定 —— 真相源是本檔後面那條 .receiver-grid（搜 `auto-fit`）。
   這行只負責 display 與 gap；2026-08-20 我先改了這裡卻沒生效，實測 grid-template-columns
   仍是 0.92fr/1.08fr ⇒ 同名規則寫了兩處時，**決定畫面的是最後那一條**。 */
.receiver-grid { display: grid; gap: 24px; }
.receiver-status { display: flex; align-items: center; gap: 16px; margin-bottom: 24px; padding: 16px; border: 1px solid var(--border); border-radius: 11px; background: #071522; }
.receiver-status > span { width: 12px; height: 12px; flex: 0 0 12px; border-radius: 50%; background: var(--red); box-shadow: 0 0 15px #ff627966; }
.receiver-status > span.online { background: var(--green); box-shadow: 0 0 15px #48d59788; }
.receiver-status strong, .receiver-status small { display: block; }
.receiver-status small { margin-top: 3px; color: var(--muted); }
.copy-field { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 12px; margin-top: 8px; }
.copy-field code { display: block; overflow: hidden; padding: 16px; border: 1px solid var(--border); border-radius: 9px; color: #76d8ff; background: #07131f; text-overflow: ellipsis; white-space: nowrap; }
.notice { padding: 16px 16px; border-left: 3px solid var(--amber); color: #f1d597; background: #392d1566; font-size: 14px; line-height: 1.55; }
.strategy-route-note { margin: 12px 0 16px; color: #8fc7ec; font-size: 14px; line-height: 1.5; }
.strategy-route-note code { color: #b7e7ff; }
.template-head { display: flex; justify-content: space-between; align-items: start; gap: 24px; }
.json-template { min-height: 328px; margin: 24px 0 16px; padding: 24px; overflow-x: hidden; overflow-y: auto; border: 1px solid #245075; border-radius: 11px; color: #9bdcff; background: #06111c; font: 14px/1.7 ui-monospace, SFMono-Regular, Consolas, monospace; white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; }
.receiver-help code { color: #84d9ff; }
.section-title.compact { margin-top: 32px; }
/* ===== 一鍵全啟（站長 2026-08-24：「總覽 右側 做一個大按鈕」）=====
   ⚠ 它是「大按鈕」但⛔ 不能長得像緊急鈕：全部平倉／強制停機是紅色系，
     那個顏色在交易軟體裡代表「會動到部位」。一鍵全啟只是連線 ⇒ 用主色系。
   flex-shrink: 0 ⇒ 標題文字變長時它不會被壓扁成看不出是按鈕的細條。 */
.start-all {
  flex: 0 0 auto;
  min-width: 160px;
  padding: 16px 28px;
  border: 0;
  border-radius: 12px;
  /* ⭐ 站長 2026-08-25：「一鍵全啟 改成 **VIP 金色按紐**」。
     ⭐ 金屬感來自**亮帶**不是單一個金色 —— 單色 #ffd700 看起來是「黃色按鈕」不是金。
        五個色標＝暗金邊 → 金 → 高光 → 金 → 暗金邊，亮帶壓在偏左上（配合 135deg 的光源）。
     ⛔ 不發明新的顏色變數：這條血統就是既有的 --amber（#f2b84b／#ffc65a／#ffd37a 三個
        主題層都在用），⛔ 不要為了這顆按鈕另開一組 token。
     ⚠ 文字用深棕 #2b1d05 不是黑：純黑配金會顯得髒，而深棕與高光的對比仍遠超 AA。
     ⚠ inset 那道細白線是「邊緣受光」，⛔ 不要拿掉改用 border —— border 會撐大盒子，
        而這顆按鈕的 min-width 是跟旁邊的分組控制列對齊過的。 */
  background: linear-gradient(135deg, #a9761f 0%, #f2c75c 22%, #fff2c4 46%, #f0c257 64%, #a97a1e 100%);
  color: #2b1d05;
  font-size: 16px;
  font-weight: 800;
  letter-spacing: .5px;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: 0 10px 28px rgba(214, 163, 62, .32), inset 0 1px 0 rgba(255, 255, 255, .55);
  text-shadow: 0 1px 0 rgba(255, 246, 214, .45);
}
/* hover 讓亮帶更亮一點點。⛔ 不做「亮帶掃過去」的動畫：那是促銷頁的手法，
   而這顆按鈕按下去會真的去連券商 —— 它該看起來貴重，⛔ 不是該看起來在催人按。 */
.start-all:hover:not(:disabled) {
  filter: brightness(1.06) saturate(1.04);
  box-shadow: 0 12px 32px rgba(214, 163, 62, .42), inset 0 1px 0 rgba(255, 255, 255, .68);
}
/* ⛔ 停用時要看得出來在忙（按鈕文字會變成「啟動中 3/7」），⛔ 不是單純變灰讓人以為壞了。 */
.start-all:disabled { cursor: progress; opacity: .72; box-shadow: none; }
/* ── 全部運行中＝**達成態**（站長 2026-08-25：「全啟以後…要變成不能按的按紐」）──
   ⛔ 不沿用上面 :disabled 那個樣子：它的 cursor: progress 講的是「正在忙、等一下」，
      而這裡的意思相反 —— 事情已經做完了。兩個狀態共用一種外觀，使用者就分不出
      「還在啟動」跟「全都啟動好了」，而那正好是這顆按鈕唯一要回答的問題。
   ⭐ 金色＝「可以按、按了會發生事」；達成態要**退場**：靜態、低飽和、綠色系。
   ⛔ 不另開顏色 token：綠就是既有的 --green（連線中的圓點用的同一支血統）。
   ⚠ opacity 明確寫回 1 —— 上面 :disabled 的 .72 會同時命中這顆（它確實是 disabled），
     不寫死就會變成一顆半透明的綠，讀起來又回到「壞掉」那一側。 */
.start-all.is-all-running {
  background: linear-gradient(135deg, rgba(72, 213, 151, .2) 0%, rgba(72, 213, 151, .08) 100%);
  color: var(--green);
  box-shadow: inset 0 0 0 1px rgba(72, 213, 151, .45);
  text-shadow: none;
  opacity: 1;
  cursor: default;
}
.start-all.is-all-running::before { content: '✓'; margin-right: 8px; font-weight: 900; }
/* 0.5.0 反熵：font-size 移交檔尾設計系統層（區段標題統一 28px）。
   ⛔ 這條 (0,2,1) 會贏過檔尾的 `.section-title h2` (0,1,1)，留著＝總覽頁那個
      「交易機器人」永遠停在 20px，比它管的卡片標題還小 ＝ 階層反轉沒修到。
   ⚠ margin-bottom 保留：它是這個變體存在的理由（compact 版標題與副標更貼）。 */
.section-title.compact h2 { margin-bottom: 4px; }
.delivery-status { display: inline-flex; padding: 4px 12px; border-radius: 999px; font-size: 14px; font-weight: 800; }
.delivery-status.accepted { color: #8bd8ff; background: #173650; }
.delivery-status.executed { color: #9af0c8; background: #173b31; }
.delivery-status.failed { color: #ff9daf; background: #4a1e2b; }
dialog { width: min(660px, calc(100vw - 40px)); padding: 0; border: 1px solid #31506f; border-radius: 18px; background: #0c1a2b; color: var(--text); box-shadow: 0 30px 100px #000a; }
dialog::backdrop { background: #020915b5; backdrop-filter: blur(4px); }
dialog form { padding: 32px; }
.dialog-title, .dialog-actions { display: flex; align-items: start; justify-content: space-between; gap: 24px; }
.dialog-title p { color: var(--muted); margin-bottom: 0; }
/* ⛔ 這顆是**正圓**，不是「圓角矩形剛好看起來圓」。
   之前只設 min-width: 40px，高度則繼承 button 的 min-height: 48px ⇒ 40×48 ＋ 圓角 10px
   ＝ 一個直立的橢圓（站長 2026-08-18 指出；同一個病之前在頭像上出現過一次）。
   ⇒ 寬高一起釘死才會圓。44 是觸控目標下限，⛔ 不要為了好看縮到 40 以下。 */
.icon-button {
  width: 44px; height: 44px; min-width: 44px; min-height: 44px;
  padding: 0; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: #172b41; font-size: 24px; line-height: 1;
}
.dialog-actions { justify-content: flex-end; margin-top: 32px; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}
.hidden { display: none !important; }
.smart-list { max-height: 160px; overflow: auto; margin-top: 24px; }
.smart-item { display: flex; justify-content: space-between; align-items: center; padding: 12px; margin-top: 8px; border-radius: 9px; background: #071522; font-size: 14px; }
#toast { position: fixed; right: 26px; bottom: 26px; z-index: 20; max-width: 390px; padding: 16px 24px; border: 1px solid #32608a; border-radius: 11px; background: #12304b; box-shadow: 0 15px 50px #0006; opacity: 0; transform: translateY(12px); pointer-events: none; transition: .2s; display: flex; align-items: center; gap: 12px; }
#toast.show { opacity: 1; transform: translateY(0); }
#toast.toast-sticky { padding-right: 48px; pointer-events: auto; }
#toast .toast-close { position: absolute; top: 7px; right: 8px; display: grid; place-items: center; width: 30px; min-width: 30px; height: 30px; min-height: 30px; padding: 0; border: 0; border-radius: 8px; color: currentColor; background: transparent; font-size: 22px; line-height: 1; opacity: .72; }
#toast .toast-close:hover, #toast .toast-close:focus-visible { background: rgba(255,255,255,.1); opacity: 1; }
#toast.toast-success { border-color: #2f9d6a; background: #12352a; color: #d8ffe9; }
#toast.toast-error { border-color: #9b4052; background: #4b1f2b; }
#toast .toast-check { display: inline-grid; place-items: center; width: 22px; height: 22px; border-radius: 50%; background: var(--green); color: #062015; font-weight: 900; font-size: 16px; flex: 0 0 auto; }
button.copied { border-color: #2f9d6a !important; color: #9cf0c4 !important; }
.symbol-picker { display: grid; gap: 12px; margin-top: 8px; }
.symbol-picker input[type="search"] { width: 100%; padding: 16px 16px; border-radius: 9px; border: 1px solid var(--border); background: #07131f; color: var(--text); }
.symbol-select { width: 100%; padding: 16px 16px; border-radius: 9px; border: 1px solid var(--border); background: #07131f; color: var(--text); }
.symbol-picker-actions { display: flex; flex-wrap: wrap; gap: 12px; }

/* ===== 選中商品的來源色（⛔ 掛在**選單框自己**身上，不是另一個框）=====
   站長 2026-08-19 講了兩次：先是「選了 NQU6 他上面不會顯示 感覺不像是選單」，
   上一輪補了一個 .symbol-chosen 回顯框；他接著說「直接將所選項目顯示在選單的那個框，
   不要再下面多做一個框」⇒ 回顯框已刪，改讓 select 收合後自己顯示。
   ⭐ 文字回顯由瀏覽器原生負責（收合後顯示選中 option 的完整文字，含來源標記）；
      這裡只補**顏色**這一項。
   ⛔ 顏色只跟著 data-origin 走，⛔ 不看文字內容 —— 文字會被 i18n 換掉，
      而顏色講的是「這個代碼能不能拿去下單」，那件事跟語言無關。 */
/* ⚠ 展開的下拉清單另外指定底色/字色：select 的 color 會被收合框拿去用，
   若不把 option 拉回中性色，整份清單會跟著變綠或變紅 ⇒ 反而看不出哪一列是哪一組。 */
.symbol-select option { background: #07131f; color: var(--text); }
.symbol-select optgroup { background: #07131f; color: #8693bc; }
/* ✅ 平台確認過的 —— 這是唯一能拿去做 100% 比對的來源，所以給它唯一的綠色。 */
.symbol-select[data-origin="broker-confirmed"] { border-color: var(--green); color: var(--green); }
/* ⛔ 圖表用代碼：送出去下不了單 ⇒ 用紅色，⛔ 不要只是淡化處理。
   站長要的是「有字說出來」，顏色是那句話的第二道保險。 */
.symbol-select[data-origin="chart-only"] { border-color: var(--red); color: var(--red); }
/* 尚未選 ⇒ 中性灰，⛔ 不用紅色：還沒選不是錯誤。 */
.symbol-select[data-origin="none"] { color: #8693bc; }
/* ===== 機器人卡上的交易帳號窗口（站長 2026-08-20）=====
   ⛔ 帳號代碼**不遮罩** —— 這是他自己的帳號，遮起來剛好擋住他換帳號時唯一要看的東西。
   ⚠ 鎖定態（機器人還在跑）仍然可按：擋的理由要由點下去之後的警告說出來，
     畫成 disabled 的話畫面上沒有任何地方會解釋「為什麼不能按」。 */
.bot-account-window {
  display: grid;
  gap: 4px;
  width: 100%;
  margin: 12px 0 0;
  padding: 12px 14px;
  text-align: left;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: rgba(6, 20, 33, .55);
  color: var(--text);
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
}
.bot-account-window:hover { border-color: var(--cyan, #19ddff); background: rgba(10, 30, 48, .75); }
.bot-account-window:focus-visible { outline: 2px solid var(--cyan, #19ddff); outline-offset: 2px; }
.bot-account-window-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
/* ⛔ 不要 text-transform: uppercase —— 這格寫的是環境名（「考核」「模擬」），
   中文吃不到大寫、而英文環境名被拉成 DEMO/PROP 反而看不出是哪一種帳號。 */
.bot-account-window-kicker { font-size: 14px; font-weight: 700; letter-spacing: .4px; color: #8693bc; }
.bot-account-window-cta { font-size: 14px; font-weight: 700; color: var(--cyan, #19ddff); }
.bot-account-window.is-locked .bot-account-window-cta { color: #8693bc; }
.bot-account-window-name { font-size: 16px; font-weight: 700; line-height: 1.3; }
/* ⭐ 帳號代碼用等寬字：它是要拿來跟交易頁上那串**逐字比對**的，
   比例字型會讓 0/O、1/l 分不出來。 */
.bot-account-window-spec { font-family: ui-monospace, Consolas, monospace; font-size: 14px; color: var(--muted); }

/* ===== 換帳號視窗的清單 ===== */
.account-switch-list { display: grid; gap: 8px; padding: 0 24px; }
.account-switch-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 12px 14px;
  text-align: left;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: rgba(6, 20, 33, .55);
  color: var(--text);
  cursor: pointer;
}
.account-switch-item:hover { border-color: var(--cyan, #19ddff); background: rgba(10, 30, 48, .75); }
.account-switch-item.is-current { border-color: var(--green, #72f2c3); }
.account-switch-item-main { display: grid; gap: 2px; flex: 1 1 auto; min-width: 0; }
.account-switch-item-main strong { font-size: 16px; font-weight: 700; }
.account-switch-item-main small { font-family: ui-monospace, Consolas, monospace; font-size: 14px; color: var(--muted); }
.account-switch-item-env { font-size: 14px; color: #8693bc; }
.account-switch-item-tag { font-size: 14px; font-weight: 700; color: var(--green, #72f2c3); }

.brand-dialog { border: 1px solid rgba(53,164,255,.35); border-radius: 16px; padding: 0; background: linear-gradient(180deg, #12253d, #0b1728); color: var(--text); max-width: min(520px, 92vw); box-shadow: 0 24px 80px #000a, 0 0 40px #1b8ee622; }
.brand-dialog::backdrop { background: rgba(3, 8, 16, .72); backdrop-filter: blur(6px); }
.brand-dialog .dialog-title { padding: 24px 24px 12px; }
.brand-dialog .dialog-actions { padding: 16px 24px 24px; display: flex; justify-content: flex-end; gap: 12px; }
.brand-dialog #confirmDialogMessage { color: var(--muted); line-height: 1.5; margin: 0; }
@media (max-width: 1050px) { .metric-grid { grid-template-columns: repeat(2, 1fr); }.settings-grid, .receiver-grid, .account-manager-grid { grid-template-columns: 1fr; }.credential-panel { border-right: 0; border-bottom: 1px solid #203a55; } }
@media (max-width: 760px) {
  .credential-form { grid-template-columns: 1fr; }
  .account-form-actions, .account-env-note, .broker-account-field, .broker-account-note, .account-name-field { grid-column: 1; }
}

/* Future command-deck theme */
:root {
  --bg: #020711;
  --surface: #071421;
  --surface-2: #0a1d30;
  --border: rgba(84, 160, 210, .27);
  --border-hot: rgba(36, 213, 255, .62);
  --text: #eaf8ff;
  --muted: #7594aa;
  --blue: #3c85ff;
  --violet: #7a5ae0;
  --cyan: #19ddff;
  --green: #46f4aa;
  --red: #ff5578;
  --amber: #ffc65a;
  --panel-glow: 0 24px 70px rgba(0, 0, 0, .34), inset 0 1px rgba(154, 224, 255, .055);
  font-family: "Segoe UI Variable", "Segoe UI", Inter, ui-sans-serif, system-ui, sans-serif;
}

html { background: var(--bg); }

body {
  min-height: 100vh;
  background:
    radial-gradient(circle at 12% -8%, rgba(22, 184, 255, .16), transparent 29%),
    radial-gradient(circle at 88% 5%, rgba(65, 87, 255, .14), transparent 31%),
    linear-gradient(155deg, #030914 0%, #020711 46%, #06111d 100%);
  color: var(--text);
  letter-spacing: .01em;
}

.ambient-grid {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: .46;
  background-image:
    linear-gradient(rgba(55, 149, 203, .07) 1px, transparent 1px),
    linear-gradient(90deg, rgba(55, 149, 203, .07) 1px, transparent 1px);
  background-size: 48px 48px;
  -webkit-mask-image: linear-gradient(to bottom, #000 0, transparent 78%);
  mask-image: linear-gradient(to bottom, #000 0, transparent 78%);
}

.ambient-grid::before,
.ambient-grid::after {
  content: '';
  position: absolute;
  pointer-events: none;
}

.ambient-grid::before {
  inset: 0;
  background: linear-gradient(90deg, transparent 0 49.8%, rgba(25, 221, 255, .07) 50%, transparent 50.2%);
}

.ambient-grid::after {
  left: 0;
  right: 0;
  top: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(25, 221, 255, .58), transparent);
  box-shadow: 0 0 18px rgba(25, 221, 255, .4);
  animation: deck-scan 11s linear infinite;
}

@keyframes deck-scan { from { transform: translateY(90px); } to { transform: translateY(920px); } }
@keyframes status-pulse { 0%, 100% { opacity: 1; box-shadow: 0 0 9px currentColor; } 50% { opacity: .5; box-shadow: 0 0 20px currentColor; } }
@keyframes panel-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }

.brand { gap: 24px; }

.brand-mark {
  position: relative;
  width: 52px;
  height: 60px;
  border: 1px solid rgba(89, 210, 255, .7);
  border-radius: 11px 4px 11px 4px;
  color: #eaffff;
  background: linear-gradient(145deg, rgba(20, 119, 238, .92), rgba(0, 217, 224, .58));
  box-shadow: 0 0 0 4px rgba(24, 177, 255, .06), 0 0 32px rgba(14, 182, 255, .28), inset 0 1px rgba(255,255,255,.3);
  transform: rotate(-3deg);
}

.brand-mark::before,
.brand-mark::after {
  content: '';
  position: absolute;
  border-color: rgba(137, 235, 255, .9);
}

.brand-mark::before { inset: -5px 9px; border-top: 1px solid; border-bottom: 1px solid; }
.brand-mark::after { inset: 9px -5px; border-left: 1px solid; border-right: 1px solid; }
.brand-mark i { position: relative; z-index: 1; font: 900 16px/1 Bahnschrift, "Segoe UI", sans-serif; letter-spacing: 1.5px; transform: rotate(3deg); }
.brand strong { font: 680 24px/1.1 Bahnschrift, "Segoe UI", sans-serif; letter-spacing: .2px; }
.brand strong em { color: var(--cyan); font-size: 14px; font-style: normal; letter-spacing: 2.2px; text-shadow: 0 0 16px rgba(25, 221, 255, .6); }
.brand small { margin-top: 8px; color: #688aa2; font: 700 14px/1 ui-monospace, Consolas, monospace; letter-spacing: 2.1px; }

.header-status { display: flex; align-items: center; gap: 16px; }
.node-chip {
  padding: 12px 16px;
  border: 1px solid rgba(84, 160, 210, .24);
  border-radius: 6px;
  color: #698ba2;
  background: rgba(7, 22, 36, .72);
  font: 700 14px/1 ui-monospace, Consolas, monospace;
  letter-spacing: 1.3px;
}

.mode-badge {
  position: relative;
  padding: 12px 24px;
  border: 1px solid rgba(70, 244, 170, .38);
  border-radius: 7px 2px 7px 2px;
  color: #8dffd0;
  background: linear-gradient(120deg, rgba(15, 62, 49, .75), rgba(8, 31, 34, .72));
  box-shadow: inset 0 0 20px rgba(70, 244, 170, .045), 0 0 24px rgba(70, 244, 170, .06);
  font: 800 14px/1 ui-monospace, Consolas, monospace;
  letter-spacing: 1px;
}

.mode-badge span { color: var(--green); animation: status-pulse 2.4s ease-in-out infinite; }

.tabs {
  position: relative;
  height: 72px;
  align-items: center;
  gap: 12px;
  padding: 0 64px;
  border-bottom: 1px solid rgba(84, 160, 210, .18);
  background: rgba(4, 14, 25, .78);
  backdrop-filter: blur(18px);
}

.tab {
  position: relative;
  height: 48px;
  min-height: 48px;
  padding: 0 24px;
  border: 1px solid transparent;
  border-radius: 7px;
  color: #6f8da3;
  background: transparent;
  font-size: 14px;
  font-weight: 720;
  letter-spacing: .5px;
  transition: .18s ease;
}

.tab::before {
  content: '';
  width: 4px;
  height: 4px;
  margin-right: 12px;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 8px currentColor;
}

.tab:hover { border-color: rgba(67, 165, 224, .2); color: #abd9ef; background: rgba(18, 53, 79, .3); }
.tab.active {
  border-color: rgba(25, 221, 255, .32);
  color: #dffaff;
  background: linear-gradient(120deg, rgba(11, 98, 147, .42), rgba(11, 42, 70, .38));
  box-shadow: inset 0 0 22px rgba(25, 221, 255, .05), 0 0 24px rgba(25, 221, 255, .04);
}
.tab.active::after { content: ''; position: absolute; left: 18px; right: 18px; bottom: -12px; height: 2px; background: var(--cyan); box-shadow: 0 0 12px var(--cyan); }
.status-link { color: #4bb7d5; font: 700 14px/1 ui-monospace, Consolas, monospace; letter-spacing: .7px; }

main { max-width: 1660px; padding: 48px 64px 72px; }
.panel.active { animation: panel-in .28s ease-out; }

.metric-grid { gap: 24px; }
.metric {
  min-height: 180px;
  padding: 64px 32px 32px;
  border: 1px solid rgba(70, 162, 217, .28);
  border-radius: 13px 3px 13px 3px;
  background:
    linear-gradient(135deg, rgba(13, 42, 65, .78), rgba(5, 17, 29, .9)),
    repeating-linear-gradient(90deg, transparent 0 13px, rgba(25, 221, 255, .025) 14px);
  box-shadow: var(--panel-glow);
  transition: border-color .2s ease, transform .2s ease, box-shadow .2s ease;
}
.metric:hover { transform: translateY(-2px); border-color: rgba(25, 221, 255, .52); box-shadow: 0 25px 80px rgba(0,0,0,.38), 0 0 24px rgba(25,221,255,.06); }
.metric::before {
  content: attr(data-code);
  position: absolute;
  top: 17px;
  left: 24px;
  color: rgba(67, 198, 238, .62);
  font: 800 14px/1 ui-monospace, Consolas, monospace;
  letter-spacing: 1.4px;
}
.metric::after {
  right: -42px;
  bottom: -59px;
  width: 148px;
  height: 172px;
  border: 1px solid rgba(25, 221, 255, .14);
  background: radial-gradient(circle, rgba(43, 144, 255, .13), transparent 64%);
  box-shadow: 0 0 0 18px rgba(36, 131, 255, .025), 0 0 0 38px rgba(36, 131, 255, .018);
}
.metric > span { color: #8eabbf; font-size: 14px; letter-spacing: .8px; }
.metric strong { margin-top: 24px; color: #effbff; font: 630 48px/1 Bahnschrift, ui-monospace, Consolas, monospace; letter-spacing: -.6px; text-shadow: 0 0 25px rgba(91, 191, 255, .12); }
.metric small { color: #6694ad; font: 700 14px/1 ui-monospace, Consolas, monospace; letter-spacing: .6px; }

/* ===== 金額自適應：放不下就縮，⛔ 不是切掉（站長 2026-08-24「沒有自適應」）=====
   交接 legacy queue 記債最久的一條。.metric 與 .bot-stats div 都是 overflow:hidden
   ＋ 固定字級 ⇒ 放不下【不是換行是切掉】，而且沒有省略號、沒有 title、沒有任何徵兆
   ⇒ -$1234567.89 被切成 -$1234567 之後，它仍然是一個讀得通的合法數字。
   ⛔ 省略號對金額沒有用（看得出被切、仍然不知道被切掉什麼）⇒ 正解是【縮字級】。
   ⭐ 用容器查詢而不是 vw：這幾張卡的寬度由 grid 欄數與側欄開合決定，跟視窗寬不是同一件事
      ⇒ 拿 vw 算會在側欄收起時整組算錯。
   ⛔ @supports 包起來：不支援容器查詢的引擎原樣退回現況（⛔ 不會比現在更糟）。
   ⚠ 兩個下限（20px／12px）是可讀性底線，⛔ 不是量出來的最佳值：真正的判準是
      改完之後 scrollWidth 不得大於 clientWidth（_verify 量測，負對照＝把 clamp 拿掉會紅）。 */
@supports (container-type: inline-size) {
  .metric { container-type: inline-size; }
  .metric strong { font-size: clamp(20px, 10cqi, 48px); }
  .bot-stats > div { container-type: inline-size; }
  .bot-stats strong { font-size: clamp(12px, 13cqi, 18px); }
}
/* 縮到下限仍放不下時（極端長的數字）至少要【看得出來被切了】——
   ⛔ 這是最後一道，不是主要手段：省略號的作用只是「別讓它安靜地說謊」。 */
.metric strong, .bot-stats strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.section-title { margin: 48px 0 24px; }
.section-title h1 { margin-bottom: 8px; font: 650 30px/1.2 Bahnschrift, "Segoe UI", sans-serif; }
.section-title h1::before, .section-title h2::before { content: '//'; margin-right: 12px; color: var(--cyan); font-family: ui-monospace, Consolas, monospace; text-shadow: 0 0 14px rgba(25,221,255,.55); }
.section-title p { line-height: 1.6; }
h2, h3 { font-family: Bahnschrift, "Segoe UI", sans-serif; letter-spacing: .2px; }

button, .button {
  min-height: 48px;
  padding: 0 24px;
 
  font-size: 14px;
 
  letter-spacing: .45px;
  transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease, filter .15s ease;
}
button:hover, .button:hover { transform: translateY(-1px); filter: brightness(1.12); }
button:active, .button:active { transform: translateY(0); }
.primary { border-color: rgba(91, 218, 255, .52); background: linear-gradient(135deg, #126de6, #09a7d7); box-shadow: 0 9px 28px rgba(13, 131, 231, .24), inset 0 1px rgba(255,255,255,.2); }
.primary:hover { box-shadow: 0 12px 34px rgba(13, 163, 231, .34), 0 0 18px rgba(25,221,255,.14); }
.secondary { border-color: rgba(77, 151, 199, .34); color: #bad9e9; background: linear-gradient(145deg, rgba(17, 48, 72, .84), rgba(9, 28, 44, .92)); }
.danger { border-color: rgba(255, 85, 120, .38); color: #ff9bb0; background: linear-gradient(145deg, rgba(87, 24, 43, .8), rgba(46, 17, 30, .9)); }
.warning { border-color: rgba(255, 198, 90, .38); color: #ffdb91; background: rgba(72, 50, 16, .74); }

.card, .bot-card {
  border: 1px solid rgba(73, 153, 203, .25);
 
  background: linear-gradient(145deg, rgba(10, 30, 49, .9), rgba(4, 15, 27, .93));
  box-shadow: var(--panel-glow);
  backdrop-filter: blur(15px);
}
.bot-grid { gap: 24px; }
.bot-card { position: relative; overflow: hidden; padding: 32px; transition: .2s ease; }
.bot-card::before { content: ''; position: absolute; left: 0; top: 0; width: 92px; height: 2px; background: linear-gradient(90deg, var(--cyan), transparent); box-shadow: 0 0 12px rgba(25,221,255,.6); }
.bot-card:hover { transform: translateY(-2px); border-color: rgba(25,221,255,.42); }
.bot-head p { font: 600 14px/1.4 ui-monospace, Consolas, monospace; letter-spacing: .5px; }
.badge { gap: 8px; border: 1px solid transparent; font: 800 14px/1 ui-monospace, Consolas, monospace; letter-spacing: .7px; }
.badge::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: currentColor; box-shadow: 0 0 8px currentColor; }
.badge.running { color: #83f7c3; background: rgba(18, 66, 50, .58); }
.badge.stopped { color: #8ba2b2; background: rgba(39, 54, 68, .7); }
.badge.connecting { color: #77d9ff; background: rgba(14, 61, 88, .7); }
.bot-stats { gap: 12px; margin: 24px 0; }
.bot-stats div { position: relative; overflow: hidden; padding: 16px; border: 1px solid rgba(72, 142, 184, .14); border-radius: 7px; background: rgba(2, 12, 21, .64); }
.bot-stats div::after { content: ''; position: absolute; right: 0; bottom: 0; width: 18px; height: 1px; background: var(--cyan); opacity: .45; }
.bot-stats strong { margin-top: 8px; font-family: ui-monospace, Consolas, monospace; color: #dff5ff; }
.webhook { border: 1px solid rgba(50, 132, 178, .18); background: rgba(1, 10, 18, .7); }
.webhook code { color: #5cdfff; }

input, select {
  min-height: 52px;
  border: 1px solid rgba(76, 145, 188, .28);
 
 
  background: rgba(3, 15, 26, .82);
  box-shadow: inset 0 0 18px rgba(8, 90, 130, .035);
}
input::placeholder { color: #49687e; }
input:focus, select:focus { border-color: rgba(25,221,255,.75); box-shadow: 0 0 0 3px rgba(25,221,255,.08), inset 0 0 20px rgba(25,221,255,.035); }

.table-wrap { border-color: rgba(77, 151, 199, .24); border-radius: 11px 2px 11px 2px; background: rgba(3, 14, 24, .84); box-shadow: var(--panel-glow); }
th, td { padding: 16px 24px; border-bottom-color: rgba(62, 125, 163, .17); font-size: 14px; }
th { color: #7196ad; background: rgba(9, 31, 50, .96); font: 760 14px/1.2 ui-monospace, Consolas, monospace; letter-spacing: .6px; }
td { color: #c9dce6; }
tbody tr { transition: background .15s ease; }
tbody tr:hover { background: rgba(19, 97, 137, .12); }

.receiver-grid { gap: 24px; }
.receiver-status { position: relative; overflow: hidden; border-color: rgba(69, 157, 207, .27); border-radius: 9px 2px 9px 2px; background: rgba(2, 15, 25, .72); }
.receiver-status::after { content: 'UPLINK STATUS'; position: absolute; top: 8px; right: 10px; color: rgba(83, 156, 195, .5); font: 700 14px/1 ui-monospace, Consolas, monospace; letter-spacing: 1px; }
.receiver-status > span { color: var(--red); animation: status-pulse 2.4s ease-in-out infinite; }
.receiver-status > span.online { color: var(--green); }
.receiver-status strong { letter-spacing: .3px; }
.copy-field code { border-color: rgba(41, 156, 207, .32); border-radius: 7px 2px 7px 2px; color: #61dcff; background: rgba(1, 11, 20, .86); font-family: ui-monospace, Consolas, monospace; }
.notice { border-left-color: var(--amber); color: #d8bb79; background: linear-gradient(90deg, rgba(89, 60, 15, .34), rgba(42, 31, 14, .2)); }
.json-template { min-height: 364px; border-color: rgba(25,221,255,.35); border-radius: 9px 2px 9px 2px; color: #70defa; background: rgba(0, 10, 18, .9); box-shadow: inset 0 0 34px rgba(13, 95, 133, .09), 0 0 24px rgba(25,221,255,.025); font-size: 14px; }
.template-head h2 { font-size: 20px; }
.strategy-route-note { color: #65a9c7; }
.delivery-status { border: 1px solid transparent; border-radius: 5px; font: 800 14px/1 ui-monospace, Consolas, monospace; letter-spacing: .5px; }
.delivery-status.accepted { border-color: rgba(119,217,255,.2); color: #77d9ff; background: rgba(14,61,88,.7); }
.delivery-status.executed { border-color: rgba(70,244,170,.2); color: #83f7c3; background: rgba(18,66,50,.58); }
.delivery-status.failed { border-color: rgba(255,85,120,.2); color: #ff9bb0; background: rgba(87,24,43,.7); }

.account-manager { border-color: rgba(25,221,255,.25); border-radius: 14px 3px 14px 3px; background: linear-gradient(145deg, rgba(8,29,46,.94), rgba(3,14,25,.96)); }
.account-manager::before { height: 2px; background: linear-gradient(90deg, var(--green), var(--cyan) 48%, transparent 88%); box-shadow: 0 0 14px rgba(25,221,255,.5); }
.account-manager-head { border-bottom-color: rgba(75, 151, 194, .19); }
.account-title-icon { border-color: rgba(25,221,255,.36); border-radius: 10px 3px 10px 3px; color: #7df4d4; background: linear-gradient(145deg, rgba(17,83,78,.7), rgba(7,41,55,.75)); box-shadow: 0 0 24px rgba(25,221,255,.08); }
.simulation-chip, .secure-chip, .account-status, .account-demo, .account-count { border-radius: 5px; font-family: ui-monospace, Consolas, monospace; letter-spacing: .5px; }
.credential-panel { border-right-color: rgba(75,151,194,.18); background: rgba(2, 15, 27, .44); }
.eyebrow { color: #4acbea; font-family: ui-monospace, Consolas, monospace; }
.account-card { border-color: rgba(75,151,194,.25); border-radius: 10px 2px 10px 2px; background: linear-gradient(145deg, rgba(13,42,65,.8), rgba(4,18,31,.92)); }
.account-card::before { width: 2px; background: linear-gradient(var(--cyan), transparent); box-shadow: 0 0 9px var(--cyan); }
.account-avatar { border: 1px solid rgba(25,221,255,.23); border-radius: 8px 2px 8px 2px; color: #95e9ff; background: rgba(14, 67, 96, .62); }
.account-card-head, .account-card-actions { border-color: rgba(66,135,174,.17); }
.account-detail-row { border-bottom-color: rgba(66,135,174,.14); }
.safety-card li { color: #8ca9bb; line-height: 1.55; }

.media-card { border-color: rgba(75,151,194,.24); border-radius: 10px 2px 10px 2px; background: rgba(5,20,33,.9); box-shadow: var(--panel-glow); }
.connection-line { border-radius: 7px 2px 7px 2px; background: rgba(1,12,21,.72); }
.empty, .account-list-empty { border-color: rgba(64,147,194,.32); border-radius: 10px 2px 10px 2px; background: rgba(3,15,26,.44); }

dialog {
  border-color: rgba(25,221,255,.34);
  border-radius: 14px 3px 14px 3px;
  background: linear-gradient(145deg, rgba(10,31,49,.98), rgba(3,14,25,.99));
  box-shadow: 0 35px 120px rgba(0,0,0,.75), 0 0 38px rgba(25,221,255,.08), inset 0 1px rgba(154,224,255,.08);
}
dialog::backdrop { background: rgba(0, 5, 12, .78); backdrop-filter: blur(9px); }
dialog form { position: relative; }
dialog form::before { content: ''; position: absolute; left: 24px; right: 58%; top: 0; height: 2px; background: linear-gradient(90deg, var(--cyan), transparent); box-shadow: 0 0 12px var(--cyan); }
.icon-button { border-color: rgba(69,151,198,.3); background: rgba(14,43,65,.8); }

#toast { border-color: rgba(25,221,255,.44); border-radius: 9px 2px 9px 2px; color: #daf7ff; background: rgba(8,40,58,.96); box-shadow: 0 18px 65px rgba(0,0,0,.62), 0 0 24px rgba(25,221,255,.1); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; }
}

@media (max-width: 1050px) {
  main { padding-left: 32px; padding-right: 32px; }
  .header-status { gap: 8px; }
  .node-chip { display: none; }
  .tab { padding: 0 16px; }
}

/* Orbital space theme */
:root {
  --bg: #050611;
  /* 2026-08-18：不透明度 .78/.72 -> .93/.90。內容表面要夠實，資料才不會躺在
     一張會變亮的照片上（大企業級產品的作法：照片只負責氛圍，內容區是實的）。
     ⚠ 誠實邊界：總覽那四張 .metric 卡片**不吃這個 token** —— 它們在下面用寫死的
     漸層自己蓋掉了，所以改這裡對它們零效果（我實測過，畫面一模一樣）。
     真正管到那四張卡的是 `.metric` 的 background 那條，兩處都要動。 */
  --surface: rgba(13, 17, 39, .93);
  --surface-2: rgba(20, 25, 55, .90);
  --border: rgba(163, 174, 255, .2);
  --border-hot: rgba(153, 181, 255, .48);
  --text: #f3f4ff;
  --muted: #8e96bb;
  --blue: #6c8cff;
  --cyan: #78e5ff;
  --violet: #9d7cff;
  --magenta: #e38cff;
  --green: #72f2c3;
  --red: #ff708e;
  --amber: #ffd37a;
  --panel-glow: 0 28px 75px rgba(0, 0, 0, .34), inset 0 1px rgba(224, 228, 255, .055);
}

body {
  background:
    radial-gradient(ellipse at 82% 0%, rgba(95, 65, 190, .19), transparent 34%),
    radial-gradient(ellipse at 9% 20%, rgba(34, 111, 184, .16), transparent 31%),
    radial-gradient(ellipse at 50% 110%, rgba(86, 45, 140, .16), transparent 38%),
    linear-gradient(155deg, #080a1a 0%, #04050e 48%, #080716 100%);
}

.ambient-grid {
  z-index: 0;
  opacity: .92;
  background-image:
    radial-gradient(circle, rgba(255,255,255,.9) 0 1px, transparent 1.4px),
    radial-gradient(circle, rgba(164,204,255,.68) 0 1px, transparent 1.5px),
    radial-gradient(circle, rgba(255,255,255,.45) 0 1px, transparent 1.5px);
  background-size: 137px 137px, 211px 211px, 307px 307px;
  background-position: 17px 29px, 83px 117px, 151px 41px;
  -webkit-mask-image: none;
  mask-image: none;
  animation: star-drift 80s linear infinite;
}

.app-header, .tabs, main { position: relative; z-index: 1; }
/* fixed 子層不能逃出 main 的 stacking context；全螢幕時把整個工作區提到頂欄之上。 */
body.workbench-fullscreen-active main { z-index: 10000; }
/* ⋯ 選單掛在 body，必須高於全螢幕 main；否則按鈕有收到點擊，選單卻畫在背後。 */
body.workbench-fullscreen-active > .wb-menu { z-index: 20040; }

.ambient-grid::before {
  inset: -15% -10%;
  background:
    radial-gradient(ellipse at 76% 20%, rgba(141, 95, 255, .17), transparent 20%),
    radial-gradient(ellipse at 68% 26%, rgba(58, 170, 255, .12), transparent 27%),
    radial-gradient(ellipse at 24% 72%, rgba(195, 96, 255, .08), transparent 24%);
  filter: blur(16px);
}

.ambient-grid::after {
  left: auto;
  right: 5vw;
  top: 160px;
  width: min(34vw, 510px);
  height: min(34vw, 588px);
  border: 1px solid rgba(150, 175, 255, .13);
  border-radius: 50%;
  background:
    radial-gradient(circle at 34% 28%, rgba(218, 231, 255, .34), transparent 4%),
    radial-gradient(circle at 35% 33%, #586fa4 0, #27355e 19%, #12182f 49%, #080a18 69%, transparent 70%),
    radial-gradient(circle, rgba(120, 151, 255, .14), transparent 72%);
  box-shadow:
    -34px -12px 90px rgba(105, 151, 255, .11),
    0 0 0 46px rgba(134, 125, 255, .015),
    0 0 0 105px rgba(91, 80, 193, .012);
  filter: drop-shadow(0 0 35px rgba(100, 128, 255, .1));
  animation: planet-float 10s ease-in-out infinite;
}

@keyframes star-drift { to { background-position: 154px 166px, 294px 328px, 458px 348px; } }
@keyframes planet-float { 0%, 100% { transform: translateY(0) rotate(-4deg); } 50% { transform: translateY(-12px) rotate(-2deg); } }

.brand-mark {
  width: 54px;
  height: 64px;
  border-color: rgba(178, 184, 255, .42);
  border-radius: 50%;
  color: #f0ebff;
  background: radial-gradient(circle at 35% 28%, #bfcaff, #667eea 30%, #352a72 65%, #15132d 100%);
  box-shadow: 0 0 0 7px rgba(133, 124, 255, .05), 0 0 34px rgba(118, 114, 255, .25), inset -8px -10px 18px rgba(4, 6, 22, .4);
  transform: none;
}
.brand-mark::before {
  inset: 22px -9px;
  border: 1px solid rgba(176, 206, 255, .62);
  border-radius: 50%;
  transform: rotate(-18deg);
}
.brand-mark::after { display: none; }
.brand-mark i { font-size: 18px; text-shadow: 0 0 12px white; transform: none; }
.brand-mark img { width: 46px; height: 52px; display: block; object-fit: contain; }
.brand strong { font-family: "Segoe UI Variable", "Segoe UI", sans-serif; font-size: 24px; font-weight: 660; letter-spacing: .1px; }
.brand strong em { color: #b9a7ff; font-size: 14px; letter-spacing: 2.8px; text-shadow: 0 0 18px rgba(166, 135, 255, .5); }
.brand small { color: #747da5; font-family: "Segoe UI Variable", "Segoe UI", sans-serif; font-size: 14px; letter-spacing: 2.4px; }

.node-chip {
  border-color: rgba(161, 173, 255, .17);
  border-radius: 999px;
  color: #7e86aa;
  background: rgba(17, 20, 45, .55);
}
.mode-badge {
  border-color: rgba(114, 242, 195, .28);
  border-radius: 999px;
  color: #a2f5d6;
  background: rgba(17, 57, 52, .42);
  box-shadow: 0 0 28px rgba(86, 238, 190, .05);
}

.tabs {
  height: 76px;
  border-bottom-color: rgba(151, 162, 232, .12);
  background: rgba(8, 10, 26, .62);
}
.tab {
  border-radius: 999px;
  color: #7f87aa;
  font-size: 14px;
  font-weight: 650;
}
.tab::before { width: 3px; height: 3px; box-shadow: 0 0 9px currentColor; }
.tab:hover { border-color: rgba(162, 174, 255, .16); color: #cbd0ee; background: rgba(60, 65, 119, .18); }
.tab.active {
  border-color: rgba(159, 175, 255, .28);
  color: #f4f2ff;
  background: linear-gradient(120deg, rgba(100, 111, 224, .34), rgba(111, 78, 174, .25));
  box-shadow: 0 9px 28px rgba(59, 51, 131, .18), inset 0 1px rgba(255,255,255,.07);
}
.tab.active::after { left: 27%; right: 27%; bottom: -13px; height: 1px; background: #afa5ff; box-shadow: 0 0 14px #8e82ff; }
.status-link { color: #9ba9e8; font-family: "Segoe UI", sans-serif; }

.metric {
  min-height: 184px;
  border-color: rgba(156, 168, 241, .2);
  border-radius: 22px;
  background:
    radial-gradient(circle at 90% 15%, rgba(132, 114, 255, .11), transparent 31%),
    linear-gradient(145deg, rgba(23, 28, 60, .74), rgba(9, 12, 29, .82));
  box-shadow: var(--panel-glow);
  backdrop-filter: blur(18px);
}
.metric:hover { border-color: rgba(177, 173, 255, .4); box-shadow: 0 28px 85px rgba(0,0,0,.4), 0 0 36px rgba(124,108,255,.07); }
.metric::before { color: rgba(183, 176, 255, .55); letter-spacing: 1.2px; }
.metric::after {
  right: -46px;
  bottom: -62px;
  border-color: rgba(177, 187, 255, .13);
  background: radial-gradient(circle at 34% 30%, rgba(166, 191, 255, .18), rgba(79, 85, 157, .08) 38%, transparent 68%);
  box-shadow: 0 0 0 22px rgba(133, 123, 255, .018), 0 0 0 49px rgba(133, 123, 255, .012);
}
.metric > span { color: #a2a8c7; }
.metric strong { color: #f8f7ff; font-family: "Segoe UI Variable", "Segoe UI", sans-serif; font-weight: 620; text-shadow: 0 0 28px rgba(175,176,255,.12); }
.metric small { color: #7e86aa; }

.section-title h1, .section-title h2 { font-family: "Segoe UI Variable", "Segoe UI", sans-serif; }
.section-title h1::before, .section-title h2::before { content: '✦'; margin-right: 16px; color: #a895ff; font-family: inherit; font-size: .7em; text-shadow: 0 0 18px rgba(168,149,255,.55); }

.primary {
  border-color: rgba(179, 182, 255, .36);
  background: linear-gradient(135deg, #667eea, #8b5fd3);
  box-shadow: 0 10px 30px rgba(92, 72, 191, .26), inset 0 1px rgba(255,255,255,.18);
}
.primary:hover { box-shadow: 0 13px 38px rgba(106, 82, 211, .34), 0 0 20px rgba(153,131,255,.14); }
.secondary { border-color: rgba(156, 168, 233, .22); color: #c5c9e5; background: rgba(28, 34, 68, .62); }
.danger { border-color: rgba(255, 112, 142, .28); color: #ffadc0; background: rgba(80, 26, 49, .56); }

.card, .bot-card {
  border-color: rgba(157, 167, 229, .18);
 
  background:
    radial-gradient(circle at 100% 0%, rgba(111, 91, 214, .08), transparent 28%),
    linear-gradient(145deg, rgba(19, 24, 52, .78), rgba(8, 11, 27, .84));
  box-shadow: var(--panel-glow);
  backdrop-filter: blur(20px) saturate(115%);
}
.bot-card::before { left: 22px; top: 0; width: 80px; height: 1px; background: linear-gradient(90deg, transparent, #b4a8ff, transparent); box-shadow: 0 0 15px rgba(176,164,255,.5); }
.bot-card:hover { border-color: rgba(174,171,255,.36); }
.bot-stats div { border-color: rgba(153, 163, 225, .1); border-radius: 13px; background: rgba(5, 8, 22, .48); }
.bot-stats div::after { width: 24px; background: #a798ff; }
.webhook { border-color: rgba(147, 161, 231, .14); background: rgba(5, 8, 22, .48); }
.webhook code { color: #a8d9ff; }

input, select {
  border-color: rgba(151, 163, 226, .22);
 
 
  background: rgba(7, 10, 28, .64);
  box-shadow: inset 0 0 22px rgba(105, 95, 192, .025);
}
input::placeholder { color: #5f6688; }
input:focus, select:focus { border-color: rgba(171,155,255,.65); box-shadow: 0 0 0 3px rgba(154,130,255,.08), 0 0 24px rgba(126,104,218,.06); }

.table-wrap { border-color: rgba(152, 163, 225, .18); border-radius: 18px; background: rgba(8, 11, 28, .74); backdrop-filter: blur(18px); }
th { color: #969dbc; background: rgba(21, 25, 54, .9); font-family: "Segoe UI", sans-serif; }
th, td { border-bottom-color: rgba(144, 155, 216, .1); }
tbody tr:hover { background: rgba(112, 99, 199, .08); }

.receiver-status {
  border-color: rgba(150, 163, 229, .2);
  border-radius: 16px;
  background: rgba(6, 10, 26, .58);
}
.receiver-status::after { color: rgba(164, 158, 232, .45); content: 'ORBITAL UPLINK'; }
.copy-field code { border-color: rgba(144, 158, 226, .22); border-radius: 13px; color: #a7dfff; background: rgba(5, 8, 22, .72); }
.notice { border-left-color: #d7b56d; color: #d9c38e; background: rgba(66, 50, 22, .32); }
.json-template { border-color: rgba(134, 160, 255, .25); border-radius: 16px; color: #a9ddff; background: rgba(4, 7, 20, .76); box-shadow: inset 0 0 42px rgba(86,72,159,.07); }
.strategy-route-note { color: #929cc4; }
.delivery-status { border-radius: 999px; }

.account-manager { border-color: rgba(158, 168, 232, .2); border-radius: 24px; background: linear-gradient(145deg, rgba(19,24,52,.82), rgba(8,11,27,.88)); }
.account-manager::before { height: 1px; background: linear-gradient(90deg, transparent, #83dfff 26%, #b18cff 68%, transparent); box-shadow: 0 0 16px rgba(160,139,255,.4); }
.account-title-icon { border-color: rgba(166, 166, 241, .26); border-radius: 50%; color: #aee8ff; background: radial-gradient(circle at 35% 30%, rgba(114,213,255,.34), rgba(82,73,155,.46)); }
.simulation-chip, .secure-chip, .account-status, .account-demo, .account-count { border-radius: 999px; }
.credential-panel { border-right-color: rgba(149,159,219,.13); background: rgba(6,9,24,.3); }
.eyebrow { color: #a996ff; font-family: "Segoe UI", sans-serif; }
.account-card { border-color: rgba(151,162,224,.18); border-radius: 18px; background: rgba(15,20,46,.68); }
.account-card::before { width: 1px; background: linear-gradient(transparent, #9e8fff, transparent); box-shadow: 0 0 12px rgba(158,143,255,.5); }
.account-avatar { border-color: rgba(163,167,236,.2); border-radius: 50%; color: #c5e7ff; background: rgba(76,81,151,.42); }

.media-card, .connection-line, .empty, .account-list-empty { border-radius: 18px; }
.empty, .account-list-empty { border-color: rgba(148, 160, 224, .24); background: rgba(6,9,24,.35); }

dialog {
  border-color: rgba(167, 168, 238, .24);
  border-radius: 26px;
  background:
    radial-gradient(circle at 82% 0%, rgba(119,91,203,.16), transparent 28%),
    linear-gradient(145deg, rgba(20,25,55,.98), rgba(7,10,26,.99));
  box-shadow: 0 38px 130px rgba(0,0,0,.78), 0 0 70px rgba(94,77,185,.1), inset 0 1px rgba(220,220,255,.07);
}
dialog::backdrop { background: rgba(2, 3, 12, .82); backdrop-filter: blur(12px); }
dialog form::before { left: 42%; right: 42%; height: 1px; background: #b3a3ff; box-shadow: 0 0 20px rgba(179,163,255,.7); }
.icon-button { border-color: rgba(160,166,230,.22); border-radius: 50%; background: rgba(42,46,84,.6); }

#toast { border-color: rgba(167,169,239,.3); border-radius: 18px; color: #eff0ff; background: rgba(27,30,68,.94); box-shadow: 0 20px 70px rgba(0,0,0,.64), 0 0 30px rgba(127,107,218,.1); }

@media (prefers-reduced-motion: reduce) {
  .ambient-grid, .ambient-grid::after { animation: none !important; }
}

/* Cinematic orbit presentation */
body {
  background: #02030a;
}

.ambient-grid {
  opacity: 1;
  background:
    linear-gradient(180deg, rgba(1, 3, 12, .16) 0%, rgba(2, 4, 14, .22) 45%, rgba(2, 3, 10, .82) 100%),
    linear-gradient(90deg, rgba(2, 4, 13, .34), transparent 50%, rgba(4, 4, 18, .04)),
    url('/assets/cinematic-orbit-v1.png') center top / cover no-repeat;
  animation: none;
}

.ambient-grid::before {
  inset: 0;
  background:
    radial-gradient(ellipse at center, transparent 28%, rgba(1, 2, 8, .18) 72%, rgba(1, 2, 7, .52) 100%),
    linear-gradient(180deg, rgba(2, 3, 10, .02), rgba(2, 3, 10, .48));
  filter: none;
}

.ambient-grid::after { display: none; }

.tabs {
  background: linear-gradient(90deg, rgba(5, 8, 23, .83), rgba(9, 9, 28, .67));
  border-bottom-color: rgba(175, 187, 242, .14);
}

main {
  padding-top: 64px;
}

.metric {
  border-color: rgba(186, 199, 255, .2);
  /* 2026-08-18：.8/.7 -> .95/.9。放大區塊之後這四張卡覆蓋到背景地球的亮部，
     卡內次要文字幾乎讀不到 —— 是「放大」的直接副作用，不是既有缺陷。
     ⛔ 別改回半透明來「保留質感」：讀不到的資料沒有質感可言。 */
  background:
    linear-gradient(145deg, rgba(16, 22, 47, .95), rgba(5, 8, 22, .9)),
    radial-gradient(circle at 88% 14%, rgba(113, 139, 255, .14), transparent 34%);
  box-shadow: 0 30px 80px rgba(0,0,0,.36), inset 0 1px rgba(235,240,255,.07);
  backdrop-filter: blur(26px) saturate(120%);
}

.metric::after {
  background: radial-gradient(circle at 35% 32%, rgba(181, 207, 255, .19), rgba(75, 91, 164, .08) 40%, transparent 69%);
}

.metric:hover {
  border-color: rgba(196, 196, 255, .42);
  box-shadow: 0 34px 92px rgba(0,0,0,.42), 0 0 38px rgba(121,119,255,.08);
}

.card, .bot-card {
  border-color: rgba(177, 190, 244, .18);
  /* ⛔ 0.3.41 路標：改主題色時**別改這裡**，這一組對現有任何一張卡都不是 cascade 贏家。
     實測（瀏覽器逐元素問出來的，不是讀碼推的）——真正在畫卡片表面的是：
       機器人卡 / 雲端卡 / 設定卡  → `.module-card`（本檔約 1845 行）
                                    ＊app.js:1766 渲染的就是 class="bot-card module-card"
       TradingView 接收卡          → `.receiver-grid .card`（約 891 行，特異性 0,2,0 最高）
       四張指標卡                  → `.metric.telemetry-card`（約 921 行）
       歷史表格容器                → `.module-table`
     index.html 裡每一個 .card 都同時帶 module-card ⇒ 這一組的 background 永遠被蓋掉。
     這正是 VIS-10「沒有單一真相源，改了不知道會不會生效」的實例：0.3.41 修 VIS-11 時
     第一版就改在這裡，量出來四張卡一格都沒動。 */
  background:
    linear-gradient(145deg, rgba(13, 18, 42, .83), rgba(5, 8, 21, .76)),
    radial-gradient(circle at 100% 0%, rgba(123, 108, 224, .08), transparent 30%);
  box-shadow: 0 32px 90px rgba(0, 0, 0, .38), inset 0 1px rgba(230, 235, 255, .055);
  backdrop-filter: blur(27px) saturate(120%);
}

.receiver-grid .card {
  /* 0.3.41 VIS-11 ⚠ 特異性 (0,2,0) ⇒ TradingView 接收頁的卡片表面由**這裡**決定，
     連 `.module-card`（約 1845 行）都蓋不過它。跟著那組一起提亮，否則同一個 app 裡
     只有這一頁的卡片是暗的（半套的一致性改動比不改更糟）。 */
  background: linear-gradient(145deg, rgba(35, 45, 86, .93), rgba(20, 27, 56, .89));
}

.table-wrap {
  border-color: rgba(177, 190, 244, .17);
  background: rgba(5, 8, 21, .78);
  box-shadow: 0 28px 75px rgba(0,0,0,.34), inset 0 1px rgba(230,235,255,.04);
  backdrop-filter: blur(24px);
}

.account-manager {
  background: linear-gradient(145deg, rgba(13, 18, 43, .9), rgba(4, 7, 20, .83));
  box-shadow: 0 35px 100px rgba(0,0,0,.42), inset 0 1px rgba(230,235,255,.06);
}

.empty, .account-list-empty {
  background: rgba(4, 7, 19, .55);
  backdrop-filter: blur(18px);
}

dialog {
  background:
    radial-gradient(circle at 86% 0%, rgba(120, 94, 213, .16), transparent 28%),
    linear-gradient(145deg, rgba(19, 24, 54, .98), rgba(5, 8, 22, .99));
}

/* Orbital telemetry cards */
.metric-grid { gap: 16px; }

.metric.telemetry-card {
 
  min-height: 160px;
  padding: 24px 24px 24px;
  border: 1px solid color-mix(in srgb, var(--tone) 24%, rgba(175, 187, 241, .18));
 
  /* 0.3.41 VIS-11 ⚠ 表面亮度是承重的，⛔ 別為了「更沉穩」再壓暗回去。
     深色介面的層級靠**更亮的表面**表達，不是靠陰影 —— 在 #050611 這種近黑底上，
     黑色陰影與背景的亮度差是 0.00203 → 0.00124，對比 1.015，人眼看不到任何東西
     （全檔 38 條純黑陰影都在付合成層的繪製成本、換不到任何視覺層級）。
     ⇒ 陰影改帶色相（在深底上才浮得出來），層級改由表面亮度扛。
     判準是量出來的：卡面 vs 頁底對比 ≥1.25（改前 1.17）。 */
  background:
    linear-gradient(140deg, color-mix(in srgb, var(--tone) 7%, rgba(26, 33, 68, .90)), rgba(13, 18, 44, .86)),
    rgba(16, 21, 48, .84);
  box-shadow: 0 16px 44px rgba(58, 48, 148, .42), inset 0 1px rgba(239,243,255,.075);
  backdrop-filter: blur(24px) saturate(118%);
}


.metric.telemetry-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 20px;
  width: 68px;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--tone), transparent);
  box-shadow: 0 0 15px color-mix(in srgb, var(--tone) 60%, transparent);
}

.metric.telemetry-card::after {
  content: '';
  position: absolute;
  top: -48px;
  right: -38px;
  bottom: auto;
  width: 124px;
  height: 144px;
  border: 0;
  border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--tone) 14%, transparent), transparent 68%);
  box-shadow: none;
}

.metric.telemetry-card:hover {
  border-color: color-mix(in srgb, var(--tone) 43%, rgba(180,190,240,.2));
  box-shadow: 0 28px 80px rgba(0,0,0,.38), 0 0 28px color-mix(in srgb, var(--tone) 7%, transparent);
}

.metric-head {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 16px;
}

.metric-head > .metric-icon {
  width: 36px;
  height: 40px;
  display: grid;
  flex: 0 0 36px;
  place-items: center;
  border: 1px solid color-mix(in srgb, var(--tone) 28%, transparent);
  border-radius: 11px;
  color: var(--tone);
  background: color-mix(in srgb, var(--tone) 9%, rgba(7,10,27,.76));
  box-shadow: inset 0 0 14px color-mix(in srgb, var(--tone) 5%, transparent), 0 0 18px color-mix(in srgb, var(--tone) 5%, transparent);
}

.metric-icon svg {
  width: 20px;
  height: 20px;
  overflow: visible;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.55;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.metric-label { min-width: 0; flex: 1; }
.metric .metric-label > small { display: block; margin: 4px 0 0; color: color-mix(in srgb, var(--tone) 65%, #646c92); font: 700 14px/1 ui-monospace, Consolas, monospace; letter-spacing: 1.05px; }

.metric-beacon {
  width: 6px;
  height: 6px;
  flex: 0 0 6px;
  border-radius: 50%;
  color: var(--tone);
  background: currentColor;
  animation: status-pulse 2.7s ease-in-out infinite;
}

.metric-reading {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-top: 16px;
}

.metric .metric-reading > strong {
  margin: 0;
  color: #f7f8ff;
  font: 620 36px/1 "Segoe UI Variable", "Segoe UI", sans-serif;
 
  text-shadow: 0 0 24px color-mix(in srgb, var(--tone) 10%, transparent);
}

.metric .metric-reading > small {
  margin: 0;
 /* 0.3.41 VIS-11 連動：卡面提亮後 5.08→4.49 掉破 AA，提亮到 4.82 */
  font: 650 14px/1.2 "Segoe UI", sans-serif;
  letter-spacing: .35px;
}

.metric .metric-signal {
  position: absolute;
  left: 20px;
  right: 20px;
  bottom: 0;
  height: 1px;
  overflow: hidden;
  background: rgba(145, 158, 213, .08);
}

.metric-signal i {
  display: block;
  width: 44%;
  height: 100%;
  background: linear-gradient(90deg, transparent, var(--tone), transparent);
  opacity: .58;
  transform: translateX(-30%);
}

.telemetry-card:nth-child(2) .metric-signal i { width: 58%; transform: translateX(22%); }
.telemetry-card:nth-child(3) .metric-signal i { width: 72%; transform: translateX(5%); }
.telemetry-card:nth-child(4) .metric-signal i { width: 55%; transform: translateX(72%); }

/* Unified orbital interface system */
body { color: #eff2ff; }

.section-kicker {
  display: block;
  margin-bottom: 8px;
  color: #8f9ee2;
  font: 750 14px/1 ui-monospace, SFMono-Regular, Consolas, monospace;
  letter-spacing: 1.65px;
}

.section-title { align-items: end; margin: 48px 0 24px; }
.section-title h1, .section-title h2 { margin-bottom: 8px; color: #f4f5ff; font-family: "Segoe UI Variable", "Segoe UI", sans-serif; }
.section-title h1::before, .section-title h2::before { display: none; }
.section-title p { max-width: 760px; line-height: 1.65; }

.module-card {
  position: relative;
  overflow: hidden;
  border-color: rgba(159, 176, 235, .2);
  background:
    linear-gradient(145deg, rgba(12, 17, 40, .89), rgba(4, 7, 19, .82)),
    rgba(7, 10, 26, .8);
  box-shadow: 0 28px 80px rgba(0,0,0,.34), inset 0 1px rgba(236,240,255,.055);
}

.module-card::after {
  content: attr(data-module);
  position: absolute;
  top: 18px;
  right: 20px;
  z-index: 2;
  color: #6974a7;
  font: 700 14px/1 ui-monospace, Consolas, monospace;
  letter-spacing: 1.3px;
}

.module-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 22px;
  width: 74px;
  height: 1px;
  background: linear-gradient(90deg, transparent, #8fa7ff, transparent);
  box-shadow: 0 0 14px rgba(134,157,255,.52);
}

.module-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 52px;
  margin: -3px 0 15px;
  padding-bottom: 16px;
  border-bottom: 1px solid rgba(145, 158, 215, .11);
}
.module-heading > div > span { display: block; color: #e6e9f9; font-size: 16px; font-weight: 700; }
.module-heading > div > small { display: block; margin-top: 4px; color: #737d9f; font-size: 14px; }
.module-heading > i { width: 28px; height: 1px; margin-right: 94px; background: linear-gradient(90deg, #788fff, transparent); opacity: .6; }

.form-card { padding: 32px; }
.form-card h2 { color: #f0f2ff; }
.muted { color: #8b94b2; font-size: 14px; line-height: 1.6; }

label {
  margin: 24px 0;
 
 
 
 
}
label input, label select { margin-top: 12px; }
/* 0.3.41 VIS-11 連動 ⚠ 這一批「卡片上的淡色說明文字」是跟著卡片表面提亮一起調的，
   ⛔ 別單獨把它們調暗回去。理由：VIS-11 把卡片表面從對比 1.05–1.17 提到 1.33–1.38 之後，
   這些字的對比全部掉破 WCAG AA 的 4.5（實測前後：#717c9f 4.67→4.18、#7e88a9 5.17→3.81、
   #7e87a7 5.09→4.15、#7c86a8 5.46→4.50、#838dac 4.98→4.00、#8593b7 5.70→4.48）——
   它們本來就只有 0.1–0.7 的餘裕，表面一亮就沒了。現值對新表面留 ≥4.75。
   ⇒ 之後若再動任何一個卡片表面亮度，這一批（grep「VIS-11 連動」共 8 處）要重量一次。 */
.field-help { display: block; margin-top: 4px; color: #8693bc; font-size: 14px; font-weight: 550; line-height: 1.5; }

input, select {
  min-height: 52px;
  padding: 0 16px;
  border-color: rgba(151, 166, 224, .2);
 
 
  background: rgba(4, 7, 20, .68);
}
input:hover, select:hover { border-color: rgba(160, 174, 234, .34); }

.receiver-grid { gap: 24px; }
.receiver-grid .form-card { min-height: 100%; }
.receiver-status {
  margin-bottom: 24px;
  padding: 24px 24px;
  border-color: rgba(150, 166, 226, .18);
  border-radius: 13px;
  background: rgba(3, 7, 20, .58);
  box-shadow: inset 0 1px rgba(236,240,255,.035);
}
.receiver-status::after { top: 10px; right: 12px; color: #626d99; font-size: 14px; letter-spacing: 1.25px; }
.receiver-status strong { color: #f0f2ff; font-size: 16px; font-weight: 680; }
.receiver-status small { color: #8993b6; font-size: 14px; } /* 0.3.41 VIS-11 連動 */

.copy-field { gap: 12px; margin-top: 12px; }
.copy-field code {
  padding: 16px 16px;
  border-color: rgba(119, 165, 232, .22);
  border-radius: 11px;
  color: #9ed7ff;
  background: rgba(2, 6, 18, .78);
  font-size: 14px;
}
.strategy-route-note { color: #8590b4; font-size: 14px; }
.strategy-route-note code { color: #b4dfff; }
.notice { padding: 16px 24px; border-left-width: 2px; color: #d4c18d; font-size: 14px; }

.template-head { align-items: start; }
.template-head h2 { margin: 0 0 8px; font-size: 20px; }
.template-head .section-kicker { margin-bottom: 8px; }
.json-template {
  position: relative;
  min-height: 352px;
  margin: 24px 0 16px;
  padding: 64px 24px 24px;
  border-color: rgba(121, 153, 221, .2);
  border-radius: 14px;
  color: #a7d9ff;
  background: rgba(2, 5, 16, .76);
  font-size: 14px;
  line-height: 1.72;
}
.json-template::before {
  content: 'JSON PAYLOAD  /  READ ONLY';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 30px;
  padding: 0 16px;
  border-bottom: 1px solid rgba(127, 145, 207, .12);
  color: #5f6b99;
  background: rgba(19, 24, 51, .36);
  font: 700 14px/51px ui-monospace, Consolas, monospace;
  letter-spacing: 1.25px;
}
.receiver-help { color: #8893b8; font-size: 14px; } /* 0.3.41 VIS-11 連動 */

.module-table {
  position: relative;
  padding-top: 48px;
  border-color: rgba(157, 172, 229, .18);
  border-radius: 16px;
  background: rgba(4, 7, 19, .76);
}
.module-table::before {
  content: attr(data-module);
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 36px;
  padding: 0 24px;
  border-bottom: 1px solid rgba(137, 151, 207, .11);
  color: #65709c;
  background: rgba(18, 22, 47, .36);
  font: 700 14px/55px ui-monospace, Consolas, monospace;
  letter-spacing: 1.35px;
}
th { height: 48px; padding: 16px 24px; color: #8e98b9; background: rgba(15, 20, 44, .75); font-size: 14px; font-weight: 720; letter-spacing: .8px; }
td { height: 56px; color: #cfd4e8; font-size: 14px; }

.filters {
  grid-template-columns: 190px 190px 1fr;
  gap: 12px;
  margin-bottom: 16px;
  padding: 16px;
  border: 1px solid rgba(152, 167, 225, .15);
  border-radius: 15px;
  background: rgba(5, 8, 21, .58);
  backdrop-filter: blur(18px);
}

.bot-card.module-card::after { display: none; }
.bot-card {
  padding: 32px;
 
  /* ⛔ 0.3.41 路標：這一行也不是機器人卡的 cascade 贏家 —— app.js:1766 渲染出來的是
     class="bot-card module-card"，而 `.module-card`（約 1845 行）排在本行之後、
     特異性相同 ⇒ 它贏。想改機器人卡表面請改那裡。 */
  background: linear-gradient(145deg, rgba(11,17,39,.9), rgba(4,7,19,.84));
}
.bot-card::before { left: 24px; width: 76px; background: linear-gradient(90deg, transparent, #84a4ff, transparent); }
.bot-identity { min-width: 0; }
.card-code { display: block; margin-bottom: 8px; color: #7181bd; font: 700 14px/1 ui-monospace, Consolas, monospace; letter-spacing: 1.05px; }
.bot-head h3 { margin-bottom: 8px; color: #f2f4ff; }
.bot-head p { font: 560 14px/1.45 "Segoe UI", sans-serif; letter-spacing: .25px; }
.badge { padding: 8px 12px; letter-spacing: .65px; }

.connection-line {
  align-items: center;
  gap: 16px;
  margin: 24px 0 8px;
  padding: 16px 16px;
  border: 1px solid rgba(147,162,219,.14);
  border-radius: 12px;
  background: rgba(3,7,19,.58);
}
.connection-line .connection-orb { width: 8px; height: 8px; flex: 0 0 8px; border-radius: 50%; background: #7983a5; box-shadow: 0 0 10px #7983a555; }
.connection-line.connected .connection-orb { background: var(--green); box-shadow: 0 0 12px #72f2c388; }
.connection-line.error .connection-orb { background: var(--red); box-shadow: 0 0 12px #ff708e88; }
.connection-line > div { min-width: 0; }
.connection-line small { display: block; color: #68749e; font: 700 14px/1 ui-monospace, Consolas, monospace; letter-spacing: 1.1px; }
.connection-line strong { display: block; margin-top: 4px; overflow: hidden; color: #bfc6dd; font-size: 14px; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }

.bot-stats { margin: 16px 0 24px; }
.bot-stats div { min-height: 76px; padding: 16px; border-color: rgba(143,158,214,.1); border-radius: 12px; }
.bot-stats strong { color: #eef1ff; font-size: 16px; font-weight: 650; }
.bot-actions { padding-top: 3px; }
.bot-actions button { min-height: 40px; font-size: 14px; }
.webhook { margin-top: 24px; padding: 16px 16px; }
.webhook span { font: 700 14px/1 ui-monospace, Consolas, monospace; }
.webhook code { margin-top: 8px; color: #96cfff; font-size: 14px; }

.account-manager.module-card::after { top: 21px; right: 25px; }
.account-manager-head { padding: 32px 32px 32px; }
.account-manager-title h2 { margin-bottom: 8px; color: #f2f4ff; font-size: 30px; font-weight: 650; }
.account-manager-title p { color: #8993b4; font-size: 14px; } /* 0.3.41 VIS-11 連動 */
.account-title-icon { width: 46px; height: 52px; flex-basis: 46px; }
.account-title-icon svg { width: 25px; height: 25px; }
.credential-panel, .account-list-panel { padding: 32px; }
.subsection-heading h3 { color: #eef1ff; }
.account-form-hint { color: #8893b7; font-size: 14px; } /* 0.3.41 VIS-11 連動 */
.account-card { border-radius: 16px; }
.account-card-title h4 { color: #f1f3ff; font-size: 18px; }
.account-detail-row dt { color: #7983a4; font-size: 14px; }
.account-detail-row dd { color: #d9deee; font-size: 14px; }

#settingsForm.module-card, .safety-card.module-card { padding-top: 48px; }
#settingsForm.module-card::after, .safety-card.module-card::after { top: 19px; }
#settingsForm h2, .safety-card h2 { margin-bottom: 32px; font-size: 24px; }
.switch-row { border-bottom-color: rgba(139,154,211,.13); }
.switch-row span strong { color: #dce0ef; font-size: 14px; }
.switch-row span small { color: #7781a2; font-size: 14px; }
.safety-card ul { margin: 0; padding-left: 24px; }
.safety-card li { margin: 16px 0; color: #9ca5c2; font-size: 14px; }

.empty-module {
  position: relative;
  overflow: hidden;
  min-height: 208px;
  padding: 48px 32px;
  border-color: rgba(143, 160, 220, .21);
  background: rgba(3, 7, 19, .52);
  backdrop-filter: blur(18px);
}
.empty-module h3 { margin-bottom: 8px; color: #dfe3f3; font-size: 18px; }
.empty-module p { color: #7f89aa; font-size: 14px; }
.empty-orbit { position: relative; width: 44px; height: 52px; display: block; margin: 0 auto 24px; border: 1px solid rgba(139,155,220,.22); border-radius: 50%; }
.empty-orbit::before { content: ''; position: absolute; inset: 19px -9px; border: 1px solid rgba(146,161,226,.4); border-radius: 50%; transform: rotate(-16deg); }
.empty-orbit i { position: absolute; left: 50%; top: 50%; width: 5px; height: 5px; border-radius: 50%; background: #9b91ff; box-shadow: 0 0 12px #9b91ffaa; transform: translate(-50%,-50%); }

dialog { border-radius: 22px; }
dialog form { padding: 32px; }
.dialog-title { align-items: start; padding-bottom: 24px; border-bottom: 1px solid rgba(141,156,211,.13); }
.dialog-title h2 { margin-bottom: 8px; color: #f3f4ff; font-size: 30px; font-weight: 650; }
.dialog-title p { color: #818aa9; font-size: 14px; }
.dialog-actions { padding-top: 24px; border-top: 1px solid rgba(141,156,211,.11); }

button, .button { letter-spacing: .25px; }
.primary { color: #fff; }
.secondary { color: #c7cce0; }
.danger { color: #ffb0c2; }

@media (max-width: 0px) {
/* Disabled mission-deck experiment; retained temporarily for safe rollback. */
:root {
  --deck-cyan: #70dfff;
  --deck-violet: #a98cff;
  --deck-green: #67e5b1;
  --deck-amber: #f0bd68;
  --deck-red: #ff718f;
}

.section-title {
  position: relative;
  min-height: 128px;
  margin: 48px 0 32px;
  padding: 32px 32px 32px 48px;
  overflow: hidden;
  border: 1px solid rgba(152, 172, 239, .18);
  border-left: 3px solid var(--deck-violet);
  border-radius: 4px 24px 4px 4px;
  background:
    linear-gradient(90deg, rgba(22, 28, 62, .9), rgba(7, 10, 29, .68) 58%, rgba(36, 24, 65, .4)),
    rgba(5, 8, 22, .78);
  box-shadow: 0 26px 70px rgba(0, 0, 0, .28), inset 0 1px rgba(235, 240, 255, .05);
  backdrop-filter: blur(24px) saturate(118%);
}

.section-title::before {
  content: '';
  position: absolute;
  left: 0;
  top: 20px;
  bottom: 20px;
  width: 2px;
  background: linear-gradient(transparent, #c3b4ff, transparent);
  box-shadow: 0 0 18px rgba(169, 140, 255, .9);
}

.section-title::after {
  content: '';
  position: absolute;
  right: 38px;
  top: 50%;
  width: 104px;
  height: 120px;
  border: 1px solid rgba(154, 169, 235, .1);
  border-radius: 50%;
  box-shadow: 0 0 0 17px rgba(126, 116, 230, .018), 0 0 0 42px rgba(126, 116, 230, .012);
  transform: translateY(-50%);
  pointer-events: none;
}

.section-title > * { position: relative; z-index: 1; }
.section-title h1 { font-size: 36px; line-height: 1.12; letter-spacing: -.65px; }
.section-title h2 { font-size: 24px; line-height: 1.15; }
.section-title p { color: #96a1c3; font-size: 14px; }
.section-title .section-kicker { color: #a99bff; }
.section-title.compact { min-height: 112px; margin-top: 32px; }

button, .button {
  min-height: 48px;
  border-radius: 9px 2px 9px 2px;
  font-size: 14px;
  letter-spacing: .45px;
}

.primary {
  border-color: rgba(172, 183, 255, .46);
  background: linear-gradient(135deg, #5f79e6, #8a5fcf);
  box-shadow: 0 10px 28px rgba(95, 75, 198, .25), inset 0 1px rgba(255, 255, 255, .18);
}
.secondary { background: linear-gradient(145deg, rgba(31, 42, 82, .78), rgba(15, 21, 48, .82)); }
.danger { background: linear-gradient(145deg, rgba(91, 30, 54, .7), rgba(52, 19, 40, .78)); }

.module-card {
  --module-accent: var(--deck-violet);
  border: 1px solid color-mix(in srgb, var(--module-accent) 25%, rgba(146, 163, 222, .17));
  border-left: 4px solid color-mix(in srgb, var(--module-accent) 76%, transparent);
  border-radius: 4px 24px 4px 4px;
  background:
    linear-gradient(115deg, color-mix(in srgb, var(--module-accent) 5%, rgba(13, 18, 42, .94)), rgba(4, 7, 20, .86) 56%),
    rgba(5, 8, 22, .84);
  box-shadow: 0 32px 84px rgba(0,0,0,.38), inset 0 1px rgba(239,243,255,.055);
}

.module-card[data-module^="UPLINK"] { --module-accent: var(--deck-cyan); }
.module-card[data-module^="PAYLOAD"] { --module-accent: var(--deck-violet); }
.module-card[data-module^="IDENTITY"] { --module-accent: var(--deck-green); }
.module-card[data-module^="AUTOMATION"] { --module-accent: var(--deck-amber); }
.module-card[data-module^="SECURITY"] { --module-accent: var(--deck-red); }
.module-card[data-module^="TRADING"] { --module-accent: var(--deck-cyan); }

.module-card::before {
  top: 14px;
  left: -4px;
  width: 4px;
  height: 64px;
  background: var(--module-accent);
  box-shadow: 0 0 22px color-mix(in srgb, var(--module-accent) 72%, transparent);
}

.module-card::after {
  top: 15px;
  right: 17px;
  padding: 8px 12px;
  border: 1px solid color-mix(in srgb, var(--module-accent) 30%, transparent);
  border-radius: 6px 1px 6px 1px;
  color: color-mix(in srgb, var(--module-accent) 72%, #aeb7dc);
  background: color-mix(in srgb, var(--module-accent) 8%, rgba(5,8,22,.82));
  letter-spacing: 1.45px;
}

.module-heading {
  min-height: 84px;
  margin: -25px -25px 21px;
  padding: 24px 155px 24px 32px;
  border-bottom: 1px solid color-mix(in srgb, var(--module-accent) 20%, transparent);
  background:
    linear-gradient(90deg, color-mix(in srgb, var(--module-accent) 8%, rgba(17, 22, 50, .9)), rgba(7, 10, 27, .34));
}
.module-heading > div > span { font-size: 18px; letter-spacing: .1px; }
.module-heading > div > small { color: #8792b5; font-size: 14px; }
.module-heading > i { display: none; }

.receiver-status {
  min-height: 84px;
  border-left: 3px solid var(--module-accent);
  border-radius: 3px 14px 3px 3px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--module-accent) 8%, rgba(3,7,20,.8)), rgba(3,7,20,.6));
}

.template-head {
  min-height: 84px;
  margin: -25px -25px 20px;
  padding: 24px 32px;
  border-bottom: 1px solid rgba(159, 137, 255, .2);
  background: linear-gradient(90deg, rgba(92, 66, 167, .14), rgba(7, 10, 27, .3));
}
.template-head > div { min-width: 0; flex: 1; padding-right: 0; }
.template-head button { position: static; flex: 0 0 auto; margin-top: 32px; }

.json-template {
  border-left: 3px solid rgba(112, 199, 255, .62);
  border-radius: 3px 16px 3px 3px;
  background:
    linear-gradient(rgba(113, 143, 220, .025) 1px, transparent 1px) 0 0 / 100% 24px,
    rgba(2, 5, 16, .82);
}

.module-table {
  --table-accent: var(--deck-cyan);
  padding-top: 64px;
  overflow: hidden;
  border: 1px solid rgba(133, 169, 231, .21);
  border-left: 4px solid rgba(112, 209, 255, .72);
  border-radius: 4px 20px 4px 4px;
  box-shadow: 0 30px 82px rgba(0, 0, 0, .34), inset 0 1px rgba(235, 240, 255, .04);
}
.module-table::before {
  height: 48px;
  padding: 0 24px;
  color: #8ea7df;
  background: linear-gradient(90deg, rgba(40, 73, 120, .34), rgba(15, 20, 45, .6));
  font-size: 14px;
  line-height: 43px;
  letter-spacing: 1.7px;
}
.module-table::after {
  content: '即時資料';
  position: absolute;
  top: 0;
  right: 18px;
  height: 48px;
  color: rgba(110, 224, 255, .6);
  font: 700 14px/74px ui-monospace, Consolas, monospace;
  letter-spacing: 1.3px;
}
th { height: 56px; color: #a8b3d1; background: rgba(24, 31, 65, .86); font-size: 14px; }
td { height: 60px; }

.filters {
  position: relative;
  padding: 64px 16px 16px;
  border-left: 4px solid rgba(169, 140, 255, .66);
  border-radius: 4px 18px 4px 4px;
  background: linear-gradient(100deg, rgba(29, 27, 68, .66), rgba(5, 8, 22, .66));
}
.filters::before {
  content: 'LOG FILTER  /  QUERY CONTROL';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 40px;
  padding: 0 24px;
  border-bottom: 1px solid rgba(158, 143, 230, .15);
  color: #9188c8;
  font: 700 14px/54px ui-monospace, Consolas, monospace;
  letter-spacing: 1.5px;
}

.bot-card.module-card::after { display: block; }
.bot-card {
  padding: 32px;
  border-radius: 4px 24px 4px 4px;
}
.bot-head {
  margin: -24px -24px 0;
  padding: 32px 154px 24px 32px;
  border-bottom: 1px solid rgba(112, 208, 255, .16);
  background: linear-gradient(90deg, rgba(43, 92, 130, .17), rgba(6, 10, 27, .34));
}
.bot-head .badge { position: absolute; top: 55px; right: 22px; }
.bot-head h3 { font-size: 24px; letter-spacing: -.3px; }
.card-code { color: #81c9ec; }
.connection-line { margin-top: 24px; border-left: 3px solid #6bd6ff; border-radius: 3px 12px 3px 3px; }
.bot-stats { gap: 0; overflow: hidden; border: 1px solid rgba(137, 162, 218, .12); border-radius: 3px 14px 3px 3px; }
.bot-stats div { min-height: 84px; border: 0; border-right: 1px solid rgba(137, 162, 218, .12); border-radius: 0; background: rgba(3, 7, 20, .58); }
.bot-stats div:last-child { border-right: 0; }
.bot-stats span { color: #8190b6; }
.webhook { border-left: 3px solid rgba(169, 140, 255, .62); border-radius: 3px 12px 3px 3px; }

.account-manager-head {
  padding: 32px 178px 32px 32px;
  border-bottom: 1px solid rgba(103, 229, 177, .18);
  background: linear-gradient(90deg, rgba(42, 118, 96, .18), rgba(7, 10, 28, .3));
}
.account-manager-head .simulation-chip { position: absolute; top: 55px; right: 25px; }
.credential-panel { background: linear-gradient(145deg, rgba(19, 45, 48, .18), rgba(4, 7, 20, .24)); }
.account-list-panel { background: linear-gradient(145deg, rgba(21, 23, 52, .26), rgba(4, 7, 20, .2)); }
.account-card {
  border-left: 3px solid rgba(103, 229, 177, .65);
  border-radius: 3px 16px 3px 3px;
  background: linear-gradient(110deg, rgba(31, 77, 71, .2), rgba(10, 14, 34, .78));
}

#settingsForm.module-card, .safety-card.module-card { padding-top: 58px; }
#settingsForm.module-card > .section-kicker, .safety-card.module-card > .section-kicker {
  position: absolute;
  top: 18px;
  left: 23px;
}
#settingsForm h2, .safety-card h2 {
  margin: -10px -25px 20px;
  padding: 0 32px 24px;
  border-bottom: 1px solid color-mix(in srgb, var(--module-accent) 20%, transparent);
}
.switch-row {
  padding: 24px 16px;
  border: 1px solid rgba(145, 162, 218, .12);
  border-left: 3px solid color-mix(in srgb, var(--module-accent) 58%, transparent);
  border-radius: 3px 11px 3px 3px;
  background: rgba(3, 7, 20, .48);
}
.safety-card ul { list-style: none; padding: 0; counter-reset: security-step; }
.safety-card li {
  position: relative;
  min-height: 48px;
  margin: 12px 0;
  padding: 16px 16px 16px 64px;
  border: 1px solid rgba(255, 113, 143, .11);
  border-radius: 3px 10px 3px 3px;
  background: rgba(34, 10, 25, .2);
  counter-increment: security-step;
}
.safety-card li::before {
  content: '0' counter(security-step);
  position: absolute;
  left: 12px;
  top: 13px;
  color: rgba(255, 113, 143, .72);
  font: 700 14px/1 ui-monospace, Consolas, monospace;
}

.media-grid:has(.empty) { grid-template-columns: 1fr; }
.media-grid > .empty {
  position: relative;
  min-height: 288px;
  display: grid;
  align-content: center;
  justify-items: start;
  padding: 64px 64px;
  overflow: hidden;
  border: 1px solid rgba(139, 161, 226, .2);
  border-left: 4px solid rgba(112, 223, 255, .68);
  border-radius: 4px 24px 4px 4px;
  background: linear-gradient(105deg, rgba(18, 39, 67, .58), rgba(5, 8, 22, .68));
  text-align: left;
}
.media-grid > .empty::before {
  content: 'ARCHIVE BAY  /  EMPTY';
  position: absolute;
  top: 20px;
  left: 46px;
  color: #78b8dc;
  font: 700 14px/1 ui-monospace, Consolas, monospace;
  letter-spacing: 1.5px;
}
.media-grid > .empty::after {
  content: '';
  position: absolute;
  right: 68px;
  top: 50%;
  width: 116px;
  height: 132px;
  border: 1px solid rgba(112, 223, 255, .16);
  border-radius: 50%;
  box-shadow: 0 0 0 24px rgba(112, 223, 255, .025), 0 0 60px rgba(112, 223, 255, .08);
  transform: translateY(-50%);
}
.media-grid > .empty h3 { margin-bottom: 12px; color: #edf3ff; font-size: 24px; }
.media-grid > .empty p { max-width: 480px; color: #8796bb; }
.media-card { border-left: 3px solid rgba(112, 223, 255, .62); border-radius: 3px 16px 3px 3px; }

dialog {
  border: 1px solid rgba(168, 149, 255, .35);
  border-left: 4px solid rgba(169, 140, 255, .78);
  border-radius: 4px 26px 4px 4px;
}
dialog form { padding: 0 32px 32px; }
dialog form::before { left: -4px; top: 20px; right: auto; width: 4px; height: 64px; background: #b49bff; }
.dialog-title {
  margin: 0 -28px 22px;
  padding: 32px 32px 32px;
  border-bottom: 1px solid rgba(161, 145, 231, .19);
  background: linear-gradient(90deg, rgba(86, 63, 156, .2), rgba(9, 12, 31, .28));
}
.dialog-title h2 { font-size: 30px; }
.dialog-actions { margin: 23px -28px -27px; padding: 24px 32px 24px; background: rgba(4, 7, 20, .4); }

@media (max-width: 760px) {
  .section-title { min-height: auto; padding: 24px; border-radius: 4px 18px 4px 4px; }
  .section-title::after { display: none; }
  .module-heading { padding-right: 120px; }
  .template-head { display: block; padding-bottom: 68px; }
  .template-head > div { padding-right: 0; }
  .template-head button { top: auto; bottom: 18px; left: 24px; right: auto; }
  .bot-head { padding-right: 118px; }
  .account-manager-head { padding-right: 32px; padding-bottom: 68px; }
  .account-manager-head .simulation-chip { top: auto; left: 28px; right: auto; bottom: 20px; }
  .media-grid > .empty::after { opacity: .35; right: -35px; }
}
}

/* QuantWing Webhook and membership console */
.cloud-grid {
  display: grid;
  grid-template-columns: minmax(360px, .82fr) minmax(480px, 1.18fr);
  gap: 24px;
}
.cloud-member-card, .cloud-device-card, .cloud-channel-card {
  position: relative;
  overflow: hidden;
  border: 1px solid transparent;
  border-radius: 26px;
  background:
    radial-gradient(circle at 88% 4%, rgba(67, 190, 239, .09), transparent 31%),
    linear-gradient(145deg, rgba(13, 25, 54, .92), rgba(4, 7, 20, .9)) padding-box,
    linear-gradient(135deg, rgba(72, 213, 230, .28), rgba(154, 132, 255, .12)) border-box;
  box-shadow: 0 28px 78px rgba(0, 0, 0, .31), inset 0 1px rgba(242, 248, 255, .04);
}
.cloud-member-card { grid-row: span 2; padding: 48px; }
.cloud-device-card, .cloud-channel-card { padding: 48px; }
.cloud-device-card { grid-column: 1 / -1; }
.cloud-member-card { grid-column: 1 / -1; grid-row: auto; }
.webhook-payload-grid { margin-top: 24px; }
.receiver-symbol-field { display: grid; gap: 10px; margin: 24px 0; }
.receiver-symbol-field input { width: 100%; }
.receiver-symbol-field small { color: var(--muted); line-height: 1.6; }
.cloud-webhook-main { flex: 1 1 auto; min-width: 0; }
.cloud-webhook-main code { display: block; margin-top: 10px; overflow-wrap: anywhere; color: #70defa; font: 14px/1.6 ui-monospace, Consolas, monospace; }
.bot-identity-ssot { display: inline-flex; align-items: center; gap: 10px; min-width: 0; }
.bot-identity-ssot > :last-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cloud-webhook-identity { max-width: 100%; }
.cloud-webhook-avatar { width: 42px; min-width: 42px; height: 42px; }
.cloud-webhook-name { color: #f2f6ff; font-size: 20px; font-weight: 760; line-height: 1.25; }
.cloud-member-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 24px; padding-bottom: 32px; border-bottom: 1px solid rgba(136, 169, 223, .13); }
.cloud-member-head h2 { margin: 8px 0 8px; color: #f1f5ff; font-size: 30px; font-weight: 630; }
.cloud-member-head p { margin: 0; color: #8694b8; } /* 0.3.41 VIS-11 連動 */
.cloud-status {
  display: inline-flex;
  align-items: center;
  min-height: 28px;
  padding: 0 12px;
  border: 1px solid rgba(151, 164, 214, .2);
  border-radius: 999px;
  color: #8a95b4;
  background: rgba(5, 9, 24, .6);
  font-size: 14px;
  font-weight: 800;
  letter-spacing: 1.2px;
}
.cloud-status.online { color: #83f1cc; border-color: rgba(89, 229, 185, .32); background: rgba(24, 103, 83, .18); box-shadow: 0 0 24px rgba(52, 216, 175, .08); }
.entitlement-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin: 32px 0; }
.entitlement-grid span { padding: 24px; border: 1px solid rgba(139, 158, 215, .12); border-radius: 15px; color: #7885a8; background: rgba(3, 7, 20, .44); font-size: 14px; }
.entitlement-grid strong { display: block; margin-top: 12px; color: #e9edff; font-size: 16px; letter-spacing: .4px; }
.member-head { display: flex; align-items: center; gap: 24px; }
.member-identity { flex: 1 1 auto; min-width: 0; }
.member-identity h2 { margin: 4px 0 8px; color: #f1f5ff; font-size: 24px; font-weight: 630; }
.member-identity p { margin: 0; overflow: hidden; color: #8694b8; text-overflow: ellipsis; white-space: nowrap; } /* 0.3.41 VIS-11 連動 */
.member-avatar { width: 56px; height: 64px; flex-basis: 56px; overflow: hidden; font-size: 20px; }
.member-avatar.has-photo { border-color: rgba(139, 158, 215, .22); background: rgba(3, 7, 20, .44); }
.member-avatar img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* 頭像要是正圓 —— 正圓的前提是**正方形**。⛔ 以前是 42x48 與 56x64，而佈景把
   border-radius 設成 50%：長方形的 50% 就是橢圓（站長 2026-08-18 指出 Google 登入
   頭像變橢圓）。aspect-ratio 是防呆：日後有人只改其中一邊，比例仍會把它拉回正方。 */
.account-avatar { height: 42px; aspect-ratio: 1 / 1; }
.member-avatar { width: 56px; height: 56px; flex-basis: 56px; aspect-ratio: 1 / 1; }
.member-facts { margin: 24px 0 0; }
.member-execution-note { line-height: 1.6; margin-top: 16px; }
.bot-risk-note { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; line-height: 1.5; }
.bot-risk-note button { font-size: 13px; padding: 6px 10px; }
#tradingRiskDialog { max-width: min(680px, calc(100vw - 32px)); line-height: 1.65; }
#settingsMemberFacts strong { font-variant-numeric: tabular-nums; white-space: nowrap; }
#membershipSubscriptionPrice { font-size: clamp(22px, 3vw, 32px); }
.member-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 24px; }
.login-device-section { margin-top: 24px; padding-top: 22px; border-top: 1px solid rgba(136, 169, 223, .13); }
.login-device-heading { display: flex; align-items: end; justify-content: space-between; gap: 16px; margin-bottom: 12px; }
.login-device-heading h3 { margin: 5px 0 0; color: #edf2ff; font-size: 17px; }
.login-device-count { color: #8fa2cc; font-size: 13px; font-weight: 800; }
.login-device-list { display: grid; gap: 8px; }
.login-device-item { display: grid; grid-template-columns: 34px minmax(0, 1fr) auto; align-items: center; gap: 12px; min-height: 58px; padding: 10px 12px; border: 1px solid rgba(139, 158, 215, .13); border-radius: 14px; background: rgba(3, 7, 20, .38); }
.login-device-item.current { border-color: rgba(85, 223, 182, .28); background: linear-gradient(105deg, rgba(20, 92, 75, .18), rgba(3, 7, 20, .4)); }
.login-device-icon { display: grid; width: 34px; height: 34px; place-items: center; border-radius: 10px; color: #7cebc9; background: rgba(43, 151, 125, .14); font-size: 16px; }
.login-device-item strong, .login-device-item small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.login-device-item strong { color: #e9efff; font-size: 14px; }
.login-device-item strong em { margin-left: 5px; color: #6fe2bf; font-size: 11px; font-style: normal; }
.login-device-item small { margin-top: 4px; color: #8290b3; font-size: 12px; }
.login-device-item button { min-height: 34px; padding: 0 12px; font-size: 12px; }
.login-device-empty { padding: 16px; border: 1px dashed rgba(139, 158, 215, .16); border-radius: 13px; color: #8290b3; text-align: center; }
@media (max-width: 620px) {
  .login-device-item { grid-template-columns: 34px minmax(0, 1fr); }
  .login-device-item button { grid-column: 1 / -1; width: 100%; }
}
.cloud-connection { display: flex; align-items: center; gap: 16px; margin-top: 24px; padding: 24px; border: 1px solid rgba(112, 174, 225, .14); border-radius: 16px; background: linear-gradient(110deg, rgba(13, 42, 67, .36), rgba(4, 7, 20, .46)); }
.cloud-connection > span { width: 10px; height: 10px; flex: 0 0 10px; border-radius: 50%; background: #59647f; box-shadow: 0 0 0 5px rgba(89, 100, 127, .08); }
.cloud-connection > span.online { background: #55dfb6; box-shadow: 0 0 0 5px rgba(85, 223, 182, .09), 0 0 18px rgba(85, 223, 182, .55); }
.cloud-connection small { display: block; color: #7d8db7; font-size: 14px; letter-spacing: 1.5px; } /* 0.3.41 VIS-11 連動：改前 4.41 已不及格、卡面提亮後更掉到 3.90，提亮到 4.82（8px 過小仍屬 VIS-13 待辦） */
.cloud-connection strong { display: block; margin-top: 4px; color: #dfe7ff; font-size: 14px; }
.cloud-inline-form { display: grid; grid-template-columns: minmax(180px, 1fr) auto; align-items: end; gap: 12px; margin: 24px 0; }
.cloud-channel-card .cloud-inline-form { grid-template-columns: minmax(170px, .7fr) minmax(220px, 1fr) auto; }
.cloud-inline-form label { margin: 0; }
.cloud-list { display: grid; gap: 12px; }
.cloud-list-item { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding: 24px; border: 1px solid rgba(137, 157, 214, .12); border-radius: 17px; background: linear-gradient(135deg, rgba(15, 24, 49, .68), rgba(3, 7, 19, .62)); }
.cloud-list-item.revoked { opacity: .58; }
.cloud-list-item > div:first-child { min-width: 0; }
/* Legacy row labels must not restyle the nested, shared bot identity. */
.cloud-list-item > div:first-child > span:not(.bot-identity-ssot) { display: block; color: #6f81b4; font-size: 14px; font-weight: 750; letter-spacing: 1.2px; overflow-wrap: anywhere; }
.cloud-list-item strong { display: block; margin: 8px 0 4px; color: #ecf1ff; font-size: 16px; }
.cloud-list-item small { color: #7e89a8; font-size: 14px; }
.cloud-list-item .actions { flex: 0 0 auto; }
.one-time-secret { margin: 24px 0; padding: 24px; border: 1px solid rgba(83, 224, 184, .32); border-radius: 18px; background: radial-gradient(circle at 95% 0, rgba(67, 214, 180, .12), transparent 31%), rgba(8, 47, 42, .24); box-shadow: 0 0 42px rgba(54, 205, 174, .07); }
.one-time-secret > div:first-child span { color: #65e4bd; font-size: 14px; font-weight: 800; letter-spacing: 1.3px; }
.one-time-secret > div:first-child strong { display: block; margin: 8px 0 16px; color: #def9ef; font-size: 14px; }
.one-time-secret .copy-field { margin-bottom: 16px; }
.cloud-binding { display: grid; grid-template-columns: 1fr auto; align-items: center; }
.cloud-binding > span { grid-column: 1 / -1; }
.cloud-binding button { margin-left: 12px; }

@media (max-width: 1120px) {
  .cloud-grid { grid-template-columns: 1fr; }
  .cloud-member-card { grid-row: auto; }
}
@media (max-width: 760px) {
  .cloud-inline-form, .cloud-channel-card .cloud-inline-form { grid-template-columns: 1fr; }
  .cloud-list-item { align-items: flex-start; flex-direction: column; }
  .cloud-list-item .actions { width: 100%; }
  .entitlement-grid { grid-template-columns: 1fr; }
}

/* Aurora orbital interface — quiet, spacious, cinematic */
:root {
  --aurora-blue: #85c9ff;
  --aurora-violet: #aa9cff;
  --aurora-mint: #7ee6c1;
  --aurora-line: rgba(170, 184, 242, .17);
  --aurora-surface: rgba(9, 13, 34, .76);
}

.section-title {
  position: relative;
  align-items: end;
  margin: 64px 0 32px;
  padding: 0 0 24px 24px;
  border-bottom: 1px solid rgba(153, 167, 224, .13);
}

.section-title::before {
  content: '';
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 18px;
  width: 2px;
  border-radius: 999px;
  background: linear-gradient(180deg, #8dd7ff, #a48dff 68%, transparent);
  box-shadow: 0 0 20px rgba(143, 159, 255, .48);
}

.section-title::after {
  content: '';
  position: absolute;
  left: 18px;
  bottom: -1px;
  width: 118px;
  height: 1px;
  background: linear-gradient(90deg, #9caeff, transparent);
  box-shadow: 0 0 13px rgba(137, 157, 255, .42);
}

.section-title h1 {
  margin-bottom: 12px;
  color: #f6f7ff;
 
 
 
}

.section-title p { line-height: 1.7; }
.section-title .section-kicker { margin-bottom: 12px; color: #9e9be8; letter-spacing: 1.9px; }
.section-title.compact { margin-top: 48px; }

button, .button {
  min-height: 48px;
 
  font-size: 14px;
  letter-spacing: .25px;
}

/* 0.3.41 VIS-02 ⚠ 這是 .primary 在**活碼**裡的最後一份（前面 61 / 469 / 740 各有一份、
   1389 那份落在 @media (max-width: 0px) 的死碼區 1324–1665 內從不渲染）。
   ⇒ 想改主按鈕外觀請改**這裡**；改前面那幾份不會生效（VIS-10 的根因，⛔ 別再踩）。
   ⛔ 漸層不可再調亮：舊值 #637ce5→#8b69d4 讓白字對比只有 3.79 / 4.00 / 4.14
   （起點 / 中點 / 終點），整段都低於 WCAG AA 的 4.5:1 —— 而 .secondary / .danger /
   .warning 全在 9–11:1，證明這不是深色主題的通病，是這一個色選錯了。
   現值三點量得 5.64 / 5.99 / 6.29；且因為 button:hover 有 filter: brightness(1.12)
   （styles.css:467，全站共用），hover 態會被打亮一截 ⇒ 留了餘裕讓 hover 仍有 4.69。
   ⛔ 別改用加粗字重或文字陰影「看起來清楚」—— 那不會改變任何量測值。
   字級 16px / 字重 690 不符 WCAG「大字」定義（需 18.66px bold 或 24px）⇒ 門檻就是 4.5。
   邊框比舊值提亮，是為了補回壓暗漸層失去的「主行動」躍出感（邊框不覆蓋文字，
   不影響上面那組對比數字）。 */
.primary {
  border-color: rgba(186, 196, 255, .55);
  background: linear-gradient(135deg, #4a5fc4, #6b4bb0);
  box-shadow: 0 12px 32px rgba(74, 62, 160, .30), inset 0 1px rgba(255, 255, 255, .18);
}
.secondary { background: rgba(22, 29, 61, .7); }
.danger { background: rgba(73, 26, 47, .62); }

.module-card {
  --aurora-accent: var(--aurora-violet);
  position: relative;
  overflow: hidden;
  border: 1px solid transparent;
 
  /* 0.3.41 VIS-11 ⚠ 這一組是機器人卡 / 雲端卡 / 設定卡表面的**真正** cascade 贏家
     （app.js:1766 的 class="bot-card module-card"）—— 改卡片外觀請改這裡，
     ⛔ 改 `.card, .bot-card`（約 876 行）或 `.bot-card`（約 1248 行）都不會生效。
     表面亮度是承重的，⛔ 別壓暗回去：深色介面的層級靠更亮的表面表達，不是靠陰影 ——
     舊值配 `0 34px 95px rgba(0,0,0,.38)` 在 #050611 底上實測對比 1.007（人眼看不到），
     等於付了 95px 模糊的合成層成本、換到零視覺層級。陰影已改帶色相（深底上才浮得出來）。
     ⚠ 提亮這個表面會連動所有「疊在卡片上」的低透明度元素（.bot-stats div / .webhook /
     .badge 各態底色）的實際渲染色 ⇒ 動這個值之後要重量一輪那些疊層的文字對比。 */
  background:
    linear-gradient(145deg, rgba(32, 41, 80, .93), rgba(17, 23, 50, .90)) padding-box,
    linear-gradient(135deg, color-mix(in srgb, var(--aurora-accent) 38%, transparent), rgba(133, 160, 223, .08) 42%, rgba(172, 145, 255, .2)) border-box;
  box-shadow: 0 18px 50px rgba(58, 48, 148, .44), inset 0 1px rgba(239, 243, 255, .075);
  backdrop-filter: blur(28px) saturate(118%);
}

.module-card[data-module^="UPLINK"] { --aurora-accent: var(--aurora-blue); }
.module-card[data-module^="PAYLOAD"] { --aurora-accent: var(--aurora-violet); }
.module-card[data-module^="IDENTITY"] { --aurora-accent: var(--aurora-mint); }
.module-card[data-module^="AUTOMATION"] { --aurora-accent: #b7a6ff; }
.module-card[data-module^="SECURITY"] { --aurora-accent: #ff9cac; }
.module-card[data-module^="TRADING"] { --aurora-accent: var(--aurora-blue); }

.module-card::before {
  content: '';
  position: absolute;
  z-index: 0;
  top: -105px;
  right: -88px;
  width: 230px;
  height: 264px;
  border: 1px solid color-mix(in srgb, var(--aurora-accent) 14%, transparent);
  border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--aurora-accent) 9%, transparent), transparent 67%);
  box-shadow: 0 0 0 32px color-mix(in srgb, var(--aurora-accent) 1.8%, transparent);
  pointer-events: none;
}

.module-card::after {
  content: attr(data-module);
  position: absolute;
  z-index: 3;
  top: 19px;
  right: 22px;
  padding: 8px 12px;
  border: 1px solid color-mix(in srgb, var(--aurora-accent) 24%, transparent);
  border-radius: 999px;
  color: color-mix(in srgb, var(--aurora-accent) 72%, #929bbd);
  background: color-mix(in srgb, var(--aurora-accent) 6%, rgba(6,9,24,.7));
  font: 700 14px/1 ui-monospace, Consolas, monospace;
  letter-spacing: 1.35px;
}

.module-card > * { position: relative; z-index: 1; }
.form-card { padding: 32px; }

.module-heading {
  position: relative;
  min-height: 64px;
  margin: -3px 130px 20px 0;
  padding: 4px 0 24px 64px;
  border-bottom: 1px solid rgba(151, 168, 225, .1);
}

.module-heading::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  width: 37px;
  height: 44px;
  border: 1px solid color-mix(in srgb, var(--aurora-accent) 32%, transparent);
  border-radius: 50%;
  background:
    radial-gradient(circle at center, var(--aurora-accent) 0 2px, transparent 3px),
    color-mix(in srgb, var(--aurora-accent) 6%, rgba(7,10,27,.7));
  box-shadow: 0 0 24px color-mix(in srgb, var(--aurora-accent) 9%, transparent);
}

.module-heading::after {
  content: '';
  position: absolute;
  left: -5px;
  top: 17px;
  width: 47px;
  height: 9px;
  border-top: 1px solid color-mix(in srgb, var(--aurora-accent) 42%, transparent);
  border-radius: 50%;
  transform: rotate(-15deg);
}

.module-heading > div > span { color: #edf0ff; font-size: 18px; font-weight: 660; }
.module-heading > div > small { color: #8893b7; font-size: 14px; line-height: 1.5; } /* 0.3.41 VIS-11 連動 */
.module-heading > i { display: none; }

/* ⭐ **欄數的單一真相源**（同名規則全檔有數處，這是最後、也就是實際生效的那條）。
   站長 2026-08-20：「右邊東西都拔掉了 版面改一下 不要這麼空」。
   ⚠ 根因：本來是兩欄（左＝中央連線、右＝快訊內容），08-19 依站長指令刪掉左欄後
     **這行沒跟著改** ⇒ 只剩的那張卡被釘在左半邊、右半邊整片空白。
   ⇒ 改成跟著內容走：剩一張就撐滿整寬，將來真要放兩張也不必再回來改。 */
.receiver-grid { grid-template-columns: repeat(auto-fit, minmax(430px, 1fr)); gap: 24px; }
.receiver-status {
  min-height: 88px;
  padding: 24px 24px;
  border: 1px solid rgba(137, 176, 228, .16);
  border-radius: 17px;
  background: linear-gradient(100deg, rgba(19, 46, 68, .3), rgba(4, 8, 22, .52));
}
.receiver-status > span { width: 9px; height: 9px; flex-basis: 9px; }
.receiver-status::after { top: 13px; right: 14px; }

.template-head {
  min-height: 80px;
  margin: -3px 0 18px;
  padding: 3px 0 24px;
  border-bottom: 1px solid rgba(158, 149, 226, .11);
}
.template-head h2 { font-size: 24px; }
.template-head button { position: static; margin-top: 32px; }

.json-template {
  border: 1px solid rgba(126, 156, 222, .18);
  border-radius: 20px;
  background:
    radial-gradient(circle at 92% 6%, rgba(133, 111, 225, .08), transparent 25%),
    rgba(2, 5, 16, .8);
  box-shadow: inset 0 0 55px rgba(74, 74, 153, .06), 0 18px 44px rgba(0,0,0,.16);
}
.json-template::before { border-radius: 20px 20px 0 0; background: rgba(25, 30, 62, .42); }

.copy-field code, .connection-line, .webhook {
  border: 1px solid rgba(137, 159, 218, .14);
 
  background: rgba(3, 7, 20, .54);
}

.module-table {
  position: relative;
  padding-top: 64px;
  overflow: hidden;
  border: 1px solid transparent;
  border-radius: 23px;
  background:
    rgba(4, 7, 20, .82) padding-box,
    linear-gradient(135deg, rgba(126, 199, 255, .25), rgba(143, 152, 218, .08), rgba(167, 143, 255, .18)) border-box;
  box-shadow: 0 30px 82px rgba(0,0,0,.34), inset 0 1px rgba(238,242,255,.045);
}
.module-table::before {
  height: 48px;
  padding: 0 24px;
  color: #7e8dc2;
  background: linear-gradient(90deg, rgba(31, 46, 85, .58), rgba(14, 18, 42, .42));
  font-size: 14px;
  line-height: 42px;
  letter-spacing: 1.6px;
}
.module-table::after {
  content: '';
  position: absolute;
  top: 20px;
  right: 20px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #79dcb9;
  box-shadow: 0 0 13px rgba(121, 220, 185, .75);
}
th { height: 56px; color: #9ca7c8; background: rgba(19, 24, 52, .78); }
td { height: 60px; }

.filters {
  gap: 12px;
  padding: 16px;
  border: 1px solid rgba(155, 165, 224, .14);
  border-radius: 18px;
  background: rgba(7, 10, 27, .58);
  box-shadow: 0 18px 52px rgba(0,0,0,.2);
}

.bot-card.module-card::after { display: block; }
.bot-card {
  padding: 32px;
 
}
.bot-head { min-height: 92px; }
.bot-head .badge { position: absolute; top: 36px; right: 0; }
.card-code { color: #7aaed0; letter-spacing: 1.35px; }
.bot-head h3 { margin-top: 4px; color: #f3f5ff; }
.connection-line { min-height: 68px; margin-top: 24px; padding: 16px 24px; }
.bot-stats { gap: 12px; }
.bot-stats div {
  min-height: 88px;
  padding: 24px;
  border: 1px solid rgba(137, 158, 216, .11);
  border-radius: 16px;
  background: linear-gradient(145deg, rgba(19, 27, 58, .58), rgba(3, 7, 20, .46));
}
.bot-stats div::after { width: 32px; background: linear-gradient(90deg, transparent, #8fa8ff); }
.bot-stats strong { margin-top: 12px; font-size: 18px; }
.webhook { margin-top: 24px; padding: 16px 24px; }

.account-manager { border-radius: 28px; }
.account-manager-head {
  padding: 48px 48px 32px;
  border-bottom: 1px solid rgba(126, 230, 193, .12);
  background: linear-gradient(90deg, rgba(37, 92, 83, .15), transparent 63%);
}
.account-manager-head .simulation-chip { margin-right: 120px; }
.account-title-icon { box-shadow: 0 0 34px rgba(104, 220, 194, .1); }

/* 0.3.41：關於卡不再 `grid-column: 1 / -1`。量到的實況是它佔滿 1360px 卻只有 431px
   高（全頁內容最少的一張），而旁邊「一般設定」那張半寬卡有 828px 的內容 —— 內容最少的
   卡吃掉最寬的一格＝密度倒置。改半寬之後它正好補上「一般設定」右邊原本空著的一格。
   ⛔ 別把 grid-column 加回來：加回來會再擠出一個空格，設定頁就又出現兩塊大空白。
   align-self 只給這一張：它的鄰居「一般設定」有 828px 的內容，等高拉伸會在關於卡底下
   留約 400px 的卡內空白 —— 有邊框有背景的空白讀起來像「這張卡沒載入完」。同排的
   會員卡與介面卡高度只差 46px，那兩張留著拉伸反而比較齊。 */
.about-card { align-self: start; }
.about-brand { display: flex; align-items: center; gap: 24px; }
.about-brand img { width: 82px; height: 96px; object-fit: contain; filter: drop-shadow(0 0 22px rgba(35, 204, 236, .2)); }
.about-brand h2 { margin: 4px 0 8px; color: #f4f6ff; font-size: 30px; }
.about-brand p, .about-note { color: #8e98b9; }
/* 四個項目排 2×2，不排 3+1：關於卡改半寬後三欄每欄只剩約 190px，「保留舊 TradingWing
   設定」這種長字串會被擠成三行。兩欄也跟會員卡的 .entitlement-grid 對齊（同樣 2 欄）。 */
.product-facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 32px; }
.product-facts span { padding: 16px; border: 1px solid rgba(142, 158, 218, .16); border-radius: 12px; color: #7e88a8; background: rgba(7, 12, 30, .38); font-size: 14px; }
.product-facts strong { display: block; margin-top: 8px; color: #e9edff; font-size: 14px; }
.about-note { margin: 24px 0 0; font-size: 14px; }
@media (max-width: 760px) { .product-facts { grid-template-columns: 1fr; } }
.credential-panel, .account-list-panel { padding: 48px; }
.account-card {
  border: 1px solid transparent;
  border-radius: 21px;
  background:
    linear-gradient(145deg, rgba(18, 29, 53, .84), rgba(7, 10, 27, .8)) padding-box,
    linear-gradient(135deg, rgba(110, 229, 192, .24), rgba(140, 153, 219, .1)) border-box;
  box-shadow: 0 20px 50px rgba(0,0,0,.2);
}

#settingsForm.module-card, .safety-card.module-card { padding-top: 58px; }
#settingsForm.module-card > .section-kicker, .safety-card.module-card > .section-kicker { position: absolute; top: 23px; left: 27px; }
#settingsForm h2, .safety-card h2 { margin: -3px 0 20px; padding-bottom: 24px; border-bottom: 1px solid rgba(151, 163, 217, .11); }
.switch-row {
  padding: 24px;
  border: 1px solid rgba(146, 160, 217, .12);
  border-radius: 16px;
  background: rgba(4, 8, 22, .45);
}
.safety-card ul { list-style: none; margin: 0; padding: 0; }
.safety-card li {
  position: relative;
  margin: 12px 0;
  padding: 16px 16px 16px 48px;
  border: 1px solid rgba(153, 164, 217, .1);
  border-radius: 14px;
  background: rgba(4, 8, 22, .4);
}
.safety-card li::before {
  content: '';
  position: absolute;
  left: 14px;
  top: 17px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #9c91ff;
  box-shadow: 0 0 12px rgba(156, 145, 255, .7);
}

.media-grid:has(.empty) { grid-template-columns: 1fr; }
.media-grid > .empty {
  position: relative;
  min-height: 264px;
  display: grid;
  align-content: center;
  justify-items: start;
  padding: 64px 64px;
  overflow: hidden;
  border: 1px solid transparent;
  border-radius: 26px;
  background:
    linear-gradient(110deg, rgba(12, 22, 48, .78), rgba(4, 7, 20, .7)) padding-box,
    linear-gradient(135deg, rgba(112, 207, 255, .24), rgba(161, 144, 255, .13)) border-box;
  text-align: left;
  box-shadow: 0 30px 82px rgba(0,0,0,.3);
}
.media-grid > .empty::before {
  content: '';
  position: absolute;
  right: 78px;
  top: 50%;
  width: 112px;
  height: 128px;
  border: 1px solid rgba(139, 190, 255, .18);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(143, 155, 255, .08), transparent 65%);
  box-shadow: 0 0 0 24px rgba(130, 142, 255, .018), 0 0 55px rgba(111, 169, 255, .06);
  transform: translateY(-50%);
}
.media-grid > .empty::after {
  content: '';
  position: absolute;
  right: 62px;
  top: 50%;
  width: 144px;
  height: 24px;
  border-top: 1px solid rgba(145, 190, 255, .25);
  border-radius: 50%;
  transform: translateY(-50%) rotate(-14deg);
}
.media-grid > .empty h3 { margin-bottom: 12px; color: #edf1ff; font-size: 24px; }
.media-grid > .empty p { color: #8490b2; }
.media-card { border-radius: 21px; box-shadow: 0 24px 64px rgba(0,0,0,.28); }

dialog {
  border: 1px solid transparent;
  border-radius: 28px;
  background:
    linear-gradient(145deg, rgba(18, 23, 53, .99), rgba(5, 8, 22, .99)) padding-box,
    linear-gradient(135deg, rgba(167, 151, 255, .35), rgba(126, 168, 235, .12)) border-box;
}
dialog form { padding: 48px; }
.dialog-title { padding-bottom: 24px; }
.dialog-title h2 { font-size: 30px; font-weight: 610; }
.dialog-actions { margin-top: 32px; padding-top: 24px; }

/* Login shell ≠ main shell: never paint main UI under a small auth window (white edges / overlap). */
body.auth-locked {
  min-width: 0;
  max-width: 100vw;
  overflow: hidden;
  background: #07111f !important;
}
body.auth-locked .app-shell,
.app-shell[hidden] { display: none !important; }
body.auth-transitioning #appShell,
body.auth-transitioning #authGate { opacity: 0; pointer-events: none; }
#appShell, #authGate { transition: opacity .18s ease; }
@media (prefers-reduced-motion: reduce) {
  #appShell, #authGate { transition: none; }
}
body.auth-locked .ambient-grid { display: none !important; }
.bot-actions {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
}
.bot-action-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
}
.bot-action-trade {
  padding: 12px 12px;
  border: 1px solid rgba(56, 120, 170, .35);
  border-radius: 12px;
  background: rgba(8, 22, 38, .72);
}
.bot-action-trade .trade-buy { border-color: rgba(72, 213, 151, .45); color: #9af0c8; }
.bot-action-trade .trade-sell { border-color: rgba(255, 98, 121, .4); color: #ff9daf; }
.bot-action-trade .trade-hint {
  margin-left: auto;
  color: #7f93b0;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: .04em;
}
.internal-browse-grid {
  display: grid;
  gap: 16px;
  min-height: min(70vh, 828px);
}
/* Adaptive: 1 full · 2 split · 3/4 quad */
.internal-browse-grid.cells-0,
.internal-browse-grid.cells-1 {
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  min-height: min(72vh, 872px);
}
.internal-browse-grid.cells-2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: 1fr;
  min-height: min(68vh, 804px);
}
.internal-browse-grid.cells-4 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: repeat(2, minmax(220px, 1fr));
}
.internal-add-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  min-height: 252px;
  margin: 0;
  padding: 32px;
  border: 1px dashed rgba(90, 160, 210, .45);
  border-radius: 18px;
  background: rgba(6, 18, 34, .55);
  color: #b9cce4;
  cursor: pointer;
  transition: border-color .15s, background .15s, transform .12s;
}
.internal-add-tile:hover {
  border-color: rgba(62, 215, 208, .65);
  background: rgba(12, 36, 58, .72);
  transform: translateY(-1px);
}
.internal-add-plus {
  display: grid;
  place-items: center;
  width: 64px;
  height: 72px;
  border-radius: 50%;
  border: 1px solid rgba(90, 170, 220, .4);
  color: #7ee7d8;
  font-size: 48px;
  font-weight: 300;
  line-height: 1;
  background: rgba(20, 50, 72, .5);
}
.internal-add-tile strong { color: #e8f2ff; font-size: 20px; }
.internal-add-tile small { color: #7f93b0; font-size: 14px; text-align: center; max-width: 220px; }
.internal-empty-hint { grid-column: 1 / -1; margin: 0; padding: 4px 12px; font-size: 14px; }
.internal-pane {
  display: flex;
  flex-direction: column;
  min-height: 252px;
  padding: 24px 24px;
}
.internal-pane-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 16px;
}
.internal-pane-head strong { display: block; font-size: 18px; }
.internal-pane-head small { display: block; margin-top: 4px; color: var(--muted); font-size: 14px; }
.internal-pane-body { display: flex; flex: 1; flex-direction: column; gap: 16px; }
.internal-pane-metrics {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}
.internal-pane-metrics span {
  display: block;
  padding: 12px 16px;
  border: 1px solid rgba(90, 120, 160, .25);
  border-radius: 10px;
  color: var(--muted);
  font-size: 14px;
  background: rgba(4, 12, 24, .55);
}
.internal-pane-metrics strong { display: block; margin-top: 4px; color: var(--text); font-size: 16px; }
.internal-pane-preview {
  flex: 1;
  margin: 0;
  padding: 16px;
  border: 1px dashed rgba(90, 140, 180, .35);
  border-radius: 12px;
  color: #9eb3cc;
  font-size: 14px;
  line-height: 1.55;
  background: rgba(6, 16, 30, .65);
}
.internal-browse-toolbar { align-items: center; gap: 12px; }
.internal-page-label {
  min-width: 88px;
  color: #c5d4e8;
  font-size: 16px;
  font-weight: 700;
  text-align: center;
}
.internal-browse-hint { margin-top: 16px; }
.display-mode-field select { width: 100%; }
body.auth-locked { min-width: 0; overflow: hidden; }
.window-drag-region { display: none; }
.window-titlebar-brand { position:fixed; top:0; left:16px; z-index:12000; display:flex; align-items:center; gap: 12px; height: 36px; color:#dce8f5; font:600 14px/1 "Segoe UI Variable","Segoe UI",sans-serif; pointer-events:none; }
/* margin-left:auto 讓三顆鈕永遠靠右。改 .app-header .tabs 的 flex-grow 之後，
   分頁不再吃掉剩餘寬度，少了這行視窗鈕會跟著分頁往左縮回去。
   ⛔ position:static / align-items:center / height:100% 三者被
   electron-packaging.test.js:202 與 runtime-ux-recovery.test.js:126 依序釘死，不可移除或換序。 */
.window-controls { position:static; z-index:1; display:flex; flex:0 0 auto; align-items:center; gap: 2px; height:100%; margin-left: auto; -webkit-app-region:no-drag; }
.window-controls button,
.auth-window-controls button { display:grid; place-items:center; width:42px; height: 36px; min-height: 36px; padding: 0; border:0; border-radius:6px; background:transparent; color:#a5afc4; box-shadow:none; -webkit-app-region:no-drag; }
.window-controls .wc-icon,
.auth-window-controls .wc-icon { width:var(--space-3); height:var(--space-3); overflow:visible; fill:none; stroke:currentColor; stroke-width:1.4; stroke-linecap:butt; stroke-linejoin:miter; }
.window-maximize .wc-icon[hidden] { display:none; }
.window-controls button:hover,
.auth-window-controls button:hover { background:rgba(118,133,164,.16); color:#f2f5ff; }
.window-controls .window-close:hover,
.auth-window-controls .window-close:hover { background:#d83c52; color:#fff; }
.auth-window-controls { position:fixed; top:0; right:0; z-index:12000; display:flex; height: 36px; -webkit-app-region:no-drag; }
.auth-window-controls button { width:46px; border-radius:0; }
/* 站長要求：三顆鈕縮小並固定右上角（靠右由 .window-controls 的 margin-left:auto 負責）。
   只縮產品 header 這一組；登入閘的 .auth-window-controls 是另一條 fixed 列，維持原尺寸。
   26px 高仍符合 WCAG 2.2 的 24×24 最小點擊目標。
   ⛔ 這裡刻意不碰 .window-controls 本身的 height:100%／align-items:center —— 那三個屬性
   被 electron-packaging.test.js:202 與 runtime-ux-recovery.test.js:126 依序釘死。 */
.app-header .window-controls button { width:36px; height: 26px; min-height: 26px; border-radius:5px; }
.app-header .window-controls .wc-icon { width:10px; height: 10px; }
/* header 是拖曳表面：圖片與文字若可被原生拖曳，按住 logo 拖時瀏覽器會啟動
   image drag／文字選取，把滑鼠捕獲搶走，startDragging 就再也收不到 pointermove。 */
.app-header, .app-header .brand, .app-header img { -webkit-user-drag: none; user-select: none; }
body:not(.auth-locked) .auth-window-controls { display:none; }
/* The embedded product header is the only visible chrome after admission. The
   host keeps its own controls solely for startup/blocked states. */
html[data-host="tauri"] .window-controls { display: flex !important; }
html[data-host="tauri"] body:not(.auth-locked) .auth-window-controls { display: none !important; }
html[data-host="tauri"] body.auth-locked .auth-window-controls { display: flex !important; }
body.auth-locked .window-titlebar-brand { color:#dce8f5; }
/* 0.3.35 刪除：這裡原有一條裸 `.app-header{height:64px;justify-content:flex-end;padding:32px 24px 0}`。
   它寫在 auth-locked 區塊裡卻沒有 `body.auth-locked` 前綴，且被檔尾 0.3.20 的 .app-header
   完整覆蓋（同特異性、行序在後）⇒ 三個宣告一個都沒生效過。登入殼本來就把 #appShell 整個
   hidden，header 不會出現，所以它連原本想解的問題都不存在。 */
.auth-gate {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: grid;
  place-items: center;
  padding: 32px 24px;
  /* Keep the cinematic Earth/orbit plate; deepen the vignette for enterprise gravity. */
  background:
    radial-gradient(ellipse 70% 55% at 50% 42%, rgba(56, 189, 248, .10), transparent 62%),
    radial-gradient(ellipse 90% 80% at 78% 18%, rgba(99, 102, 241, .14), transparent 48%),
    linear-gradient(155deg, rgba(2,5,16,.92), rgba(6,11,28,.78) 52%, rgba(3,8,20,.70)),
    url('/assets/cinematic-orbit-v1.png') center/cover no-repeat;
  -webkit-app-region: drag;
}
.auth-gate-card, .auth-gate button, .auth-gate a, .auth-gate input { -webkit-app-region: no-drag; }
.auth-gate.hidden { display: none; }
.auth-gate-card {
  position: relative;
  width: min(520px, calc(100% - 36px));
  max-width: 520px;
  min-height: 0;
  padding: 64px 48px 48px;
  border: 1px solid rgba(148, 163, 224, .38);
  border-radius: 28px;
  background:
    linear-gradient(150deg, rgba(12, 18, 44, .96), rgba(5, 9, 26, .93) 62%, rgba(4, 10, 28, .95));
  box-shadow:
    0 32px 90px rgba(0, 0, 0, .58),
    0 0 0 1px rgba(255, 255, 255, .03) inset,
    0 1px 0 rgba(167, 211, 255, .12) inset,
    0 0 80px rgba(56, 189, 248, .08);
  color: #f5f8ff;
  text-align: left;
  backdrop-filter: blur(18px);
}
.auth-gate-card::before {
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(135deg, rgba(125, 211, 252, .22), transparent 36%, transparent 68%, rgba(129, 140, 248, .18));
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  padding: 1px;
}
.auth-gate-brand {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 32px;
  color: #f5f8ff;
  font-size: 30px;
  font-weight: 700;
  letter-spacing: .01em;
}
.auth-gate-brand img {
  width: 64px;
  height: 72px;
  padding: 0;
  border-radius: 0;
  background: transparent;
  object-fit: contain;
  filter: drop-shadow(0 0 18px rgba(45, 200, 220, .28));
}
.auth-gate-brand b { color: #f7fbff; font-weight: 750; }
.auth-gate-card h1 {
  margin: 0 0 16px;
  color: #f8fbff;
  font-size: 48px;
  line-height: 1.18;
  letter-spacing: -.03em;
  font-weight: 720;
  text-shadow: 0 1px 0 rgba(255, 255, 255, .04);
}
.auth-gate-card > p {
  margin: 0;
  color: #b7c3dc;
  font-size: 20px;
  line-height: 1.72;
  font-weight: 500;
}
.auth-google-button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  width: 100%;
  margin: 32px 0 24px;
  padding: 24px 32px;
  min-height: 64px;
  border: 1px solid rgba(116, 135, 180, .45);
  border-radius: 14px;
  background: #f5f7fb;
  color: #151b2b;
  font-size: 20px;
  font-weight: 750;
  letter-spacing: .01em;
  box-shadow: 0 10px 28px rgba(0, 0, 0, .22);
}
.auth-google-button:hover:not(:disabled) {
  border-color: #38d7c1;
  background: #fff;
  box-shadow: 0 12px 32px rgba(32, 198, 223, .16);
}
.auth-remember { display: flex; align-items: center; gap: 10px; margin: 16px 0 6px; cursor: pointer; font-size: 18px; }
.auth-remember input { width: 20px; height: 20px; min-height: 0; padding: 0; margin: 0; flex: 0 0 20px; box-sizing: border-box; accent-color: #a5e2f5; }
.auth-remember-note { display: block; margin-bottom: 16px; }
.auth-sign-out { display: block; margin: 20px auto 0; }
.auth-gate.checking .auth-sign-out { display: none; }
.auth-gate.checking .auth-remember { display: none; }
.auth-google-button img { width: 24px; height: 24px; }
.auth-google-button:disabled { opacity: .58; cursor: not-allowed; }
.auth-gate-card > small {
  display: block;
  color: #93a0bb;
  font-size: 16px;
  line-height: 1.7;
  text-align: center;
}
.auth-gate-progress {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin: 12px 0 16px;
  color: #8ad9c8;
  font-size: 16px;
}
.auth-gate-progress .brand-loader-logo { width: 34px; height: 40px; }
/* 登入閘第三態（不知道有沒有登入）。⛔ 未知不能畫登入畫面：/api/auth/status
   要等交易核心回答，api() 沒有客戶端逾時、失聯走 2s→30s 退避重試，那段期間
   舊版把登入卡連同「可以按」的 Google 鍵一起畫出來 —— 站長其實已經登入，卻
   來得及按到它。⚠ 這不是把閘藏起來：拿到否定答案時 checking 會被移除，整張
   卡照常擋住 app。*/
.auth-gate.checking #authGateTitle,
.auth-gate.checking #authGateMessage,
.auth-gate.checking #authGateSignIn,
.auth-gate.checking #authGateProgress,
.auth-gate.checking .auth-divider,
.auth-gate.checking .auth-gate-card > small { display: none; }
.auth-gate:not(.checking) #authGateChecking { display: none; }
.auth-gate-feedback {
  margin: 16px 0 8px !important;
  padding: 16px 16px;
  border: 1px solid rgba(116, 135, 180, .35);
  border-radius: 12px;
  background: rgba(12, 19, 42, .72);
  color: #c0cadf !important;
  font-size: 16px !important;
  text-align: center;
}
.auth-gate-feedback.error {
  border-color: rgba(239, 115, 137, .45);
  background: rgba(86, 23, 39, .35);
  color: #ffb1c2 !important;
}
.auth-gate-feedback.success {
  border-color: rgba(50, 215, 176, .42);
  background: rgba(23, 62, 59, .52);
  color: #89f4d3 !important;
}
.auth-divider {
  display: flex;
  align-items: center;
  gap: 16px;
  margin: 32px 0 24px;
  color: #7b89a8;
  font-size: 16px;
  letter-spacing: .12em;
  text-transform: uppercase;
  font-weight: 650;
}
.auth-divider::before, .auth-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(125, 211, 252, .28), transparent);
}
#toast { z-index:11000; }
/* ⛔ Opacity only — no scale. The 1.8s period is 0.56Hz and begins after a
   400ms delay, so short waits do not flash a loader into existence. */
.brand-loader-logo { object-fit: contain; opacity: 0; animation: brandBreath 1.8s ease-in-out .4s infinite; }
@keyframes brandBreath { 0%, 100% { opacity: .28; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .brand-loader-logo { animation: none; opacity: .82; }
}

@media (max-width: 1050px) {
  .receiver-grid { grid-template-columns: 1fr; }
}

@media (max-width: 760px) {
  .section-title { align-items: stretch; padding-left: 24px; }
  .module-heading { margin-right: 0; padding-right: 110px; }
  .template-head { padding-right: 0; padding-bottom: 62px; }
  .template-head button { top: auto; left: 0; right: auto; bottom: 14px; }
  .bot-head { padding-right: 112px; }
  .account-manager-head .simulation-chip { margin-right: 0; }
  .media-grid > .empty::before, .media-grid > .empty::after { opacity: .28; right: -20px; }
}

/* 0.3.20 — desktop task navigation: one persistent bar, five user-facing areas, readable type. */
body { font-size: 18px; line-height: 1.55; }
.app-header {
  position: sticky;
  /* The header itself is the draggable title bar. Interactive children carve
     out no-drag islands, so the old empty 32px strip is no longer necessary. */
  --qw-header-h: 88px;
  top: 0;
  margin-top: 0;
  z-index: 9000;
  display: flex;
  min-height: var(--qw-header-h);
  align-items: center;
  justify-content: flex-start;
  gap: 32px;
  padding: 0 16px 0 32px;
  border-bottom: 1px solid rgba(143, 169, 196, .22);
  background: rgba(5, 9, 21, .94);
  box-shadow: 0 16px 40px rgba(0, 0, 0, .32);
  backdrop-filter: blur(24px) saturate(125%);
  -webkit-app-region: drag;
}
.app-header .tabs, .app-header .header-status, .app-header .emergency-controls, .app-header .window-controls, .app-header button, .app-header a { -webkit-app-region: no-drag; }
.app-header .brand { display: flex; flex: 0 0 auto; align-items: center; gap: 16px; min-width: 190px; }
.brand-logo { width: 58px; height: 68px; flex: 0 0 58px; object-fit: contain; filter: drop-shadow(0 0 14px rgba(35, 204, 236, .16)); }
.app-header .brand-text { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; min-width: 0; }
.app-header .brand strong { color: #f5f8ff; font: 700 30px/1 "Segoe UI Variable", "Segoe UI", sans-serif; letter-spacing: -.02em; }

/* ── 0.3.35 頂欄：單一聚合狀態燈 ＋ 條件式更新徽章 ──────────────────────────
   依據 `_AI_BRAIN/05_Planning/QuantWing_頂欄UX_業界慣例研究_2026-08-07.md`：
   · 10 個抽樣桌面 app 沒有一個把「檢查更新」常駐頂欄 ⇒ 已搬進「設定 → 關於」。
   · 頂欄只留「有更新才長出來」的徽章（Slack／Spotify／VS Code 的形狀）。
   · 狀態走方案 A：一顆聚合燈（文字 ≤4 字）＋ 點開明細；聚合色取最高警示色（Carbon）。
   · 不可只靠顏色（WCAG 1.4.1）⇒ 色 ＋ 形狀符號 ＋ 文字三者同時帶。 */
/* ── 緊急控制（全平 ／ 停機）────────────────────────────────────────────
   實色紅、最小 36px 高、⛔ 不做成圖示鈕：出事時要找得到、按得中。停用時保留可讀
   的對比（WCAG 1.4.3），因為「為什麼按不下去」得看得懂 title 才問得出下一步。
   兩顆不同形：全平是實心紅（主要動作），停機是外框（次要、且它會擋掉平倉）——
   ⛔ 不可只靠顏色區分（WCAG 1.4.1），所以兩顆的文字本身就寫明白在做什麼。 */
/* ⛔ 不可加 margin-left:auto。header 的剩餘寬度是刻意留給 .window-controls 的
   margin-left:auto 吸收的 —— 那段空隙屬於 .app-header 本身，是視窗唯一可拖曳的
   中性表面（見 .app-header .tabs 的註解）。這裡再放一個 auto 會把它分掉，讓標題列
   變得拖不動。用 0 1 auto：不生長，但窄視窗仍可收縮不溢出。 */
.emergency-controls { display: flex; flex: 0 1 auto; align-items: center; gap: var(--space-2); }
.emergency-flatten,
.emergency-halt {
  min-height: 40px;
  padding: 0 var(--space-3);
  border-radius: 999px;
  /* ⛔ 不可換行。窄視窗實測（900px）字會斷成「全部平／倉」，既難讀又把
     height:76px 的頂欄從內部撐破。緊急按鈕寧可讓分頁列先收縮。 */
  white-space: nowrap;
  flex: 0 0 auto;
  border-width: 1px;
  border-style: solid;
  font-size: 14px;
  font-weight: 800;
  letter-spacing: .04em;
  transition: transform .14s ease, filter .14s ease;
}
.emergency-flatten {
  color: #fff;
  border-color: #ff7188;
  background-color: #9f2945;
  background-image: linear-gradient(135deg, #cf4864, #9f2945);
  box-shadow: inset 0 1px rgba(255, 255, 255, .18), 0 8px 20px rgba(0, 0, 0, .22);
}
.emergency-halt {
  color: #ffc7d1;
  border-color: rgba(255, 113, 136, .55);
  background: rgba(60, 12, 24, .72);
}
.emergency-halt.active {
  color: #fff;
  border-color: #ffd166;
  background: linear-gradient(135deg, #b4531a, #7d3410);
}
.emergency-flatten:hover:not(:disabled),
.emergency-halt:hover:not(:disabled) { filter: brightness(1.12) saturate(1.08); transform: translateY(-1px); }
.emergency-flatten:focus-visible,
.emergency-halt:focus-visible { outline: 3px solid #70d7ff; outline-offset: 3px; }
.emergency-flatten:disabled,
.emergency-halt:disabled { opacity: .58; filter: saturate(.55); cursor: not-allowed; transform: none; }

/* 停機橫幅：全域狀態要有全域提示，⛔ 不能只靠一顆按鈕變色。 */
.halt-banner {
  /* ⛔ position/z-index 不是裝飾。這個外殼裡的 <main> 是 position:relative;z-index:1，
     而定位過的元素會畫在未定位的兄弟之上 —— 橫幅原本是 static，實測（像素取樣）
     底色整條沒被畫出來，連臨時改成 !important 的純紅都畫不出來，只有版面被推開。
     一個佔了位置卻看不見的警示橫幅，比沒有橫幅更糟：它讓人以為全域提示已經有了。 */
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  padding: 12px 32px;
  border-bottom: 1px solid rgba(255, 113, 136, .55);
  color: #ffe0e6;
  background: linear-gradient(135deg, #7d1226, #4a0a17);
  font-size: 14px;
  font-weight: 700;
}
.halt-banner[hidden] { display: none; }
.halt-banner strong { color: #fff; font-size: 16px; letter-spacing: .03em; }
.halt-banner .halt-banner-note { color: #ffc7d1; font-weight: 800; }

.header-status { position: relative; display: flex; flex: 0 0 auto; align-items: center; gap: var(--space-2); min-width: 0; }

.header-update-badge {
  min-height: 40px;
  padding: 0 var(--space-3);
  border: 1px solid rgba(242, 184, 75, .5);
  border-radius: 999px;
  color: #ffd98f;
  background: rgba(60, 44, 14, .82);
  font-size: var(--text-sm);
  font-weight: 700;
  white-space: nowrap;
  -webkit-app-region: no-drag;
}
.header-update-badge::before { content: '▲'; margin-right: 8px; font-size: 14px; }
.header-update-badge:hover { border-color: rgba(242, 184, 75, .85); color: #fff0cd; }
.header-update-badge[hidden] { display: none; }

.system-status {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 40px;
  padding: 0 var(--space-3);
  border: 1px solid rgba(143, 169, 196, .3);
  border-radius: 999px;
  color: #cfe0f2;
  background: rgba(12, 24, 41, .72);
  font-size: var(--text-sm);
  font-weight: 700;
  white-space: nowrap;
  -webkit-app-region: no-drag;
}
.system-status:hover { border-color: rgba(143, 169, 196, .6); color: #f1f8ff; }
/* 形狀符號隨狀態改變 —— 色盲與單色列印下仍分得出來 */
.system-status-icon { font-size: 14px; line-height: 1; color: var(--green); }
.system-status.warn .system-status-icon { color: var(--amber); }
.system-status.pending .system-status-icon { color: var(--cyan); }
.system-status.error .system-status-icon { color: var(--red); }
.system-status.warn { border-color: rgba(242, 184, 75, .45); }
.system-status.pending { border-color: rgba(120, 229, 255, .48); background: rgba(20, 69, 94, .2); }
.system-status.error { border-color: rgba(255, 98, 121, .5); }

.system-status-panel {
  position: absolute;
  top: calc(100% + var(--space-2));
  right: 0;
  z-index: 9500;
  min-width: 268px;
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: rgba(8, 14, 30, .98);
  box-shadow: 0 18px 44px rgba(0, 0, 0, .55);
  -webkit-app-region: no-drag;
}
.system-status-panel[hidden] { display: none; }
/* 緊急控制搬進系統狀態面板（站長 2026-08-18：頂欄太擠）。
   在頂欄時它得跟分頁列搶寬度，所以是 flex:0 0 auto 的窄膠囊；在面板裡沒有競爭者
   ⇒ 改成兩欄等寬撐滿，點擊目標反而比搬家前**更大**，多少補回一點「多一次點擊」的代價。
   ⛔ 分隔線在下緣不在上緣：緊急控制是這個面板的第一區塊，線的作用是把它跟下面
      四行狀態切開，不是替它加一頂帽子。 */
.system-status-panel .emergency-controls {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--border);
}
.system-status-panel .emergency-flatten,
.system-status-panel .emergency-halt {
  width: 100%;
  min-height: 48px;
  padding: 0 var(--space-2);
}
.system-status-row { display: flex; align-items: center; gap: var(--space-2); padding: 8px 0; font-size: var(--text-sm); }
.system-status-row .system-status-icon { flex: 0 0 auto; }
.system-status-row > span:nth-child(2) { flex: 1 1 auto; color: var(--muted); }
.system-status-row > strong { flex: 0 0 auto; font-weight: 700; }
.system-status-row.error {
  margin: 2px -8px;
  padding: 8px 12px;
  border: 1px solid rgba(255, 98, 121, .24);
  border-radius: 8px;
  background: rgba(255, 98, 121, .08);
}
.system-status-row.error .system-status-icon,
.system-status-row.error .system-status-label,
.system-status-row.error > strong { color: var(--red); }
.system-status-row.pending {
  margin: 2px -8px;
  padding: 8px 12px;
  border: 1px solid rgba(120, 229, 255, .22);
  border-radius: 8px;
  background: rgba(39, 122, 154, .08);
}
.system-status-row.pending .system-status-icon,
.system-status-row.pending .system-status-label,
.system-status-row.pending > strong { color: var(--cyan); }
.system-status-link { display: inline-block; margin-top: var(--space-2); padding-top: var(--space-2); border-top: 1px solid var(--border); color: #8fc9e8; font-size: var(--text-sm); text-decoration: none; }
.system-status-link:hover { color: #dff3ff; }
/* Fleet card: name always owns exactly 2 lines; meta never steals line 2 */
.bot-identity .bot-name {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  min-height: calc(1.3em * 2);
  max-height: calc(1.3em * 2);
  margin: 4px 0 8px;
  color: #f3f5ff;
  /* 0.5.0 反熵：font-size / font-weight / letter-spacing 三條移交檔尾的設計系統層。
     ⛔ 這一條的 specificity 是 (0,2,0)，會贏過檔尾的 `.bot-head h3` (0,1,1) ——
        留著就等於檔尾那層對卡片標題**完全沒有效果**，而畫面不會有任何錯誤。
     ⛔ 其餘屬性一個都不能刪：line-clamp 2 行 ＋ min/max-height 是
        「名字永遠佔兩行、meta 不會偷走第二行」的承重機制（見上方註解）。
     ⚠ min/max-height 用 em ⇒ 跟著新的 22px 走（1.3em × 2 = 57.2px），版面自動對齊。 */
  line-height: 1.3;
  word-break: break-word;
  overflow-wrap: anywhere;
}
.bot-identity .bot-meta {
  margin: 0;
  color: #7f88aa;
  font-size: 14px;
  line-height: 1.4;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.bot-identity .card-code {
  display: block;
  margin-bottom: 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.app-header .tabs {
  position: static;
  display: flex;
  /* ⛔ 不可改回 flex-grow:1。分頁列同時在 CSS 的 no-drag 排除集（:2505）與
     shell-host-bridge.js 的 INTERACTIVE_SELECTOR 裡，一旦它吃掉 header 的剩餘寬度，
     中段與右半那片「看起來是空白」的區域命中的其實是 nav.tabs ⇒ 拖不動。
     用 0 1 auto：不生長，但保留收縮（窄視窗才不會溢出）。
     剩餘寬度改由 .window-controls 的 margin-left:auto 吸收，那段空隙屬於
     .app-header 本身，所以是可拖曳的中性表面。 */
  flex: 0 1 auto;
  height: 88px;
  min-width: 0;
  align-items: stretch;
  gap: 4px;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  backdrop-filter: none;
}
.app-header .tab {
  position: relative;
  min-width: 96px;
  height: 88px;
  min-height: 88px;
  padding: 0 24px;
  border: 0;
  border-bottom: 3px solid transparent;
  border-radius: 0;
  color: #9da9c3;
  background: transparent;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: .01em;
}
.app-header .tab::before, .app-header .tab::after { display: none; }
.app-header .tab:hover { color: #f1f5ff; background: rgba(111, 135, 180, .1); }
.app-header .tab.active {
  color: var(--sel-ink);
  border-bottom-color: var(--sel-border);
  background: var(--sel-bg);
  box-shadow: none;
}
.header-status .mode-badge { padding: 12px 16px; font: 750 16px/1 "Segoe UI Variable", "Segoe UI", sans-serif; letter-spacing: .02em; white-space: nowrap; }
main { padding: 64px 48px 72px; }
.section-title { align-items: flex-start; margin: 48px 0 32px; }
.section-title h1 { margin-bottom: 12px; }
.section-title h2 { margin-bottom: 8px; }
.section-title p, .muted { line-height: 1.6; }
.section-kicker, .eyebrow { font-size: 14px; letter-spacing: .1em; }
.module-card::after { font-size: 14px; letter-spacing: .08em; }
.module-heading > div > span { font-size: 20px; }
.module-heading > div > small { font-size: 16px; }
.template-head h2 { font-size: 30px; }
.json-template::before { font-size: 14px; line-height: 30px; }
button, .button { min-height: 52px; padding-right: 24px; padding-left: 24px; font-size: 18px; }
input, select { min-height: 56px; }
body small { font-size: 16px; }
/* 0.3.41 VIS-04：這裡原本還有一條 `.metric span { font-size: 16px }`，已刪除。
   它是死規則 —— `.metric .metric-label > span`（styles.css:995，特異性較高）已經把
   指標卡標題釘在 12px，而 `.metric span` 剩下能選到的只有 .metric-icon 與
   .metric-signal 兩個 span，這兩個都是固定 px 尺寸、無文字內容，font-size 對它們
   零視覺效果。留著只會讓下一個想調標題字級的人改到它然後發現沒反應。 */
.metric strong { font-size: 48px; }
.metric small, .metric-label small, .metric-reading small { font-size: 16px; }
/* 0.3.41 VIS-04 ⚠ font-weight: 500 是承重的，⛔ 別「順手」拿掉。
   996 用 `font: 700 8px/1 ui-monospace…` 簡寫把副標釘成 700；這裡只覆蓋 font-size
   時，主標（995：12px/700）與副標會變成**完全相同的 12px/700**，元件內階層歸零 ——
   副標是等寬字 + letter-spacing 1.05px，渲染寬度比主標還長，反而搶走主標的重量。
   ⛔ 降階不要走 font-size：再縮小會掉進 VIS-13 點名的那批 8–9px 過小字。
   ⛔ 降階也不要走 opacity：實測 opacity .85 就把副標對比壓到 4.13（低於 AA 4.5），
   等於一邊修階層一邊製造新的無障礙問題。字重是唯一不動字級也不動對比的通道。 */
.metric .metric-label > small { font-size: 14px; font-weight: 500; }
.bot-head p, .account-manager-title p, .account-form-hint, .receiver-status small, .strategy-route-note, .notice { font-size: 16px; }
.bot-stats strong, .account-detail-row dd, .account-username { font-size: 18px; }
.bot-actions button, .account-card-actions button { min-height: 48px; font-size: 16px; }
.webhook code, .copy-field code, .json-template { font-size: 16px; }
th, td { padding: 24px 24px; font-size: 16px; }
/* Enterprise scale — keep above base auth-gate rules (do not shrink). */
.auth-gate-card h1 { font-size: 48px; }
.auth-gate-card > p { font-size: 20px; }
.auth-google-button { font-size: 20px; }
.auth-gate-card > small, .auth-gate-progress, .auth-gate-feedback { font-size: 16px !important; }
.auth-divider { font-size: 16px !important; }
.api-safety-badge { display: inline-flex; align-items: center; min-height: 48px; padding: 0 16px; border: 1px solid rgba(72, 213, 151, .35); border-radius: 999px; color: #9af0c8; background: rgba(20, 54, 45, .7); font-size: 14px; font-weight: 800; letter-spacing: .06em; white-space: nowrap; }
.api-layout { display: grid; grid-template-columns: minmax(390px, .78fr) minmax(560px, 1.22fr); gap: 24px; align-items: start; }
.api-key-card, .api-command-card { overflow: hidden; }
.api-key-status { display: flex; align-items: center; gap: 16px; min-height: 88px; margin-bottom: 24px; padding: 24px 24px; border: 1px solid rgba(137, 176, 228, .18); border-radius: 16px; background: rgba(4, 10, 26, .58); }
.api-key-status > span { width: 12px; height: 12px; flex: 0 0 12px; border-radius: 50%; background: var(--red); box-shadow: 0 0 15px rgba(255, 98, 121, .55); }
.api-key-status > span.online { background: var(--green); box-shadow: 0 0 15px rgba(72, 213, 151, .6); }
.api-key-status small, .api-key-status strong { display: block; }
.api-key-status small { color: var(--muted); font-size: 16px; }
.api-key-status strong { margin-top: 3px; font-size: 20px; }
.api-key-actions { margin: 24px 0 24px; }
.api-key-dialog { width: min(720px, calc(100vw - 40px)); }
.api-key-dialog form { padding: 48px; }
.api-key-full-value { margin-top: 32px; padding: 24px; border: 1px solid rgba(126, 170, 235, .28); border-radius: 15px; background: rgba(2, 6, 18, .86); }
.api-key-full-value code { display: block; color: #a7dfff; font: 700 18px/1.7 ui-monospace, Consolas, monospace; white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-all; user-select: all; }
.api-command-head { display: flex; align-items: end; justify-content: space-between; gap: 24px; margin-bottom: 24px; padding-bottom: 24px; border-bottom: 1px solid rgba(153, 167, 224, .14); }
.api-command-head h2 { margin: 4px 0 8px; font-size: 30px; }
.api-command-head p { margin: 0; color: var(--muted); font-size: 18px; }
.api-command-head label { width: 180px; flex: 0 0 180px; margin: 0; }
.api-command-list { display: grid; gap: 16px; }
.api-command-item { padding: 24px; border: 1px solid rgba(137, 159, 218, .16); border-radius: 15px; background: rgba(4, 9, 24, .62); }
.api-command-item.critical { border-color: rgba(242, 184, 75, .42); background: rgba(57, 42, 15, .34); }
.api-command-title { display: flex; align-items: center; gap: 16px; }
.api-command-title strong, .api-command-title small { display: block; }
.api-command-title strong { font-size: 20px; }
.api-command-title small { margin-top: 2px; color: var(--muted); font-size: 14px; }
.api-method { min-width: 68px; padding: 8px 12px; border-radius: 8px; color: #c9eaff; background: rgba(31, 91, 126, .54); font: 800 14px/1 ui-monospace, Consolas, monospace; text-align: center; }
.api-method.post, .api-method.patch, .api-method.put { color: #bff4d9; background: rgba(28, 101, 75, .48); }
.api-method.delete { color: #ffbcc8; background: rgba(119, 43, 62, .48); }
.api-command-item p { margin: 16px 0; color: #a8b5cd; font-size: 16px; }
.api-command-item .copy-field code { white-space: pre-wrap; overflow-wrap: anywhere; }

/* 頂欄多了兩顆緊急按鈕（約 168px），分頁列的收縮斷點要跟著提早。
   下面那條 1180px 是按「沒有這兩顆」算出來的：實測（量 scrollWidth − clientWidth）
   1200px 時分頁文字就會溢出 .tabs 自己的框 145px 而壓到緊急按鈕上 —— flex 兄弟
   不會互相重疊，但「內容溢出自己的框」會，兩者在版面上長得一模一樣。
   ⛔ 這裡只縮分頁列，不動 1180px 那段的其他規則（api-layout 等）。
   ⛔ 這個斷點跟緊急按鈕的寬度綁在一起：改按鈕文字或 padding 要回頭重量一次。 */
@media (max-width: 1400px) {
  .app-header { gap: 24px; }
  .app-header .tab { min-width: 84px; padding-right: 16px; padding-left: 16px; font-size: 16px; }
}

/* 只縮 padding 救不了：.tabs 這個容器會收縮（flex 0 1 auto），但裡面每顆 .tab 有
   min-width 不收縮 ⇒ 內容照樣溢出容器、視覺上壓在右邊的鄰居身上。實測 1200px 仍
   溢出 89px。放不下就捲動是分頁列的通用做法（VS Code／Chrome 都是），⛔ 不用
   overflow:hidden —— 那會直接切掉最右邊的「設定」，看不到又點不到。
   捲軸隱藏：這是桌面殼的標題列，一條橫捲軸會把 76px 的頂欄壓掉一截。 */
.app-header .tabs {
  overflow-x: auto;
  scrollbar-width: none;
}
.app-header .tabs::-webkit-scrollbar { display: none; }

@media (max-width: 1180px) {
  .app-header { gap: 16px; padding-right: 12px; padding-left: 24px; }
  .app-header .brand { min-width: 170px; }
  .header-status .mode-badge { display: none; }
  .app-header .tab { min-width: 88px; padding-right: 16px; padding-left: 16px; }
  .api-layout { grid-template-columns: 1fr; }
}

@media (max-width: 900px) {
  .app-header { gap: 12px; }
  .app-header .brand { min-width: auto; }
  .app-header .brand strong { display: none; }
  .brand-logo { width: 54px; height: 64px; flex-basis: 54px; }
  /* 研究 §2 方案 C：極窄才降級成 icon-only，且必須靠 aria-label 補文字（見 app.js
     renderSystemStatus）。⛔ 0.3.34 以前這裡是 `.header-status{display:none}`，
     狀態在窄視窗直接消失、連 aria 都拿不到。 */
  .system-status { padding: 0 12px; }
  .system-status-text { display: none; }
  .app-header .tab { flex: 1 1 0; min-width: 0; padding-right: 12px; padding-left: 12px; }
  main { padding-right: 32px; padding-left: 32px; }
}

@media (max-width: 520px) {
  .app-header {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-rows: 48px 48px;
    gap: 0 6px;
    min-height: 96px;
    padding: 0 6px;
  }
  .app-header .brand { grid-column: 1; grid-row: 1; min-width: 0; gap: 0; }
  .brand-logo { width: 38px; height: 44px; flex-basis: 38px; }
  .app-header .brand-text { display: none; }
  .header-status { grid-column: 2; grid-row: 1; justify-self: end; }
  .app-header .window-controls { grid-column: 3; grid-row: 1; margin-left: 0; }
  .app-header .tabs { grid-column: 1 / -1; grid-row: 2; width: 100%; height: 48px; }
  .app-header .tab { flex: 1 0 72px; height: 48px; min-height: 48px; padding: 0 8px; font-size: 13px; }
  .app-header .tab.active::after { bottom: 0; }
  main { padding: 24px 14px 48px; }
}

/* ── 0.3.32 Workbench（案 A：一次一機主焦點）＋ ⋯ 選單 ＋ Order Ticket ── */

/* ⛔ display 必須綁在 .active 上，且特異性要贏 .panel.active{display:block}（0,2,0）。
   0.3.32 原寫 `.workbench-panel{display:flex}`（0,1,0）造成兩個 bug：
   ① 排在 .panel{display:none}(45行) 之後 ⇒ 沒 active 也永遠顯示，與當前分頁疊在一起，
      使用者點分頁時畫面看起來沒變＝「分頁點不動／整個混一起」（0.3.33 站長回報）。
   ② 輪到自己 active 時反被 .panel.active(0,2,0) 蓋成 display:block ⇒ flex 直向版面失效。 */
.workbench-panel { flex-direction: column; gap: var(--space-3); min-height: 0; }
.panel.workbench-panel.active { display: flex; }

.workbench-bar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  /* 3:2 畫布比視窗高時整頁會捲。頁籤不跟著捲走，才看得出選中哪台。 */
  position: sticky;
  top: var(--qw-header-h, 88px);
  z-index: 50;
}
.workbench-picker-stack {
  position: relative;
  display: grid;
  grid-template-columns: minmax(112px, .72fr) minmax(210px, 1.28fr);
  flex: 0 1 clamp(360px, 34vw, 440px);
  min-width: 340px;
  border: 1px solid color-mix(in srgb, var(--cyan) 24%, var(--border));
  border-radius: 14px;
  background: linear-gradient(145deg, color-mix(in srgb, var(--surface-2) 92%, #19234f), var(--surface));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.04), 0 10px 30px rgba(0,0,0,.14);
}
.workbench-group-picker {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  align-content: center;
  gap: 0;
  min-height: 54px;
  margin: 0;
  padding: 5px 9px 4px;
  border-right: 1px solid color-mix(in srgb, var(--cyan) 16%, var(--border));
  color: var(--muted);
  font-size: 11px;
  font-weight: 650;
  letter-spacing: .08em;
}
.workbench-group-picker select {
  width: 100%;
  min-width: 0;
  max-width: none;
  min-height: 27px;
  margin: 0;
  padding: 0 22px 0 4px;
  border: 0;
  background-color: transparent;
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
}
.workbench-group-picker select:focus-visible { outline: 2px solid var(--cyan); outline-offset: -2px; border-radius: 7px; }
.workbench-bot-picker { position: relative; }
.workbench-bot-picker > summary {
  position: relative;
  display: block;
  min-height: 54px;
  padding: 7px 34px 7px 10px;
  list-style: none;
  cursor: pointer;
  border-radius: 0 13px 13px 0;
  transition: background .14s ease, box-shadow .14s ease;
}
.workbench-bot-picker > summary::-webkit-details-marker { display: none; }
.workbench-bot-picker > summary::after {
  content: '';
  position: absolute;
  right: 13px;
  top: 50%;
  width: 7px;
  height: 7px;
  border-right: 1.7px solid var(--muted);
  border-bottom: 1.7px solid var(--muted);
  transform: translateY(-65%) rotate(45deg);
  transition: transform .16s ease, border-color .16s ease;
}
.workbench-bot-picker[open] > summary { background: color-mix(in srgb, var(--cyan) 7%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--cyan) 24%, transparent); }
.workbench-bot-picker[open] > summary::after { border-color: var(--cyan); transform: translateY(-30%) rotate(225deg); }
.workbench-bot-picker > summary:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.workbench-tabs {
  position: absolute;
  z-index: 90;
  top: calc(100% + 8px);
  left: -1px;
  display: grid;
  gap: 4px;
  width: min(340px, calc(100vw - 48px));
  max-height: min(420px, 62vh);
  overflow: auto;
  padding: 7px;
  border: 1px solid color-mix(in srgb, var(--cyan) 30%, var(--border));
  border-radius: 14px;
  background: color-mix(in srgb, var(--surface) 96%, #050816);
  box-shadow: 0 24px 70px rgba(0,0,0,.52), inset 0 1px 0 rgba(255,255,255,.05);
}
.workbench-bot-picker:not([open]) .workbench-tabs { display: none; }
.wb-bot-option {
  width: 100%;
  min-height: 54px;
  padding: 6px 9px;
  border: 1px solid transparent;
  border-radius: 10px;
  background: transparent;
  color: var(--text);
  text-align: left;
}
.wb-bot-option:hover, .wb-bot-option:focus-visible { background: var(--surface-2); border-color: color-mix(in srgb, var(--cyan) 22%, transparent); }
.wb-bot-option.active { background: color-mix(in srgb, var(--cyan) 10%, var(--surface-2)); border-color: color-mix(in srgb, var(--cyan) 42%, transparent); }
.wb-bot-picker-row { display: grid; grid-template-columns: 8px 36px minmax(0, 1fr); align-items: center; gap: 9px; width: 100%; min-width: 0; }
.wb-bot-picker-row.is-all { grid-template-columns: 36px minmax(0, 1fr); }
.wb-bot-picker-copy { display: grid; gap: 2px; min-width: 0; }
.wb-bot-picker-name, .wb-bot-picker-meta { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-2); min-width: 0; }
.wb-bot-picker-name { overflow: hidden; color: var(--text); font-size: 13px; font-weight: 720; white-space: nowrap; text-overflow: ellipsis; }
.wb-bot-picker-name small { flex-shrink: 0; color: var(--muted); font-size: 10px; font-weight: 550; letter-spacing: .03em; }
.wb-bot-picker-meta { color: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; }
.wb-bot-picker-meta strong { color: color-mix(in srgb, var(--text) 88%, var(--cyan)); font-weight: 650; }
.wb-bot-all-icon { display: grid; grid-template-columns: repeat(2, 6px); grid-auto-rows: 6px; gap: 3px; place-content: center; width: 36px; height: 36px; border-radius: 50%; background: color-mix(in srgb, var(--cyan) 9%, var(--surface-2)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--cyan) 25%, transparent); }
.wb-bot-all-icon i { display: block; border-radius: 2px; background: color-mix(in srgb, var(--cyan) 68%, var(--muted)); }
.wb-bot-option-all { display: grid; grid-template-columns: 36px minmax(0, 1fr); align-items: center; gap: 17px; }
.workbench-bar-actions { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-2); flex: 1 1 auto; min-width: 0; }
.wb-layout-switch { display: inline-flex; padding: 3px; gap: 2px; border: 1px solid var(--border); border-radius: 11px; background: var(--surface-2); }
.wb-layout-switch button { width: 34px; min-width: 34px; height: 34px; min-height: 34px; padding: 0; border: 0; border-radius: 8px; background: transparent; color: var(--muted); }
.wb-layout-switch svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.7; }
.wb-layout-switch button[aria-pressed="true"] { color: var(--cyan); background: color-mix(in srgb, var(--cyan) 14%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--cyan) 45%, transparent); }
.workbench-panel[data-layout="grid"] .workbench-robot-trade,
.workbench-panel[data-layout="grid"] #workbenchMoreBtn { display: none; }
.workbench-trade { min-width: 112px; min-height: 52px; font-size: 18px; letter-spacing: .06em; }
/* 「機器人操作・市價」小標：一組兩顆按鈕共用一行字。
   ⛔ 不做成每顆按鈕各寫一次 —— 同一件事在相鄰兩顆鍵上講兩遍就是視覺雜訊。
   ⛔ 也不把它做成一整段說明：它要在不擠壓版面的前提下，把「這是機器人下的
   市價單」跟畫面裡 Tradovate 網頁自己的買賣鍵分開。 */
.workbench-robot-trade { display: flex; flex-direction: column; gap: 4px; }
/* ⚠ 12px 不是隨手挑的：11px 實測（verify-ui2-connected.png）淡到要湊近才看得清，
   而這行字的整個作用就是讓人一眼分出「機器人下的單」和「網頁上自己按的單」——
   看不清就等於沒有。⛔ 但也不再放大：它不該跟兩顆主按鈕搶注意力。 */
/* ⛔ 顏色只用 --muted：這支 CSS 有三組主題各自重寫調色盤（第 7／252／585 行），
   寫死色碼的那一版會在另外兩個主題下失準。 */
.wb-trade-label { font-size: 14px; line-height: 1; letter-spacing: .04em; color: var(--muted); text-align: center; white-space: nowrap; }
.wb-trade-row { display: flex; gap: var(--space-2); }

/* 瀏覽方式切換（外部／背景／內部）：分段控制，當前那格反白。 */

/* 舞台空狀態裡的操作列（啟動連線／改為內建瀏覽）。 */
.wb-stage-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-2); }

.wb-tab { display: inline-flex; align-items: center; gap: var(--space-2); padding: var(--space-1) var(--space-3); border-radius: 999px; background: transparent; border: 1px solid transparent; color: var(--muted); font-size: var(--text-sm); white-space: nowrap; cursor: pointer; }
.wb-tab-select { display: inline-flex; align-items: center; gap: var(--space-2); min-height: 28px; padding: 0; border: 0; background: transparent; color: inherit; font: inherit; cursor: pointer; }
.wb-tab .bot-avatar-button { width: 28px; min-width: 28px; height: 28px; min-height: 28px; }
.wb-tab:hover { background: var(--surface-2); color: var(--text); }
.wb-tab.active {
  background: var(--sel-bg);
  border-color: var(--sel-border);
  color: var(--sel-ink);
  font-weight: 700;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sel-border) 55%, transparent);
}
.wb-tab-name { max-width: 12ch; overflow: hidden; text-overflow: ellipsis; }
.wb-tab-pnl { font-variant-numeric: tabular-nums; font-size: 14px; }
.wb-tab-empty { font-size: var(--text-sm); }

.wb-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); flex-shrink: 0; }
.wb-dot.running { background: var(--green); box-shadow: 0 0 0 3px rgba(72, 213, 151, .18); }
.wb-dot.connecting, .wb-dot.awaiting_verification { background: var(--amber); }
.wb-dot.error { background: var(--red); }

.workbench-stage { position: relative; flex: 1 1 64%; min-height: 300px; display: flex; flex-direction: column; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md); overflow: hidden; }
.workbench-stage.is-single-fullscreen { position: fixed; inset: 0; z-index: 9500; width: 100dvw; height: 100dvh; min-height: 0; margin: 0; overflow: hidden; border: 0; border-radius: 0; background: var(--bg); box-shadow: none; }
.wb-stage-head { display: flex; align-items: center; gap: var(--space-4); padding: var(--space-2) var(--space-4); border-bottom: 1px solid var(--border); font-size: var(--text-sm); font-variant-numeric: tabular-nums; }
.wb-stage-identity { display: inline-flex; align-items: center; gap: 9px; min-width: 0; }
.wb-stage-identity-copy { display: flex; flex-direction: column; min-width: 0; line-height: 1.15; }
.wb-stage-identity-copy strong { max-width: 16ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
.wb-stage-identity-copy small { color: var(--muted); }
.wb-stage-metrics { display: flex; align-items: center; gap: var(--space-2); flex: 1 1 auto; min-width: 0; }
.wb-stage-head-spacer { flex: 1 1 auto; min-width: 0; }
.wb-pane-tabs { display: inline-flex; align-items: center; gap: 2px; padding: 2px; border-radius: 9px; background: var(--surface-2); }
.wb-pane-tabs button { width: 30px; min-width: 30px; height: 28px; min-height: 28px; padding: 0; border: 0; border-radius: 7px; background: transparent; color: var(--muted); }
.wb-pane-tabs button[aria-selected="true"] { color: var(--cyan); background: color-mix(in srgb, var(--cyan) 14%, transparent); }
.wb-pane-tabs svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.bot-avatar-frame {
  position: relative;
  box-sizing: border-box;
  aspect-ratio: 1 / 1;
  overflow: visible;
  border: 0;
  border-radius: 50%;
  padding: 2px;
  background: conic-gradient(from 215deg, #57e6d1 0 20%, #6ccfff 34%, #8d78ff 58%, #ff78c8 77%, #57e6d1 100%);
  box-shadow: 0 0 0 1px rgba(115,224,255,.18), 0 0 14px rgba(78,185,255,.20), inset 0 0 0 1px rgba(255,255,255,.28);
  flex: 0 0 auto;
}
.bot-avatar-frame::before {
  content: '';
  position: absolute;
  inset: 2px;
  z-index: 0;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 24%, rgba(128,232,255,.20), rgba(8,13,33,.92) 72%);
}
.bot-avatar-frame img { position: relative; z-index: 1; display: block; width: 100%; height: 100%; aspect-ratio: 1 / 1; border-radius: 50%; object-fit: cover; background: #0b1024; }
.bot-avatar-button { width: 40px; min-width: 40px; max-width: 40px; height: 40px; min-height: 40px; max-height: 40px; cursor: pointer; }
.bot-avatar-display { width: 36px; min-width: 36px; max-width: 36px; height: 36px; min-height: 36px; max-height: 36px; }
.bot-avatar-button::after {
  content: attr(data-avatar-tooltip);
  position: absolute;
  z-index: 80;
  left: 50%;
  top: calc(100% + 8px);
  bottom: auto;
  width: max-content;
  max-width: 160px;
  padding: 6px 9px;
  border: 1px solid rgba(151, 166, 224, .38);
  border-radius: 8px;
  background: rgba(17, 21, 48, .98);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .38);
  color: var(--text);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, 4px);
  transition: opacity .14s ease, transform .14s ease;
}
.bot-avatar-button:hover::after, .bot-avatar-button:focus-visible::after { opacity: 1; transform: translate(-50%, 0); }
.bot-avatar-button:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; }
.bot-head .bot-card-avatar {
  width: clamp(68px, 6vw, 82px);
  min-width: clamp(68px, 6vw, 82px);
  max-width: clamp(68px, 6vw, 82px);
  height: clamp(68px, 6vw, 82px);
  min-height: clamp(68px, 6vw, 82px);
  max-height: clamp(68px, 6vw, 82px);
  flex: 0 0 clamp(68px, 6vw, 82px);
  border-width: 2px;
  box-shadow: 0 0 0 4px rgba(10, 18, 43, .72), 0 10px 28px rgba(0, 0, 0, .32);
}
.bot-head-main { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.bot-record-cell { display: inline-flex; align-items: center; gap: 8px; }
.bot-record-cell .bot-avatar-button { width: 28px; min-width: 28px; max-width: 28px; height: 28px; min-height: 28px; max-height: 28px; }
.wb-stage-symbol { font-weight: 600; color: var(--text); }
.wb-stage-side, .wb-stage-price { color: var(--muted); }
.wb-stage-pnl { margin-left: auto; font-weight: 600; }
.wb-stage-resource-toggle {
  position: relative;
  flex: 0 0 36px;
  width: 36px;
  height: 32px;
  min-width: 36px;
  min-height: 32px;
  padding: 0;
  border: 1px solid rgba(151, 166, 224, .28);
  border-radius: 10px;
  background: rgba(24, 30, 65, .62);
  color: #83d9bd;
}
.wb-stage-resource-toggle svg { width: 20px; height: 20px; overflow: visible; }
.wb-stage-resource-toggle .battery-shell,
.wb-stage-resource-toggle .battery-cap { fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; }
.wb-stage-resource-toggle .battery-level { fill: currentColor; transform-origin: left center; transition: transform .16s ease, color .16s ease; }
.wb-stage-resource-toggle.is-saving { border-color: rgba(255, 198, 90, .35); color: #ffd06d; background: rgba(72, 50, 16, .42); }
.wb-stage-resource-toggle.is-saving .battery-level { transform: scaleX(.28); }
.wb-stage-resource-toggle:hover { border-color: currentColor; }
.wb-stage-fullscreen {
  flex: 0 0 36px; width: 36px; height: 32px; min-width: 36px; min-height: 32px;
  padding: 0; border: 1px solid rgba(151, 166, 224, .28); border-radius: 10px;
  background: rgba(24, 30, 65, .62); color: var(--text);
}
.wb-stage-fullscreen svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.wb-stage-fullscreen:hover { border-color: var(--cyan); color: var(--cyan); }
.wb-stage-resource-tooltip {
  position: absolute;
  z-index: 30;
  top: 50%;
  right: calc(100% + 8px);
  width: max-content;
  max-width: min(260px, calc(100vw - 24px));
  padding: 8px 10px;
  border: 1px solid rgba(151, 166, 224, .38);
  border-radius: 8px;
  color: var(--text);
  background: rgba(17, 21, 48, .98);
  box-shadow: 0 10px 28px rgba(0, 0, 0, .42);
  font-size: 13px;
  font-weight: 500;
  line-height: 1.35;
  white-space: normal;
  pointer-events: none;
  opacity: 0;
  transform: translate(4px, -50%);
  transition: opacity .14s ease, transform .14s ease;
}
.wb-stage-resource-toggle:hover .wb-stage-resource-tooltip,
.wb-stage-resource-toggle:focus-visible .wb-stage-resource-tooltip {
  opacity: 1;
  transform: translate(0, -50%);
}
.wb-stage-canvas { position: relative; flex: 1; display: grid; place-items: center; padding: var(--space-5); }
.wb-stage-problem-strip {
  position: static;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border: 1px solid rgba(255, 191, 71, .82);
  border-radius: 10px;
  color: #fff4d0;
  background: rgba(45, 34, 14, .92);
  box-shadow: 0 6px 20px rgba(0, 0, 0, .34);
  backdrop-filter: blur(12px);
  font-size: 13px;
  line-height: 1.45;
}
.wb-stage-problem-strip strong { flex: 0 0 auto; color: #ffd56a; }
.wb-stage-problem-strip { flex-wrap: wrap; height: auto; max-height: none; white-space: normal; }
.wb-stage-problem-strip > span { flex: 1 1 240px; min-width: 0; overflow-wrap: anywhere; white-space: normal; }

.wb-stage-state { display: flex; flex-direction: column; align-items: center; gap: var(--space-3); text-align: center; }
.wb-stage-state strong { font-size: var(--text-xl); color: var(--text); }
.wb-stage-state span { max-width: 42ch; font-size: var(--text-sm); }
.wb-stage-resource-state { max-width: 580px; padding: var(--space-5); }
.wb-stage-resource-state strong { color: var(--cyan); }

.workbench-stage[data-layout="split"] { display: grid; grid-template-columns: minmax(0, 1.65fr) minmax(360px, 1fr); gap: var(--space-3); padding: 0; overflow: visible; border: 0; background: transparent; min-height: min(760px, calc(100vh - 190px)); }
.wb-focus-pane { position: relative; display: flex; flex-direction: column; min-width: 0; min-height: 360px; overflow: hidden; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); }
.wb-focus-pane .wb-stage-head { flex-wrap: wrap; gap: 8px; padding: 8px 10px; }
.wb-focus-pane .wb-stage-identity { margin-right: auto; }
.wb-focus-pane .wb-stage-metrics { display: flex; align-items: center; gap: 8px; order: 3; width: 100%; }
.wb-focus-pane .wb-stage-pnl { margin-left: auto; }
.wb-focus-pane .wb-stage-canvas { min-height: 0; padding: 0; }
.wb-focus-pane .wb-stage-live, .wb-focus-pane .wb-stage-img { width: 100%; height: 100%; min-height: 0; object-fit: contain; }
.wb-focus-pane.is-fullscreen { position: fixed; inset: 0; z-index: 9500; width: 100dvw; height: 100dvh; min-height: 0; margin: 0; overflow: hidden; border: 0; border-radius: 0; background: var(--bg); box-shadow: none; }
.workbench-stage.has-fullscreen .wb-focus-pane:not(.is-fullscreen) { visibility: hidden; }
.wb-pane-record { width: 100%; height: 100%; min-height: 0; overflow: auto; align-self: stretch; }
.wb-pane-record-table { min-width: 520px; }
.wb-pane-record-table td:last-child { max-width: 28ch; overflow: hidden; text-overflow: ellipsis; }

.workbench-stage[data-layout="grid"] {
  position: relative; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(2, minmax(0, 1fr));
  gap: var(--space-3); padding: 0; overflow: visible; border: 0; background: transparent;
  /* 頁首＋標題＋統一單行選擇列約占 325px；保留 5px 安全邊界，
     讓 1600×1000 的四格完整落在第一屏，不把紀錄列偷偷推到視埠外。 */
  height: clamp(520px, calc(100vh - 330px), 760px); min-height: 520px;
}
.wb-grid-tile { position: relative; display: flex; flex-direction: column; min-width: 0; min-height: 0; overflow: hidden; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); transition: transform 140ms cubic-bezier(.2,0,0,1), border-color .14s ease, box-shadow .14s ease; }
.wb-grid-tile.is-focused, .wb-grid-empty.is-focused { border-color: color-mix(in srgb, var(--cyan) 72%, var(--border)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--cyan) 28%, transparent); }
.wb-grid-tile.is-dragging { opacity: .96; border-color: var(--cyan); box-shadow: 0 18px 50px rgba(0,0,0,.44); transition: none; }
.wb-grid-tile.is-shifting, .wb-grid-empty.is-shifting { transition: transform 140ms cubic-bezier(.2,0,0,1); }
.wb-grid-drop-line { position: absolute; z-index: 40; width: 3px; border-radius: 999px; background: var(--cyan); box-shadow: 0 0 14px color-mix(in srgb, var(--cyan) 70%, transparent); pointer-events: none; }
.wb-grid-drop-line.is-horizontal { width: auto; height: 3px; }
.wb-grid-tile .wb-stage-head { cursor: grab; flex-wrap: wrap; gap: 8px; padding: 8px 10px; }
.wb-grid-tile.is-dragging .wb-stage-head { cursor: grabbing; }
.wb-grid-tile .wb-stage-identity { margin-right: auto; }
.wb-grid-tile .bot-avatar-button { width: 32px; min-width: 32px; max-width: 32px; height: 32px; min-height: 32px; max-height: 32px; }
.wb-grid-tile .wb-stage-identity-copy strong { max-width: 12ch; }
.wb-grid-tile .wb-stage-metrics { display: flex; align-items: center; gap: 8px; order: 3; width: 100%; }
.wb-grid-tile .wb-stage-pnl { margin-left: auto; }
.wb-grid-tile .wb-grid-trade { display: flex; align-items: center; gap: 5px; }
.wb-grid-tile .wb-grid-trade .qt-side { min-height: 34px; padding: 0 12px; }
.wb-grid-tile .wb-grid-trade .qt-qty input { width: 40px; }
.wb-grid-tile .wb-stage-canvas { min-height: 0; padding: 0; }
.wb-grid-tile .wb-stage-live, .wb-grid-tile .wb-stage-img { width: 100%; height: 100%; min-height: 0; object-fit: contain; }
.wb-grid-tile.is-fullscreen { position: fixed; inset: 0; z-index: 9500; width: 100dvw; height: 100dvh; min-height: 0; margin: 0; overflow: hidden; border: 0; border-radius: 0; background: var(--bg); box-shadow: none; }
.workbench-stage.has-fullscreen .wb-grid-tile:not(.is-fullscreen) { visibility: hidden; }
.wb-grid-empty {
  display: grid;
  place-items: center;
  min-height: 0;
  padding: 0;
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--surface) 90%, transparent);
  color: var(--muted);
}
.wb-grid-empty > .wb-grid-add-mark { display: grid; place-items: center; width: 72px; height: 72px; border: 1px solid var(--border); border-radius: 50%; }
.wb-grid-empty > .wb-grid-add-mark svg { display: block; width: 32px; height: 32px; overflow: visible; fill: none; stroke: currentColor; stroke-width: 1.45; stroke-linecap: round; }
.wb-grid-empty:hover, .wb-grid-empty:focus-visible { border-color: var(--cyan); color: var(--cyan); background: color-mix(in srgb, var(--cyan) 7%, var(--surface)); }

.bot-avatar-dialog { width: min(580px, calc(100vw - 32px)); }
.bot-avatar-crop { width: min(360px, calc(100vw - 80px)); aspect-ratio: 1; margin: 0 auto var(--space-3); overflow: hidden; border-radius: 50%; border: 2px solid var(--cyan); background: #090c13; box-shadow: 0 0 0 8px rgba(25, 221, 255, .06), 0 20px 56px rgba(0, 0, 0, .34); touch-action: none; }
.bot-avatar-crop canvas { display: block; width: 100%; height: 100%; cursor: grab; touch-action: none; user-select: none; -webkit-user-select: none; }
.bot-avatar-crop canvas.is-dragging { cursor: grabbing; }
.bot-avatar-crop canvas:focus-visible { outline: 3px solid #fff; outline-offset: -5px; }
.bot-avatar-controls { display: grid; grid-template-columns: 42px minmax(0, 1fr) 42px auto; align-items: center; gap: 8px; }
.bot-avatar-zoom { display: grid; grid-template-columns: auto 1fr; align-items: center; gap: var(--space-3); min-width: 0; }
.bot-avatar-zoom input { width: 100%; }
.bot-avatar-zoom-button { width: 42px; min-width: 42px; height: 42px; min-height: 42px; padding: 0; font-size: 22px; line-height: 1; }

@media (max-width: 640px) {
  .bot-head .bot-card-avatar {
    width: 64px;
    min-width: 64px;
    max-width: 64px;
    height: 64px;
    min-height: 64px;
    max-height: 64px;
    flex-basis: 64px;
  }
  .bot-avatar-controls { grid-template-columns: 40px minmax(0, 1fr) 40px; }
  #botAvatarReset { grid-column: 1 / -1; }
}

@media (max-width: 720px) {
  .wb-stage-head { gap: var(--space-2); padding-right: var(--space-2); padding-left: var(--space-2); }
  .wb-stage-resource-tooltip { max-width: min(220px, calc(100vw - 24px)); }
  .workbench-stage[data-layout="grid"] { grid-template-columns: 1fr; grid-template-rows: none; height: auto; min-height: 0; }
  .wb-grid-tile, .wb-grid-empty { min-height: 300px; }
  .workbench-stage[data-layout="split"] { grid-template-columns: 1fr; }
}

/* ── 0.3.60 舊版控制艙（0.3.61 已由檔尾單一規則取代）──────────────────────
   一個左側控制艙承接機器人、畫面工具、買賣口數與損益；右側只留完整券商畫面。
   窗格本身才是真正可用寬度，所以用 container query，不能只看 app viewport。
   2×2 會縮小控制艙，但不把下單列拆到畫面下方，也不生成有大片空白的第三欄。 */
@media not all {
:is(.workbench-stage[data-layout="single"], .wb-focus-pane, .wb-grid-tile) {
  container-type: inline-size;
}

.wb-stage-rail {
  grid-area: rail;
  z-index: 42;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  overflow: auto;
  border-right: 1px solid var(--border);
  background: var(--surface);
}
.wb-stage-rail > .wb-stage-head.wb-stage-head {
  display: flex;
  flex: 0 0 auto;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  min-width: 0;
  min-height: 0;
  padding: 8px;
  overflow: visible;
  border: 0;
  background: transparent;
}
.wb-stage-rail > .wb-trade-summary {
  position: static;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  width: 100%;
  min-width: 0;
  margin-top: 0;
  padding: 8px;
  gap: 7px;
  overflow: visible;
  border: 0;
  border-top: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
  background: linear-gradient(180deg, color-mix(in srgb, var(--surface-2) 72%, var(--surface)), var(--surface));
}
.wb-stage-rail > .wb-trade-summary .wb-grid-trade {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 38px;
  grid-template-areas:
    "buy sell lock"
    "qty qty lock";
  gap: 6px;
  min-width: 0;
  padding: 0;
  background: transparent;
}
.wb-stage-rail > .wb-trade-summary .qt-buy { grid-area: buy; }
.wb-stage-rail > .wb-trade-summary .qt-sell { grid-area: sell; }
.wb-stage-rail > .wb-trade-summary .qt-qty { grid-area: qty; }
.wb-stage-rail > .wb-trade-summary .qt-lock { grid-area: lock; width: 38px; min-width: 38px; height: 100%; }
.wb-stage-rail > .wb-trade-summary .wb-grid-trade .qt-side { min-width: 0; min-height: 40px; padding-inline: 6px; }
.wb-stage-rail > .wb-trade-summary .wb-grid-trade .qt-qty { min-width: 0; height: 36px; }
.wb-stage-rail > .wb-trade-summary .wb-grid-trade .qt-qty input { min-width: 32px; width: 100%; }
.wb-stage-rail > .wb-trade-summary .wb-stage-metrics {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 4px;
  width: 100%;
  min-width: 0;
  padding: 0;
  background: transparent;
}
.wb-stage-rail > .wb-trade-summary .wb-stage-metric {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
  min-width: 0;
  padding: 7px 9px;
  border: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
  border-radius: 8px;
  background: color-mix(in srgb, var(--surface-2) 88%, var(--surface));
  text-align: left;
}
.wb-stage-rail > .wb-trade-summary .wb-stage-metric small { font-size: clamp(10px, 4.8cqi, 12px); }
.wb-stage-rail > .wb-trade-summary .wb-stage-metric strong {
  max-width: 100%;
  overflow: hidden;
  font-size: clamp(12px, 6.2cqi, 17px);
  text-align: right;
  text-overflow: ellipsis;
}

@container (min-width: 901px) {
  :is(.workbench-stage[data-layout="single"], .wb-focus-pane, .wb-grid-tile) {
    display: grid;
    grid-template-columns: clamp(240px, 22cqi, 304px) minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas:
      "rail notices"
      "rail canvas";
    align-items: stretch;
  }
  .wb-stage-head > .wb-pane-drag-handle { align-self: flex-start; height: 30px; }
  .wb-stage-head > .wb-pane-picker {
    display: grid;
    grid-template-columns: 1fr;
    align-content: start;
    gap: 7px;
    width: 100%;
    min-width: 0;
    height: auto;
    max-height: min(52cqh, 460px);
    overflow: hidden;
    border: 0;
    background: transparent;
  }
  .wb-stage-head .wb-pane-group {
    width: 100%;
    min-width: 0;
    height: 38px;
    border: 1px solid color-mix(in srgb, var(--cyan) 24%, var(--border));
    border-radius: 9px;
    background: var(--surface-2);
  }
  .wb-stage-head .wb-pane-picker > .bot-avatar-button { display: none; }
  .wb-stage-head .wb-pane-bot-picker { width: 100%; min-width: 0; }
  .wb-stage-head .wb-pane-bot-picker > summary { display: none; }
  .wb-stage-head .wb-pane-bot-picker > .workbench-tabs,
  .wb-stage-head .wb-pane-bot-picker:not([open]) > .workbench-tabs {
    position: static;
    display: grid;
    width: 100%;
    max-height: min(44cqh, 360px);
    overflow: auto;
    padding: 4px;
    border-color: var(--border);
    border-radius: 10px;
    background: var(--surface-2);
    box-shadow: none;
  }
  .wb-stage-head .wb-bot-option { min-height: 42px; padding: 5px 7px; }
  .wb-stage-head .wb-bot-picker-row { grid-template-columns: 8px 30px minmax(0, 1fr); gap: 6px; }
  .wb-stage-head .wb-bot-picker-name { font-size: clamp(11px, 7cqi, 13px); }
  .wb-stage-head > .wb-stage-tools {
    display: grid;
    grid-template-columns: repeat(3, minmax(34px, 1fr));
    gap: 5px;
    width: 100%;
    margin-top: 0;
  }
  .wb-stage-head .wb-pane-tabs { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); width: 100%; }
  .wb-stage-head .wb-pane-tabs button,
  .wb-stage-head > .wb-stage-tools > button { width: 100%; min-width: 0; }

  :is(.workbench-stage[data-layout="single"], .wb-focus-pane, .wb-grid-tile) > .wb-stage-notices { grid-area: notices; min-width: 0; }
  :is(.workbench-stage[data-layout="single"], .wb-focus-pane, .wb-grid-tile) > :is(.wb-stage-viewer-layer, .wb-stage-record-layer) {
    grid-area: canvas;
    align-self: stretch;
    width: 100%;
    height: 100%;
    min-width: 0;
    min-height: 0;
  }
}

/* 左右 1:1 與 2×2：仍維持同一個左側控制艙，只把它收窄。 */
@container (min-width: 521px) and (max-width: 900px) {
  :is(.workbench-stage[data-layout="single"], .wb-focus-pane, .wb-grid-tile) {
    display: grid;
    grid-template-columns: clamp(176px, 34cqi, 220px) minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas:
      "rail notices"
      "rail canvas";
    align-items: stretch;
  }
  .wb-stage-head .wb-pane-picker { display: grid; width: 100%; min-width: 0; height: auto; gap: 6px; border: 0; background: transparent; }
  .wb-stage-head .wb-pane-group { width: 100%; min-width: 0; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2); }
  .wb-stage-head .wb-pane-picker > .bot-avatar-button { display: none; }
  .wb-stage-head .wb-pane-bot-picker > summary { padding-inline: 7px 22px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2); }
  .wb-stage-head .wb-pane-current strong { font-size: clamp(10px, 8cqi, 13px); }
  .wb-stage-head .wb-pane-bot-picker > .workbench-tabs { inset: calc(100% + 5px) auto auto 0; width: min(300px, calc(100vw - 24px)); }
  .wb-stage-head > .wb-stage-tools { display: grid; grid-template-columns: repeat(2, minmax(34px, 1fr)); gap: 5px; margin-top: 0; }
  .wb-stage-head .wb-pane-tabs { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); width: 100%; }
  .wb-stage-head .wb-pane-tabs button,
  .wb-stage-head > .wb-stage-tools > button { width: 100%; min-width: 0; }
  :is(.workbench-stage[data-layout="single"], .wb-focus-pane, .wb-grid-tile) > .wb-stage-notices { grid-area: notices; min-width: 0; }
  :is(.workbench-stage[data-layout="single"], .wb-focus-pane, .wb-grid-tile) > :is(.wb-stage-viewer-layer, .wb-stage-record-layer) { grid-area: canvas; width: 100%; height: 100%; min-width: 0; min-height: 0; }
  .wb-stage-rail > .wb-trade-summary { padding: 6px; gap: 5px; }
  .wb-stage-rail > .wb-trade-summary .wb-grid-trade { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 34px; gap: 4px; }
  .wb-stage-rail > .wb-trade-summary .qt-lock { width: 34px; min-width: 34px; }
  .wb-stage-rail > .wb-trade-summary .wb-stage-metric { padding: 5px 7px; }
}

/* 手機：把同一個控制艙置頂；內容仍是一塊，不再拆成前後兩套操作列。 */
@container (max-width: 520px) {
  :is(.workbench-stage[data-layout="single"], .wb-focus-pane, .wb-grid-tile) {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto minmax(280px, 1fr);
    grid-template-areas: "rail" "notices" "canvas";
  }
  .wb-stage-rail {
    overflow: visible;
    border-right: 0;
    border-bottom: 1px solid var(--border);
  }
  .wb-stage-rail > .wb-stage-head.wb-stage-head {
    display: grid;
    flex: 0 0 auto;
    grid-template-columns: 28px minmax(0, 1fr);
    overflow: visible;
  }
  .wb-stage-head > .wb-stage-tools { grid-column: 1 / -1; grid-template-columns: repeat(3, minmax(34px, 1fr)); }
  .wb-stage-head .wb-pane-tabs { grid-column: 1 / -1; }
  .wb-stage-rail > .wb-trade-summary { margin-top: 0; }
}
}

.workbench-log { flex: 0 1 36%; min-height: 160px; overflow: auto; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md); }
.workbench-table { width: 100%; border-collapse: collapse; font-size: 14px; font-variant-numeric: tabular-nums; }
.workbench-table th, .workbench-table td { padding: 6px var(--space-3); text-align: left; white-space: nowrap; }
.workbench-table thead th { position: sticky; top: 0; background: var(--surface-2); color: var(--muted); font-weight: 500; z-index: 1; }
.workbench-table tbody tr:nth-child(even) { background: rgba(255, 255, 255, .02); }
.workbench-table td.empty { text-align: center; color: var(--muted); padding: var(--space-5); }

/* ── 紀錄工具列（2026-08-18：「交易紀錄」分頁併進內部瀏覽後的動作＋篩選列）──────
   ⛔ 不塞進 .workbench-bar：那條已經有分組選單＋機器人頁籤＋買賣鍵，站長明講太擠。
   ⚠ 尺寸對齊**這個面板自己的**刻度（.workbench-group-picker select 是 40px），⛔ 不用
      全域的 56px／18px —— 那是給總覽那種一頁一件事的版面用的，貼在表格上方會壓垮表。
   ⚠ 最小字級 14px（--text-sm）：2026-08-18 上午才把全 app 的 9px 拉到 12px，⛔ 別倒退。
   ⚠ 間距全走 8px 網格的 --space-* token，⛔ 不寫裸數字。 */
.record-bar {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.record-bar-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
}
/* ⛔ 明寫字級：它不在 .section-title 裡，拿不到那層的 30px，也不該拿 —— 這是表格的
   標題不是分頁的標題，比舞台上方那排小一階才讀得出從屬關係。 */
.record-bar-title {
  margin: 0;
  color: var(--text);
  font-size: var(--text-xl);
  font-weight: 650;
  line-height: 1.2;
  letter-spacing: .01em;
}
.record-bar-actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

/* ── 操作紀錄 ⇄ 交易紀錄 的左右切換（站長 2026-08-26）────────────────────────
   逐字：「將他們做成 左右切換的那種方式 如機器人內建瀏覽畫面」。
   ⇒ 沿用 .wb-tab 的視覺（就是他指的那一種），這裡只補容器的排列與間距，
     ⛔ 不複製一份 .wb-tab 的顏色／圓角 —— 那會在下一次改主題時分岔。
   ⛔ 這一排取代了原本的 <h2>成交明細</h2>，所以它要吃掉標題原本的垂直節奏，
      否則工具列會比表格上緣矮一截。 */
.record-tabs { display: inline-flex; align-items: center; gap: 2px; flex-wrap: nowrap; padding: 3px; border: 1px solid var(--border); border-radius: 11px; background: var(--surface-2); }
.record-tabs .record-tab { min-height: 34px; padding: 0 var(--space-3); border: 0; border-radius: 8px; white-space: nowrap; }

/* 操作紀錄的「結果」欄：失敗才上色。⛔ 成功不上色 —— 一整欄都是綠字等於沒有訊號，
   而使用者掃這一欄要找的正是那少數幾筆失敗。 */
.operation-table .record-failed { color: var(--danger, #f87171); font-weight: 600; }
.record-bar-actions button, .record-bar-actions .button {
  min-height: 40px;
  padding: 0 var(--space-4);
  font-size: var(--text-sm);
}
/* 三欄：方向／觸發窄，搜尋吃剩下的。⛔ 用 minmax 不用固定 px —— 視窗一窄固定值會
   把搜尋框擠成負寬度，然後整個 body 出現水平捲軸。 */
.record-bar-filters {
  display: grid;
  grid-template-columns: minmax(150px, 190px) minmax(150px, 210px) minmax(180px, 1fr);
  gap: var(--space-2);
}
.record-bar-filters select, .record-bar-filters input {
  width: 100%;
  min-height: 40px;
  padding: 0 var(--space-3);
  font-size: var(--text-sm);
}
@media (max-width: 900px) {
  .record-bar-filters { grid-template-columns: 1fr; }
}

/* ── 交易紀錄分頁列 ────────────────────────────────────────────────────────
   ⚠ 跟 .record-bar 同一套刻度：8px 網格的 --space-* token、最小字級 14px
      (--text-sm)、外框與底色沿用同一組，⛔ 不寫裸數字也不自創顏色。
   ⚠ 唯一的強調是「第 N／M 頁」那一格（--text 亮色）；按鈕一律 .secondary。
   ⛔ flex-wrap 必開：視窗一窄，三組東西擠不下時要換行，⛔ 不是讓 body 出現水平捲軸。 */
.record-pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding: var(--space-2) var(--space-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--muted);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
}
/* 一筆紀錄都沒有時 renderHistoryPager 寫入空字串 ⇒ 整條收起來，不留一排空框。 */
.record-pager:empty { display: none; }
.record-pager-nav { display: flex; align-items: center; gap: var(--space-1); }
/* ⛔ 覆寫全域 button 的 48px：這排比 .record-bar-actions 的 40px 再小一階，
   讀得出「它是表格的附屬控制項」而不是又一組主要動作。32 = 8×4，仍在網格上。 */
.record-pager-nav button {
  min-height: 32px;
  padding: 0 var(--space-3);
  font-size: var(--text-sm);
  font-weight: 600;
}
.record-pager-nav button[disabled] { opacity: .45; cursor: default; }
.record-pager-page { padding: 0 var(--space-2); color: var(--text); font-weight: 600; }
.record-pager-size { display: flex; align-items: center; gap: var(--space-2); }
.record-pager-size select {
  min-height: 32px;
  padding: 0 var(--space-2);
  font-size: var(--text-sm);
}
@media (max-width: 700px) {
  .record-pager { justify-content: center; }
}

/* 頁籤列最前面的「全部」：它不是一台機器人，所以跟後面那排之間要有一道分隔，
   ⛔ 但不能用 border（.wb-tab 的 1px transparent border 是它對齊 .active 的基準，
   加一邊實線會讓這一格比鄰居高／寬一點點）。用絕對定位的 ::after 畫線。 */
.wb-tab-all { position: relative; margin-right: var(--space-3); flex-shrink: 0; }
.wb-tab-all::after {
  content: '';
  position: absolute;
  top: 20%;
  right: -6px;
  width: 1px;
  height: 60%;
  background: var(--border);
}
.wb-tab-count { font-size: 14px; font-variant-numeric: tabular-nums; color: var(--muted); }

/* 全部態＝純紀錄視圖：舞台與機器人操作鍵收起來，表格吃掉整個高度。
   ⚠ 特異性 (0,3,0) 要贏 .workbench-stage(0,1,0) 與 .workbench-log(0,1,0)。 */
.workbench-panel[data-scope="all"] .workbench-stage { display: none; }
.workbench-panel[data-scope="all"] .workbench-bar-actions { display: none; }
.workbench-panel[data-scope="all"] .workbench-log { flex: 1 1 auto; }
/* 單一機器人已由每個窗格自己的「畫面／操作紀錄／交易紀錄」切換負責。
   舊的大表只服務「全部紀錄」，否則同一批資料會同時在窗格與下方各出現一次。 */
.workbench-panel[data-scope="bot"] .record-bar,
.workbench-panel[data-scope="bot"] .workbench-log,
.workbench-panel[data-scope="bot"] .record-pager { display: none; }

/* ── 交易紀錄頁：內建瀏覽＋下單佔滿一屏，紀錄往下捲（站長 2026-08-20 交辦）────────
   站長原話：「交易紀錄頁直接呈現內建瀏覽+下單操作佔滿畫面（紀錄往下滑）」。

   ⛔ 舊寫法 `.workbench-stage { flex: 1 1 64%; min-height: 300px }` 做不到，而且**壞得很安靜**：
      `flex-basis: 64%` 要有「確定的父層高度」才解析得出來，而 .workbench-panel 沒有高度
      （body 只有 min-height:100vh，整頁是文件流捲動）⇒ 64% 解析成 auto
      ⇒ 舞台實際只吃到 min-height 的 **300px**。
   ⚠ 這就是為什麼「看起來像設定過比例、實際上完全沒有生效」—— 兩條規則都在，
      瀏覽器只用得到其中一條，而畫面上看不出任何錯誤。

   改法＝給舞台**視埠相對**的確定高度（⛔ 不再依賴解析不出來的百分比）：
     78vh + 上方 chrome（頂欄＋分頁標題＋工作台工具列＋main 上留白，1080p 約 16vh）
     ≈ 94vh ⇒ 舞台盡量吃滿一屏，紀錄自然被推到下面。
   ⚠ min-height 420px 是給「視窗被拉到最小 620px 高」那種情況的地板。

   ⛔ .workbench-log 的 overflow 從 auto 改成 visible ＝ 讓表格長出來、由**頁面**捲，
      ⛔ 不是在表格內部再開一個小捲軸（那正是站長說的「沒佔滿」的另一半：
      畫面被切成上下兩個各自捲動的小框）。
   ⚠ [data-scope="all"]（純紀錄視圖）那三條是 (0,3,0)，仍然贏這裡的 (0,1,0) ——
      舞台照樣收起來、表格照樣吃滿，⛔ 本段不影響它。 */
/* 內建瀏覽：舞台與紀錄同寬；畫布鎖 3:2 由寬推高，紀錄文件流緊貼下方。
   ⛔ 不可再用 height: calc(100vh - chrome) —— 寬 ~1496 時 3:2 要 ~997 高，
      鎖 960 就把 Tradovate 底部時間軸裁掉。
   ⭐ 高度跟寬走。預設視窗 1360×1200＝從 1520×1340 等比例縮小，上下 padding
      留給呼吸感（站長 08-26：「整體視窗太大了 上下方有空間」）。 */
.panel.workbench-panel.active {
  --qw-browse-chrome: 292px;
  /* 紀錄頁的標題與版面切換已合成一條工具列；不再套全站 48px 標題前留白。 */
  padding-top: 0;
}
body:has(#internal-browse.panel.active) main {
  padding: 20px 16px 28px;
  max-width: none;
}
body[data-qw-window="maximized"] main {
  padding: 4px 4px 8px;
  max-width: none;
}
body[data-qw-window="maximized"] .panel.workbench-panel.active {
  --qw-browse-chrome: 200px;
}
body[data-qw-window="maximized"] .workbench-log,
body[data-qw-window="maximized"] .record-bar,
body[data-qw-window="maximized"] .record-pager {
  max-width: none;
}
#internal-browse.panel.active .section-title.panel-head {
  margin-bottom: 8px;
}
#internal-browse.panel.active .section-title.panel-head p {
  display: none;
}
#internal-browse.panel.active .section-title.panel-head h1 {
  margin: 0;
  font-size: 24px;
  line-height: 1.2;
}
.workbench-stage {
  flex: 0 0 auto;
  width: 100%;
  height: auto;
  min-height: 0;
  max-height: none;
  margin-inline: 0;
  margin-block: 8px 16px;
}
.workbench-stage:not(:has(.wb-stage-live)) {
  min-height: 300px;
}

/* 即時推送斷線橫幅。⛔ 常駐不是 toast —— 這是狀態不是事件。
   ⚠ 用 sticky 不用 fixed：fixed 會蓋住捲動中的內容，sticky 只在頂端佔一條。 */
#streamHealthBanner {
  position: sticky;
  top: 0;
  z-index: 60;
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-sm);
  font-weight: 600;
  text-align: center;
  color: #2b1500;
  background: linear-gradient(180deg, #ffcf70 0%, #ffb648 100%);
  border-bottom: 1px solid rgba(0, 0, 0, .25);
}
.workbench-log { flex: 0 0 auto; overflow: visible; }

/* ── 方向欄（買／賣）─────────────────────────────────────────────────────
   交易紀錄與內建瀏覽共用。買賣是掃這張表時第一個要找的東西，所以給它顏色和
   一個框，讓一列是進場還是出場不必逐字讀。
   ⛔ 沿用既有的 --green／--red（就是 .positive／.negative 和下單按鈕在用的那
   兩個），不另立一套買賣配色 —— 同一個系統裡的綠色必須都是同一個綠色。
   ⚠ 顏色不是唯一訊號：字本身就寫著「買進」「賣出」，色盲或單色列印都讀得到。 */
.side-cell { white-space: nowrap; }
/* ⚠ text-align 是 **left** 不是 center（2026-08-18）：整張紀錄表統一走業界慣例
   ——文字欄左、數值欄右、表頭跟著它那一欄走，⛔ 沒有任何一處置中。
   min-width 留著是為了讓買/賣兩顆膠囊等寬（欄看起來才整齊），但字一律靠左起。
   ⚠ 膠囊的**字**會比表頭的字右 13px（＝下面的 padding 12px ＋ border 1px）——
      那是 badge 本來就有的內距，貼齊儲存格左緣的是膠囊的**框**（實測 0px），
      ⛔ 不是對齊沒做好。要連字都對齊得給負 margin 把膠囊拉出儲存格內距，
      那會讓它貼死在表格框線上，不划算。 */
.side-tag {
  display: inline-block; min-width: 4em; padding: 1px 12px;
  border: 1px solid transparent; border-radius: 999px;
  font-size: var(--text-sm); font-weight: 600; text-align: left; letter-spacing: .02em;
}
.side-tag.side-buy { color: var(--green); border-color: rgba(72, 213, 151, .45); background: rgba(72, 213, 151, .12); }
.side-tag.side-sell { color: var(--red); border-color: rgba(255, 98, 121, .42); background: rgba(255, 98, 121, .12); }
/* 平倉：方向照樣顯示（平一口多單送出去的是賣單，那是事實），但要看得出它是
   出場不是新開一口 —— 這也是為什麼 side 和 intent 是兩個欄位。 */
.side-note { margin-left: 8px; font-size: 14px; color: var(--muted); }
/* 沒有方向的列：生命週期事件（啟動／截圖）本來就沒有方向，舊紀錄則是在這個
   欄位出生前寫下的。⛔ 不猜、不回填 —— 空格是對「不知道」的誠實陳述。 */
.side-none { color: var(--muted); }

.wb-menu { position: fixed; z-index: 90; min-width: 200px; display: flex; flex-direction: column; padding: var(--space-1); background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: 0 18px 40px rgba(0, 0, 0, .5); }
.wb-menu button { all: unset; box-sizing: border-box; width: 100%; padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm); font-size: var(--text-sm); color: var(--text); cursor: pointer; }
.wb-menu button:hover { background: rgba(53, 164, 255, .16); }
.wb-menu button:focus-visible { outline: 2px solid var(--cyan); outline-offset: -2px; }
.wb-menu button:disabled { cursor: not-allowed; opacity: .42; background: transparent; }
.wb-menu .wb-menu-radio.checked::after { content: "✓"; float: right; color: var(--blue); }
.wb-menu .wb-menu-danger { color: var(--red); }
.wb-menu-sep { height: 1px; margin: var(--space-1) 0; background: var(--border); }
.wb-menu-label { padding: var(--space-1) var(--space-3); font-size: 14px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
/* 錨點（卡片上的 ⋯ 按鈕）被捲出可視範圍時整個藏起來。⛔ 用 visibility 不用 display：
   display:none 會讓 offsetWidth 歸零，下次算位置時寬度取到 0 而貼錯邊。 */
.wb-menu-detached { visibility: hidden; pointer-events: none; }
.wb-submenu-host { position: relative; display: flex; flex-direction: column; }
.wb-menu .wb-submenu-trigger { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.wb-menu .wb-submenu-caret { color: var(--muted); font-size: var(--text-sm); }
/* 子選單貼在父選單右側。⚠ 這裡刻意不再開一個 fixed 浮層 —— 它跟著父選單一起
   被 trackAnchoredMenu 移動，所以不需要自己一份跟隨邏輯。 */
/* ⚠ 子選單要放得下一個能打字的輸入框 —— 200px 扣掉內距後只剩不到 180px，中文分組
   名稱打三四個字就看不到前面。240px 是「不擋住父選單」與「打得了字」的折衷。 */
.wb-submenu { position: absolute; left: 100%; top: 0; margin-left: 4px; min-width: 240px; max-height: 320px; overflow-y: auto; display: flex; flex-direction: column; padding: var(--space-1); background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: 0 18px 40px rgba(0, 0, 0, .5); z-index: 1; }
.wb-submenu[hidden] { display: none; }
.wb-submenu-flip { left: auto; right: 100%; margin-left: 0; margin-right: 4px; }
.wb-menu .wb-submenu-new { color: var(--blue); }
/* ⛔ min-width:0 不能省：flex item 預設的最小寬度是「內容本身的寬度」（CSS 規格的
   intrinsic minimum size，不是 bug）⇒ 輸入框會把子選單撐開、跑出水平捲軸，而輸入框
   自己被擠到幾乎看不見（站長 2026-08-18 截圖）。子選單是 flex column，所以這一列與
   裡面的 input 都要解除。 */
.wb-submenu-input { min-width: 0; padding: var(--space-1) var(--space-2); }
.wb-menu .wb-submenu-input input { width: 100%; min-width: 0; box-sizing: border-box; padding: var(--space-2) var(--space-3); font-size: var(--text-sm); color: var(--text); background: var(--surface-1); border: 1px solid var(--blue); border-radius: var(--radius-sm); }

.bot-more { min-width: 40px; }
.bot-cloud { margin: 0; font-size: 14px; }
.startup-visual-status { display: flex; align-items: center; gap: 12px; margin: 12px 0 4px; padding: 12px 16px; border: 1px solid var(--border); border-radius: var(--radius-md); background: rgba(10, 18, 34, .72); }
.startup-visual-status > span { width: 9px; height: 9px; flex: 0 0 9px; border-radius: 50%; }
.startup-visual-status.ready > span { background: var(--green); box-shadow: 0 0 12px rgba(114, 242, 195, .58); }
.startup-visual-status.missing > span { background: var(--amber); box-shadow: 0 0 12px rgba(255, 202, 101, .48); }
.startup-visual-status div { min-width: 0; }
.startup-visual-status strong, .startup-visual-status small { display: block; }
.startup-visual-status strong { color: var(--text); font-size: var(--text-sm); }
.startup-visual-status small { margin-top: 3px; color: var(--muted); font-size: 14px; }

.ticket-dialog { border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); color: var(--text); padding: 0; }
.ticket-form { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-5); min-width: 320px; }
.ticket-form h3 { margin: 0; font-size: var(--text-xl); }
.ticket-target { display: flex; flex-direction: column; gap: 2px; margin: 0; }
.ticket-field { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); font-size: var(--text-sm); }
.ticket-field input, .ticket-field select { flex: 0 0 150px; }
.ticket-type-value {
  flex: 0 0 150px;
  box-sizing: border-box;
  min-height: 48px;
  display: inline-flex;
  align-items: center;
  padding: 0 16px;
  border: 1px solid rgba(141, 165, 205, .24);
  border-radius: 10px;
  color: #dbe8f8;
  background: rgba(17, 26, 52, .72);
  font-weight: 700;
}
.ticket-note { margin: 0; font-size: 14px; }
.ticket-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-2); }
.ticket-actions .trade-buy,
.ticket-actions .trade-sell { min-width: 148px; min-height: 56px; font-size: 18px; }

/* Trading convention: buy is green and sell is red. Text remains present, so
   colour is reinforcement rather than the only way to identify the action. */
.trade-buy,
.trade-sell {
  min-width: 104px;
  color: #fff;
  border-width: 1px;
  border-style: solid;
  box-shadow: inset 0 1px rgba(255, 255, 255, .18), 0 8px 20px rgba(0, 0, 0, .22);
  transition: transform .14s ease, filter .14s ease, box-shadow .14s ease;
}
.trade-buy {
  border-color: #55dda6;
  background-color: #087448;
  background-image: linear-gradient(135deg, #159564, #087448);
}
.trade-sell {
  border-color: #ff7188;
  background-color: #9f2945;
  background-image: linear-gradient(135deg, #cf4864, #9f2945);
}
.trade-buy:hover:not(:disabled),
.trade-sell:hover:not(:disabled) {
  filter: brightness(1.12) saturate(1.08);
  transform: translateY(-1px);
}
.trade-buy:focus-visible,
.trade-sell:focus-visible {
  outline: 3px solid #70d7ff;
  outline-offset: 3px;
}
.trade-buy:disabled,
.trade-sell:disabled {
  cursor: not-allowed;
  opacity: .48;
  filter: grayscale(.35);
  box-shadow: none;
  transform: none;
}

@media (max-width: 768px) {
  .workbench-bar { align-items: stretch; flex-wrap: wrap; }
  .workbench-picker-stack { flex: 1 1 100%; width: 100%; min-width: 0; }
  .workbench-tabs { width: 100%; }
  .workbench-bar-actions { width: 100%; flex-wrap: wrap; }
  /* 固定欄寬的紀錄表保留可讀寬度，但捲動只發生在表框內，不把整個 app 撐寬。 */
  .workbench-log { width: 100%; max-width: 100%; overflow-x: auto; overflow-y: visible; }
  /* 兩顆交易鍵包了一層「機器人操作」群組後，要讓群組先撐開，裡面那排才長得回來。
     少了這行，窄畫面上兩顆鍵會縮回 112px 的最小寬度、擠在右邊。 */
  .workbench-robot-trade { flex: 1 1 auto; min-width: 0; }
  .wb-trade-row { width: 100%; }
  .workbench-trade { flex: 1 1 0; min-width: 0; }
}

/* 0.3.35 設定 → 介面（語言）與關於區塊 */
.interface-card .language-note { margin: var(--space-2) 0 0; }
.about-card .product-facts + .actions { margin-top: var(--space-3); }

/* 0.3.32 live view: MJPEG from the bot's own Chrome fills the stage.
   畫布 width:100% + aspect-ratio:16/9 + height:auto：子層百分比高度要父層有確定高，
   所以 live 用 absolute inset 0（業界影片 16:9 盒的寫法）。 */
.wb-stage-canvas:has(.wb-stage-live) {
  position: relative;
  display: block;
  flex: 0 0 auto;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  padding: 0;
  background: #000;
}
.wb-stage-live {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: grid;
  place-items: center;
  background: #000;
  box-sizing: border-box;
  padding: 0;
}
/* 畫布鎖 16:9、遠端也是 16:9：contain＝填滿且不裁底。比例萬一漂移只會出黑邊，
   ⛔ 不再用 fill 把扁盒子裡的底部時間軸壓沒。 */
.wb-stage-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  display: block;
}
.wb-stage-live [hidden] { display: none !important; }
.brand-loading-state { position: absolute; inset: 0; z-index: 2; display: none; place-items: center; align-content: center; gap: 16px; color: #c7d9ed; background: radial-gradient(circle at 50% 42%, #173458 0%, #0b1c35 48%, #071427 100%); }
.brand-loading-state .brand-loader-logo { width: 72px; height: 84px; }
.wb-stage-live.is-loading .brand-loading-state { display: grid; }
.wb-stage-live.is-loading .wb-stage-img { opacity: 0; }
.wb-stage-live .wb-stage-state { position: absolute; inset: 0; z-index: 3; display: grid; place-items: center; align-content: center; background: var(--surface); }
.wb-stage-live.has-frame.is-reconnecting .wb-stage-img { opacity: .78; }
.wb-stage-live.has-frame.is-reconnecting .wb-stage-state {
  inset: auto 18px 18px;
  width: min(460px, calc(100% - 36px));
  min-height: 0;
  padding: 16px 24px;
  border: 1px solid rgba(255, 98, 121, .55);
  border-radius: var(--radius-md);
  background: rgba(23, 10, 20, .92);
  box-shadow: 0 12px 32px rgba(0, 0, 0, .42);
}
.wb-stage-live.has-frame.is-reconnecting .wb-stage-state strong { color: var(--red); font-size: var(--text-base); }
/* ⛔ 這裡曾經寫死 `cursor: crosshair`，於是整張遠端畫面 —— 工具列、下拉選單、
   下單按鈕、表單欄位，每一個像素 —— 在本地都變成十字游標。十字只有在圖表畫布
   上才是對的，而圖表只佔畫面的一部分，所以那個寫法對「大多數」區域都是錯的。

   ⚠ 本地無從得知遠端那一點底下是什麼元素：這是一張 MJPEG 影格，不是 DOM。
   要真的做到「圖表內十字、其他地方各歸各的」，唯一的來源是遠端頁面自己的
   getComputedStyle(...).cursor，得由截圖串流那端隨影格回傳（後端的事）。

   在那之前，`default` 是誠實的預設：TradingView／Tradovate 的十字準線是遠端
   *畫進畫面裡* 的線，會跟著影格一起送回來，所以圖表區的十字視覺不依賴這個
   CSS 屬性；而選單與按鈕區則終於拿回它們本來就該有的箭頭。 */
.wb-stage-img { cursor: default; }
.wb-stage-img:focus { outline: 2px solid var(--blue); outline-offset: -2px; }

/* ── 內建畫面唯讀開關（使用者自己按，預設關閉）────────────────────────────
   後端的唯讀是「點了沒反應」，畫面本身一個像素都不會變 —— 所以這裡的視覺
   是承重的，不是裝飾：沒有它，操作者只會以為程式當掉。故兩態各給三個訊號
   （文字／圓點顏色／整顆按鈕的底色與邊框），再加畫面上一條常駐標示。
   ⛔ 唯讀只擋人；機器人自己的下單不經過這條路徑。 */
.workbench-viewer-mode { display: inline-flex; align-items: center; gap: var(--space-2); min-height: 52px; white-space: nowrap; }
.viewer-mode-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; background: var(--green); box-shadow: 0 0 0 3px rgba(72, 213, 151, .18); }
.workbench-viewer-mode.is-read-only { border-color: var(--amber); color: var(--amber); background: rgba(242, 184, 75, .16); font-weight: 600; }
.workbench-viewer-mode.is-read-only .viewer-mode-dot { background: var(--amber); box-shadow: 0 0 0 3px rgba(242, 184, 75, .22); }
.workbench-viewer-mode[disabled] .viewer-mode-dot { background: var(--muted); box-shadow: none; }

/* 唯讀時整個畫面套一圈琥珀色內框，游標也換掉 —— 在點下去之前就看得出來。 */
.wb-stage-live.is-read-only::after {
  content: ''; position: absolute; inset: 0; z-index: 4; pointer-events: none;
  border: 2px solid rgba(242, 184, 75, .62); border-radius: 2px;
}
.wb-stage-live.is-read-only .wb-stage-img { cursor: not-allowed; }
/* 整條橫幅（跟同一格上方的 .bot-reconciling-warning 同一種），⛔ 不做成蓋在畫面
   上的浮層：實測頂端置中會蓋掉 Tradovate 的淨值／保證金那一列，貼底部又會被捲到
   摺線以下。橫幅排在畫面之上、永遠在視野裡，而且一個 Tradovate 像素都不擋。 */
.wb-stage-readonly {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  margin: 0 var(--space-3) var(--space-2); padding: 12px 16px;
  border: 1px solid rgba(242, 184, 75, .5); border-radius: 11px;
  background: linear-gradient(90deg, rgba(94, 66, 16, .34), rgba(44, 33, 12, .2));
  color: #d9b97f; font-size: 14px; line-height: 1.45;
}
.wb-stage-readonly[hidden] { display: none; }
.wb-stage-readonly strong { color: #ffe5b8; font-weight: 600; }
.wb-readonly-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; background: var(--amber); box-shadow: 0 0 0 3px rgba(242, 184, 75, .22); }

/* 0.3.41 VIS-01 ⛔ 不要把 .stopped 再塗回紅色 —— 這是刻意的，不是漏改。
   0.3.40 的理由（stopped/disconnected 是「連線不可用」不是中性裝飾）只對了一半：
   它把 stopped 塗成 #ff9daf，而 .badge.error（styles.css:79）用的就是同一個 #ff9daf
   ⇒ 兩態前景色 ΔE2000 = 0.00（同一個顏色），顏色通道對這兩態的鑑別力等於零。
   而「我按了停止」（不用動作）與「它自己壞了」（要立刻處理）在自營交易情境下
   是完全相反的資訊 —— 兩者的 class 來源也不同：
     .stopped  ← botVisualStatus() = bot.status，是使用者自己按停的（app.js:1596/1634）
     .error / .disconnected / .closed ← connectionStatus，是非我意願的失敗
   ⇒ 紅色**只留給非自願的失敗態**；自願停止一律走中性藍灰。
   ⚠ 選色不是隨手挑的：舊的中性灰 #b5c5d7 / #8ba2b2 在**紅色盲（protanopia）**模擬下
   與 #ff9daf 只差 ΔE 9.4（幾乎又黏回去）；現用的 #aeb8e8 偏藍，同條件下 ΔE 16.1。
   改色前請用 CIEDE2000 + 色覺缺陷模擬量過，⛔ 別靠肉眼「看起來不一樣」。 */
.badge.stopped { color: #aeb8e8; background: rgba(40, 52, 74, .78); }
.wb-dot.stopped { background: #8f9cd0; box-shadow: 0 0 0 3px rgba(143, 156, 208, .16); }
.connection-line.disconnected,
.connection-line.closed {
  color: #ff9daf;
  border: 1px solid rgba(255, 112, 142, .48);
  border-left: 3px solid var(--red);
  background: rgba(72, 20, 39, .28);
}
.connection-line.disconnected .connection-orb,
.connection-line.closed .connection-orb { background: var(--red); box-shadow: 0 0 12px #ff708e88; }
.connection-line.disconnected strong,
.connection-line.closed strong { color: #ffb2c1; }
/* ⚠ 順序承重：機器人停掉時，app.js:1596 會同時給出 connectionStatus（closed/
   disconnected）與 ' stopped' 兩個 class，兩者特異性都是 (0,2,0) ⇒ 誰在後面誰贏。
   ⛔ 不要把這一組移到上面那組之前，否則「使用者自己按停」又會被畫成紅色錯誤。 */
.connection-line.stopped {
  color: #aeb8e8;
  border: 1px solid rgba(150, 165, 215, .34);
  border-left: 3px solid #8f9cd0;
  background: rgba(34, 44, 66, .30);
}
.connection-line.stopped .connection-orb { background: #8f9cd0; box-shadow: 0 0 12px #8f9cd088; }
.connection-line.stopped strong { color: #ccd4ee; }

/* An order click with an unconfirmed broker result is a safety exception, not
   "running" or "stopped". Keep diagnosis visible while making the lock clear
   in text and colour. */
.badge.reconciling {
  color: #ffd38a;
  border-color: rgba(255, 184, 77, .58);
  background: rgba(91, 48, 15, .78);
}
.wb-dot.reconciling {
  background: var(--amber);
  box-shadow: 0 0 0 3px rgba(255, 184, 77, .18), 0 0 12px rgba(255, 184, 77, .5);
}
.connection-line.reconciling {
  color: #ffd38a;
  border: 1px solid rgba(255, 184, 77, .5);
  border-left: 3px solid var(--amber);
  background: rgba(91, 48, 15, .3);
}
.connection-line.reconciling .connection-orb {
  background: var(--amber);
  box-shadow: 0 0 12px rgba(255, 184, 77, .78);
}
.connection-line.reconciling strong { color: #ffe0a8; }
.bot-reconciling-warning {
  display: grid;
  gap: 4px;
  margin: 12px 0 16px;
  padding: 16px 16px;
  border: 1px solid rgba(255, 184, 77, .46);
  border-radius: 11px;
  color: #ffd38a;
  background: rgba(91, 48, 15, .32);
  font-size: 14px;
  line-height: 1.45;
}
.bot-reconciling-warning strong { color: #ffe5b8; }
.bot-reconciling-warning span { color: #d9b97f; }
.wb-menu-warning {
  display: block;
  max-width: 260px;
  padding: 12px 12px;
  color: #ffd38a;
  font-size: 14px;
  line-height: 1.35;
  white-space: normal;
}

/* 0.3.38 更新確認框 —— 版面沿用 .brand-dialog，警告塊沿用既有 .notice（它已在
   每個主題區塊各自重定義過 --amber 與底色，所以自動跟著主題走）。這裡只補
   dialog 內的留白，⛔ 不重畫一套警告樣式。 */
.brand-dialog .update-dialog-warning { margin: 0 24px 4px; }
.brand-dialog #updateDialogMessage { color: var(--muted); line-height: 1.5; margin: 0; }
.brand-dialog .update-progress { margin: 14px 24px 4px; display: grid; gap: 10px; }
.brand-dialog .update-progress[hidden] { display: none; }
.update-progress-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.update-progress-head span, #updateProgressDetail { color: var(--muted); font-size: 13px; }
.update-progress progress { width: 100%; height: 10px; accent-color: var(--accent); }
#updateProgressDetail { margin: 0; line-height: 1.45; }
/* 0.3.39 settings disclosure: four common cards stay visible; advanced options
   live in one non-nested disclosure. Search is the escape hatch for anything
   that is collapsed, so "hidden" never means "undiscoverable". */
.settings-toolbar {
  display: grid;
  grid-template-columns: minmax(260px, 560px) auto 1fr;
  align-items: end;
  gap: 16px;
  margin: 32px 0 24px;
}
.settings-search { display: grid; gap: 8px; color: var(--muted); font-size: 14px; }
.settings-search input { width: 100%; }
.settings-search-count { align-self: center; min-height: 22px; color: #9fb3cb; font-size: 14px; }
#openOnboarding { justify-self: end; }
.settings-item[hidden] { display: none !important; }
/* Module numbers and faux telemetry headers duplicate the real headings. Keep
   data-module as internal metadata for colour hooks, but do not paint it. */
.module-card::after, .module-table::before, .module-table::after { display: none !important; content: none !important; }
.module-table { padding-top: 0; }
.advanced-settings { grid-column: 1 / -1; padding: 0 !important; overflow: clip; }
.advanced-settings > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 32px 32px;
  cursor: pointer;
  list-style: none;
}
.advanced-settings > summary::-webkit-details-marker { display: none; }
.advanced-settings > summary::after { content: '›'; color: #a9b9cf; font-size: 36px; transform: rotate(90deg); transition: transform .18s ease; }
.advanced-settings[open] > summary::after { transform: rotate(-90deg); }
.advanced-settings > summary span { display: grid; gap: 8px; }
.advanced-settings > summary strong { color: #eef5ff; font-size: 24px; }
.advanced-settings > summary small { color: var(--muted); font-size: 14px; }
.advanced-settings-body {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
  padding: 0 32px 32px;
  border-top: 1px solid rgba(143, 169, 196, .16);
}
.advanced-settings-body > section { padding-top: 32px; }
.advanced-settings-body h3 { margin: 0 0 16px; color: #e9f2ff; }
.advanced-safety ul { margin: 0; padding-left: 24px; }
.advanced-safety li { margin: 12px 0; color: #9caec3; font-size: 14px; }

.onboarding-dialog { width: min(880px, calc(100vw - 32px)); }
.onboarding-steps { display: grid; gap: 12px; margin: 24px 0 0; padding: 0; list-style: none; }
.onboarding-steps li {
  display: grid;
  grid-template-columns: 38px minmax(0, 1fr) auto;
  align-items: center;
  gap: 16px;
  padding: 16px;
  border: 1px solid rgba(122, 155, 194, .22);
  border-radius: 14px;
  background: rgba(6, 17, 32, .58);
}
.onboarding-steps li > span { display: grid; place-items: center; width: 34px; height: 40px; border-radius: 50%; color: #bff7ec; background: rgba(41, 171, 150, .18); font-weight: 800; }
.onboarding-steps strong { display: block; color: #eef5ff; }
.onboarding-steps p { margin: 3px 0 0; color: var(--muted); font-size: 14px; }

@media (max-width: 760px) {
  .settings-toolbar { grid-template-columns: 1fr; align-items: stretch; }
  #openOnboarding { justify-self: stretch; }
  .advanced-settings-body { grid-template-columns: 1fr; }
  .onboarding-steps li { grid-template-columns: 34px minmax(0, 1fr); }
  .onboarding-steps li > button { grid-column: 1 / -1; }
}

/* Webhook 快訊設定卡：先格式、後範例。
   ⛔ 放在檔尾是刻意的：.json-template 在前面三層主題各定義過一次，寫在這裡才不用
   逐層去追誰蓋誰。放大範圍只綁 #tradingview 這一節 —— 全域字級一動，設定頁、交易紀錄
   等十幾個版面都會跟著跑位，這些規則只綁定 Webhook 的快訊設定。 */
#cloud .receiver-grid { gap: 32px; align-items: start; }
#cloud .receiver-grid .card.form-card { padding: 32px; }
#cloud .receiver-grid .template-head h2 { font-size: 30px; }
#cloud .receiver-grid .template-head .muted { font-size: 18px; }
#cloud .receiver-grid label { font-size: 18px; }
#cloud .receiver-grid .receiver-help { font-size: 18px; line-height: 1.8; }
#cloud .receiver-grid .notice, #cloud .receiver-grid .strategy-route-note { font-size: 16px; line-height: 1.75; }

.alert-block { margin-top: 32px; }
.alert-block + .alert-block { margin-top: 32px; padding-top: 32px; border-top: 1px solid var(--border); }
.alert-block-head { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; margin-bottom: 16px; }
.alert-block-head h3 { margin: 0; font-size: 24px; font-weight: 800; letter-spacing: .5px; }
.alert-block-head button { margin-left: auto; }
.alert-tag { padding: 8px 16px; border-radius: 999px; font-size: 16px; font-weight: 700; line-height: 1.4; }
.alert-tag-required { border: 1px solid rgba(255, 148, 173, .38); color: #ffb3c4; background: rgba(120, 26, 48, .38); }
.alert-tag-sample { border: 1px solid rgba(134, 200, 240, .3); color: #9ecfee; background: rgba(18, 52, 78, .5); }

/* 格式圖：每一行左邊釘一個「第幾行」的標籤，右邊才是內容。 */
.alert-shape { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 16px 16px; margin: 0 0 24px; padding: 24px; border: 1px solid rgba(25, 221, 255, .32); border-radius: 11px; background: rgba(0, 10, 18, .9); font: 18px/1.7 ui-monospace, SFMono-Regular, Consolas, monospace; white-space: normal; overflow-wrap: anywhere; }
.alert-shape-tag { padding: 3px 12px; border-radius: 6px; color: #8fd6ff; background: rgba(23, 70, 104, .8); font-size: 16px; font-weight: 700; white-space: nowrap; }
.alert-shape-free { color: #cfe4f2; }
.alert-shape-code { color: #7ff0c4; font-weight: 700; }

/* ⚠ 卡片撐滿寬度後，若欄位表仍是單欄，每一行會被拉到破千 px ⇒ 反而更難讀
   （行長超過 75 字元眼睛會找不到下一行的開頭）。⇒ 寬度用在**並排**。 */
.alert-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 16px; margin: 0 0 24px; }
.alert-fields > div { display: grid; grid-template-columns: minmax(150px, auto) minmax(0, 1fr); gap: 8px 24px; align-items: baseline; padding: 16px 16px; border: 1px solid var(--border); border-radius: 9px; background: rgba(6, 20, 33, .55); }
.alert-fields dt { display: flex; align-items: center; gap: 12px; margin: 0; }
.alert-fields dt code { color: #7ff0c4; font-size: 18px; font-weight: 800; }
.alert-fields dd { margin: 0; color: var(--muted); font-size: 18px; line-height: 1.7; }
.alert-fields dd code { color: #84d9ff; font-size: 16px; }
.alert-need { padding: 2px 12px; border-radius: 5px; font-size: 14px; font-weight: 700; }
.alert-need-must { color: #ffc0cd; background: rgba(120, 26, 48, .55); }
.alert-need-opt { color: #a9bdd0; background: rgba(46, 66, 87, .6); }

.alert-rules { display: grid; gap: 12px; margin: 0; padding: 24px 24px 24px 48px; border-left: 3px solid var(--amber); border-radius: 2px 8px 8px 2px; background: rgba(89, 60, 15, .22); color: #e7cf9e; font-size: 18px; line-height: 1.7; }
.alert-rules code { padding: 0 8px; border-radius: 4px; color: #ffd9a0; background: rgba(0, 0, 0, .35); font-weight: 800; }

/* 範例框本來留了 315px 的空白高度給一份很短的範例；格式區搬進來之後改成貼齊內容。 */
.alert-block-example .json-template { min-height: 0; margin: 0 0 16px; padding: 24px; font-size: 18px; line-height: 1.8; }
.alert-block-example .receiver-help strong { color: #ffc9a2; }

@media (max-width: 760px) {
  .alert-shape { grid-template-columns: minmax(0, 1fr); gap: 8px; }
  .alert-fields > div { grid-template-columns: minmax(0, 1fr); }
}

/* type floor */
/* <small> 與 <sub>/<sup> 的 UA 預設是相對縮小（smaller ≈ .8em），會繞過所有以 px
   重建的型階 —— 實測畫面最小字停在 9px 就是這條路徑。企業儀表板的 caption 下限
   是 12px（lollypop.design 2026-07），所以在這裡把地板釘死。
   ⛔ 不要改成 font-size: inherit：那會讓 <small> 完全失去「次要資訊」的視覺分級。 */
small { font-size: max(12px, .875em); }

/* 系統詳細狀態分頁（2026-08-18 取代獨立的 /status 頁面）。
   ⛔ 刻意複用既有的 .section-title / var(--space-*) / 色彩 token —— 舊的 status.html
   自己帶了一整套 700 行的行內樣式，那是第二套設計系統，改一邊另一邊永遠不會跟上。
   auto-fit + minmax 讓卡片數從 4 張長到十幾張都不必改 CSS（站長問的「未來十幾張」）。 */
.status-page-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-5);
  margin-bottom: var(--space-6);
}
.status-page-section { margin-top: var(--space-6); }
.status-page-section h2 { margin: 0 0 var(--space-4); font-size: 20px; font-weight: 600; }
.status-page-card {
  padding: var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: rgba(13, 17, 39, .93);
}
.status-page-card.ok { border-color: rgba(70, 244, 170, .34); }
.status-page-card.pending { border-color: rgba(120, 229, 255, .34); }
.status-page-card.error { border-color: rgba(255, 85, 120, .46); }
.status-page-card.ok .system-status-icon { color: var(--green); }
.status-page-card.pending .system-status-icon { color: var(--cyan); }
.status-page-card.error .system-status-icon { color: var(--red); }
.status-page-line { display: flex; align-items: center; gap: var(--space-2); }
.status-page-line h3 { margin: 0; font-size: 16px; font-weight: 600; }
.status-page-value { margin: var(--space-3) 0 var(--space-1); font-size: 20px; font-weight: 700; overflow-wrap: anywhere; }
.status-page-message { margin: 0; color: var(--muted); font-size: 14px; line-height: 1.5; overflow-wrap: anywhere; }
/* 這顆以前是 <a>，改成 <button> 之後要把按鈕的預設外觀清掉，維持原本的連結樣子 */
button.system-status-link { width: 100%; border: 0; border-top: 1px solid var(--border); background: none; text-align: left; cursor: pointer; }

/* ── 機器人分組 ─────────────────────────────────────────────────────────── */
.bot-group-bar { display: flex; align-items: center; gap: var(--space-4); margin-bottom: var(--space-4); }
.bot-group-picker {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  color: var(--muted);
  font-size: var(--text-sm);
  font-weight: 600;
  white-space: nowrap;
}
/* ⛔ 寬度要有上限也要有下限：組名可以到 40 字，讓它撐開會把頁籤擠出畫面；
   而太窄則連「未分類」都顯示不完。 */
.bot-group-picker select {
  width: auto;
  min-width: 160px;
  max-width: 260px;
  min-height: 40px;
  margin: 0;
  padding: 0 var(--space-3);
}
.bot-group-count { font-size: var(--text-sm); }
/* 紀錄切換與其他沿用 .wb-tab 的地方，名稱仍需截斷。 */
.wb-tab-name {
  display: inline-block;
  max-width: 14ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}

/* ── 歷史表格：固定欄寬 ＋ 單行截斷 ＋ 可拖曳 ────────────────────────────────
   站長 2026-08-18：「每個欄位要有固定寬度，超出的部分要截斷，表頭可以手動拖曳調整」。

   ⛔ 這段放檔尾是刻意的：欄寬要蓋過前面每一套主題層（105/524/793/1244/1519/2014
      各自重寫過 th,td），插在中間會被後面的層吃掉，而那種失效不會報錯 —— 畫面照
      渲染，只是寬度不是你寫的那個。
   ⛔ 只認 .history-table 這一個 class：交易紀錄與內建瀏覽兩張表講同一批紀錄、列由
      同一個 historyRowHtml 產生，欄寬自然也只該有一份。⛔ 訊號紀錄（SIGNAL LOG）
      欄位不同，故意不套。
   ⚠ 真正的欄寬由 JS 注入的 <colgroup> 決定（app.js applyHistoryColumnWidths），
      這裡的 nth-child 只是 colgroup 還沒掛上前的預設，兩邊的數字要一致。 */
.history-table { table-layout: fixed; }
.history-table th, .history-table td {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 預設寬度按內容特性給：口數／方向最窄，動作最寬。
   ⚠ 第 5 欄（動作）刻意不給寬度 —— 它是彈性欄，吸收剩餘空間。給了它寬度，剩餘
      空間就會被平均分攤到每一欄，使用者拖到的值與看到的值從此對不上。 */
/* ⚠ 這些數字必須逐一等於 app.js 的 HISTORY_COLUMN_DEFAULT_WIDTHS（第 5 欄是彈性欄，
      兩邊都不指定）。2026-08-26 加「商品」「原因」兩欄：非彈性欄合計 1078px。
      ⛔ 只改一邊＝colgroup 掛上前後會跳一次寬度。 */
.history-table th:nth-child(1), .history-table td:nth-child(1) { width: 180px; }
.history-table th:nth-child(2), .history-table td:nth-child(2) { width: 168px; }
.history-table th:nth-child(3), .history-table td:nth-child(3) { width: 96px; }
.history-table th:nth-child(4), .history-table td:nth-child(4) { width: 96px; }
.history-table th:nth-child(6), .history-table td:nth-child(6) { width: 158px; }
.history-table th:nth-child(7), .history-table td:nth-child(7) { width: 96px; }
.history-table th:nth-child(8), .history-table td:nth-child(8) { width: 104px; }
.history-table th:nth-child(9), .history-table td:nth-child(9) { width: 120px; }
.history-table th:nth-child(10), .history-table td:nth-child(10) { width: 160px; }
/* 數值靠右、等寬數字。價格／口數／損益三欄（第 7/8/9）都是數值。 */
.history-table th:nth-child(7), .history-table td:nth-child(7),
.history-table th:nth-child(8), .history-table td:nth-child(8),
.history-table th:nth-child(9), .history-table td:nth-child(9) {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* 空狀態那一列橫跨整表，⛔ 不能被上面的 nth-child 綁死成第一欄的寬度。 */
.history-table td.empty, .history-table td[colspan] { width: auto; white-space: normal; text-overflow: clip; }

/* 拖曳把手：由 app.js 注入到每個 th 裡，⛔ 不寫進手寫的 <thead>（那有兩份，
   會變成第三個要記得同步的地方）。4-6px 是業界常見的命中寬度。 */
/* ⛔ 把手放欄「內側」不是騎在邊界上：騎邊界要 th overflow:visible，那會把上面
   剛設的表頭截斷整條取消掉（表頭一長就撐破欄寬，而且不會報錯）。th 本來就有
   24px 水平 padding，6px 的把手正好落在留白裡，不會壓到字。 */
.history-table th > .col-resizer {
  position: absolute;
  top: 0;
  right: 0;
  width: 6px;
  height: 100%;
  cursor: col-resize;
  user-select: none;
  touch-action: none;
  z-index: 2;
}
.history-table th > .col-resizer::after {
  content: '';
  position: absolute;
  top: 25%;
  left: 2px;
  width: 2px;
  height: 50%;
  border-radius: 1px;
  background: transparent;
  transition: background .12s ease;
}
.history-table th > .col-resizer:hover::after,
.history-table th > .col-resizer.dragging::after { background: var(--blue, #35a4ff); }
/* 鍵盤也要能調 —— 只有滑鼠拖得動等於這個功能對鍵盤使用者不存在。 */
.history-table th > .col-resizer:focus-visible { outline: 2px solid var(--cyan, #78e5ff); outline-offset: -1px; }
.history-table th > .col-resizer:focus-visible::after { background: var(--cyan, #78e5ff); }
/* 拖曳中：全域游標不要跟著儲存格變回 text，否則手感像斷掉。 */
body.history-col-resizing, body.history-col-resizing * { cursor: col-resize !important; user-select: none !important; }

/* 「紀錄」是使用者進入這一頁的定位點；版面切換是同一列的次要控制。 */
.workbench-page-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: 44px;
  margin: 0 0 8px;
  padding: 0 2px;
}
.workbench-page-heading h1 { margin: 0; color: var(--text); font-size: 22px; line-height: 1.15; }
.workbench-page-heading p { margin: 2px 0 0; color: var(--muted); font-size: 12px; }
.workbench-page-heading .wb-layout-switch { flex: 0 0 auto; }
.workbench-page-heading .wb-layout-switch button { width: 38px; min-width: 38px; height: 36px; min-height: 36px; }
.workbench-page-heading .wb-layout-switch svg { width: 21px; height: 21px; }
.wb-stage-head {
  position: relative;
  z-index: 30;
  display: grid;
  grid-template-columns: 28px minmax(220px, 1fr) auto;
  align-items: center;
  gap: 6px;
  min-height: 56px;
  padding: 8px;
  overflow: visible;
}
.wb-pane-drag-handle {
  display: grid;
  place-items: center;
  width: 28px;
  height: 40px;
  border-radius: 8px;
  color: var(--muted);
  cursor: grab;
  touch-action: none;
  user-select: none;
}
.wb-pane-drag-handle:hover, .wb-pane-drag-handle:focus-visible { color: var(--cyan); background: color-mix(in srgb, var(--cyan) 12%, transparent); outline: none; }
.wb-pane-drag-handle:active { cursor: grabbing; }
.wb-pane-drag-handle svg { width: 18px; height: 24px; fill: currentColor; }
.wb-grid-tile .wb-stage-head, .wb-focus-pane .wb-stage-head { display: grid; gap: 6px; padding: 8px; }
.wb-pane-picker {
  position: relative;
  display: flex;
  align-items: center;
  width: 100%;
  min-width: 220px;
  max-width: none;
  height: 40px;
  border: 1px solid color-mix(in srgb, var(--cyan) 24%, var(--border));
  border-radius: 10px;
  background: var(--surface-2);
}
.wb-pane-group {
  width: clamp(88px, 31%, 132px);
  min-width: 88px;
  height: 38px;
  padding: 0 20px 0 8px;
  border: 0;
  border-right: 1px solid var(--border);
  border-radius: 9px 0 0 9px;
  background-color: transparent;
  color: var(--muted);
  font-size: 12px;
}
.wb-pane-picker > .bot-avatar-button {
  width: 30px;
  min-width: 30px;
  max-width: 30px;
  height: 30px;
  min-height: 30px;
  max-height: 30px;
  margin-left: 7px;
  flex: 0 0 30px;
}
.wb-pane-bot-picker { position: static; min-width: 0; flex: 1 1 auto; }
.wb-pane-bot-picker > summary {
  min-height: 38px;
  padding: 0 26px 0 7px;
  border-radius: 0 9px 9px 0;
  display: flex;
  align-items: center;
}
.wb-pane-bot-picker > summary::after { right: 10px; }
.wb-pane-current { display: flex; align-items: center; gap: 7px; min-width: 0; }
.wb-pane-current strong { overflow: hidden; color: var(--text); font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
.wb-pane-bot-picker > .workbench-tabs { top: calc(100% + 6px); left: clamp(88px, 31%, 132px); width: min(320px, calc(100vw - 32px)); }
.wb-stage-tools { display: flex; align-items: center; justify-content: flex-end; gap: 5px; min-width: 0; }
.wb-stage-tools > .wb-stage-resource-toggle,
.wb-stage-tools > .wb-stage-fullscreen,
.wb-stage-tools > .wb-stage-more { display: grid; place-items: center; width: 36px; min-width: 36px; height: 36px; min-height: 36px; padding: 0; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2); }
.wb-stage-more {
  width: 34px; min-width: 34px; height: 34px; min-height: 34px; padding: 0;
  border-radius: 9px; color: var(--text); background: rgba(24, 30, 65, .62);
  transition: color .14s ease, border-color .14s ease, background .14s ease, transform .14s ease;
}
.wb-stage-more:hover { color: var(--cyan); border-color: var(--cyan); background: color-mix(in srgb, var(--cyan) 10%, rgba(24, 30, 65, .72)); }
.wb-stage-more:active { transform: translateY(1px); }

/* 畫面／操作／交易在每個窗格都要看得到，和版面切換共用同一種分段控制語彙。 */
.wb-pane-tabs { display: inline-flex; align-items: center; gap: 2px; padding: 3px; border: 1px solid var(--border); border-radius: 11px; background: var(--surface-2); }
.wb-pane-tabs button { display: grid; place-items: center; width: 38px; min-width: 38px; height: 36px; min-height: 36px; padding: 0; border: 0; border-radius: 8px; background: transparent; color: var(--muted); touch-action: none; cursor: grab; transition: color 140ms ease, background 140ms ease, transform 140ms cubic-bezier(.2,.8,.2,1); }
.wb-pane-tabs button:active { cursor: grabbing; transform: scale(.94); }
.wb-pane-tabs svg { width: 22px; height: 22px; }
.wb-stage-record-layer { flex: 1 1 0; min-height: 0; width: 100%; overflow: hidden; display: grid; place-items: stretch; }
.wb-stage-viewer-layer[hidden], .wb-stage-record-layer[hidden] { display: none !important; }

/* 按住哪顆內容圖示，就讓那顆原按鈕本身浮起跟手；placeholder 只守住原列寬度。
   不再另生右下角 ghost，避免看起來像同時有兩顆按鈕。 */
.wb-pane-tabs button.is-mode-drag-source {
  position: fixed;
  inset: 0 auto auto 0;
  z-index: 13000;
  width: var(--wb-mode-drag-width);
  min-width: var(--wb-mode-drag-width);
  height: var(--wb-mode-drag-height);
  min-height: var(--wb-mode-drag-height);
  padding: 0;
  border: 1px solid color-mix(in srgb, var(--cyan) 76%, white);
  border-radius: 10px;
  color: #eaffff;
  background: color-mix(in srgb, var(--surface-2) 78%, transparent);
  box-shadow: 0 16px 46px rgba(0,0,0,.48), 0 0 28px color-mix(in srgb, var(--cyan) 34%, transparent);
  backdrop-filter: blur(16px) saturate(1.25);
  pointer-events: none;
  will-change: transform;
  transform: translate3d(var(--wb-mode-drag-x), var(--wb-mode-drag-y), 0) scale(1.08);
  transition: none;
}
.wb-mode-drag-placeholder { display: inline-block; flex: 0 0 auto; border-radius: 8px; background: color-mix(in srgb, var(--cyan) 10%, transparent); }
.workbench-mode-dragging { cursor: grabbing; user-select: none; }
.is-mode-drop-target {
  isolation: isolate;
  border-color: color-mix(in srgb, var(--cyan) 84%, white) !important;
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--cyan) 46%, transparent), 0 0 32px color-mix(in srgb, var(--cyan) 24%, transparent) !important;
}
.is-mode-drop-target::after {
  content: '放開即可覆蓋';
  position: absolute;
  inset: 50% auto auto 50%;
  z-index: 95;
  transform: translate(-50%,-50%) scale(1);
  padding: 10px 15px;
  border: 1px solid color-mix(in srgb, var(--cyan) 72%, white);
  border-radius: 999px;
  color: #eaffff;
  background: rgba(5,25,38,.84);
  box-shadow: 0 12px 36px rgba(0,0,0,.38);
  backdrop-filter: blur(12px);
  font-size: 13px;
  font-weight: 800;
  pointer-events: none;
  animation: wb-mode-drop-pulse 760ms ease-in-out infinite alternate;
}
@keyframes wb-mode-drop-pulse { from { transform: translate(-50%,-50%) scale(.96); opacity: .82; } to { transform: translate(-50%,-50%) scale(1.04); opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .is-mode-drop-target::after { animation: none; } }
.workbench-panel[data-layout="grid"] .wb-pane-picker {
  min-width: 220px;
  max-width: none;
}
.workbench-panel[data-layout="grid"] .wb-pane-group {
  width: clamp(88px, 31%, 120px);
  min-width: 88px;
  padding-inline: 8px 20px;
}
.workbench-panel[data-layout="grid"] .wb-pane-bot-picker > .workbench-tabs { left: clamp(88px, 31%, 120px); }

.wb-trade-summary {
  /* 硬限制：交易控制列永遠留在文件流自己的列，不能用 absolute／sticky
     疊到券商畫面上。畫面可以縮放，控制列不可以拿它當背景。 */
  position: static;
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: minmax(320px, 1.1fr) minmax(330px, .9fr);
  gap: 1px;
  padding: 1px;
  border-bottom: 1px solid var(--border);
  background: var(--border);
}
.wb-trade-summary .wb-grid-trade {
  display: flex;
  align-items: stretch;
  gap: 6px;
  min-width: 0;
  padding: 6px 8px;
  background: var(--surface-2);
}
.wb-trade-summary .wb-grid-trade .qt-side { flex: 1 1 72px; min-width: 0; min-height: 38px; padding: 0 clamp(6px, 1.2vw, 12px); }
.wb-trade-summary .wb-grid-trade .qt-qty { flex: 0 1 116px; min-width: 92px; }
.wb-trade-summary .wb-grid-trade .qt-qty button { width: 28px; min-width: 28px; }
.wb-trade-summary .wb-grid-trade .qt-qty input { width: 42px; min-width: 38px; }
.wb-stage-metrics {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  flex: 0 0 auto;
  padding: 1px;
  background: var(--border);
  font-variant-numeric: tabular-nums;
}
.wb-trade-summary > .wb-stage-metrics { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); order: initial; width: 100%; min-width: 0; gap: 1px; border: 0; }
.wb-stage-metric { container-type: inline-size; display: grid; gap: 2px; min-width: 0; padding: 7px clamp(4px, 1vw, 10px); background: var(--surface-2); text-align: center; }
.wb-stage-metric small { overflow: visible; color: var(--muted); font-size: clamp(9px, 12cqi, 12px); line-height: 1.15; white-space: nowrap; }
.wb-stage-metric strong { overflow: visible; color: var(--text); font-size: clamp(10px, 14cqi, 16px); line-height: 1.2; letter-spacing: -.02em; white-space: nowrap; }
.wb-stage-metric strong.positive { color: var(--green); }
.wb-stage-metric strong.negative { color: var(--danger); }

@container (max-width: 480px) {
  .wb-stage-head { grid-template-columns: 28px minmax(0, 1fr); }
  .wb-stage-tools { grid-column: 1 / -1; justify-content: flex-start; }
  .wb-pane-picker { min-width: 0; }
  .wb-pane-tabs button { width: 34px; min-width: 34px; height: 34px; min-height: 34px; }
  .wb-pane-tabs svg { width: 20px; height: 20px; }
  .wb-stage-tools > .wb-stage-resource-toggle,
  .wb-stage-tools > .wb-stage-fullscreen,
  .wb-stage-tools > .wb-stage-more { width: 34px; min-width: 34px; height: 34px; min-height: 34px; }
  .wb-trade-summary { grid-template-columns: minmax(0, 1fr); }
  .wb-trade-summary .wb-grid-trade .qt-side { font-size: 12px; }
}

/* 0.3.70 工作台窗格工具：四顆固定尾端動作共用同一外觀；關閉只有在靠近時轉紅，
   資源節省在未啟動時仍保留位置並降階，避免工具列隨狀態左右跳動。 */
.wb-stage-head .wb-stage-window-tools > .wb-stage-close {
  display: grid;
  place-items: center;
  width: 32px;
  min-width: 32px;
  height: 36px;
  min-height: 36px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface-2);
  color: var(--muted);
  transition: color .14s ease, border-color .14s ease, background .14s ease, transform .14s ease;
}
.wb-stage-close svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }
.wb-stage-close:hover,
.wb-stage-close:focus-visible { color: #ff718e; border-color: #ff5579; background: color-mix(in srgb, #ff315f 16%, var(--surface-2)); }
.wb-stage-close:active { transform: scale(.94); }
.wb-stage-resource-toggle:disabled { opacity: .42; cursor: not-allowed; filter: saturate(.35); }
.wb-stage-resource-toggle:disabled:hover { border-color: var(--border); color: var(--muted); }

/* 空窗格只選既有機器人，不再把使用者送去建立流程。清單採分組＋大頭照＋名稱的
   單欄選擇器，觸控與滑鼠都有足夠命中面積。 */
.wb-empty-pane {
  position: relative;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 8px;
  width: 100%;
  height: 100%;
  min-height: 220px;
  padding: 24px;
  color: var(--muted);
  text-align: center;
  background: radial-gradient(circle at 50% 45%, color-mix(in srgb, var(--cyan) 8%, transparent), transparent 42%);
}
.wb-empty-pane > strong { color: var(--text); font-size: 15px; }
.wb-empty-pane > span { font-size: 12px; }
.wb-empty-picker { position: relative; }
.wb-empty-picker > summary {
  display: grid;
  place-items: center;
  width: 74px;
  height: 74px;
  padding: 0;
  border: 1px solid color-mix(in srgb, var(--cyan) 35%, var(--border));
  border-radius: 50%;
  background: color-mix(in srgb, var(--cyan) 7%, var(--surface-2));
  color: var(--cyan);
  cursor: pointer;
  list-style: none;
  transition: transform .18s cubic-bezier(.2,.8,.2,1), border-color .18s ease, box-shadow .18s ease;
}
.wb-empty-picker > summary::-webkit-details-marker { display: none; }
.wb-empty-picker > summary:hover,
.wb-empty-picker > summary:focus-visible { transform: scale(1.06); border-color: var(--cyan); box-shadow: 0 0 30px color-mix(in srgb, var(--cyan) 24%, transparent); }
.wb-empty-picker > summary svg { width: 34px; height: 34px; fill: none; stroke: currentColor; stroke-width: 1.55; stroke-linecap: round; }
.wb-empty-picker-menu {
  position: fixed;
  z-index: 21050;
  top: 50%;
  left: 50%;
  width: min(380px, calc(100vw - 28px));
  max-height: min(70vh, 560px);
  padding: 10px;
  overflow: auto;
  border: 1px solid color-mix(in srgb, var(--cyan) 28%, var(--border));
  border-radius: 14px;
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  box-shadow: 0 24px 80px rgba(0,0,0,.55);
  backdrop-filter: blur(22px) saturate(1.25);
  transform: translate(-50%, -50%);
  text-align: left;
}
.wb-empty-picker:not([open]) > .wb-empty-picker-menu { display: none; }
.wb-empty-picker-group + .wb-empty-picker-group { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); }
.wb-empty-picker-group h4 { margin: 0 0 6px; padding: 0 8px; color: var(--muted); font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.wb-empty-bot-option { display: block; width: 100%; min-height: 58px; padding: 7px 9px; border: 0; border-radius: 10px; background: transparent; color: var(--text); text-align: left; }
.wb-empty-bot-option:hover,
.wb-empty-bot-option:focus-visible { background: color-mix(in srgb, var(--cyan) 12%, transparent); }
.wb-empty-bot-option .wb-bot-picker-row { grid-template-columns: 9px 42px minmax(0, 1fr); gap: 10px; }
.wb-empty-bot-option .bot-avatar-display { width: 42px; min-width: 42px; max-width: 42px; height: 42px; min-height: 42px; max-height: 42px; }
.wb-empty-bot-option .wb-bot-picker-name { font-size: 15px; font-weight: 760; }

/* 身分選擇器把大頭照與名稱視為同一組，尺寸比一般工具圖示大一級。 */
.wb-pane-identity-row { grid-template-columns: 42px minmax(0, 1fr); gap: 7px; }
.wb-pane-identity-row > .bot-avatar-button { width: 42px; min-width: 42px; max-width: 42px; height: 42px; min-height: 42px; max-height: 42px; }
.wb-stage-head .wb-pane-bot-picker > summary { min-height: 42px; }
.wb-stage-head .wb-pane-current strong { font-size: 14px; }
.wb-stage-head .wb-bot-option { min-height: 54px; }
.wb-stage-head .wb-bot-picker-row { grid-template-columns: 9px 38px minmax(0, 1fr); gap: 8px; }
.wb-stage-head .wb-bot-option .bot-avatar-display { width: 38px; min-width: 38px; max-width: 38px; height: 38px; min-height: 38px; max-height: 38px; }

/* 四格仍使用同一個摘要元件，但 480px 以上維持同一列，避免控制列吃掉畫面的高度。 */
.wb-grid-tile .wb-trade-summary { grid-template-columns: minmax(230px, .95fr) minmax(240px, 1.05fr); }
.wb-grid-tile .wb-stage-metrics { display: grid; order: initial; width: auto; }

/* 四格／上下格的外框高度固定時，live 畫布必須只吃「控制列之外」的剩餘空間。
   ⛔ 若沿用單格的 16:9 固定高度，控制列 + 畫布會高過 tile，再被 overflow:hidden
      裁掉底部，看起來就像買賣列蓋住券商畫面。這裡改成 flex 剩餘空間，影像本身
      仍由 object-fit:contain 保證四邊完整顯示。 */
.wb-grid-tile > .wb-stage-canvas:has(.wb-stage-live),
.wb-focus-pane > .wb-stage-canvas:has(.wb-stage-live) {
  flex: 1 1 0;
  width: 100%;
  height: auto;
  min-height: 0;
  aspect-ratio: auto;
  overflow: hidden;
}

@media (max-width: 820px) {
  .workbench-page-heading p { display: none; }
  .wb-trade-summary { grid-template-columns: minmax(0, 1fr); }
  .wb-trade-summary .wb-grid-trade { gap: 4px; padding-inline: 6px; }
}

/* 1×1 是上／下兩格，預設給上方寬螢幕較多空間；2×2 的整體改成桌面橫向比例。 */
.workbench-stage[data-layout="split"] {
  --wb-split-ratio: 50%;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, var(--wb-split-ratio)) 8px minmax(0, 1fr);
  gap: 0;
  /* 兩格都要有足夠的實際畫布；寬螢幕時依寬度增高，寧可讓頁面捲動，也不把
     Tradovate 與紀錄壓成兩張縮圖。使用者拖過的 1:1 比例仍完整保留。 */
  height: clamp(1400px, max(135vh, calc((100vw - 32px) * 1.22)), 2360px);
}
.workbench-stage[data-layout="split"][data-split-axis="horizontal"] {
  grid-template-columns: minmax(0, var(--wb-split-ratio)) 8px minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  height: clamp(620px, calc(100vh - 118px), 900px);
}
.workbench-stage[data-layout="split"][data-split-axis="horizontal"] .wb-split-resizer { cursor: col-resize; }
.workbench-stage[data-layout="split"][data-split-axis="horizontal"] .wb-split-resizer::after { inset: 12px 3px; }
.workbench-stage[data-layout="grid"] {
  --wb-grid-x: 50%;
  --wb-grid-y: 50%;
  width: 100%;
  height: clamp(1280px, calc((100vw - 32px) * 1.04), 2000px);
  min-height: 0;
  aspect-ratio: auto;
  align-self: center;
  grid-template-columns: minmax(0, var(--wb-grid-x)) minmax(0, 1fr);
  grid-template-rows: minmax(0, var(--wb-grid-y)) minmax(0, 1fr);
}
.wb-grid-tile { container-type: inline-size; }
.wb-snap-overlay {
  position: fixed;
  inset: auto;
  z-index: 80;
  pointer-events: none;
  border-radius: inherit;
  background: color-mix(in srgb, var(--bg) 28%, transparent);
  backdrop-filter: blur(2px);
}
.wb-snap-preview {
  position: absolute;
  display: grid;
  place-items: center;
  min-width: 72px;
  min-height: 72px;
  border: 2px solid color-mix(in srgb, var(--cyan) 78%, white);
  border-radius: 14px;
  color: var(--text);
  background: color-mix(in srgb, var(--cyan) 22%, rgba(8, 17, 34, .82));
  box-shadow: 0 18px 55px rgba(0, 0, 0, .42), inset 0 0 0 1px rgba(255,255,255,.12);
  opacity: .72;
  transition: left 110ms cubic-bezier(.2,0,0,1), top 110ms cubic-bezier(.2,0,0,1), width 110ms cubic-bezier(.2,0,0,1), height 110ms cubic-bezier(.2,0,0,1), opacity 90ms ease, box-shadow 90ms ease;
}
.wb-snap-preview strong { padding: 8px 12px; border-radius: 999px; background: rgba(3, 8, 19, .78); font-size: 13px; }
.wb-snap-preview.is-armed {
  opacity: 1;
  box-shadow: 0 18px 55px rgba(0, 0, 0, .42), inset 0 0 0 2px color-mix(in srgb, var(--cyan) 82%, white), 0 0 34px color-mix(in srgb, var(--cyan) 34%, transparent);
}
.wb-focus-pane.is-snap-dragging, .wb-grid-tile.is-snap-dragging {
  opacity: .92;
  box-shadow: 0 24px 70px rgba(0,0,0,.55);
  will-change: transform;
}
@media (prefers-reduced-motion: reduce) {
  .wb-snap-preview { transition: none; }
}
.wb-pane-resizer { touch-action: none; user-select: none; z-index: 45; }
.wb-split-resizer { cursor: row-resize; position: relative; }
.wb-split-resizer::after,
.wb-grid-resizer-x::after,
.wb-grid-resizer-y::after {
  content: '';
  position: absolute;
  border-radius: 999px;
  background: color-mix(in srgb, var(--cyan) 38%, var(--border));
  transition: background .12s ease, box-shadow .12s ease;
}
.wb-split-resizer::after { inset: 3px 12px; }
.wb-grid-resizer-x,
.wb-grid-resizer-y { position: absolute; }
.wb-grid-resizer-x { top: 0; bottom: 0; left: calc(var(--wb-grid-x) - 5px); width: 10px; cursor: col-resize; }
.wb-grid-resizer-x::after { top: 12px; bottom: 12px; left: 4px; width: 2px; }
.wb-grid-resizer-y { left: 0; right: 0; top: calc(var(--wb-grid-y) - 5px); height: 10px; cursor: row-resize; }
.wb-grid-resizer-y::after { left: 12px; right: 12px; top: 4px; height: 2px; }
.wb-pane-resizer:hover::after,
.wb-pane-resizer:focus-visible::after,
.wb-pane-resizer.is-dragging::after { background: var(--cyan); box-shadow: 0 0 10px color-mix(in srgb, var(--cyan) 55%, transparent); }
body.workbench-pane-resizing { user-select: none; }

/* 交易表只留一個「觸發」欄；八欄全部有獨立把手。 */
.history-table th:nth-child(n), .history-table td:nth-child(n) { width: auto; }
.history-table th:nth-child(1), .history-table td:nth-child(1) { width: 180px; }
.history-table th:nth-child(2), .history-table td:nth-child(2) { width: 168px; }
.history-table th:nth-child(3), .history-table td:nth-child(3) { width: 104px; }
.history-table th:nth-child(4), .history-table td:nth-child(4) { width: 96px; }
.history-table th:nth-child(5), .history-table td:nth-child(5) { width: 158px; }
.history-table th:nth-child(6), .history-table td:nth-child(6) { width: 104px; }
.history-table th:nth-child(7), .history-table td:nth-child(7) { width: 88px; }
.history-table th:nth-child(8), .history-table td:nth-child(8) { width: 128px; }
.history-table th:nth-child(6), .history-table td:nth-child(6),
.history-table th:nth-child(7), .history-table td:nth-child(7),
.history-table th:nth-child(8), .history-table td:nth-child(8) { text-align: right; font-variant-numeric: tabular-nums; }

.resizable-table { table-layout: fixed; }
.resizable-table th, .resizable-table td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.resizable-table th { position: relative; }
.resizable-table th > .col-resizer {
  position: absolute;
  top: 0;
  right: 0;
  z-index: 4;
  width: 7px;
  height: 100%;
  cursor: col-resize;
  touch-action: none;
}
.resizable-table th > .col-resizer::after { content: ''; position: absolute; top: 24%; bottom: 24%; right: 2px; width: 2px; border-radius: 2px; background: transparent; }
.resizable-table th > .col-resizer:hover::after,
.resizable-table th > .col-resizer:focus-visible::after,
.resizable-table th > .col-resizer.dragging::after { background: var(--cyan); }

.pnl-heading { position: relative; display: inline-flex; align-items: center; justify-content: flex-end; gap: 5px; }
.pnl-info { display: inline-grid; place-items: center; width: 16px; height: 16px; border: 1px solid currentColor; border-radius: 50%; color: var(--muted); font: 700 11px/1 sans-serif; cursor: help; }
.pnl-tooltip {
  position: fixed;
  z-index: 10020;
  top: 112px;
  right: 24px;
  width: min(360px, calc(100vw - 32px));
  padding: 9px 11px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 14px 40px rgba(0,0,0,.45);
  font-size: 12px;
  font-weight: 500;
  line-height: 1.5;
  text-align: left;
  white-space: normal;
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
}
.pnl-info:hover + .pnl-tooltip, .pnl-info:focus-visible + .pnl-tooltip { visibility: visible; opacity: 1; }

/* 這兩條放在既有摘要規則之後，封住相同 specificity 的舊橫排設定。 */
@container (min-width: 901px) {
  .wb-trade-summary .wb-grid-trade {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(86px, .9fr) minmax(0, 1fr) 38px;
  }
  .wb-trade-summary > .wb-stage-metrics { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 980px) {
  .wb-stage-head { grid-template-columns: 28px minmax(210px, 1fr) auto; }
  .wb-pane-picker { max-width: none; }
}

@media (max-width: 720px) {
  .workbench-page-heading { align-items: flex-start; }
  .workbench-layout-rail { justify-content: flex-start; margin: 0 0 8px; }
  .workbench-stage[data-layout="grid"] { display: flex; flex-direction: column; width: 100%; aspect-ratio: auto; }
  .workbench-stage[data-layout="split"] { height: auto; min-height: 0; grid-template-rows: none; }
  .workbench-stage[data-layout="split"][data-split-axis="horizontal"] { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; height: auto; }
  .workbench-stage[data-layout="split"] .wb-focus-pane { min-height: 440px; }
  .wb-pane-resizer { display: none; }
  .wb-stage-head { grid-template-columns: 28px minmax(0, 1fr); }
  .wb-stage-tools { grid-column: 1 / -1; justify-content: flex-start; flex-wrap: wrap; }
  .wb-pane-picker { min-width: 0; }
}

/* ── 建立／編輯機器人對話框（0.4.3：同一個表單兩種模式）─────────────────────
   ⛔ 間距一律取既有 --space-* 網格、圓角取 --radius-*、顏色取既有 token，
      ⛔ 不新增一次性數值 —— 這個檔已經有五處各寫一份 .app-header 的前例。 */

/* 編輯模式的說明條。橘色那版沿用 .bot-reconciling-warning 的語彙（警示但非錯誤）；
   剛建立完那一次改用綠色 —— 那是成功回饋，用警示色會讓人以為出事了。 */
.dialog-note {
  margin: var(--space-4) 0 0;
  padding: var(--space-3) var(--space-4);
  border: 1px solid rgba(255, 184, 77, .46);
  border-radius: var(--radius-md);
  color: #ffd38a;
  background: rgba(91, 48, 15, .32);
  font-size: var(--text-sm);
  line-height: 1.5;
}
.dialog-note.created {
  border-color: rgba(72, 213, 151, .46);
  color: #b9f5d7;
  background: rgba(15, 62, 44, .38);
}

/* 建立後不可更改的欄位。⛔ 停用而不是藏起來：藏起來使用者只覺得「欄位怎麼不見了」，
   看得到＋鎖頭＋說明條才知道「這是刻意的，而且原因寫在上面」。 */
.field-locked { opacity: .58; }
.field-locked > input, .field-locked > select { cursor: not-allowed; }
/* ⛔ ::before 不是 ::after —— label 是 display:block 且 input 包在 label 裡面，
   ::after 會把鎖頭掉到輸入框「下面」而不是接在欄位名旁邊。 */
.field-locked::before {
  content: '🔒';
  margin-right: var(--space-1);
  font-size: var(--text-xs);
}

/* 建立成功後彈回來時，指定商品那一格閃一下 —— 那是整個彈窗存在的理由，
   ⛔ 不要讓使用者自己在表單裡找。動畫尊重 prefers-reduced-motion（見檔頭那條 media query）。 */
.field-attention {
  border-radius: var(--radius-md);
  animation: field-attention-pulse 1.6s ease-out 1;
}
@keyframes field-attention-pulse {
  0% { box-shadow: 0 0 0 0 rgba(62, 215, 208, .55); }
  100% { box-shadow: 0 0 0 12px rgba(62, 215, 208, 0); }
}


/* ===== 0.4.3 建立／編輯機器人對話框：說明收成 `?` 提示 + 收緊列距 =====
   站長 2026-08-19：「新增機器人 這邊不要空這麼大」＋「將說明 全部做成 標題旁邊的 ?
   游標移動過去才顯示 就好」。
   ⚠ 說明字放在 .field-hint 的 aria-label（i18n.js 的 ATTRS 會翻譯它）；
     這裡用 content: attr(aria-label) 把同一份字畫成泡泡 ⇒ ⛔ 只有一份真相源。
   ⛔ 不要改用 title 屬性：那會同時跳出瀏覽器原生 tooltip ＝ 兩個泡泡。 */
#botForm .form-grid { gap: 0 20px; }
#botForm .form-grid > label { margin: 0 0 16px; }
#botForm .form-grid > label > input,
#botForm .form-grid > label > select { margin-top: 6px; }
#botForm .form-grid-full { grid-column: 1 / -1; }
/* 「指定商品」跨整行，但它的三個零件（篩選框／選單／同步鈕）橫著排成一列。
   ⭐ 起因（站長 2026-08-24）：「那個選單 應該是在表格裡面 而不是在表格下面 很奇怪」。
      在此之前這一格不只排在全表最後，內部還是【垂直三列】⇒ 在兩欄網格底下自成一大塊，
      看起來就是「掛在表格外面的東西」。
   ⛔ 試過改成佔半格（拿掉 form-grid-full）：那格量到 242px 高而隔壁「分組」只有 56px
      ⇒ 右邊留一大片空白，比原本更不整齊 ⇒ 已放棄，改成「表格中間的一整列」。
   ⚠ 選單那欄要 2fr：option 文字是全表最長的（「MNQU6 — Micro E-mini NASDAQ 100（此帳號已確認）」），
      給 1fr 會把來源標記截掉，而那個標記正是它唯一的安全提示。 */
#botForm .symbol-picker {
  margin-top: 6px;
  gap: 8px 12px;
  /* ⭐ 站長 2026-08-24 講了兩次，第二次是逐字的：
        「你是聽不懂喔 叫你把這兩個框 功能合併成一個框就好」
     ⇒ 篩選框與選單已合併成一個可搜尋的 combobox（index.html：input list + datalist）
     ⇒ 這一列只剩兩個零件：商品框 ＋ 同步鈕。
     ⛔ 這裡刻意【不用 grid】：這段先前累積了三輪互相矛盾的規則，而最後那三行 grid-area
        把選單釘死在第二列 —— 改它上面的任何宣告都不會生效，且每一版都像「CSS 沒載到」。
     ⇒ 一個伸縮 ＋ 一個固定，flex 是最直接的表達；flex-wrap 自帶「窄到放不下就折」
        ＝ 站長要的自適應，⛔ 不必再寫 media query 猜斷點。 */
  display: flex;
  flex-wrap: wrap;
  align-items: center;
}
/* flex-basis 240px：比這窄的話商品名稱會被切到看不出是哪個月份的合約 ⇒ 寧可折行。
   ⛔ min-width: 0 不可省：flex item 預設 min-width:auto 會被內容撐開，
      長商品名（MNQU6 — Micro E-mini NASDAQ 100（此帳號已確認））會把整列推寬。
   ⭐ 而「被切掉的是什麼」這件事已經在 app.js 那邊治本了：來源標記 ✅ 移到最前面
      ⇒ 任何寬度下先被切掉的都是名稱，⛔ 不會是那個唯一的安全提示。 */
/* ⚠ 2026-08-24 下午：輸入框被包進 .symbol-select-wrap（面板要跟它同寬，見 index.html 的註解）
   ⇒ flex 欄位由 wrapper 接管，輸入框在 wrapper 裡撐滿。
   ⛔ 舊的 `> .symbol-select` 選擇器留著會失效而且看不出來（輸入框不再是直接子層）—— 直接換掉。 */
#botForm .symbol-picker > .symbol-select-wrap { flex: 1 1 240px; min-width: 0; position: relative; }
#botForm .symbol-picker > .symbol-select-wrap > .symbol-select { width: 100%; }
#botForm .symbol-picker > .symbol-picker-actions { flex: 0 0 auto; }
/* 按鈕文字不折行：折成兩行會把整列撐高，而它撐高的是「指定商品」那一整列。 */
#botForm .symbol-picker-actions button { white-space: nowrap; }
#botForm .dialog-actions { margin-top: 16px; }
#botForm .dialog-note { margin: 0 0 12px; }
.bot-wizard-progress { display: grid; grid-template-columns: max-content minmax(32px, 96px) max-content; align-items: center; gap: 12px; margin: 0 0 20px; padding: 0; color: var(--muted); list-style: none; }
.bot-wizard-progress > li { display: inline-flex; align-items: center; gap: 9px; min-width: 0; }
.bot-wizard-progress > li > span { display: grid; place-items: center; width: 30px; height: 30px; border: 1px solid rgba(151, 166, 224, .35); border-radius: 50%; font-weight: 800; }
.bot-wizard-progress > li.active > span { border-color: #45d9d0; color: #b9fff5; background: rgba(44, 205, 192, .14); }
.bot-wizard-progress > li.done > span { border-color: #45d9d0; color: #07121b; background: #45d9d0; }
.bot-wizard-progress > li > strong { color: #aeb9d8; font-size: 14px; white-space: nowrap; }
.bot-wizard-progress > li.active > strong, .bot-wizard-progress > li.done > strong { color: #e9efff; }
.bot-wizard-progress > i { height: 1px; background: rgba(151, 166, 224, .3); }
.bot-wizard-hidden { display: none !important; }
#botDialog[data-bot-mode="create"] .bot-wizard-page-2 { grid-column: 1 / -1; }

.field-hint {
  display: inline-grid;
  place-items: center;
  width: 18px;
  height: 18px;
  margin-left: 6px;
  border: 1px solid rgba(151, 166, 224, .45);
  border-radius: 50%;
  background: rgba(4, 7, 20, .55);
  color: #9fb0e0;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  cursor: help;
  vertical-align: middle;
  position: relative;
}
.field-hint:hover, .field-hint:focus-visible { border-color: #4fa3ff; color: #cfe2ff; outline: none; }
/* 泡泡本體。⚠ 對話框內容會捲動，所以用 position:absolute 貼著問號本身，
   ⛔ 不用 fixed —— fixed 的泡泡捲動時會留在原地。 */
.field-hint::after {
  content: attr(aria-label);
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  /* ⭐ 2026-08-24：固定的 -50%（純置中）在窄視窗會把泡泡推出畫面 ——
     實測 640px 下左溢 9px 與 26px，站長截圖就是這個。
     ⇒ 加一個由 JS 量出來的位移把它推回畫面內（tooltip 的標準做法）。
     ⛔ 預設 0px ⇒ JS 沒跑（或還沒 hover）時行為與改動前完全一樣，不會變更糟。 */
  transform: translateX(calc(-50% + var(--hint-shift, 0px)));
  z-index: 30;
  width: max-content;
  /* 窄畫面下泡泡本身也不該比畫面寬：24px 是左右各留 12px 安全邊。 */
  max-width: min(280px, calc(100vw - 24px));
  padding: 10px 12px;
  border: 1px solid rgba(79, 163, 255, .38);
  border-radius: 10px;
  background: #0d1b2e;
  box-shadow: 0 12px 32px #000a;
  color: #d6e2f7;
  font-size: 14px;
  font-weight: 500;
  line-height: 1.55;
  letter-spacing: 0;
  text-align: left;
  white-space: normal;
  opacity: 0;
  visibility: hidden;
  transition: opacity .12s;
  pointer-events: none;
}
.field-hint:hover::after, .field-hint:focus-visible::after { opacity: 1; visibility: visible; }
/* ⛔ 泡泡一律往**上**開。實測（headless 量測 + 截圖）：往下開會正好蓋住這個欄位
   自己的輸入框 —— 使用者滑過去看說明，結果看不到要填的地方。往上開只會蓋到對話框
   標題那一區（那裡沒有需要同時看的東西），且第一排欄位 y≈302 遠低於泡泡高度，
   不會被切掉。 */

.bot-webhook-field .copy-field { margin-top: 6px; }
.bot-webhook-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin-top: 8px; }
.bot-webhook-note { color: #8693bc; font-size: 14px; font-weight: 500; line-height: 1.5; }

/* 刪除警告視窗要逐行讀 —— brandConfirm 用 textContent 寫入，沒有這行換行符會被吃掉。 */
.brand-dialog #confirmDialogMessage { white-space: pre-line; }


/* ===== 必填欄位的紅色 ＊（站長 2026-08-19：「將必填寫項目左上角 * 紅色的
   選填則沒有 主流都這樣區分」）=====

   ⛔ 唯一真相源是 HTML 的 `required` 屬性 —— 也就是**送出時真的會被擋下**的那個條件。
      ⛔ 不在 label 文字裡手寫 *：那是第二份判準，拿掉一個 required 時沒有任何東西
      會提醒你星號還留著，而畫面會繼續要求一個其實選填的欄位。
   ⚠ `:has()` 是這裡的關鍵（Electron 43 ＝ Chromium 130+，本檔已用過三次）。
   ⚠ `/ ''` 是 content 的替代文字：`required` 本身已經傳達給輔助技術，
      螢幕閱讀器不需要再唸一次「星號」。
   ⛔ 用 ::before 不是 ::after —— label 是 display:block 且輸入框包在 label 裡面，
      ::after 會把星號掉到輸入框「下面」，而站長要的是「左上角」。 */
label:has(> input[required]),
label:has(> select[required]),
label:has(> textarea[required]) { position: relative; }

label:has(> input[required])::before,
label:has(> select[required])::before,
label:has(> textarea[required])::before {
  content: '＊' / '';
  margin-right: 2px;
  color: var(--red);
  font-weight: 700;
  line-height: 1;
}

/* 同一個 label 同時「建立後不可更改」＋「必填」時（編輯模式的交易帳號／預設口數／
   每點價值就是這一類），一個元素只有一個 ::before ⇒ 上面那條會把 .field-locked 的
   🔒 蓋掉。合併成一串，兩件事都要說得出來。
   ⚠ 🔒 是彩色 emoji 字形，不吃 `color` ⇒ 這串裡只有 ＊ 是紅的，正是要的效果。 */
.field-locked:has(> input[required])::before,
.field-locked:has(> select[required])::before,
.field-locked:has(> textarea[required])::before {
  content: '🔒 ＊' / '';
  font-size: var(--text-xs);
  margin-right: var(--space-1);
}

/* ===== 快訊商品 vs 機器人商品：三級差異警示 =====================================
   站長 2026-08-19：「快訊 NQ 機器人選 NQU6 要顯示有差別、黃色警示；GC 這種差很多的
   要出現紅色警告 —— **但還是能下單**」。⛔ 這組樣式只上色，擋單的邏輯一行都沒有。

   ⛔ 不可只靠顏色傳達（約 8% 男性有紅綠色覺辨識障礙，而黃/紅正好是那一組）：
      徽章一律自帶圖示（⚠／⛔）＋ 文字（有差別／不同商品），灰階列印也讀得出來。
      顏色是**加速辨識**的第三層，不是唯一那層。
   ⭐ match／none 不產生任何徽章 —— 天天亮的綠勾會訓練人不看這一欄，於是真正該紅
      的那一次被一起忽略。這是本功能唯一的失敗模式，比漏報更貴。
   ⚠ 放在檔尾是刻意的：本檔沒有主題切換機制（0 個 data-theme／data-skin），
      同名規則靠先後順序決勝（.delivery-status 就被覆寫過三次）⇒ 檔尾＝最高優先。 */
.symbol-cell { white-space: nowrap; }
.symbol-flag {
  display: inline-flex; align-items: center; gap: var(--space-1, 4px);
  margin-left: var(--space-2, 8px); padding: 2px 8px;
  border: 1px solid transparent; border-radius: 999px;
  font-size: var(--text-xs, 12px); font-weight: 800; line-height: 1.6;
  vertical-align: middle; cursor: help;
}
/* 黃：同一個商品，但寫法／到期月不同，或我們無法確認 —— 值得看一眼，不是錯誤。 */
.symbol-flag.warn {
  border-color: rgba(255, 198, 90, .38);
  color: var(--amber, #f2b84b);
  background: rgba(89, 60, 15, .34);
}
/* 紅的語意只有一個：**這是不同的商品**。⛔ 其他異常一律留在黃色，
   否則紅色天天出現就等於沒有紅色（symbol-mismatch.js 檔頭同一條原則）。 */
.symbol-flag.danger {
  border-color: rgba(255, 98, 121, .42);
  color: var(--red, #ff6279);
  background: rgba(87, 24, 43, .55);
}
@media (max-width: 760px) {
  .symbol-cell { white-space: normal; }
  .symbol-flag { margin-left: 0; margin-top: var(--space-1, 4px); }
}

/* ===== 分頁左上角標題（站長 2026-08-19：「點下去的按鈕文字 要跟左上角的標題一樣」）=====
   八個 panel 的第一個子元素統一成 .section-title。其中 #internal-browse 是 flex column
   （.workbench-stage 靠 flex 分高度，48px 的上外距會直接從舞台身上扣）、#settings 底下
   緊接著自己的 .settings-toolbar —— 這兩處用 .panel-head 把上外距收掉、下外距收緊。
   ⛔ 特異性必須是 (0,2,0)：.section-title 在本檔出現 40+ 次，後面四套佈景（:506 / :794 /
   :1147 / :1424）各自重設過 margin，(0,1,0) 的規則寫在這裡也贏不了它們。 */
.section-title.panel-head { margin-top: 0; margin-bottom: var(--space-3, 12px); }

/* ===== AI 輔助機器人（設定頁 AI ASSIST / 04·站長 2026-08-21）=========================
   ⚠ 放在檔尾是刻意的：本檔沒有主題切換機制（0 個 data-theme／data-skin），同名規則靠
      先後順序決勝 ⇒ 檔尾＝最高優先。與 .symbol-flag 那一段同一個理由。

   ⛔⛔ 圖示一律放在【淺色底】上，不是直接貼在深色卡片上。
      實測 public/assets/providers/ 底下 13 個檔：openai／openrouter／groq／moonshot／
      ollama／xai 六個是 fill="currentColor"，而 <img> 載入的 SVG 拿不到外面的 color
      （它是獨立文件，currentColor 退回自己的初值＝黑）⇒ 直接貼在深色卡片上會【整個看不見】。
      淺色磚同時也讓 anthropic(#D97757)／gemini／mistral 那些自帶顏色的正常顯示。
      ⛔ 不要改用 CSS filter 硬把它們反白 —— 那會把有顏色的那幾家一起弄壞。

   ⛔ 尺寸寫在這裡而不是靠 SVG 自己的屬性：provider-generic.svg（自訂那一項用的）是
      width/height="24" 不是 1em（⚠ 該目錄的 README 寫「全部都是 1em」，實測不是），
      不明寫尺寸的話「自訂」那顆會比別家大一圈。 */
/* ⛔⛔ 承重，⛔ 別刪：沒有這一條，這張卡裡【每一個 hidden 都是假的】。
   hidden 只有瀏覽器內建樣式表的 [hidden]{display:none} 在撐，而本檔有這幾條作者規則：
     · styles.css:132  label      { display: block }   ← 蓋掉四個 <label ... hidden>
     · styles.css:167  .field-help{ display: block }   ← 蓋掉 <p class="field-help" hidden>（型號說明）
     · button 那組     button     { display: flex  }   ← 蓋掉 <button hidden>（清除金鑰）
   **作者樣式表贏過瀏覽器內建樣式表 —— 這是串接【來源】的層級，與特異性無關。**
   ⇒ JS 那邊 element.hidden 全部是 true、每一個「它藏起來了嗎」的檢查都回答藏起來了，
      而畫面上型號下拉／型號手打／Effort／API 端點／清除金鑰【五格同時看得見】。
   ⇒ 站長 2026-08-21：「我供應商都選了 模型都用選單選了 還叫我輸入模型 網址?」

   2026-08-21 實測（chromium headless 載入真的 index.html + 本檔，量 computed style）：
     #aiAssistEndpointField  <label hidden>                        → display: block  ❌
     #aiAssistModelPickField <label hidden>                        → display: block  ❌
     #aiAssistModelNote      <p class="field-help" hidden>         → display: block  ❌
     #aiAssistClearKey       <button hidden>                       → display: flex   ❌
     #aiAssistTestResult     <p class="ai-assist-test-result" hidden> → display: none ✅ 負對照
     .system-status-panel    <div hidden>（本檔已明寫覆寫）        → display: none ✅ 負對照
   ⭐ 負對照才是這組證據的關鍵：有作者 display 規則的看得見、沒有的藏起來
      ⇒ 排除了「hidden 整體壞掉」這個解釋。
   ⚠ 用 !important 是刻意的：hidden 的語意就是「絕對不顯示」，而下一條 `.ai-assist-xxx`
     規則只要設了 display 就會是同樣的特異性（0,2,0）而且排在後面 ⇒ 又贏回去。
   同款坑本檔 :203-207 已經踩過一次（.broker-account-field[hidden]），
   ⛔ 那次只修了那一個 class、沒有變成習慣 —— 所以這裡再踩了一次。 */
.ai-assist-card [hidden] { display: none !important; }

.ai-assist-card .ai-assist-field { display: grid; gap: 8px; padding: 16px 0; }
/* 18px＝與這張卡裡真正的 <label> 對齊（styles.css:3024 全域 label 18px）。
   ⛔ 它是欄位標籤不是註腳：14px 會讓「供應商」比它下面的每一格都小一階半。 */
.ai-assist-card .ai-assist-label { color: var(--muted, #8b94b2); font-size: 18px; font-weight: 600; }
.ai-assist-providers { display: flex; flex-wrap: wrap; gap: 10px; }
.ai-assist-provider {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 14px 8px 8px;
  border: 1px solid rgba(139, 154, 211, .28); border-radius: 999px;
  cursor: pointer; font-size: 16px; font-weight: 600; line-height: 1.5;
}
.ai-assist-provider:hover { border-color: rgba(139, 154, 211, .55); }
/* ⭐ 選中狀態同時給【邊框顏色】與【底色】——⛔ 不可以只靠顏色傳達（約 8% 男性有色覺
   辨識障礙）。radio 本身也留在畫面上，它才是真正說得出「哪一個被選中」的那個元素。 */
.ai-assist-provider input { width: 16px; height: 16px; margin: 0; }
.ai-assist-provider-logo {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 8px;
  background: #f4f6fb;   /* ⛔ 承重：見上面「currentColor 退回黑色」那段 */
}
.ai-assist-provider-logo img { width: 16px; height: 16px; display: block; }
.ai-assist-card .field-help { margin-bottom: 4px; }
/* ⭐ 站長 2026-08-21 點名：「模型說明文字太小看不清楚 我不是有最低字體大小限制?」
   這是清單模組替每個型號留的那句誠實註記（例「這一版本身還沒上榜」），
   站長要在【選之前】讀它 ⇒ 它是主要資訊，與卡片開頭那段 .muted（18px）同級，
   ⛔ 不是欄位底下的註腳（.field-help 16px）。
   ⛔ 也不可以只靠繼承 .field-help：那條的字級由三條同名規則的【最後一條】決定
      （:167 12px → :1288 12px → :3044 16px ⇒ 現在是 16px），
      而 `.ai-assist-card .field-help` 特異性更高卻沒設 font-size
      ⇒ 只要有人往那條加一個 font-size，這段文字就會靜默縮小回去。 */
.ai-assist-model-note { font-size: 18px; line-height: 1.6; }
/* 金鑰欄位：清除鈕住在欄位【裡面】的右側。
   ⚠ 外層要 `display:block` ＋ 補回 `margin-top` —— `label input` 那條規則靠的是
      input 直接是 label 的子元素，包了一層之後它就不生效了。 */
.key-field { position: relative; display: block; margin-top: 8px; }
.key-field input { margin-top: 0; padding-right: 52px; }
.key-field-clear {
  position: absolute; top: 50%; right: 8px; transform: translateY(-50%);
  width: 34px; min-width: 34px; height: 34px; min-height: 34px; margin: 0; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid transparent; border-radius: 8px; background: transparent;
  color: #6f8da8; font-size: 16px; line-height: 1; cursor: pointer;
}
.key-field-clear:hover { color: var(--red, #ff6279); border-color: rgba(255, 98, 121, .35); background: rgba(255, 98, 121, .08); }
.key-field-clear:focus-visible { outline: 2px solid var(--cyan, #19ddff); outline-offset: 1px; }
.key-field-clear[hidden] { display: none; }

.ai-assist-key-state { margin: 4px 0 0; font-size: 16px; line-height: 1.5; color: var(--muted, #8b94b2); }
.ai-assist-key-state[hidden] { display: none; }
.ai-assist-key-state.is-set { color: var(--green, #4ad991); }
.ai-assist-key-state.is-blocked { color: var(--red, #ff6279); font-weight: 600; }
.ai-assist-readiness { margin: 16px 0 0; font-size: 16px; line-height: 1.5; color: var(--muted, #8b94b2); }
.ai-assist-readiness.is-incomplete { color: var(--amber, #f2b84b); }
.ai-assist-readiness.is-ready { color: var(--green, #4ad991); }
.ai-assist-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 16px; }
/* 探測結果可能夾著對方回傳的原文（我們只留 300 字）——⛔ 要能換行也要能捲，
   否則一條長 JSON 會把整張卡撐到橫向捲動。 */
.ai-assist-test-result {
  margin: 12px 0 0; padding: 12px 14px;
  border: 1px solid rgba(139, 154, 211, .28); border-radius: 12px;
  font-size: 16px; line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere;
  max-height: 220px; overflow-y: auto;
}
.ai-assist-test-result.is-ok { border-color: rgba(74, 217, 145, .45); color: var(--green, #4ad991); }
.ai-assist-test-result.is-bad { border-color: rgba(255, 98, 121, .45); color: var(--red, #ff6279); }
/* ⛔ 這一段【不可以】改成 <details> 收起來：test/ui-five-improvements.test.js:32 釘死
   整份 index.html 只能有一個 <details>（「進階區不得嵌套第二層」），而且那條規則的用意
   本來就對 —— 站長要的「詳細說明」收起來就等於沒寫。⇒ 永遠展開的 <section>。 */
.ai-assist-explainer { margin-top: 24px; border-top: 1px solid rgba(139, 154, 211, .18); padding-top: 16px; }
.ai-assist-explainer > h3 { margin: 0 0 8px; font-size: 20px; font-weight: 650; line-height: 1.2; }
.ai-assist-explainer-body { font-size: 16px; line-height: 1.6; color: var(--muted, #8b94b2); }
.ai-assist-explainer-body h4 { margin: 24px 0 8px; font-size: 18px; font-weight: 700; letter-spacing: .02em; line-height: 1.2; }
.ai-assist-explainer-body p { margin: 0 0 8px; }
.ai-assist-explainer-body ul { margin: 0 0 8px; padding-left: 20px; }
.ai-assist-explainer-body li { margin-bottom: 6px; }
@media (max-width: 760px) {
  .ai-assist-providers { gap: 8px; }
  .ai-assist-provider { padding: 6px 12px 6px 6px; font-size: 14px; }
  .ai-assist-actions > button { flex: 1 1 100%; }
}

/* QW-DESIGN-SYSTEM:BEGIN v0.5.0 — 儀表艙。⛔ 這對哨兵之間是自動替換區，手改會被蓋掉。 */
/* ═══════════════════════════════════════════════════════════════════════════
   0.5.0 儀表艙（Flight Deck）設計系統 — 單一權威層
   來源：design canvas「QuantWing 儀表艙視覺方案」四張板（刻度／主控台／設定／色彩）
   ⛔ 這一段刻意放在檔尾：本檔有五套主題疊加（:root@1 / @401 command-deck /
      @734 orbital / @1517 deck / @1947 aurora），同 specificity 時**最後一條贏**。
      新的元件規則放這裡才會生效 —— 改前面幾套是白改（實例：.bot-head h3 全檔
      宣告 6 次，只有第 6 條 @3071 在生效，@2219 那條 30px 被靜默廢掉）。
   ⭐ 本層是**收斂**不是第六套主題：每加一條就刪掉被它取代的舊宣告。
      ⛔ 下一個人要改視覺請改這裡，不要再往後面疊。

   ⚠ 施工當下實測推翻了 design 的幾個現況數字，本層照**實測**做，不照 design 的數字：
      · 指標卡圓角 18px（design 說 22）／高 160px（說 184）／內距 24px（說 64）
      · 指標卡標籤字重 700 比數值的 620 還**重** —— 標籤在字重上贏了數值
        （⚠ 更正：我一度回報標籤是 18px，那是量錯的——`.metric span` 的第一個
          是圖示 span 不是標籤。標籤字級本來就是 14px，**design 這個數字是對的**，
          真正要動的只有字重與字距。）
      · 徽章早就是中文，五態實測 86/86/86/102/102 = 1.19 倍（design 說 2.6 倍，
        那是它的量測頁沒跑 app.js、餵自己編的英文字串量出來的）
      · 真正會爆版的是第六態 .reconciling（258px），design 沒發現
      · design 的 56px／44px 都會讓金額被**靜默裁切**（見下方指標卡與機器人卡兩段）
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  /* ── 型階：用途命名的七檔（design 01 刻度）───────────────────────────
     ⛔ 跟 :root@1 那組 --fs-sm…4xl（數值命名）不同用途：那組是尺寸階，
        這組是**刻度**——每一檔綁定一個用途，用途決定字級，不是反過來。
     ⭐ 核心比值：卡內數值 ÷ 標籤 = 32/14 ≈ 2.3 倍（改前 18/16 = 1.125 倍）。
        「字大」來自這個落差，⛔ 不是每個字加 2px。 */
  --fs-label: 14px;        /* 標籤（下限·站長 2026-08-22 定） */
  --fs-body: 18px;         /* 內文、說明、輸入值 */
  --fs-card-title: 22px;   /* 卡片標題 h3 */
  --fs-section: 28px;      /* 區段標題 h2 */
  --fs-stat: 32px;         /* 卡內數值 */
  --fs-page: 40px;         /* 頁標題 h1 */
  --fs-metric: 56px;       /* 指標卡數值 */

  /* ── 字重四檔（改前全檔 18 種：300…900）─────────────────────────── */
  --fw-body: 400;
  --fw-label: 600;
  --fw-value: 650;
  --fw-badge: 800;

  /* ── 色彩：1 品牌 ＋ 4 語意 ＋ 6 階中性（design 04）─────────────────
     ⭐ 全部映射到既有 token，⛔ 不新發明顏色：--accent 就是既有的 --violet
        (#9d7cff)，只是從「第二張卡的裝飾色」升格成唯一品牌色。
     ⛔ 顏色只有一個工作：告訴我這件事要不要緊。
        顏色一旦有裝飾用途，就再也不能用來報警。 */
  /* ⚠⚠ 定義 --accent 這個動作本身有一處**靜默副作用**：檔案裡已經有人在用
     `var(--accent, #4c8dff)`（AI 輔助的供應商選中框），它今天是靠 fallback 的**藍**活著。
     一旦 --accent 有了定義，那個 fallback 就不再生效 ⇒ 邊框變紫、底色卻還是寫死的藍。
     ⇒ 本層下面有一條把那格的底色一起接上 --accent，⛔ 兩條要一起在，缺一個就配色打架。 */
  --accent: var(--violet, #9d7cff);
  --ok: var(--green, #72f2c3);       /* 在跑・獲利 */
  --warn: var(--amber, #ffd37a);     /* 待確認・演練 */
  --danger: var(--red, #ff708e);     /* 錯誤・虧損・鎖定 */
  --info: var(--cyan, #78e5ff);      /* 連線中・唯讀 */

  --n0: var(--bg, #050611);          /* 底 */
  --n1: var(--surface, rgba(13, 17, 39, .93)); /* 卡 */
  --n2: rgba(24, 30, 62, .92);       /* 浮層（新增：收斂散在檔裡的近似深藍） */
  --n3: rgba(163, 174, 255, .2);     /* 分隔（= 既有 --border） */
  --n4: var(--muted, #8e96bb);       /* 次要字 */
  --n5: var(--text, #f3f4ff);        /* 主字 */

  /* ── 圓角四檔封頂（design 03）───────────────────────────────────
     ⚠ 只套用在 design 點名的元件上。全檔 241 條 border-radius 裡大宗是
        2px(33)／3px(37)／4px(27)，那些是分隔線、光點、進度條這類微裝飾，
        ⛔ 強行拉到 8px 會讓它們變形 —— 收斂的是**卡片級**的值（26/22/18/15/13/11）。 */
  --radius-pill: 999px;
}

/* ── 型階套用 ────────────────────────────────────────────────────── */

/* 頁標題。⛔ 這是左上角那顆「總覽」，不是區段標題。 */
.section-title h1 {
  font-size: var(--fs-page);
  font-weight: var(--fw-value);
  line-height: 1.1;
  letter-spacing: -.02em;
}

/* 區段標題（「交易機器人」）。
   ⭐ 改前實測 20px/610，比它管的 .bot-head h3 (24px) 還**小 4px** ＝ 階層反轉。
      這一條把它重新蓋回 h3 之上。 */
.section-title h2 {
  font-size: var(--fs-section);
  font-weight: var(--fw-value);
  line-height: 1.15;
  letter-spacing: -.015em;
}

.section-title p {
  font-size: var(--fs-body);
  font-weight: var(--fw-body);
  color: var(--n4);
}

/* 卡片標題。⚠ 這是**降**（24→22）不是升 —— 讓區段標題 28px 拿回層級。 */
.bot-head h3,
.bot-name {
  font-size: var(--fs-card-title);
  font-weight: var(--fw-value);
  letter-spacing: -.01em;
}

/* 卡片副標（TXFH6・元大模擬）。
   ⭐ 這一條是**對比修正**不是造型：改前 rgb(127,136,170) 坐在卡片實際底色
      rgb(29,38,74) 上，用截圖取樣量到 **4.20:1 < AA 4.5**。
   ⚠ design 的對比表把這列標成「5.89 過」，但它自己註明背景是拿**頁面底色**算的
      ——卡片比頁面亮 ⇒ 真實值只會更低。它預言了「5.89 這列可能落在 AA 邊緣，
      需要在真的 app 裡再量一次」，這次量了，確實不過。 */
.bot-head p,
.bot-meta {
  color: var(--n4);
}

.form-card h2,
.subsection-heading h3 {
  font-size: var(--fs-card-title);
  font-weight: var(--fw-value);
}

/* ── 指標卡（金融儀表板密度）────────────────────────────────────────
   2026-09-04：完整保留幣別、正負號、千分位與小數，不用 K／M 縮寫犧牲精度。
   四欄桌面依視窗寬度在 27–30px 間微調；切成 2×2 後有足夠欄寬，回到 30px。 */
.metric .metric-reading > strong,
.metric > strong {
  /* ⛔⛔ 這裡**不是** design 說的 56px，而且是被實測擋下來的，不是我保守：
     `.metric` 有 overflow:hidden、`.metric-reading` 是 flex nowrap
     ⇒ 放不下不是換行，是**靜默裁切**（沒有省略號、沒有 title、沒有任何徵兆）。
     真實金額格式是 app.js:1804 `money()` = `-$` + toFixed(2)，**無千分位**
     ⇒ 最長會出現 `-$1234567.89`（12 字元）。
     實測（真 Chromium，六種視窗寬度，electron.js:795 預設開 1520、允許縮到 1100）：
       56px → 1920/1600/1520/1440/1280/1100 **全部裁切**
       48px → 1440/1280/1100 裁切
       40px → 1280/1100 裁切
       36px → 只有 1100 裁切（＝**現況**）
     ⛔ 56px 在 1100px 下把 `-$123456.78` 與 `-$1234567.89` 都切成「-$12345」——
        兩個相差 10 倍的損益長得一模一樣，而且都是合法數字。這是交易軟體，不能這樣。
     ⇒ 取「任何寬度都不比現況更容易裁切」為硬條件：≥1440px 給 40px，其餘退回 36px。
     ⭐ 落差改由**標籤**扛：標籤 18px→14px ⇒ 比值從 36/18 = 2.0 拉到 40/14 = 2.86。
        「字大」的來源是落差，⛔ 不是把數值撐到會切掉自己。
     ⚠ 記債：**現況 36px 在 1100px 也會裁**（need 219 / have 189）—— 那是既有問題，
        本輪沒有讓它更糟，但也沒有解決它。真正的根治是讓它放不下時縮小或換行。 */
  font-size: clamp(27px, 1.85vw, 30px);
  font-weight: var(--fw-value);
  line-height: 1;
  letter-spacing: -.03em;
  /* 數字等寬 —— 每 2 秒就地改寫的數值不等寬會左右跳動 */
  font-variant-numeric: tabular-nums;
}

@media (max-width: 1439px) {
  .metric .metric-reading > strong,
  .metric > strong { font-size: 30px; }
  /* ⭐⭐ 這一條才是指標卡那邊真正的根治。
     四張卡擠在一排時，1440px 以下每格內容區只剩 234px（1100px 更只有 189px），
     而最長金額 `-$1,234,567.89` 在 36px 下需要 236px ⇒ **必切**。
     ⛔ 再往下調字級沒有用：1100px 下連 32px 都要 210px，還是超過 189px。
        ⇒ 這不是字級問題，是**一排塞四張卡**在窄視窗下本來就不成立。
     ⇒ 窄視窗改 2×2。卡片寬度直接翻倍（1280px 下 536px、1100px 下 446px）
        ⇒ 完整金額綽綽有餘，字級也不必再退。
     ⚠ 代價是指標區從一列變兩列、機器人清單被往下推 —— 這是刻意換的：
        **寧可多捲一點，也不要看到一個被切掉、但仍然合法的金額。** */
  .metric-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ⚠ text-transform: uppercase 對中文是**空操作** —— 繁中介面下「持倉」不會變成
   任何東西。design 的「標籤全大寫＋字距」在中文介面有一半不會發生。
   ⇒ 中文的標籤區隔靠：字級落差 ＋ 字重差 ＋ 顏色差 ＋ **字距**（中文有效）。
      uppercase 保留是為了英文介面（i18n 有 en），對中文無害。 */
.metric .metric-label > span {
  font-size: var(--fs-label);
  font-weight: var(--fw-label);
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--n4);
}

/* 單位（個／口／點）降一階，⛔ 不跟著數值放大 */
.metric .metric-reading > small,
.metric .metric-reading > .metric-unit {
  font-size: var(--fs-label);
  font-weight: var(--fw-label);
  color: var(--n4);
}

/* ⭐ 四張卡共用同一個色調 ＝ design 04 的核心主張落地。
   改前 tone-cyan/violet/blue/green 讓「綠色」只代表「排第四張」。
   ⛔ 不刪 class、不刪 --tone 機制：--tone 是整張卡的視覺骨架（16 處 var(--tone)：
      邊框、背景漸層、頂部光條、beacon、數值色），清空會讓 color-mix() 整條失效。
   ⇒ 只讓四張卡落回同一個值。 */
.metric.telemetry-card,
.metric.telemetry-card.tone-cyan,
.metric.telemetry-card.tone-violet,
.metric.telemetry-card.tone-blue,
.metric.telemetry-card.tone-green {
  --tone: var(--accent);
  border-radius: var(--radius-lg, 16px);
}

/* ── 機器人卡：四格分兩層 ────────────────────────────────────────
   ⛔⛔ 用 `[data-stat="…"]` **不是** `:nth-child`：app.js:4016 的註解明說
      「改欄位順序不影響（updateBotCardNumbers），但刪掉 data-stat 就等於讓數字停更」
      ⇒ 這個產品的定位錨是 data-stat，位置是可變的。
      我第一版用 nth-child ⇒ 任何人調順序，樣式就會套到錯的欄位上，而且畫面不會報錯。

   ⭐ 分層依據被紅隊改過一次，這裡記錄理由：
      design 5.2 主張「未實現／持倉放大，最新價壓小」。⛔ 這個分法把因果倒過來了 ——
      **最新價格是唯一每 2 秒真的在跳的欄位**（app.js:328 write('[data-stat="price"]')），
      未實現損益是**由它算出來的衍生值**；而「持倉」是「多 3／空倉」這種幾分鐘才變一次
      的狀態值。用大字顯示不動的「空倉」、用灰色小字顯示一直在跳的價格，
      掃視時眼睛會被不動的那個吸走。
   ⇒ 改成：**會跳的兩個（未實現／最新價）在上層，設定與狀態（持倉／指定商品）在下層。**

   ⚠ 字級 20px 也是被實測擋下來的，不是我保守：`.bot-stats div` 有 overflow:hidden，
      2 欄 grid 每格內容區 1920px 下只有 **97px**。實測 `-$123456.78`：
        32px → 需 196px（1920 起全部視窗裁切）／24px → 需 148px／20px → 需 123px
        18px（**現況**）→ 需 111px ⇒ **現況本來就在裁**（1920/1600/1280）。
      20px 的裁切範圍與現況 18px **完全相同** ⇒ 不比現況更糟，而落差從 1.125 提到 1.43。
   ⚠ 記債：這一格放不下六位數金額是**既有問題**（overflow:hidden 靜默截斷）。
      真正的根治是讓「未實現」跨兩欄拿到 206px，那是版面改動，本輪沒做。 */
/* ⭐⭐ 根治「金額被靜默切掉」：讓**未實現**跨滿兩欄。
   改前是 2×2，每格內容區在 1920px 下只有 **97px** —— 那裡塞不下任何六位數金額，
   連現況 18px 都在切。⛔ 這不是字級問題，是**格子太窄**。
   跨欄後有 **206px**，完整的 `-$1,234,567.89` 放得下 ⇒ 不必犧牲精度也不必縮字。
   版面變成三列：
     [ 未實現（跨兩欄·大） ]
     [ 最新價 ] [ 持倉 ]
     [ 指定商品（跨兩欄·小） ]
   ⚠ 卡片會長高約一列，⛔ 這是刻意換來的 —— 站長要「字大」，而字要大就得有地方放。 */
.bot-stats > div:has([data-stat="pnl"]),
.bot-stats > div:has([data-stat="price"]) {
  grid-column: 1 / -1;
}

/* ⚠ 為什麼**最新價格**也要跨欄：第一版只讓未實現跨欄，實測（1920px 三欄卡片）
   剩下那兩格各只有 89px，而 `23418.00` 在 20px 下需要 96px ⇒ **最新價被切成
   「23418.…」**。它是每 2 秒在跳的即時價格，切掉它跟切掉損益一樣不能接受。
   ⇒ 版面定案（**兩個會跳的數字各佔一整列，兩個幾乎不動的併一列**）：
     [ 未實現（跨欄·28px） ]
     [ 最新價（跨欄·20px） ]
     [ 持倉 ]  [ 指定商品 ]
   ⭐ 持倉是「多 3／空倉」、指定商品是設定值 —— 它們短、又幾乎不變，併一列剛好。 */

.bot-stats [data-stat="pnl"] {
  font-size: 28px;
  font-weight: var(--fw-value);
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
}

.bot-stats [data-stat="price"] {
  font-size: 20px;
  font-weight: var(--fw-value);
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
}

.bot-stats [data-stat="side"],
.bot-stats [data-stat="symbol"] {
  font-size: 16px;
  font-weight: var(--fw-label);
  line-height: 1.2;
  color: var(--n4);
}

/* ⛔⛔ 截斷必須**看得見**。
   改前這些格子是 `overflow:hidden` 而已 ⇒ 放不下就直接切掉，沒有省略號、沒有任何徵兆
   ⇒ `-$1234567.89` 被切成「-$12345」，而那**仍然是一個合法金額**，使用者不會知道。
   ⭐ 主流作法（Carbon Design System）：截斷一律配省略號**＋ hover tooltip 顯示完整值**。
      title 由 app.js 的 updateBotCardNumbers 跟著每 2 秒的更新一起寫（⛔ 不能只寫 textContent，
      否則 tooltip 會停在第一次的值 —— 那比沒有 tooltip 更糟）。
   ⇒ 這一條讓「切掉」變成「看得出來被切了，而且移上去看得到完整的」。 */
.bot-stats strong,
.metric .metric-reading > strong {
  display: block;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bot-stats span {
  font-size: var(--fs-label);
  font-weight: var(--fw-label);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--n4);
}

.bot-stats > div {
  border-radius: var(--radius-md, 12px);
}

/* 卡片圓角統一（改前實測 26px；同一張卡上有 26/16/15/13/12/999 六種） */
.bot-card,
.card,
.form-card,
.module-card {
  border-radius: var(--radius-lg, 16px);
}

/* ⭐ 狀態靠左緣色條表達 ⇒ 卡片列表遠看就分得出誰在跑，不必逐張讀徽章。
   ⛔ **不能用偽元素**：這張卡的兩個都被佔走了 ——
      `::before` 是卡片頂部那條漸層光條（643/922/1436，跟指標卡同一個視覺語彙），
      `::after` 是右上角的模組編號（`data-module="TRADING UNIT / 01"`，1719 開啟）。
      我第一版用了 ::before ⇒ 把頂部光條**靜默換掉**了，截圖才看出來。
   ⇒ 改用 border-left。⚠ 我原本以為 border 會撐大版面 —— 這份檔第 61 行有全域
      `* { box-sizing: border-box }` ⇒ 外部尺寸不變，只吃掉 2px 內部空間（原本就有
      1px 透明邊框），肉眼看不出來。 */
.bot-card {
  border-left: 3px solid var(--n3);
}

.bot-card:has(.badge.running) { border-left-color: var(--ok); }
.bot-card:has(.badge.connecting) { border-left-color: var(--info); }
.bot-card:has(.badge.awaiting_verification) { border-left-color: var(--warn); }
.bot-card:has(.badge.reconciling) { border-left-color: var(--warn); }
.bot-card:has(.badge.error) { border-left-color: var(--danger); }

/* ── 狀態徽章 ────────────────────────────────────────────────────
   實測（用 app.js:botStatusLabel 的**真值**，不是自己編的英文）：
     運行中 86 ／ 已停止 86 ／ 連線中 86 ／ 等待驗證 102 ／ 連線錯誤 102 ＝ 1.19 倍
   ⇒ design 說的「2.6 倍、改成中文三字」**已經是現況**，不需要改文案。
   真正的兩個問題：
   ① connecting／awaiting_verification／error 三態的 border-color 是**透明**的
      ⇒ 同一組元件出現兩種長相（running／stopped 看得見邊框）。
   ② ⭐ 第六態 .reconciling 是「訂單結果待確認・不會自動重送」14 個字 ＝ **258px**，
      而 .bot-head 只用 padding-right 讓出 142px、.badge 又是 absolute right:0
      ⇒ **溢出 116px 直接蓋住機器人名稱**，偏偏那是最需要看清楚是哪台機器人的時刻。 */
.badge {
  min-width: 102px;
  justify-content: center;
  border-radius: var(--radius-pill);
  font-size: var(--fs-label);
  font-weight: var(--fw-badge);
}

/* ⚠⚠ 每一態都必須**自己帶 color**，而且要寫成 #rrggbb 字面 —— 這不是我囉嗦，是
   test/runtime-ux-recovery.test.js:238 的 `lastDeclaration()` 釘死的：它只讀
   **最後一條**同名規則（理由寫在它上面的註解：「.badge.stopped 有三處宣告，只有最後
   一條在畫面上，所以 grep 整個檔案的斷言會在沒人看得到的規則上開心通過」——
   跟本層存在的理由是同一件事）。
   ⇒ 我這一層一旦成為最後一條，就繼承了「說清楚顏色」的責任。只寫 border-color
     會讓那條測試判定「the winning rule sets no color」而紅（我第一版就是這樣被擋下的）。
   ⛔ 不能改用 var()：那條測試的 rgb() 只認 #rrggbb，var() 會讓它 throw。
   ⛔ 這五個值是**實測現況**（Chromium 計算樣式），不是重新調色 —— 顏色一個都沒改，
     只是把它們搬到 winning rule 上。
   ⭐ 那條測試守的是「已停止 ≠ 故障」：0.3.40 曾把 .stopped 塗成 #ff9daf，
     跟 .error 一模一樣（CIEDE2000 = 0.00）⇒ 顏色在那裡不帶任何資訊。 */
.badge.running { color: #83f7c3; border-color: color-mix(in srgb, var(--ok) 46%, transparent); }
.badge.stopped { color: #aeb8e8; border-color: color-mix(in srgb, var(--n4) 46%, transparent); }
.badge.connecting { color: #77d9ff; border-color: color-mix(in srgb, var(--info) 46%, transparent); }
.badge.awaiting_verification { color: #ffd77b; border-color: color-mix(in srgb, var(--warn) 46%, transparent); }
.badge.error { color: #ff9daf; border-color: color-mix(in srgb, var(--danger) 46%, transparent); }

/* ⭐⭐ 徽章**全部**改回參與排版，⛔ 不再絕對定位。
   改前：`.bot-head .badge` 是 `position:absolute; right:0`，靠 `.bot-head` 的
   `padding-right: 142px` 讓位 —— 那是一個**寫死的寬度**，只要徽章比它寬就直接蓋住標題。
   ⚠ 我第一版只把讓位從 142px 收到 118px（照中文徽章實測的 102px 量的），
     那在繁中介面剛好，但 **i18n 有 en locale** 而 `.badge` 帶 `text-transform: uppercase`：
       RUNNING / STOPPED 102px ✅
       CONNECTING 122.5px ⇒ 蓋掉標題 4.5px
       CONNECTION ERROR 172.6px ⇒ 蓋掉 54.6px
       AWAITING VERIFICATION 214.3px ⇒ **蓋掉 96.3px**
     ⛔ 被蓋掉的正好是「連線中／連線錯誤／等待驗證」——**你最需要知道是哪一台出事的三個時刻**。
   ⇒ 治本：`.bot-head` 本來就是 `display:flex; justify-content: space-between`，
     徽章當 flex item 自然靠右，而 `.bot-identity` 有 `min-width: 0` 可以收縮
     ⇒ 長徽章會把標題**壓窄**（標題本來就有 line-clamp 2 行），⛔ 而不是蓋住它。
     這樣任何語言、任何長度都不會再蓋到標題，也不需要維護那個寫死的數字。 */
.bot-head {
  /* ⚠ nowrap 是刻意的：先前用 wrap，結果**標題長的卡片徽章掉到下一行、短的留在右上**
     ⇒ 同一排卡片徽章高低不一（截圖看得很清楚）。徽章的位置應該由版面決定，
     不該由機器人名字有多長決定。
     ⇒ nowrap ＋ 標題可收縮（.bot-identity 本來就有 min-width: 0，且 .bot-name 有
       line-clamp 2 行）⇒ 徽章永遠在右上、標題最多被壓窄，**但不會被蓋住**。 */
  flex-wrap: nowrap;
  padding-right: 0;
  gap: 12px;
}

.bot-head .badge {
  position: static;
  flex: 0 0 auto;
  align-self: flex-start;
}

/* 卡面「全啟」開關（站長 2026-08-28：放進機器人卡，不要長句「關閉一鍵啟動功能」）。
   跟右上狀態徽章同一排；金色＝跟一鍵全啟那顆同一血統（既有 --amber），關掉就退成中性。
   ⚠ 它是按鈕，標題列拖曳已經排除 button，按它不會被當成拖卡片。 */
.bot-head-flags {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 8px;
  flex: 0 0 auto;
}
.bot-include-start-all {
  min-height: 44px;
  min-width: 56px;
  padding: 0 12px;
  border: 1px solid var(--n3);
  border-radius: var(--radius-pill);
  background: var(--n2);
  color: var(--n4);
  font-size: var(--fs-label);
  font-weight: var(--fw-badge);
  letter-spacing: .08em;
  cursor: pointer;
}
.bot-include-start-all.is-on {
  border-color: color-mix(in srgb, var(--amber, #f2b84b) 55%, transparent);
  color: #f2c75c;
  background: rgba(242, 184, 75, .12);
}
.bot-include-start-all:hover {
  filter: brightness(1.08);
}
.bot-head:has(.badge.reconciling) .bot-head-flags {
  flex: 1 1 100%;
  flex-wrap: wrap;
  justify-content: flex-end;
}

/* 只有第六態例外：它是一整句話（258–422px），nowrap 會把標題壓到幾乎沒有
   ⇒ 單獨讓這張卡換行，徽章自己佔滿一行。 */
.bot-head:has(.badge.reconciling) {
  flex-wrap: wrap;
}

/* 第六態 `reconciling` 是「訂單結果待確認・不會自動重送」13 個字，實測 258px
   （視窗越寬越長，量到 290–628px）⇒ 讓它自己佔滿一行，⛔ 不跟標題擠。
   ⛔ 不用 max-width + 省略號：那會把「不會自動重送」這句**安全資訊**截掉。
   ⚠ 這也是為什麼「五態等寬」這個目標在這個產品上本來就不成立 —— 它有第六態。 */
.bot-head .badge.reconciling {
  flex: 1 1 100%;
  min-width: 0;
  margin-top: 12px;
}

/* ── 按鈕 ───────────────────────────────────────────────────────
   ⛔ 焦點環（2px cyan ring ＋ 3px offset）design 自己說「現況本來就做對了」⇒ 不動。
   ⚠ 裸 <button> 實測 border-radius 竟是 0px（被後層規則重設），只有 .primary/
      .secondary/.danger 是 12px ⇒ 沒掛修飾類的按鈕是直角，跟旁邊對不齊。 */
button,
.button {
  border-radius: var(--radius-md, 12px);
  font-weight: var(--fw-value);
}

button:disabled,
.button:disabled,
button[disabled] {
  /* ⛔ 不用整顆 opacity 調降 —— 那會讓底下的卡片紋理透出來 */
  opacity: 1;
  color: var(--n4);
  background: var(--n2);
  border-color: var(--n3);
  box-shadow: none;
  cursor: not-allowed;
}

/* ── 表單 ───────────────────────────────────────────────────────
   改前實測：label 18px/w650、input 也 18px/w650 ⇒ **完全同級**，
   掃視表單時每一行都要重讀才知道哪個是欄位名。
   改後：標籤 14/600 灰、值 18/400 白 ⇒ 眼睛只會停在值上。 */
label {
  font-size: var(--fs-label);
  font-weight: var(--fw-label);
  letter-spacing: .08em;
  color: var(--n4);
}

input,
select,
textarea {
  font-size: var(--fs-body);
  font-weight: var(--fw-body);
  color: var(--n5);
  border-radius: var(--radius-md, 12px);
}

/* ── 網址／指令的唯讀顯示格 ──────────────────────────────────────
   ⛔⛔ design 說的「Webhook 條」**認錯選擇器了**：`.webhook` 這個 class 在
      index.html 與 app.js 裡**一個都沒有**（`querySelectorAll('.webhook').length === 0`），
      styles.css 卻為它留了 6 條宣告 —— 全是鬼。design 量到的 15px 值是對的，
      但那個值屬於 `.copy-field code`（它們寫在同一條三合一宣告裡）。
   ⚠ 這個坑我自己也踩了兩次：第一版把規則寫給 `.webhook`（＝空操作），
      而且量測夾具**自己造了一個 .webhook 元素** ⇒ 量到的是我編的東西。
   ⚠ 連帶事故：反熵刪除因為比對到 `.webhook` + border-radius，把那條**三合一**
      宣告的圓角刪掉了 ⇒ `.copy-field code` 與 `.connection-line` 一起失去圓角。
      下面這兩條把它補回來，⛔ 而且是補成 design 要的刻度而不是原本的 15px。
   ⇒ `.copy-field code` 實質是唯讀輸入框（API 網址／驗證方式／Webhook 網址共用）⇒ 8px。 */
.copy-field code {
  border-radius: var(--radius-sm, 8px);
}

/* 機器人卡上的「瀏覽器連線」橫條 —— 卡內狀態列 ⇒ 12px。 */
.connection-line {
  border-radius: var(--radius-md, 12px);
}

/* 收斂剩下兩個不在刻度上的卡片級圓角（17px / 13px）。 */
.receiver-status {
  border-radius: var(--radius-lg, 16px);
}

.notice {
  border-radius: var(--radius-md, 12px);
}

/* ── 圓角長尾收斂 ───────────────────────────────────────────────
   ⭐ 這一組不是掃 CSS 檔挑出來的，是**對真 DOM 普查**每個可見元素的 computed
      borderRadius 之後，逐一往上找容器確認身分才決定歸哪一檔的
      （掃檔案會掃到一堆從不渲染的鬼規則 —— 這份檔 241 條宣告裡有大量是死的）。
   ⛔ 刻意**不碰** 2px / 4px / 5px / 6px 那些：它們是分隔線、視窗控制鈕、
      小標籤這類微裝飾，design 的退場清單也沒點名，強行拉到 8px 只會讓它們變形。 */
.entitlement-grid > span { border-radius: var(--radius-md, 12px); }   /* 15px · 會員資訊小格 */
/* ⚠ selector 要寫 .metric-head > .metric-icon：贏家在 styles.css:1144 是
   (0,2,0)，裸 .metric-icon (0,1,0) 排在後面也贏不了（第一版就是這樣沒生效）。 */
.metric-head > .metric-icon { border-radius: var(--radius-md, 12px); }  /* 11px · 指標卡圖示 36×40 */
.alert-fields > div { border-radius: var(--radius-sm, 8px); }          /* 9px  · 欄位小格 */
.account-list-empty { border-radius: var(--radius-lg, 16px); }         /* 18px · 空狀態框 */
td.empty { border-radius: var(--radius-lg, 16px); }                    /* 18px · 表格空列 */
#toast { border-radius: var(--radius-md, 12px); }                      /* 18px · 浮層通知 ⇒ 對話框那一檔 */
pre.alert-shape { border-radius: var(--radius-md, 12px); }             /* 11px · 格式圖 */
.json-template { border-radius: var(--radius-lg, 16px); }              /* 20px · 範例區塊 */
.table-wrap { border-radius: var(--radius-lg, 16px); }                 /* 23px · design 明說表格外框 16 */

/* ── API 金鑰（design 03）────────────────────────────────────────
   ⚠ 2026-08-22 上一輪剛把狀態寫進 placeholder、清除鈕移進欄位內 ⇒ ⛔ 不推翻那個改動。
   ⛔ 遮罩只進 placeholder，**絕不進 value**（進了 value 按儲存會把圓點當新金鑰送出）。
   本層只補 design 要的兩件事：等寬字（遮罩點與尾碼對得齊、位數數得出來）＋ 危險區。 */
#apiKeyMasked,
.api-key-value,
.key-mask,
input[data-key-field] {
  /* 改前用介面字體 ⇒ 遮罩點與尾碼對不齊，也數不出位數。等寬字兩件事都解決。
     ⚠ #apiKeyMasked 是 readonly 顯示欄（app.js:6856 寫入 key.masked），
       ⛔ 不是使用者輸入的欄位 —— 這裡的遮罩在 value 是安全的，因為它送不出去。 */
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  letter-spacing: .04em;
}

/* ⛔ 這裡原本有一整組 `.qw-danger-zone`（危險區外框＋外框按鈕）。
   站長 2026-08-22 要求「API Key 寫進欄位裡面，用按鈕來確認和刪除」
   ⇒ 撤銷鍵改用跟 AI 金鑰同一套 `.key-field` 的欄位內 ✕，危險區整組移除（HTML 也拿掉了）。
   ⚠ 誤按的阻力沒有變少：它來自 app.js:7080 的 brandConfirm（danger 二次確認），
     ⛔ 不是來自按鈕擺得遠。 */

/* ⚠ 這一條是上面 `--accent` 那段註解說的配套，⛔ 不可單獨刪：
   `.ai-assist-provider.is-selected` 原本寫 `var(--accent, #4c8dff)` ＋ 寫死的藍底，
   在 --accent 沒有定義的年代靠 fallback 兩邊都是藍、剛好一致。
   本層一定義 --accent，邊框就變紫而底色還是藍 ⇒ 把底色也接上同一個來源。
   ⛔ radio 本身與邊框都要留著：這格不可以只靠顏色傳達選中
      （理由沿用本檔原有註解「⭐ 選中狀態同時給【邊框顏色】與【底色】」那一段，
        ⛔ 不是我新起的規定）。 */
.ai-assist-provider.is-selected {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}

/* ── 窄視窗 ─────────────────────────────────────────────────────
   ⛔ 這一段必須有：本層在檔尾，同 specificity 會蓋掉前面 @media 內的規則。
      不重新宣告的話，窄視窗的調整會被本層的桌面值蓋掉。 */
@media (max-width: 1050px) {
  .metric .metric-reading > strong,
  .metric > strong { font-size: 30px; }
}

@media (max-width: 760px) {
  /* 手機兩欄每格僅約 120px，完整金額必然被切；單欄保留精確數值與可讀點按區。 */
  .metric-grid { grid-template-columns: minmax(0, 1fr); }
  .bot-head { padding-right: 118px; }
  .bot-head:has(.badge.reconciling) { padding-right: 0; }
  .section-title h1 { font-size: var(--fs-section); }
  .section-title h2 { font-size: var(--fs-card-title); }
  .metric .metric-reading > strong,
  .metric > strong { font-size: 28px; }
  .bot-stats > div:nth-child(1) > strong,
  .bot-stats > div:nth-child(2) > strong { font-size: var(--fs-card-title); }
}
/* ── ⛔ 指標卡金額仍被切 —— 實機截圖打臉上一版的「六種寬度全過」───────────
   2026-08-22 重啟後背景截圖（1536px 視窗）：未實現顯示「$0....」、已實現顯示
   「+$1...」⇒ 上一版的 fit.mjs 說全過，真機在切。

   ⭐ 根因是我上一版**量錯了對象**：我只量金額字串本身有多寬，卻沒有把
      **同一行裡的兩個 <small>** 算進去。真實 DOM 是

        <div class="metric-reading">
          <strong>$0.00</strong><small>USD</small><small>本機算・Tradovate 未連線</small>
        </div>

      而 `.metric-reading` 是 `display:flex`（gap 8px）⇒ 那段 12 字的中文說明
      在 14px 下就吃掉約 168px，1536px 四張一排時每張內容區只有約 302px
      ⇒ 剩不到 90px 給金額，40px 的 `$0.00` 需要約 100px ⇒ **必切**。
      （截圖佐證：那段說明自己已經折成兩行，代表它確實在搶寬度。）

   ⛔ 而且我上一版加的 `text-overflow: ellipsis` 讓**錢**成為被犧牲的那一個 ——
      優先順序完全反了：這是交易軟體，該折行的是「資料來源說明」，不是金額。

   ⇒ 修法：來源說明**永遠**獨佔第二行（不是「放不下才換」）。
      永遠換行還順帶解掉一個沒人會注意的毛病：文字在「等待連線 / 本機算 /
      Tradovate 已連線」之間切換時長度不同，若採「放不下才換」，卡片高度會
      隨著連線狀態忽高忽低。

   ⚠ 這兩個 id 是 index.html:163/167 寫死的，⛔ 不用 :last-child —— 那會連
      「個」「口」這種兩字單位也推到第二行，白白多佔一行。 */
.metric-reading { flex-wrap: wrap; }

#metricUnrealizedSource,
#metricRealizedSource {
  flex: 1 0 100%;
  margin-top: 10px;
  line-height: 1.35;
  /* 它是輔助資訊，不該跟金額搶視覺重量 */
  font-size: var(--fs-label);
  font-weight: var(--fw-label);
  color: var(--n4);
}

/* 金額拿回整行後仍保留省略號當最後防線（配 title，app.js 每次更新同步寫）。
   tabular + lining numerals 是行情／券商終端常用排法：更新時不左右跳，所有位數同高。
   ⛔ flex item 預設 min-width:auto 會讓 overflow:hidden 失效 ⇒ 必須歸零。 */
.metric .metric-reading > strong {
  min-width: 0;
  max-width: 100%;
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
  letter-spacing: -.04em;
}

/* QW-DESIGN-SYSTEM:END */

/* ── 尚未指定商品（站長 2026-08-24：指定商品移到「機器人建立後」）────────────
   ⛔ 這是**正常的起始狀態**不是錯誤：建立完成後、還沒選商品之前每一台都會經過它。
   ⇒ 刻意**不**用 .bot-reconciling-warning 那組橘色警示語彙（那組代表「訂單結果待確認」
     ＝真的出事了）。這裡用藍色資訊色，並且**自帶出路**（右邊那顆「指定商品」）——
     一個只說「你少了東西」卻不告訴你去哪裡補的橫幅，等於把問題丟回給使用者。
   ⚠ 顏色是唯一區別時對色盲不成立 ⇒ 文案自己就講得完整（「還不能下單」＋按鈕文字），
     ⛔ 不要把「這是什麼狀態」的資訊只放在顏色上。 */
.bot-symbol-missing {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin: 12px 0 16px;
  padding: 14px 16px;
  border: 1px solid rgba(53, 164, 255, .42);
  border-radius: 11px;
  background: rgba(12, 52, 92, .34);
  font-size: var(--fs-sm);
  line-height: 1.45;
}
.bot-symbol-missing > div {
  display: grid;
  gap: 4px;
  flex: 1 1 220px;
  min-width: 0;
}
.bot-symbol-missing strong { color: #cfe8ff; font-size: var(--fs-base); }
.bot-symbol-missing span { color: #9dc2e6; }
/* ⛔ 這顆不縮：它是這張橫幅存在的唯一理由。窄卡片時整顆換行到下一列（flex-wrap），
   ⛔ 不要讓它被壓成一個看不出是按鈕的細條。 */
.bot-symbol-missing button { flex: 0 0 auto; white-space: nowrap; }

/* ===== X 按鈕統一：游標過去就是實心紅底白字（站長 2026-08-24）=====
   「X 關閉的按鈕 全部統一 一律游標過去 背景色變紅 那種方式」——
   「那種方式」指的是標題列 .window-close 本來就有的行為（#d83c52 實心紅底白字）。

   統一之前各自為政：
   · .icon-button（對話框右上角的 ×，全 codebase 只有 3 處、內容全是「×」）—— hover 完全沒有紅
   · .key-field-clear（清除金鑰／撤銷 API Key 的 ✕）—— 只有 8% 透明度的淡紅，幾乎看不出來

   ⚠ 這一段【必須放在檔案最後】：.icon-button 在本檔有三處定義（L314／759／1014），
     寫在它們前面會被後面那兩處蓋掉。同一份 CSS 今天稍早才因為這件事白改三版
     （symbol-picker 的 grid-area）。
   ⛔ 不用 !important：靠位置贏就好 —— !important 會讓下一個要做深色／高對比主題的人無路可走。
   ⭐ 紅色沿用 #d83c52（.window-close 的值），⛔ 不另外挑一個接近的紅：
     兩個「幾乎一樣的紅」在同一個畫面上，看起來就是沒對齊。 */
.icon-button:hover,
.icon-button:focus-visible,
.key-field-clear:hover,
.key-field-clear:focus-visible {
  border-color: #d83c52;
  background: #d83c52;
  color: #fff;
}
/* 鍵盤操作要看得出焦點在哪 —— 紅底之外再加一圈，⛔ 不能只靠顏色（色覺障礙者分不出來）。 */
.icon-button:focus-visible,
.key-field-clear:focus-visible {
  outline: 2px solid var(--cyan, #19ddff);
  outline-offset: 1px;
}

/* ⭐ 指定商品的自訂下拉（站長 2026-08-24：「有夠醜 做成一體成形的吧」）。
   ⛔ 一體成形的關鍵是三件事，少一件就又變成「貼在輸入框下面的另一個東西」：
      ① 底色與輸入框【同一個值】（#07131f）  ② 去掉上邊框、只有下方圓角
      ③ 面板寬度 = 輸入框寬度（靠 position:absolute + left/right:0，⛔ 不寫死 px）
   ⚠ 它住在 <dialog> 裡，而 dialog 的 UA 樣式帶 overflow:auto ⇒ 面板會被對話框切
     （同一天的說明泡泡就是這個坑）⇒ 給它 max-height 自己捲，⛔ 不讓它長到被切。
   ⚠ .symbol-picker 是 flex，面板要定位就必須有個 position:relative 的參考框
     ⇒ 那個框是 .symbol-select-wrap（index.html 沒有多一層 div 的話，
        這裡讓 .symbol-picker 自己當參考框：面板 left 對齊到輸入框的 flex 欄）。 */
/* ⛔ 定位參考框是 .symbol-select-wrap 不是 .symbol-picker：後者是整列（含同步鈕），
   面板會橫跨到按鈕下面（實測 562px vs 輸入框 372px）⇒ 那不是一體成形。
   ⚠ wrapper 的 position:relative 寫在上面那條 flex 規則裡（同一處，⛔ 不分兩地）。 */
.symbol-menu {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 40;
  max-height: 264px;
  overflow-y: auto;
  margin-top: -1px;                     /* 蓋掉輸入框的下邊框 ⇒ 接縫消失 */
  background: #07131f;                  /* ⛔ 與 .symbol-select 同一個值 */
  border: 1px solid rgba(25, 221, 255, .75);
  border-top: none;
  border-radius: 0 0 9px 9px;
  box-shadow: 0 18px 38px rgba(2, 8, 18, .55);
}
.symbol-menu[hidden] { display: none; }
/* 面板開著時，輸入框只留上方圓角 ⇒ 兩者是同一塊。 */
.symbol-select[aria-expanded="true"] {
  border-radius: 9px 9px 0 0;
  border-color: rgba(25, 221, 255, .75);
}
.symbol-option {
  display: grid;
  gap: 2px;
  padding: 10px 14px;
  cursor: pointer;
  border-bottom: 1px solid rgba(84, 160, 210, .12);
}
.symbol-option:last-child { border-bottom: none; }
.symbol-option:hover,
.symbol-option[data-active="1"] { background: rgba(25, 221, 255, .09); }
.symbol-option-code { font-weight: 700; color: var(--text); letter-spacing: .2px; }
/* ⭐ 說明只有一行、放得下就不截斷；真的太長才用省略號
   ⛔ 不用 white-space:nowrap 加寬面板：面板一寬就不再與輸入框同寬 ＝ 不是一體成形了。 */
.symbol-option-note { font-size: 13px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.symbol-menu-empty { padding: 12px 14px; font-size: 13px; color: var(--muted); }


/* ── 快速下單列（站長 2026-08-24）─────────────────────────────────────────
   買綠賣紅是業界標準配色（Quantower / NinjaTrader / Tradovate 一致）。
   ⛔ 鎖住時【變灰不能按】而不是隱藏：使用者要看得到「它在那裡、只是鎖著」，
      隱藏會讓人以為功能不見了（站長要的原話就是「變灰不能按」）。 */
.bot-quick-trade {
  display: grid;
  grid-template-columns: 1fr auto 1fr auto;
  gap: 8px;
  align-items: stretch;
  margin: 10px 0 0;
}
.bot-quick-trade .qt-side,
.wb-grid-trade .qt-side {
  border: 1px solid transparent;
  border-radius: 9px;
  font-weight: 700;
  font-size: var(--fs-base, 14px);
  letter-spacing: .04em;
  padding: 10px 4px;
  cursor: pointer;
  color: #04120a;
  transition: filter .12s ease, opacity .12s ease;
}
.bot-quick-trade .qt-buy, .wb-grid-trade .qt-buy { background: var(--green, #2ecc71); }
.bot-quick-trade .qt-sell, .wb-grid-trade .qt-sell { background: var(--danger, #e74c3c); color: #1a0505; }
.bot-quick-trade .qt-side:not(:disabled):hover, .wb-grid-trade .qt-side:not(:disabled):hover { filter: brightness(1.12); }
.bot-quick-trade .qt-side:not(:disabled):active, .wb-grid-trade .qt-side:not(:disabled):active { filter: brightness(.92); }
/* 鎖住／未連線 ⇒ 變灰。⛔ 不用 opacity 一招帶過：那會讓文字糊掉，
   而使用者要讀得出上面寫的是「買入」還是「賣出」。 */
.bot-quick-trade .qt-side:disabled,
.wb-grid-trade .qt-side:disabled {
  background: #39414d;
  color: #8b95a3;
  cursor: not-allowed;
}
/* ⛔ 這一組【不綁】.bot-quick-trade：同一個元件也長在工作台的「機器人操作・市價」
   那一列（#workbenchQtyBox）。綁在卡片底下的話，工作台那份會完全沒有樣式。 */
.qt-qty {
  display: grid;
  /* 兩側等寬才不會歪。⚠ 中間用 minmax 而不是固定寬：口數可以到 10000（5 位數）。 */
  grid-template-columns: 32px minmax(48px, 1fr) 32px;
  align-items: stretch;
  border: 1px solid var(--border, #2a3341);
  border-radius: 9px;
  overflow: hidden;
}
.qt-qty button {
  display: grid;
  place-items: center;
  /* ⛔ padding: 0 是關鍵：<button> 的瀏覽器預設 padding 在 32px 寬的格子裡會把
     字元推離中心，看起來就是站長說的「+- 位置歪了」。 */
  padding: 0;
  background: transparent;
  border: 0;
  color: inherit;
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}
.qt-qty .qt-step-icon {
  display: block;
  width: 18px;
  height: 18px;
  overflow: visible;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  pointer-events: none;
}
.qt-qty button:hover { background: rgba(255, 255, 255, .07); }
.qt-qty button:active { background: rgba(255, 255, 255, .12); }
.qt-qty input {
  box-sizing: border-box;
  margin: 0;
  padding: 0;                      /* 同上：input 也有預設 padding */
  border: 0;
  background: transparent;
  color: inherit;
  text-align: center;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: var(--fs-base, 14px);
  line-height: 1;
  min-width: 0;
}
/* 原生上下箭頭在深色底下很醜、也太小不好點 ⇒ 隱藏，用旁邊那兩顆。 */
.qt-qty input::-webkit-outer-spin-button,
.qt-qty input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.qt-qty input[type=number] { -moz-appearance: textfield; appearance: textfield; }
.qt-qty.is-disabled { opacity: .45; pointer-events: none; }
.qt-lock {
  display: grid;
  place-items: center;
  padding: 0;
  width: 38px;
  border: 1px solid var(--border, #2a3341);
  border-radius: 9px;
  background: transparent;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}
.qt-lock:hover { background: rgba(255, 255, 255, .07); }
/* 解鎖狀態要一眼看得出來 —— 這是「現在按下去就會真的下單」的唯一視覺提示。 */
.bot-quick-trade[data-locked="0"] .qt-lock,
.wb-trade-row .qt-lock[aria-pressed="false"] {
  border-color: var(--green, #2ecc71);
  box-shadow: 0 0 0 1px var(--green, #2ecc71) inset;
}


/* 工作台「機器人操作・市價」那一列 —— 買入 ─ 口數 ─ 賣出 ─ 鎖。
   ⛔ 買賣鍵不准縮小（index.html 的註解：「大顆好按就是它留在這裡的理由」）
   ⇒ 用 auto 給口數與鎖，剩下的空間全留給那兩顆。 */
.wb-trade-row { display: flex; align-items: stretch; gap: 8px; }
.wb-trade-row .qt-qty { flex: 0 0 auto; }
.wb-trade-row .qt-lock { flex: 0 0 auto; align-self: stretch; }
.wb-trade-row .qt-lock:disabled { opacity: .4; cursor: not-allowed; }


/* 口數提示。⚠ 兩態要看得出差別：調整中（還在動）vs 已定案。 */
.wb-qty-hint {
  display: block;
  margin-top: 4px;
  font-size: var(--fs-sm, 12px);
  text-align: right;
  color: #8b95a3;
  transition: color .15s ease;
}
.wb-qty-hint[data-state="settling"] { color: var(--amber, #f0b429); }
/* 「送出去了，但交易頁還沒回報」——⛔ 不能跟 settled 同色：這兩者的差別正是站長
   在意的那件事（上面那格到底變了沒）。琥珀＋閃爍表示【還在追】。 */
.wb-qty-hint[data-state="syncing"] {
  color: var(--amber, #f0b429);
  animation: wb-qty-syncing 1s ease-in-out infinite;
}
@keyframes wb-qty-syncing { 50% { opacity: .55; } }
.wb-qty-hint[data-state="settled"] { color: var(--green, #2ecc71); }
/* 只有【交易頁那一格真的變了】才給綠色 —— 後端把值讀回來比對過才算。 */
.wb-qty-hint[data-state="synced"] { color: var(--green, #2ecc71); font-weight: 600; }
.wb-qty-hint[data-state="failed"] { color: var(--red, #e74c3c); }
@media (prefers-reduced-motion: reduce) {
  .wb-qty-hint[data-state="syncing"] { animation: none; }
}

/* ── 機器人自訴：常駐錯誤區（站長 2026-08-25 傍晚）──────────────────────────
   逐字：「無法自行解決的 用**系統提示＋常駐錯誤顯示** 來讓用戶除錯」
   ＋「先前的錯誤也都要顯示 只是多了 提示 呼叫救援AI 中...」

   ⭐ 顏色**只編碼一件事：誰該出手**。⛔ 不編碼嚴重度 ——
      使用者要的答案是「我現在要不要動手」，不是「這有多糟」。
      · needs-you（human）＝ 琥珀：跟既有的唯讀橫幅、待確認警示同一支血統
      · 機器人處理中（self）＝ 藍：跟連線中的圓點同一支，語意是「在跑，別急」
      · 救援 AI（ai）＝ 紫：⭐ 刻意**不是紅** —— 有人在處理不等於出事了
   ⚠ **`--violet` 只有第三個主題層原本就有**（`#9d7cff`），前兩層是本輪補的（2026-08-25）。
      ⭐ 這段註解自己踩過兩次同一個坑，留著當警告：
        第一版寫「沿用既有的 --amber / --blue / --violet」—— 沒查，錯。
        第二版查了但 `head -6` **截斷**，於是寫成「--violet 當時並不存在」—— 也錯，
        第三層一直都有；我那次補的第三個是**重複定義**、而且排在原有的前面 ⇒ 後者贏 ⇒ 我加的是死碼。
      ⇒ 判準：印集合前先印 `len()`，⛔ 別用截斷過的輸出寫全稱句。
   ⛔ 另外兩個確實是既有的（--amber / --blue）；⛔ 不為這一塊再開別的色票。 */
.bot-diagnostics {
  margin: var(--space-2) 0 0;
  padding: 12px 16px;
  border: 1px solid var(--amber, #f2b84b);
  border-left-width: 3px;
  border-radius: 10px;
  background: rgba(242, 184, 75, .1);
  font-size: var(--text-sm, 14px);
  line-height: 1.55;
}
.bot-diagnostics.tier-self { border-color: var(--blue, #35a4ff); background: rgba(53, 164, 255, .1); }
.bot-diagnostics.tier-ai { border-color: var(--violet, #a98bff); background: rgba(169, 139, 255, .12); }
.bot-diagnostics > strong { display: block; margin-bottom: 6px; color: var(--text, #e8f1fb); }
.bot-diagnostic-list { margin: 0; padding: 0; list-style: none; display: grid; gap: 8px; }
.bot-diagnostic-item { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; }
/* 「誰該出手」是這一整塊唯一要人一眼看到的東西 ⇒ 它排最前面且是唯一有底色的。 */
.bot-diagnostic-tier {
  flex: 0 0 auto;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .1);
  font-weight: 700;
  white-space: nowrap;
}
.tier-human > .bot-diagnostic-tier,
.bot-diagnostic-item.tier-human .bot-diagnostic-tier { color: var(--amber, #f2b84b); }
.tier-self > .bot-diagnostic-tier,
.bot-diagnostic-item.tier-self .bot-diagnostic-tier { color: var(--blue, #35a4ff); }
.tier-ai > .bot-diagnostic-tier,
.bot-diagnostic-item.tier-ai .bot-diagnostic-tier { color: var(--violet, #a98bff); }
.bot-diagnostic-text { flex: 1 1 auto; min-width: 0; color: var(--text, #e8f1fb); }
/* 後端補的上下文（場上有幾個面板、各是什麼商品）—— 給要查的人看，⛔ 不跟主文案搶注意力。 */
.bot-diagnostic-detail { flex: 1 1 100%; color: var(--muted, #8fa8c4); font-size: 13px; }
.bot-diagnostic-age { flex: 0 0 auto; color: var(--muted, #8fa8c4); font-variant-numeric: tabular-nums; }
.bot-diagnostic-more { color: var(--muted, #8fa8c4); }

/* ── 卡片上的診斷槽：**永遠佔位**（站長 2026-08-25）────────────────────────
   逐字：「將它顯示在標題下方 且**固定要有一個空白區域** 這樣才不會變成插入
   讓整個列表位移」

   ⭐ 病灶不是「多了一塊東西」，是**它會來會走**：券商快照每 2 秒一輪，問題出現／
      消失都會重畫卡片 ⇒ 以前那塊「有才插」的警示一出現，它下面的未實現／最新價格／
      持倉／買入賣出**整排往下掉**，同一列的鄰居卡片卻不動。
      ⇒ 兩張卡片的同一個欄位不再對齊，**而使用者的手正停在「賣出」上面**。
   ⛔ 用 height 不用 min-height：min-height 只擋得住「空 ⇄ 一行」，
      擋不住「一行 ⇄ 兩行」，而後者一樣會位移（所以下面 compact 改成不換行）。
   ⚠ 高度用 calc 由字級推出來，⛔ 不寫死 42px —— 這個產品在 100%／125%／150%
      三種縮放下都被驗過，寫死的值只有在其中一種下是對的。
     22px ＝ 上下 padding 16 ＋ tier 膠囊的上下 padding 4 ＋ 上下 border 2。 */
.bot-diagnostics-slot {
  --diagnostic-line: 13px;
  margin: var(--space-2) 0 0;
  height: calc(var(--diagnostic-line) * 1.55 + 22px);
}
/* margin 由槽負責 ⇒ 裡面那塊歸零，否則會頂出槽外又變成位移。 */
.bot-diagnostics-slot > .bot-diagnostics { margin: 0; height: 100%; }

/* 卡片上的緊湊版：⛔ 只有一行 —— 卡片上已經有連線狀態、持倉、損益在搶注意力。
   ⚠ 但「另有 N 個問題」一定要說，⛔ 不可以只顯示一條讓人以為只有一個問題。 */
.bot-diagnostics.compact {
  display: flex;
  align-items: center;
  gap: 8px;
  /* ⛔ nowrap：換行 ＝ 槽被撐破 ＝ 站長這次要根治的位移又回來了。
     ⇒ 擠不下的是文案本身，交給下面的省略號，⛔ 不是換行。 */
  flex-wrap: nowrap;
  padding: 8px 12px;
  font-size: var(--diagnostic-line, 13px);
  overflow: hidden;
}
/* 被擠掉的字仍然查得到：整塊有 title（app.js 給的），hover 看得到全文。 */
.bot-diagnostics.compact .bot-diagnostic-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bot-diagnostics.compact .bot-diagnostic-more { flex: 0 0 auto; white-space: nowrap; }

/* ⚠ 呼叫救援 AI 時的呼吸感 —— 它是「正在發生」的唯一線索（其餘全是靜態文字）。
   ⛔ 只動 opacity：這一塊會跟卡片一起被整批重繪，動 transform 會在重繪時跳一下。 */
.bot-diagnostic-item.ai-calling .bot-diagnostic-tier { animation: diagnosticPulse 1.6s ease-in-out infinite; }
@keyframes diagnosticPulse { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }
@media (prefers-reduced-motion: reduce) {
  .bot-diagnostic-item.ai-calling .bot-diagnostic-tier { animation: none; }
}

/* ── 卡片拖曳排序（站長 2026-08-25 兩輪需求）──────────────────────────────
   第一輪：「自由左右拖曳 從左至右是順序」＋「不要因為加東西變太亂」
   第二輪：「還會畫出一條線 還會自動推擠別張卡片 且是整張卡片被帶走
             其它卡片動態排位置 猶如 iPhone APP 主畫面 拖曳 APP 那樣」

   ⭐ 動的全部是 `transform` 與 `opacity` —— 這兩個屬性由合成器處理，⛔ 不觸發
      重排（reflow）。改 `left/top` 或 `order` 會在每一格重算整個 grid 的版面，
      12 張卡就開始掉幀，而拖曳是全 app 對流暢度最敏感的動作。
   ⚠ 位移量由 JS 算（見 app.js 的 applyShift）—— CSS 這裡只負責**怎麼動**，
     ⛔ 不負責**動到哪**。 */
.bot-head { cursor: grab; }
.bot-grid { position: relative; }         /* 插入線是它的絕對定位子元素 */
.bot-grid.is-drag-sorting { cursor: grabbing; user-select: none; }
/* 拖曳期間關掉 hover 位移 —— 它跟推擠共用 transform，兩者會互相覆蓋，
   症狀是游標經過的卡片會忽然彈一下。 */
.bot-grid.is-drag-sorting .bot-card:hover { transform: none; }

/* ③ 整張卡片被帶走：卡片**本體**跟著游標，⛔ 不是瀏覽器產的半透明快照。
   輕微放大＋傾斜＋加深陰影 ＝「它離開桌面了」，這是 iOS 主畫面拖 App 的語彙。
   ⛔ 不要調 opacity：那正是原生拖放最被詬病的地方（它強制壓到 .8 且不可控），
      而站長要的是「整張卡片被帶走」——被帶走的東西應該是實心的。 */
.bot-card.is-dragging {
  cursor: grabbing;
  z-index: 30;
  box-shadow: 0 26px 60px rgba(0, 0, 0, .55);
  transition: box-shadow 160ms ease;
  will-change: transform;
}
/* ② 推擠 ＋ ④ 動態排位：其他卡片滑到讓位後的那一格。
   ⚠ 只寫 transform 的 transition，⛔ 不寫 `all` —— 卡片上還有連線狀態、獲利顏色
     在變，把它們一起補間會讓數字跟顏色糊成一團。 */
.bot-card.is-shifting {
  transition: transform 230ms cubic-bezier(.2, 0, 0, 1);
  will-change: transform;
}

/* ① 落點那條線。畫在**空出來的那一格**的左緣（JS 定位）。
   ⚠ 用獨立元素而不是卡片的 ::after —— 前一版用 ::after 時撞到
     `.bot-card.module-card::after` 被設成 display:none，整條線靜默消失。
     獨立元素不跟任何既有裝飾共用選擇器，那類衝突從結構上就不會發生。
   ⛔ 顏色沿用既有的 --cyan，不發明第 N 個強調色（站長：不要變亂）。 */
.bot-drop-line {
  position: absolute;
  width: 3px;
  border-radius: 2px;
  background: var(--cyan, #19ddff);
  box-shadow: 0 0 12px rgba(25, 221, 255, .85);
  pointer-events: none;
  opacity: 0;
  transition: opacity 120ms ease, left 180ms cubic-bezier(.2, 0, 0, 1), top 180ms cubic-bezier(.2, 0, 0, 1);
  z-index: 20;
}

@media (prefers-reduced-motion: reduce) {
  /* ⚠ 只關動畫，⛔ 不關功能：拖曳本身照常，卡片仍會到正確的位置，
     只是不補間。這是 reduced-motion 的正確語意（少動，不是少功能）。 */
  .bot-card.is-shifting,
  .bot-drop-line { transition: none; }
  .bot-card.is-dragging { box-shadow: 0 12px 28px rgba(0, 0, 0, .5); }
}

/* ── 刪除分組：游標靠近才變紅（站長 2026-08-25：「游標靠近要變紅色按鈕」）──────
   ⭐ **平時不紅**：它就在分組下拉旁邊，長駐紅色會讓整條工具列看起來像在警告什麼。
      hover 才轉紅 ＝ 使用者把游標移過去的那一刻才知道「這顆是會刪東西的」。
   ⛔ 不改成 class="danger"（那會平時就紅）；⛔ 也不自己調色 —— 直接引用既有 .danger
      那組值（styles.css:98 起 ／ 各主題層 685 / 960 / 1630 / 2067 都有覆蓋），
      不然主題一換這顆就會跟旁邊的危險鍵不同色。 */
#botGroupDelete { transition: background-color .15s ease, border-color .15s ease, color .15s ease; }
#botGroupDelete:hover:not(:disabled) {
  background: linear-gradient(145deg, rgba(87, 24, 43, .8), rgba(46, 17, 30, .9));
  border-color: rgba(255, 85, 120, .55);
  color: #ff9bb0;
}
/* ⚠ 鍵盤使用者也要看得到同一個訊號 —— 只綁 :hover 等於把它藏起來給不用滑鼠的人。 */
#botGroupDelete:focus-visible {
  background: linear-gradient(145deg, rgba(87, 24, 43, .8), rgba(46, 17, 30, .9));
  border-color: rgba(255, 85, 120, .55);
  color: #ff9bb0;
}

/* ── 唯讀浮水印（站長 2026-08-25：「唯讀模式時 要有浮水印在右上角 唯讀」）─────────
   ⭐ 為什麼右上角可以、而「頂端置中」不行：styles.css:3624 那段註解否決的是**置中**
      浮層（實測會蓋掉 Tradovate 的淨值／保證金那一列）。右上角是圖表的留白帶，
      而且這是**半透明浮水印**不是實心橫幅 —— 底下的東西看得穿。
   ⛔ pointer-events:none 是承重的：唯讀模式底下的畫面**仍然要能被機器人操作**，
      而且解除唯讀後使用者第一個動作極可能就是點右上角那一區。
   ⚠ 它與 .wb-stage-readonly 橫幅**兩個都要在**，職責不同：
      浮水印回答「我現在為什麼點不動」（一眼、永遠在視野裡），
      橫幅回答「這是什麼狀態、機器人會不會受影響」（有完整說明文字）。 */
.wb-stage-live.is-read-only::before {
  content: '唯讀';
  position: absolute;
  top: 10px;
  right: 12px;
  z-index: 5;
  pointer-events: none;
  padding: 4px 12px;
  border: 1px solid rgba(242, 184, 75, .55);
  border-radius: 6px;
  background: rgba(58, 42, 12, .58);
  color: rgba(255, 214, 138, .92);
  font: 700 13px/1.2 ui-monospace, Consolas, monospace;
  letter-spacing: 2px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .55);
}
/* 英文介面：i18n 走的是文字節點與屬性，⛔ 摸不到 CSS 的 content
   ⇒ 用 :lang() 換掉，⛔ 不要把浮水印改成需要 JS 寫入的元素（那會多一條要同步的路）。 */
:lang(en) .wb-stage-live.is-read-only::before { content: 'READ-ONLY'; letter-spacing: 1px; }

/* ══════════════════════════════════════════════════════════════════════════
   AI 求救的隱私揭露 —— 站長 2026-08-25 裁決：「明確寫出來會傳給第三方API 即可」
   ══════════════════════════════════════════════════════════════════════════
   ⛔ 這段要看得見才算揭露。用 .field-help.ai-assist-disclosure 兩個 class 疊出
      特異性（0,2,0），⛔ 不靠「寫在後面就會贏」—— .field-help 在本檔有兩處定義，
      靠順序等於把樣式綁在「誰在下面」這個會被反熵整理弄壞的性質上。
   ⭐ 顏色走既有 --warn token（不新發明顏色），帶 fallback 以防它在某層沒定義。 */
.field-help.ai-assist-disclosure {
  margin-top: 10px;
  padding: 12px 14px;
  border-left: 3px solid var(--warn, #ffd37a);
  border-radius: 0 8px 8px 0;
  background: color-mix(in srgb, var(--warn, #ffd37a) 8%, transparent);
  color: inherit;
  font-size: 14px;
  line-height: 1.65;
}
.field-help.ai-assist-disclosure strong { color: var(--warn, #ffd37a); font-weight: 650; }

/* ══════════════════════════════════════════════════════════════════════════
   複製按鈕統一成圖示 ＋ 金鑰欄位排版（站長 2026-08-26）
   ══════════════════════════════════════════════════════════════════════════
   站長逐字：「API Key 兩顆按鈕黏一起／大改 複製按鈕全部改 複製的圖案 即可 單一
   真相源／API Key 只是在 複製按鈕前 有顆眼睛點了看完整 預設眼睛關的 跟主流一樣／
   複製按鈕不要在 文字框裡面 眼睛才要在文字框裡面」。

   ## ⛔ 真兇：三顆鈕疊在同一個座標上，不是「靠太近」
   `.key-field-clear` 是 `position:absolute; right:8px`。2026-08-26 稍早往同一個
   `.key-field` 裡塞了 👁 ／ ⧉ ／ ✕ 三顆 —— 三顆都絕對定位在 right:8px
   ⇒ **完全重疊**，畫面上看起來是一坨、而且只有最上面那顆（✕ 撤銷）點得到。
   ⚠ 這種壞法不會有任何錯誤訊息：DOM 三顆都在、都有 aria-label、測試也抓得到，
     只有真的用滑鼠點下去才會發現按到的是撤銷。
   ⇒ 修法是給它們一個 flex 容器（`.key-field-actions`）排隊，⛔ 不是各自調 right 值
     （那等於把「有幾顆鈕」寫死在座標裡，下次再加一顆又要重算一輪）。

   ## 版面（站長指定）
       [ 文字框 ……………… 👁 ✕ ]   [複製圖示]
       └── .key-field（框內）──┘ gap └ 框外，與框並排

   ⛔ 撤銷 ✕ 留在框內：站長 2026-08-22 明說「寫進欄位裡面，用按鈕來確認和刪除」，
      2026-08-26 這輪只點名了「複製」要出去、「眼睛」要進來 ⇒ 不動它。 */

/* 框內的操作鍵排成一列。⛔ 只有這個容器負責定位，裡面的鈕一律 static —— 
   否則 `.key-field-clear` 那條 absolute 會讓它們又疊回同一點。 */
.key-field-actions {
  position: absolute; top: 50%; right: 8px; transform: translateY(-50%);
  display: flex; align-items: center; gap: 12px;
}
.key-field-actions .key-field-clear {
  position: static; top: auto; right: auto; transform: none;
}
/* ⛔ gap 不是 6px（站長 2026-08-26 實測後再提）。
   真瀏覽器量到的 6px 是【眼睛右緣 488.19 到撤銷左緣 494.19】——
   兩顆 34px 的鈕中間只隔 6px，而右邊那顆是【撤銷】（不可逆）。
   ⭐ 12px 是本列已經在用的間距：.key-row 的 gap 就是 12px，實測框外複製鈕
     離框也是 12.00px ⇒ 框內、框外同一個節奏，⛔ 不另挑一個差不多的數字。
   ⚠ ⛔ 不能拉到 60px（那是框【外】眼睛到複製的距離）：這兩顆在輸入框【內】，
     拉得越開，遗給金鑰文字的寬度越少，遮罩值會提早被截掉。 */

/* 圖示鈕的 SVG 尺寸。⛔ 別靠上面那條 font-size:16px —— 它只對【✕ 這個文字字元】
   有效；換成 <svg> 之後 font-size 一點作用都沒有，而 SVG 沒寬高時會用 viewBox 的
   24×24 擐滿整顆 34px 的框—— 不會報錯，只會看起來「圖大得很奇怪」。
   ⭐ 18px 是跟旁邊框外複製鈕（19px）對齊的【視覺重量】，⛔ 不是按框寬比例算
     （按比例算出來是 14.6px，兩顆貼在一起時眼睛會明顯比複製小一截）。
   ⚠ pointer-events:none —— 讓點擊一律落在 <button> 上，⛔ 不是落在 <svg>：
     任何用 event.target 判按到哪顆的地方都會因此静默失效。 */
.key-field-clear svg { width: 18px; height: 18px; display: block; pointer-events: none; }

/* 輸入框右側要空出「按鈕總寬 + 兩側留白」的位置，⛔ 否則遮罩文字會被鈕蓋住。
   用 :has() 依實際有幾顆鈕決定，⛔ 不寫死一個數字（加減鈕時不必回來改）。
   ⚠ 98 = 兩顆 34px + 中間 12px + 右側 right:8px + 10px 寬限；
     gap 從 6 改成 12 這一行必須跟著加 6，⛔ 否則遮罩值會被眼睛蓋掉。 */
.key-field:has(.key-field-actions) input { padding-right: 98px; }
.key-field:has(.key-field-actions > :only-child) input { padding-right: 52px; }

/* 金鑰列：文字框 ＋ 框外的複製鈕。gap 就是站長要的「不要黏一起」。 */
.key-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: 12px; align-items: center; margin-top: 8px;
}
.key-row .key-field { margin-top: 0; }

/* ── 複製按鈕：全 app 唯一的一套外觀 ──────────────────────────────────────
   圖案本身（SVG）只有一份，定義在 app.js 的 COPY_ICON_SVG；這裡只管框與狀態。
   ⛔ 不要在這裡再放一份 mask-image 的 SVG —— 那就是第二個真相源。 */
.copy-icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
  width: 44px; min-width: 44px; height: 44px; margin: 0; padding: 0;
  border: 1px solid var(--border, #23415e); border-radius: 9px;
  background: rgba(7, 19, 31, .72);
  color: #8fc7ec; cursor: pointer;
  transition: color .12s ease, border-color .12s ease, background .12s ease;
}
.copy-icon-btn svg { width: 19px; height: 19px; display: block; pointer-events: none; }
.copy-icon-btn:hover { color: #cfefff; border-color: #2f7fb5; background: rgba(20, 52, 78, .85); }
.copy-icon-btn:focus-visible { outline: 2px solid var(--cyan, #19ddff); outline-offset: 1px; }
.copy-icon-btn:disabled { opacity: .45; cursor: not-allowed; }
/* 已複製：沿用既有的 button.copied 綠（那條帶 !important，⛔ 不要再挑一個接近的綠）。 */
.copy-icon-btn.copied { border-color: #2f9d6a; background: rgba(24, 66, 48, .8); }

/* .copy-field 的第二欄本來是一顆文字鈕（寬度隨字長跑）。改成圖示鈕之後，
   讓它靠上對齊 code 區塊的第一行，⛔ 不要拉伸成跟多行 <code> 一樣高。 */
.copy-field .copy-icon-btn { align-self: start; }

/* 對話框動作列（「複製完整 API Key」那顆）：跟旁邊的主要按鈕等高，
   ⛔ 不要因為變成圖示就縮成一顆小方塊、在一排大鈕旁邊看起來像壞掉。 */
.dialog-actions .copy-icon-btn { height: 48px; min-width: 48px; width: 48px; }

/* ── 機器人卡收斂（2026-08-26）──────────────────────────────────────────
   上半部先前被五套主題疊成：頭 92px、連線列 68px、帳號窗三行、四格統計還把
   未實現／最新價各拉成整列 ⇒ 一張卡還沒到按鈕就比視窗高。
   主流交易面板（FreqUI / PrimeAutomation / 3Commas）是「一行身分＋一列 KPI
   ＋主操作」，次要動作進 overflow ⋯。本層只壓高度，不刪欄位。 */
.bot-card {
  padding: 14px 16px 12px;
}
.bot-head {
  min-height: 0;
  margin: 0;
  padding: 0;
  border-bottom: 0;
  background: none;
}
.bot-head h3,
.bot-name {
  font-size: 18px;
  margin: 0 0 2px;
}
.bot-name-trigger {
  display: -webkit-box;
  width: 100%;
  min-height: 0;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: inherit;
  font: inherit;
  line-height: inherit;
  letter-spacing: inherit;
  text-align: left;
  word-break: break-word;
  overflow-wrap: anywhere;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  cursor: text;
}
.bot-name-trigger:hover { color: #ffffff; text-decoration: underline; text-decoration-color: rgba(143, 199, 236, .62); text-underline-offset: 3px; }
.bot-name-trigger:focus-visible { outline: 2px solid var(--cyan, #19ddff); outline-offset: 2px; }
.bot-identity .bot-name.is-editing {
  display: block;
  overflow: visible;
}
.bot-name-input {
  display: block;
  width: min(100%, 360px);
  min-height: 32px;
  height: 32px;
  margin: -4px 0 0;
  padding: 3px 8px;
  border: 1px solid var(--cyan, #19ddff);
  border-radius: 6px;
  background: rgba(3, 12, 25, .92);
  color: #fff;
  font: inherit;
  line-height: 1.2;
  box-shadow: 0 0 0 3px rgba(25, 221, 255, .12);
}
.bot-identity .card-code {
  margin-bottom: 2px;
  font-size: 12px;
  letter-spacing: .6px;
}
.bot-identity .bot-meta {
  font-size: 13px;
  line-height: 1.3;
}
.connection-line {
  min-height: 0;
  margin: 8px 0 0;
  padding: 8px 10px;
  gap: 10px;
}
.connection-line strong { font-size: 13px; margin-top: 0; }
.bot-account-window {
  margin: 8px 0 0;
  padding: 8px 10px;
  gap: 2px;
}
.bot-account-window-name { font-size: 14px; }
.startup-visual-status {
  margin: 8px 0 0;
  padding: 8px 10px;
  gap: 8px;
}
.startup-visual-status small { display: none; }
.bot-stats {
  margin: 10px 0 8px;
  gap: 8px;
}
.bot-stats > div:has([data-stat="pnl"]),
.bot-stats > div:has([data-stat="price"]) {
  grid-column: auto;
}
.bot-stats div {
  min-height: 0;
  padding: 8px 10px;
}
.bot-stats [data-stat="pnl"] { font-size: 20px; }
.bot-stats [data-stat="price"] { font-size: 16px; }
.bot-stats [data-stat="side"],
.bot-stats [data-stat="symbol"] { font-size: 14px; }
.bot-actions {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  gap: 8px;
  padding-top: 0;
  align-items: center;
}
.bot-actions button {
  min-height: 36px;
  font-size: 14px;
  padding: 0 12px;
  flex: 1 1 auto;
}
.bot-more {
  flex: 0 0 36px;
  min-width: 36px;
  margin-left: auto;
}
.bot-cloud { margin-top: 8px; font-size: 12px; }

/* 截圖預覽：暗底 lightbox + 滾輪縮放 + 拖曳平移（ImageGlass / Google Photos） */
.shot-viewer {
  position: fixed;
  inset: 0;
  z-index: 12000;
  display: flex;
  flex-direction: column;
  background: rgba(4, 6, 16, .92);
}
.shot-viewer[hidden] { display: none; }
.shot-viewer-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 48px;
  padding: 8px 12px 8px 16px;
  background: rgba(8, 10, 24, .96);
  border-bottom: 1px solid rgba(163, 174, 255, .16);
}
.shot-viewer-bar > strong {
  min-width: 0;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 14px;
  font-weight: 600;
  color: #eef1ff;
}
.shot-viewer-tools {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
}
.shot-viewer-tools button {
  min-height: 32px;
  min-width: 32px;
  padding: 0 10px;
  font-size: 14px;
}
#shotViewerScale {
  min-width: 52px;
  text-align: center;
  font: 600 13px/1 ui-monospace, Consolas, monospace;
  color: #c5cce8;
}
.shot-viewer-close {
  flex: 0 0 auto;
  width: auto;
  min-width: 72px;
  height: 36px;
  margin: 0 8px 0 0;
  padding: 0 12px;
  border: 1px solid rgba(163, 174, 255, .28);
  border-radius: 8px;
  background: transparent;
  color: #eef1ff;
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
}
.shot-viewer-close:hover {
  background: rgba(163, 174, 255, .14);
  color: #fff;
}
.shot-viewer-stage {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  cursor: grab;
  touch-action: none;
}
.shot-viewer-stage.is-panning { cursor: grabbing; }
.shot-viewer-stage img {
  display: block;
  max-width: none;
  user-select: none;
  pointer-events: none;
}

/* ── 0.3.61 舊工作台版型（0.3.63 已由檔尾單一規則取代）──────────────────
   寬窗格（單格、上下 1:1）使用左側控制艙；窄窗格（左右 1:1、2×2）把同一份
   控制元件 reflow 到上方。沒有第二套 HTML，也不靠 viewport 猜窗格寬度。 */
@media not all {
:is(.workbench-stage[data-layout="single"], .workbench-stage[data-layout="split"] .wb-focus-pane, .workbench-stage[data-layout="grid"] .wb-grid-tile) {
  container-type: inline-size;
  display: grid;
  align-items: stretch;
}

:is(.workbench-stage[data-layout="single"], .workbench-stage[data-layout="split"][data-split-axis="vertical"] .wb-focus-pane) {
  grid-template-columns: clamp(256px, 23%, 320px) minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr);
  grid-template-areas: "rail notices" "rail canvas";
}

:is(.workbench-stage[data-layout="split"][data-split-axis="horizontal"] .wb-focus-pane, .workbench-stage[data-layout="grid"] .wb-grid-tile) {
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  grid-template-areas: "rail" "notices" "canvas" "problem";
}

:is(.workbench-stage[data-layout="single"], .workbench-stage[data-layout="split"] .wb-focus-pane, .workbench-stage[data-layout="grid"] .wb-grid-tile) > .wb-stage-notices { grid-area: notices; min-width: 0; }
:is(.workbench-stage[data-layout="single"], .workbench-stage[data-layout="split"] .wb-focus-pane, .workbench-stage[data-layout="grid"] .wb-grid-tile) > :is(.wb-stage-viewer-layer, .wb-stage-record-layer) {
  grid-area: canvas;
  align-self: stretch;
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
}

.wb-stage-rail {
  grid-area: rail;
  z-index: 42;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  overflow: auto;
  border: 0;
  border-right: 1px solid var(--border);
  background: linear-gradient(180deg, color-mix(in srgb, var(--surface-2) 52%, var(--surface)), var(--surface));
}

.wb-stage-rail > .wb-stage-head.wb-stage-head {
  display: grid;
  flex: 0 0 auto;
  grid-template-columns: 28px minmax(0, 1fr);
  align-items: start;
  gap: 8px;
  min-width: 0;
  min-height: 0;
  padding: 10px;
  overflow: visible;
  border: 0;
  background: transparent;
}
.wb-stage-head > .wb-pane-drag-handle { align-self: start; width: 28px; height: 44px; }
.wb-stage-head > .wb-pane-picker {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 7px;
  width: 100%;
  min-width: 0;
  height: auto;
  border: 0;
  background: transparent;
}
.wb-stage-head .wb-pane-group {
  width: 100%;
  min-width: 0;
  height: 38px;
  padding-inline: 10px 28px;
  border: 1px solid color-mix(in srgb, var(--cyan) 24%, var(--border));
  border-radius: 9px;
  background: var(--surface-2);
  color: var(--muted);
  font-size: 13px;
}
.wb-pane-identity-row { display: grid; grid-template-columns: 48px minmax(0, 1fr); align-items: center; gap: 8px; min-width: 0; }
.wb-pane-identity-row > .bot-avatar-button {
  display: grid;
  place-items: center;
  width: 46px;
  min-width: 46px;
  max-width: 46px;
  height: 46px;
  min-height: 46px;
  max-height: 46px;
  margin: 0;
  border-width: 2px;
}
.wb-pane-identity-row > .bot-avatar-button :is(img, .bot-avatar-fallback) { width: 100%; height: 100%; }
.wb-stage-head .wb-pane-bot-picker { position: relative; width: 100%; min-width: 0; }
.wb-stage-head .wb-pane-bot-picker > summary {
  display: flex;
  align-items: center;
  min-height: 46px;
  padding: 0 30px 0 11px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--surface-2);
}
.wb-stage-head .wb-pane-current { gap: 8px; }
.wb-stage-head .wb-pane-current strong { color: var(--text); font-size: 15px; font-weight: 750; }
.wb-stage-head .wb-pane-bot-picker > .workbench-tabs {
  inset: calc(100% + 6px) auto auto 0;
  display: grid;
  width: min(320px, calc(100vw - 32px));
  max-height: min(52vh, 420px);
  overflow: auto;
  padding: 5px;
}
.workbench-panel[data-layout="grid"] .wb-pane-picker { min-width: 0; max-width: none; }
.workbench-panel[data-layout="grid"] .wb-pane-bot-picker > .workbench-tabs { left: 0; }

.wb-stage-head > .wb-stage-tools {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) repeat(3, minmax(36px, 44px));
  gap: 6px;
  width: 100%;
  min-width: 0;
  margin: 0;
}
.wb-stage-head .wb-pane-tabs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); width: 100%; }
.wb-stage-head .wb-pane-tabs button,
.wb-stage-head > .wb-stage-tools > button { width: 100%; min-width: 0; height: 40px; min-height: 40px; }
.wb-stage-head .wb-pane-tabs svg { width: 23px; height: 23px; }

:is(.workbench-stage[data-layout="single"], .workbench-stage[data-layout="split"][data-split-axis="vertical"] .wb-focus-pane) .wb-stage-tools {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
:is(.workbench-stage[data-layout="single"], .workbench-stage[data-layout="split"][data-split-axis="vertical"] .wb-focus-pane) .wb-stage-tools .wb-pane-tabs { grid-column: 1 / -1; }

.wb-stage-rail > .wb-trade-summary {
  position: static;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  width: 100%;
  min-width: 0;
  margin: 0;
  padding: 10px;
  gap: 8px;
  overflow: visible;
  border: 0;
  border-top: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
  background: transparent;
}
.wb-stage-rail > .wb-trade-summary .wb-grid-trade {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(100px, 1.2fr) minmax(0, 1fr);
  grid-template-areas: "buy qty sell" "lock lock lock";
  gap: 6px;
  min-width: 0;
  padding: 0;
  background: transparent;
}
.wb-stage-rail > .wb-trade-summary .qt-buy { grid-area: buy; }
.wb-stage-rail > .wb-trade-summary .qt-sell { grid-area: sell; }
.wb-stage-rail > .wb-trade-summary .qt-qty {
  grid-area: qty;
  display: grid;
  grid-template-columns: 32px minmax(32px, 1fr) 32px;
  align-items: stretch;
  width: 100%;
  min-width: 0;
  height: 48px;
  min-height: 48px;
  overflow: hidden;
}
.wb-stage-rail > .wb-trade-summary .qt-qty button { display: grid; place-items: center; width: 100%; min-width: 0; height: 100%; padding: 0; font-size: 18px; line-height: 1; }
.wb-stage-rail > .wb-trade-summary .qt-qty input { align-self: stretch; width: 100%; min-width: 0; height: 100%; padding: 0; font-size: 16px; line-height: 48px; }
.wb-stage-rail > .wb-trade-summary .wb-grid-trade .qt-side { min-width: 0; min-height: 48px; padding: 0 6px; font-size: 15px; }
.wb-stage-rail > .wb-trade-summary .qt-lock {
  grid-area: lock;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  min-width: 0;
  height: 28px;
  min-height: 28px;
  padding: 0 8px;
  border-radius: 7px;
  color: var(--muted);
  font-size: 12px;
}
.wb-grid-trade .qt-lock[aria-pressed="false"] { border-color: var(--green); color: var(--green); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--green) 62%, transparent); }
.qt-lock-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.wb-stage-rail > .wb-trade-summary .wb-stage-metrics {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 5px;
  width: 100%;
  min-width: 0;
  padding: 0;
  background: transparent;
}
.wb-stage-rail > .wb-trade-summary .wb-stage-metric {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, auto);
  align-items: center;
  gap: 8px;
  min-width: 0;
  padding: 8px 10px;
  border: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
  border-radius: 8px;
  background: color-mix(in srgb, var(--surface-2) 88%, var(--surface));
  text-align: left;
}
.wb-stage-rail > .wb-trade-summary .wb-stage-metric small { color: var(--muted); font-size: 14px; line-height: 1.2; white-space: nowrap; }
.wb-stage-rail > .wb-trade-summary .wb-stage-metric strong {
  max-width: 100%;
  overflow: hidden;
  font-size: clamp(13px, 12cqi, 18px);
  line-height: 1.2;
  text-align: right;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 左右 1:1 與 2×2：控制艙改在畫面上方。寬窗格時身份／工具與交易摘要並排；
   窄窗格再自然堆疊，永遠優先保住券商畫面的完整寬度。 */
:is(.workbench-stage[data-layout="split"][data-split-axis="horizontal"] .wb-focus-pane, .workbench-stage[data-layout="grid"] .wb-grid-tile) > .wb-stage-rail {
  display: grid;
  grid-template-columns: minmax(220px, .9fr) minmax(300px, 1.1fr);
  align-items: start;
  overflow: visible;
  border-right: 0;
  border-bottom: 1px solid var(--border);
}
:is(.workbench-stage[data-layout="split"][data-split-axis="horizontal"] .wb-focus-pane, .workbench-stage[data-layout="grid"] .wb-grid-tile) > .wb-stage-rail > .wb-trade-summary {
  align-self: stretch;
  border-top: 0;
  border-left: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
}
:is(.workbench-stage[data-layout="split"][data-split-axis="horizontal"] .wb-focus-pane, .workbench-stage[data-layout="grid"] .wb-grid-tile) .wb-stage-metrics {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
:is(.workbench-stage[data-layout="split"][data-split-axis="horizontal"] .wb-focus-pane, .workbench-stage[data-layout="grid"] .wb-grid-tile) .wb-stage-metric {
  grid-template-columns: minmax(0, 1fr);
  gap: 3px;
  text-align: center;
}
:is(.workbench-stage[data-layout="split"][data-split-axis="horizontal"] .wb-focus-pane, .workbench-stage[data-layout="grid"] .wb-grid-tile) .wb-stage-metric strong { text-align: center; }

@container (max-width: 640px) {
  .wb-stage-rail { grid-template-columns: minmax(0, 1fr) !important; }
  .wb-stage-rail > .wb-trade-summary { border-left: 0 !important; border-top: 1px solid color-mix(in srgb, var(--border) 72%, transparent) !important; }
  .wb-stage-head > .wb-stage-tools { grid-template-columns: minmax(0, 1fr) repeat(3, minmax(34px, 40px)); }
  .wb-stage-rail > .wb-trade-summary { padding: 8px; gap: 6px; }
  .wb-stage-rail > .wb-trade-summary .wb-grid-trade { grid-template-columns: minmax(0, 1fr) minmax(88px, 1.15fr) minmax(0, 1fr); }
  .wb-stage-rail > .wb-trade-summary .wb-stage-metric { padding: 7px 6px; }
  .wb-stage-rail > .wb-trade-summary .wb-stage-metric small { font-size: 13px; }
}

@media (max-width: 520px) {
  :is(.workbench-stage[data-layout="single"], .workbench-stage[data-layout="split"] .wb-focus-pane, .workbench-stage[data-layout="grid"] .wb-grid-tile) {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto minmax(280px, 1fr);
    grid-template-areas: "rail" "notices" "canvas";
  }
  .wb-stage-rail { overflow: visible; border-right: 0; border-bottom: 1px solid var(--border); }
  .workbench-stage[data-layout="split"][data-split-axis="vertical"] .wb-focus-pane {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto minmax(280px, 1fr);
    grid-template-areas: "rail" "notices" "canvas";
  }
}

/* 0.3.74：畫面生命週期修復。
   工具列留出固定空間，身份選單只拿需要的寬度，避免最右側眼睛／全螢幕被蓋住。 */
.wb-stage-rail > .wb-stage-head.wb-stage-head {
  grid-template-columns: 22px minmax(140px, 210px) minmax(0, 1fr);
}
.wb-stage-head > .wb-pane-picker { max-width: 210px; }
.wb-stage-head .wb-pane-combined-picker > summary { padding-right: 24px; }
.wb-stage-head .wb-pane-combined-picker > .workbench-tabs { width: min(270px, calc(100vw - 24px)); }
.wb-stage-head .wb-stage-window-tools { flex: 0 0 auto; }

/* 已有最後成功影格時，啟動圖層絕不能再蓋上來。重連只用右下角小卡回報。 */
.wb-stage-live.is-loading.has-frame .brand-loading-state { display: none; }
.wb-stream-retry-countdown {
  min-height: 1.2em;
  color: var(--amber);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

/* 全螢幕＝把同一個券商 viewer 放大，不混入下單列或交易紀錄。上方只留身份與退出鍵。 */
:is(.wb-focus-pane, .wb-grid-tile).is-fullscreen,
.workbench-stage.is-single-fullscreen {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: 50px auto minmax(0, 1fr);
  grid-template-areas: "rail" "notices" "canvas";
}
:is(.wb-focus-pane, .wb-grid-tile).is-fullscreen > .wb-stage-rail,
.workbench-stage.is-single-fullscreen > .wb-stage-rail {
  grid-area: rail;
  display: block;
  height: 50px;
  overflow: visible;
  border: 0;
  border-bottom: 1px solid var(--border);
}
:is(.wb-focus-pane, .wb-grid-tile).is-fullscreen > .wb-stage-rail > .wb-trade-summary,
.workbench-stage.is-single-fullscreen > .wb-stage-rail > .wb-trade-summary { display: none; }
:is(.wb-focus-pane, .wb-grid-tile).is-fullscreen .wb-stage-head,
.workbench-stage.is-single-fullscreen .wb-stage-head {
  grid-template-columns: minmax(150px, 220px) minmax(0, 1fr);
  height: 49px;
  padding: 4px 8px;
}
:is(.wb-focus-pane, .wb-grid-tile).is-fullscreen .wb-pane-drag-handle,
:is(.wb-focus-pane, .wb-grid-tile).is-fullscreen .wb-pane-tabs,
:is(.wb-focus-pane, .wb-grid-tile).is-fullscreen .wb-stage-resource-toggle,
:is(.wb-focus-pane, .wb-grid-tile).is-fullscreen .wb-stage-more,
:is(.wb-focus-pane, .wb-grid-tile).is-fullscreen .wb-stage-close,
.workbench-stage.is-single-fullscreen :is(.wb-pane-drag-handle, .wb-pane-tabs, .wb-stage-resource-toggle, .wb-stage-more, .wb-stage-close) {
  display: none !important;
}
:is(.wb-focus-pane, .wb-grid-tile).is-fullscreen .wb-stage-tools,
.workbench-stage.is-single-fullscreen .wb-stage-tools { justify-content: flex-end; }
:is(.wb-focus-pane, .wb-grid-tile).is-fullscreen .wb-stage-viewer-layer,
.workbench-stage.is-single-fullscreen .wb-stage-viewer-layer {
  grid-area: canvas;
  width: 100%;
  height: 100%;
  max-height: none;
  aspect-ratio: auto;
  padding: 0;
}

@container (max-width: 480px) {
  .wb-stage-rail > .wb-stage-head.wb-stage-head {
    grid-template-columns: 20px minmax(0, 1fr);
    grid-template-rows: 36px 36px;
  }
  .wb-stage-head > .wb-pane-picker { max-width: none; }
  :is(.wb-focus-pane, .wb-grid-tile).is-fullscreen .wb-stage-head {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: 40px;
  }
}
}

/* ── 0.3.69 多窗格交易工作台單一版型真相 ────────────────────────────────
   CBUA 裁決：不可逆下單控制維持 pane-scoped，但在所有多窗格一律收進上方兩列。
   不再依窗格寬度把整組控制器搬到左側：搬位會讓操作目標跳動，也會永久犧牲
   券商畫面的寬。2×2 與左右 1:1 的舞台由可視高度決定，內容不得把頁面撐高。 */
:is(.workbench-stage[data-layout="single"], .workbench-stage[data-layout="split"] .wb-focus-pane, .workbench-stage[data-layout="grid"] .wb-grid-tile) {
  container-type: inline-size;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  grid-template-areas: "rail" "notices" "canvas" "problem";
  align-items: stretch;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}

:is(.workbench-stage[data-layout="single"], .workbench-stage[data-layout="split"] .wb-focus-pane, .workbench-stage[data-layout="grid"] .wb-grid-tile) > .wb-stage-notices {
  grid-area: notices;
  min-width: 0;
  max-height: 64px;
  overflow: hidden;
}
:is(.workbench-stage[data-layout="single"], .workbench-stage[data-layout="split"] .wb-focus-pane, .workbench-stage[data-layout="grid"] .wb-grid-tile) > :is(.wb-stage-viewer-layer, .wb-stage-record-layer) {
  grid-area: canvas;
  align-self: stretch;
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
}
:is(.workbench-stage[data-layout="single"], .workbench-stage[data-layout="split"] .wb-focus-pane, .workbench-stage[data-layout="grid"] .wb-grid-tile) > .wb-stage-problem-layer {
  grid-area: problem;
  z-index: 46;
  align-self: end;
  justify-self: stretch;
  min-width: 0;
  padding: 8px;
  pointer-events: none;
  background: transparent;
}
.wb-stage-problem-layer[hidden] { display: none !important; }
.wb-stage-problem-layer .wb-stage-problem-strip {
  width: 100%;
  margin: 0;
  padding: 10px 14px;
  border: 1px solid color-mix(in srgb, #f4c653 74%, transparent);
  border-radius: 10px;
  background: color-mix(in srgb, #171108 88%, transparent);
  box-shadow: 0 12px 30px rgba(0, 0, 0, .34);
  backdrop-filter: blur(16px) saturate(135%);
}
/* 問題列已有獨立自動高度區；畫面復原提示只在剩餘 canvas 內定位。 */
:is(.wb-grid-tile, .wb-focus-pane):has(> .wb-stage-problem-layer:not([hidden]))
  .wb-stage-live.has-frame.is-reconnecting .wb-stage-state {
  bottom: 12px;
}

/* 2×2 與左右 1:1 是一屏工作模式：用 flex 讓 main 取得「頁首與警示實際占用後」
   的剩餘高度，禁止猜 88px，也不依賴任何使用者的螢幕解析度。1:1 上下刻意不套
   這條固定高度鏈：它維持每格滿寬的長頁，由使用者用整頁滾輪上下瀏覽。 */
body:has(#internal-browse.panel.active[data-layout="grid"]),
body:has(#workbenchStage[data-layout="split"][data-split-axis="horizontal"]) {
  overflow: hidden;
}
body:has(#internal-browse.panel.active[data-layout="grid"]) #appShell,
body:has(#workbenchStage[data-layout="split"][data-split-axis="horizontal"]) #appShell {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100dvh;
  min-height: 0;
  overflow: hidden;
}
body:has(#internal-browse.panel.active[data-layout="grid"]) main,
body:has(#workbenchStage[data-layout="split"][data-split-axis="horizontal"]) main {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  flex: 1 1 0;
  width: 100%;
  height: auto;
  min-height: 0;
  max-width: none;
  padding: 4px;
  overflow: hidden;
}
#internal-browse.panel.active[data-layout="grid"],
body:has(#workbenchStage[data-layout="split"][data-split-axis="horizontal"]) #internal-browse.panel.active {
  box-sizing: border-box;
  flex: 1 1 0;
  height: auto;
  min-height: 0;
  gap: 0;
  overflow: hidden;
}
#internal-browse.panel.active[data-layout="grid"] > .workbench-stage,
body:has(#workbenchStage[data-layout="split"][data-split-axis="horizontal"]) #internal-browse.panel.active > .workbench-stage {
  flex: 1 1 0;
}
.workbench-stage[data-layout="grid"] {
  width: 100%;
  height: 100%;
  min-height: 0;
  margin: 0;
  gap: 8px;
  overflow: hidden;
  aspect-ratio: auto;
  align-self: stretch;
  grid-template-columns: minmax(0, var(--wb-grid-x, 50%)) minmax(0, 1fr);
  grid-template-rows: minmax(0, var(--wb-grid-y, 50%)) minmax(0, 1fr);
}
.workbench-stage[data-layout="split"][data-split-axis="horizontal"] {
  width: 100%;
  /* 左右 1:1 是兩個完整工作欄，不是只占上半屏的半套 2×2。下方空間由各欄自己的
     交易／操作紀錄接手，整個舞台貼滿可視高度，頁面本身不生成捲軸。 */
  height: 100%;
  min-height: 0;
  margin: 0;
  overflow: hidden;
  align-self: stretch;
}
.workbench-stage[data-layout="grid"] > :is(.wb-grid-tile, .wb-grid-empty),
.workbench-stage[data-layout="split"][data-split-axis="horizontal"] > .wb-focus-pane {
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}

.wb-stage-rail {
  grid-area: rail;
  z-index: 42;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: 48px 58px;
  width: 100%;
  min-width: 0;
  min-height: 0;
  overflow: visible;
  border: 0;
  border-bottom: 1px solid var(--border);
  background: linear-gradient(90deg, color-mix(in srgb, var(--surface-2) 58%, var(--surface)), var(--surface));
}
.wb-stage-rail > .wb-stage-head.wb-stage-head {
  display: grid;
  grid-template-columns: 22px minmax(174px, 1fr) minmax(174px, .92fr);
  align-items: center;
  gap: 5px;
  width: 100%;
  min-width: 0;
  min-height: 0;
  height: 48px;
  padding: 5px 6px;
  overflow: visible;
  border: 0;
  background: transparent;
}
.wb-stage-head > .wb-pane-drag-handle {
  align-self: center;
  width: 22px;
  height: 36px;
}
.wb-stage-head > .wb-pane-picker {
  display: grid;
  grid-template-columns: minmax(76px, .62fr) minmax(94px, 1fr);
  align-items: center;
  gap: 5px;
  width: 100%;
  min-width: 0;
  height: 38px;
  border: 0;
  background: transparent;
}
.wb-stage-head .wb-pane-group {
  width: 100%;
  min-width: 0;
  height: 36px;
  min-height: 36px;
  max-height: 36px;
  padding: 0 24px 0 8px;
  border: 1px solid color-mix(in srgb, var(--cyan) 24%, var(--border));
  border-radius: 8px;
  background: var(--surface-2);
  color: var(--muted);
  font-size: 12px;
}
.wb-pane-identity-row {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr);
  align-items: center;
  gap: 5px;
  min-width: 0;
}
.wb-pane-identity-row > .bot-avatar-button {
  display: grid;
  place-items: center;
  width: 36px;
  min-width: 36px;
  max-width: 36px;
  height: 36px;
  min-height: 36px;
  max-height: 36px;
  margin: 0;
  border-width: 2px;
}
.wb-pane-identity-row > .bot-avatar-button :is(img, .bot-avatar-fallback) { width: 100%; height: 100%; }
.wb-stage-head .wb-pane-bot-picker { position: relative; width: 100%; min-width: 0; }
.wb-stage-head .wb-pane-bot-picker > summary {
  display: flex;
  align-items: center;
  min-width: 0;
  min-height: 36px;
  padding: 0 24px 0 8px;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface-2);
}
.wb-stage-head .wb-pane-current { min-width: 0; gap: 5px; }
.wb-stage-head .wb-pane-current strong {
  min-width: 0;
  overflow: hidden;
  color: var(--text);
  font-size: 13px;
  font-weight: 750;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.wb-stage-head .wb-pane-bot-picker > .workbench-tabs {
  inset: auto;
  display: grid;
  position: fixed;
  z-index: 20030;
  width: min(300px, calc(100vw - 24px));
  max-height: min(52vh, 420px);
  overflow: auto;
  padding: 5px;
}
.workbench-panel[data-layout="grid"] .wb-pane-picker { min-width: 0; max-width: none; }
.workbench-panel[data-layout="grid"] .wb-pane-bot-picker > .workbench-tabs { left: 0; }

.wb-stage-head > .wb-stage-tools {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 4px;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  margin: 0;
}
.wb-stage-head .wb-stage-window-tools {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
}
.wb-stage-head .wb-pane-tabs {
  display: inline-grid;
  grid-template-columns: repeat(3, 32px);
  width: auto;
  min-width: 0;
}
.wb-stage-head .wb-pane-tabs button,
.wb-stage-head .wb-stage-window-tools > button {
  width: 32px;
  min-width: 32px;
  height: 36px;
  min-height: 36px;
  padding: 0;
}
.wb-stage-head .wb-pane-tabs svg { width: 21px; height: 21px; }
.wb-stage-head .wb-pane-tabs button[aria-pressed="true"] {
  color: var(--cyan);
  background: color-mix(in srgb, var(--cyan) 14%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--cyan) 26%, transparent);
}

.wb-stage-rail > .wb-trade-summary {
  position: static;
  display: grid;
  grid-template-columns: minmax(212px, .82fr) minmax(252px, 1.18fr);
  align-items: stretch;
  gap: 6px;
  width: 100%;
  min-width: 0;
  height: 58px;
  margin: 0;
  padding: 5px 6px;
  overflow: hidden;
  border: 0;
  border-top: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
  background: transparent;
}
.wb-stage-rail > .wb-trade-summary .wb-grid-trade {
  display: grid;
  grid-template-columns: minmax(42px, .8fr) minmax(82px, 1.2fr) minmax(42px, .8fr) 30px;
  grid-template-areas: "buy qty sell lock";
  gap: 4px;
  min-width: 0;
  padding: 0;
  background: transparent;
}
.wb-stage-rail > .wb-trade-summary .qt-buy { grid-area: buy; }
.wb-stage-rail > .wb-trade-summary .qt-sell { grid-area: sell; }
.wb-stage-rail > .wb-trade-summary .qt-qty {
  grid-area: qty;
  display: grid;
  grid-template-columns: 28px minmax(30px, 1fr) 28px;
  align-items: stretch;
  width: 100%;
  min-width: 0;
  height: 46px;
  min-height: 46px;
  overflow: hidden;
}
.wb-stage-rail > .wb-trade-summary .qt-qty button,
.wb-stage-rail > .wb-trade-summary .qt-qty input {
  width: 100%;
  min-width: 0;
  height: 100%;
  padding: 0;
  line-height: 1;
}
.wb-stage-rail > .wb-trade-summary .qt-qty button { display: grid; place-items: center; font-size: 17px; }
.wb-stage-rail > .wb-trade-summary .qt-qty input { align-self: stretch; padding-block: 0; font-size: 15px; }
.wb-stage-rail > .wb-trade-summary .wb-grid-trade .qt-side {
  min-width: 0;
  min-height: 46px;
  padding: 0 4px;
  font-size: 14px;
}
.wb-stage-rail > .wb-trade-summary .qt-lock {
  grid-area: lock;
  display: grid;
  place-items: center;
  width: 30px;
  min-width: 30px;
  height: 46px;
  min-height: 46px;
  padding: 0;
  overflow: hidden;
  border-radius: 8px;
  color: var(--muted);
  font-size: 15px;
}
.wb-grid-trade .qt-lock[aria-pressed="false"] {
  border-color: var(--green);
  color: var(--green);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--green) 62%, transparent);
}
.wb-stage-rail .qt-lock-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.wb-stage-rail > .wb-trade-summary .wb-stage-metrics {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 4px;
  width: 100%;
  min-width: 0;
  padding: 0;
  background: transparent;
}
.wb-stage-rail > .wb-trade-summary .wb-stage-metric {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  align-items: center;
  gap: 1px;
  min-width: 0;
  padding: 4px 5px;
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
  border-radius: 7px;
  background: color-mix(in srgb, var(--surface-2) 88%, var(--surface));
  text-align: center;
}
.wb-stage-rail > .wb-trade-summary .wb-stage-metric small {
  overflow: hidden;
  color: var(--muted);
  font-size: clamp(11px, 12cqi, 13px);
  line-height: 1.1;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.wb-stage-rail > .wb-trade-summary .wb-stage-metric strong {
  max-width: 100%;
  overflow: hidden;
  font-size: clamp(11px, 14cqi, 17px);
  line-height: 1.12;
  text-align: center;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 手機寬度不能把身份、六顆工具、下單與三格損益硬塞在同一條；那會讓它們彼此覆蓋。
   控制艙固定拆成四條：身份、工具、下單、損益。DOM 與事件仍是同一份，只改 grid placement，
   因此不是另一套手機功能，也不會重建串流。 */
@container (max-width: 480px) {
  .wb-stage-rail {
    grid-template-rows: auto auto;
  }
  .wb-stage-rail > .wb-stage-head.wb-stage-head {
    grid-template-columns: 20px minmax(0, 1fr);
    grid-template-rows: 36px 36px;
    gap: 4px;
    height: auto;
    min-height: 82px;
    padding: 4px;
  }
  .wb-stage-head > .wb-pane-drag-handle { grid-column: 1; grid-row: 1; }
  .wb-stage-head > .wb-pane-picker { grid-column: 2; grid-row: 1; grid-template-columns: 70px minmax(78px, 1fr); gap: 3px; }
  .wb-pane-identity-row { grid-template-columns: 32px minmax(0, 1fr); gap: 3px; }
  .wb-pane-identity-row > .bot-avatar-button { width: 32px; min-width: 32px; max-width: 32px; height: 32px; min-height: 32px; max-height: 32px; }
  .wb-stage-head > .wb-stage-tools { grid-column: 1 / -1; grid-row: 2; gap: 3px; }
  .wb-stage-head .wb-stage-window-tools { gap: 3px; }
  .wb-stage-head .wb-pane-tabs { grid-template-columns: repeat(3, 30px); }
  .wb-stage-head .wb-pane-tabs button,
  .wb-stage-head .wb-stage-window-tools > button { width: 30px; min-width: 30px; }
  .wb-stage-rail > .wb-trade-summary {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: 46px 48px;
    gap: 4px;
    height: auto;
    min-height: 106px;
    padding: 4px;
  }
  .wb-stage-rail > .wb-trade-summary .wb-grid-trade { grid-template-columns: minmax(42px, .75fr) minmax(84px, 1.2fr) minmax(42px, .75fr) 30px; gap: 3px; }
  .wb-stage-rail > .wb-trade-summary .qt-lock { width: 30px; min-width: 30px; }
  .wb-stage-rail > .wb-trade-summary .wb-stage-metric { padding-inline: 3px; }
  .wb-stage-rail > .wb-trade-summary .wb-stage-metric strong { font-size: clamp(10px, 11cqi, 14px); }
}

/* 左右 1:1 的每欄只保留一套身份與下單列；畫面和紀錄共用這套單一真相源。
   viewer 固定在上、紀錄固定在下，因此切頁籤只是換下層內容，不會拔掉上層串流。 */
.workbench-stage[data-layout="split"][data-split-axis="horizontal"] > .wb-focus-pane.is-horizontal-composite,
:is(.wb-focus-pane, .wb-grid-tile).is-viewer-record-composite,
.workbench-stage.is-viewer-record-composite {
  /* 複合窗格的可用高度由同一個 grid 分配。viewer 與紀錄一起伸縮，不能再讓
     16:9 影格先拿完高度、再把無法使用的差額丟成黑色空區。2:1 保留完整交易畫面，
     同時確保下方交易紀錄是可操作區；Chrome viewport 會再依 canvas 實際尺寸調整。 */
  grid-template-rows: auto auto minmax(0, 2fr) auto minmax(148px, 1fr);
  grid-template-areas: "rail" "notices" "canvas" "problem" "record";
}
.workbench-stage > :is(.wb-focus-pane, .wb-grid-tile).is-viewer-record-composite > .wb-stage-viewer-layer,
#workbenchStage.workbench-stage.is-viewer-record-composite > .wb-stage-viewer-layer {
  grid-area: canvas;
  justify-self: stretch;
  width: 100%;
  height: 100%;
  max-height: none;
  aspect-ratio: auto;
  overflow: hidden;
  padding: 0;
}
.workbench-stage > :is(.wb-focus-pane, .wb-grid-tile).is-viewer-record-composite > .wb-stage-viewer-layer > :is(.wb-stage-live, .wb-stage-state),
#workbenchStage.workbench-stage.is-viewer-record-composite > .wb-stage-viewer-layer > :is(.wb-stage-live, .wb-stage-state) {
  width: 100%;
  height: 100%;
  min-height: 0;
}
.workbench-stage > :is(.wb-focus-pane, .wb-grid-tile).is-viewer-record-composite > .wb-stage-record-layer,
#workbenchStage.workbench-stage.is-viewer-record-composite > .wb-stage-record-layer {
  grid-area: record;
  min-height: 148px;
  border-top: 1px solid color-mix(in srgb, var(--cyan) 24%, var(--border));
  background: color-mix(in srgb, var(--surface) 96%, var(--surface-2));
}

/* ── 0.3.64 共用產品導覽＋薄桌面殼 ──────────────────────────────────────
   五顆產品按鈕只有這一份 DOM：寬螢幕是頂部玻璃 dock，手機移到底部；桌面殼僅
   追加右上角原生視窗控制。基本導覽不依賴 WebGPU，低階裝置與 reduced-motion
   仍可立即操作；WebGPU/TSL 只適合日後加非承重的光影。 */
.app-header .tabs {
  height: 62px;
  align-items: center;
  gap: 6px;
  padding: 5px;
  overflow: visible;
  border: 1px solid rgba(151, 176, 232, .18);
  border-radius: 18px;
  background: linear-gradient(135deg, rgba(31, 42, 78, .7), rgba(12, 17, 38, .62));
  box-shadow: inset 0 1px rgba(255, 255, 255, .08), 0 14px 34px rgba(0, 0, 0, .25);
  backdrop-filter: blur(20px) saturate(140%);
}
.app-header .tab {
  display: grid;
  grid-template-rows: 24px auto;
  place-items: center;
  gap: 2px;
  min-width: 68px;
  height: 50px;
  min-height: 50px;
  padding: 4px 10px;
  border: 1px solid transparent;
  border-radius: 13px;
  color: #aeb9d2;
  background: transparent;
  font-size: 12px;
  font-weight: 750;
  line-height: 1;
  transition: transform 160ms cubic-bezier(.2,.8,.2,1), color 140ms ease,
    background 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
}
.app-header .tab svg {
  width: 21px;
  height: 21px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.app-header .tab:first-child svg path:first-child { fill: currentColor; stroke: none; }
.app-header .tab:hover {
  color: #f4f8ff;
  border-color: rgba(139, 204, 255, .2);
  background: rgba(119, 156, 230, .12);
  transform: translateY(-2px);
}
.app-header .tab.active {
  color: #ecfbff;
  border-color: rgba(92, 218, 255, .42);
  background: linear-gradient(145deg, rgba(54, 169, 219, .28), rgba(93, 91, 210, .24));
  box-shadow: inset 0 1px rgba(255,255,255,.16), 0 8px 22px rgba(26, 126, 184, .22);
}
.app-header .tab:active { transform: translateY(0) scale(.96); }
.app-header .tab:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

/* 網頁不渲染假的縮小／關閉鍵；Tauri 只帶這個薄控制島，產品導覽仍由上面的共用 DOM 負責。 */
html:not([data-host="tauri"]) :is(.window-controls, .auth-window-controls) { display: none !important; }
html[data-host="tauri"] .app-header .window-controls {
  position: fixed;
  top: 0;
  right: 0;
  z-index: 12000;
  gap: 0;
  height: var(--qw-titlebar-h, 44px);
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  backdrop-filter: none;
}
html[data-host="tauri"] :is(.app-header .window-controls, .auth-window-controls) button {
  box-sizing: border-box;
  width: 46px;
  height: var(--qw-titlebar-h, 44px);
  min-height: var(--qw-titlebar-h, 44px);
  border: 1px solid transparent;
  border-radius: 0;
  color: #c7cfdd;
  background: transparent;
}
html[data-host="tauri"] :is(.app-header .window-controls, .auth-window-controls) button:hover {
  border-color: rgba(211, 222, 241, .2);
  color: #fff;
  background: rgba(125, 139, 164, .3);
}
html[data-host="tauri"] :is(.app-header .window-controls, .auth-window-controls) .window-close:hover {
  border-color: #e81123;
  color: #fff;
  background: #e81123;
}

/* 2x2 must preserve every broker control just like single/split/fullscreen.
   The backend now renders each stream at the pane's measured aspect ratio, so
   contain fills the pane without either cover-cropping or side bars. */
.workbench-stage[data-layout="grid"] .wb-grid-tile:not(.is-fullscreen) .wb-stage-img {
  object-fit: contain;
}

@media (max-width: 760px) {
  .app-header {
    display: flex;
    min-height: 56px;
    padding: 5px 10px;
  }
  .app-header .brand { min-width: 0; }
  .app-header .brand-logo { width: 40px; height: 46px; flex-basis: 40px; }
  .app-header .brand-text { display: none; }
  .app-header .header-status { margin-left: auto; }
  .app-header .tabs {
    position: fixed;
    left: 10px;
    right: 10px;
    bottom: calc(10px + env(safe-area-inset-bottom));
    z-index: 11000;
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    width: auto;
    height: 64px;
    padding: 6px;
    overflow: visible;
    border-radius: 20px;
  }
  .app-header .tab {
    min-width: 0;
    width: 100%;
    height: 52px;
    min-height: 52px;
    padding: 4px 2px;
  }
  .app-header .tab span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  main { padding-bottom: calc(94px + env(safe-area-inset-bottom)); }
}

@media (prefers-reduced-motion: reduce) {
  .app-header .tab { transition: none; }
  .app-header .tab:hover, .app-header .tab:active { transform: none; }
}

/* 0.3.72：分組與機器人合併為一顆身份選單。已選機器人的既有頭貼就在 summary 裡，
   不再另放一顆可編輯頭貼；節能與視窗工具仍由右側工具列獨立承載。 */
.wb-stage-head > .wb-pane-picker {
  display: block;
  width: 100%;
  min-width: 0;
  height: 42px;
}
.wb-stage-head .wb-pane-combined-picker { width: 100%; min-width: 0; }
.wb-stage-head .wb-pane-combined-picker > summary {
  min-height: 42px;
  padding: 2px 28px 2px 9px;
  border-color: color-mix(in srgb, var(--cyan) 28%, var(--border));
}
.wb-stage-head .wb-pane-combined-picker > summary .wb-bot-picker-row {
  width: 100%;
  grid-template-columns: 9px 34px minmax(0, 1fr);
  gap: 8px;
}
.wb-stage-head .wb-pane-combined-picker > summary .bot-avatar-display {
  width: 34px;
  min-width: 34px;
  max-width: 34px;
  height: 34px;
  min-height: 34px;
  max-height: 34px;
}
.wb-stage-head .wb-pane-combined-picker > summary .wb-bot-picker-name {
  display: block;
  overflow: hidden;
  color: var(--text);
  font-size: 14px;
  font-weight: 750;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.wb-pane-picker-group + .wb-pane-picker-group {
  margin-top: 7px;
  padding-top: 7px;
  border-top: 1px solid color-mix(in srgb, var(--border) 74%, transparent);
}
.wb-pane-picker-group-label {
  padding: 2px 9px 5px;
  color: var(--muted);
  font-size: 11px;
  font-weight: 650;
  line-height: 1.2;
}
.wb-pane-picker-group .wb-bot-option {
  width: 100%;
  min-height: 50px;
  padding: 5px 8px;
  text-align: left;
}
.wb-pane-picker-group .wb-bot-option .wb-bot-picker-row {
  grid-template-columns: 9px 36px minmax(0, 1fr);
  gap: 8px;
}
.wb-pane-picker-group .wb-bot-option .bot-avatar-display {
  width: 36px;
  min-width: 36px;
  max-width: 36px;
  height: 36px;
  min-height: 36px;
  max-height: 36px;
}

/* 中窄窗格仍維持單列，但讓身份欄先吃剩餘空間，工具才用實際需要的寬度。
   七顆圖示在這段寬度縮成 26px；再窄於 481px 才由下一條規則換到第二列。 */
@container (min-width: 481px) and (max-width: 680px) {
  .wb-stage-rail > .wb-stage-head.wb-stage-head {
    grid-template-columns: 20px minmax(112px, 1fr) auto;
    gap: 3px;
    padding-inline: 4px;
  }
  .wb-stage-head > .wb-pane-drag-handle { width: 20px; }
  .wb-stage-head > .wb-pane-picker { height: 36px; }
  .wb-stage-head .wb-pane-combined-picker > summary {
    min-height: 36px;
    padding: 1px 20px 1px 6px;
  }
  .wb-stage-head .wb-pane-combined-picker > summary .wb-bot-picker-row {
    grid-template-columns: 8px 28px minmax(0, 1fr);
    gap: 5px;
  }
  .wb-stage-head .wb-pane-combined-picker > summary .bot-avatar-display {
    width: 28px;
    min-width: 28px;
    max-width: 28px;
    height: 28px;
    min-height: 28px;
    max-height: 28px;
  }
  .wb-stage-head .wb-pane-combined-picker > summary .wb-bot-picker-name { font-size: 12px; }
  .wb-stage-head > .wb-stage-tools,
  .wb-stage-head .wb-stage-window-tools { gap: 2px; }
  .wb-stage-head .wb-pane-tabs { grid-template-columns: repeat(3, 26px); }
  .wb-stage-head .wb-pane-tabs button,
  .wb-stage-head .wb-stage-window-tools > button {
    width: 26px;
    min-width: 26px;
    height: 34px;
    min-height: 34px;
  }
  .wb-stage-head .wb-pane-tabs svg,
  .wb-stage-head .wb-stage-window-tools svg { width: 18px; height: 18px; }
}

@container (max-width: 480px) {
  .wb-stage-head > .wb-pane-picker {
    grid-column: 2;
    grid-row: 1;
    height: 36px;
  }
  .wb-stage-head .wb-pane-combined-picker > summary { min-height: 36px; }
  .wb-stage-head .wb-pane-combined-picker > summary .wb-bot-picker-row {
    grid-template-columns: 8px 30px minmax(0, 1fr);
    gap: 6px;
  }
  .wb-stage-head .wb-pane-combined-picker > summary .bot-avatar-display {
    width: 30px;
    min-width: 30px;
    max-width: 30px;
    height: 30px;
    min-height: 30px;
    max-height: 30px;
  }
}
/* The browser shell stays usable when its execution desktop is offline. */
#webWorkspaceStatus { padding: 10px 16px; margin-block: 0 16px; color: #73dec0; font-size: 15px; }
.system-status.offline, .system-status.offline #systemStatusIcon { color: #a2aab5; }
.workspace-offline #webWorkspaceStatus { color: #a2aab5; }
.workspace-local-unavailable { display: none; }
.workspace-offline .workspace-local-unavailable { display: block; padding: 24px; color: #b8c4d3; border: 1px solid #334453; border-radius: 16px; }
.workspace-offline .workspace-local-data { display: none !important; }
.workspace-inline-pairing { margin-block: 20px; padding: 18px; border: 1px solid #334453; border-radius: 16px; }
.workspace-inline-pairing h2 { font-size: 18px; }
.workspace-offline .section-title .actions[inert] { opacity: .45; }
/* SVG defs must not create an anonymous inline line box before the viewport shell. */
.ui-icon-defs { position: absolute; width: 0; height: 0; overflow: hidden; pointer-events: none; }
