/* ============================================================================
   TRUST DESIGN · Hive 改版 — 共用零件
   ----------------------------------------------------------------------------
   ⚠ 呢個檔**只准用 var(--td-*)**，一個 #hex 都唔准出現（tokens.css 先係正本）。
   ⚠ 呢套嘢同 Bootstrap 冇關 —— 改版版面唔載 Bootstrap，所以唔會撞 .card/.table/
     .text-muted 嗰堆。舊版面照舊行 Bootstrap + static/style.css，兩套並存。
   ⚠ 數值全部由 docs/TRUST_redesign 三個 mockup 嘅 inline style 抄返，唔准「差不多」。
   ========================================================================== */

/* ── reset（只影響改版版面，因為成頁包住 .td）──────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

html, body { height: 100%; }

body.td {
  margin: 0;
  background: var(--td-canvas);
  color: var(--td-text);
  font-family: var(--td-font);
  font-size: var(--td-fs-body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ⚠⚠ 下面呢段 reset **一律用淨元素 selector，唔准寫 `body.td xxx`**。
   踩過兩次同一個坑：`body.td a` 嘅 specificity 係 (0,1,2)，大過 `.td-side__brand`
   同 `.td-btn--primary` 嗰啲 (0,1,0) —— 結果 sidebar 個 logo 字變咗 teal、
   primary 掣嘅白字變咗 body 色。呢個檔淨係喺改版版面載，唔使再加 body.td 圈範圍。
   同樣道理：呢度**唔准寫 `color: inherit`**，字色一律由 .td-btn* / .td-input* 自己負責。 */
h1, h2, h3, p, figure { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
a { color: var(--td-brand); text-decoration: none; }
a:hover { color: var(--td-brand-text); }
button, input, select, textarea { font-family: inherit; font-size: inherit; }
:focus-visible { outline: 2px solid var(--td-brand); outline-offset: 1px; }

/* mono 一律 tabular —— 金額、日期、編號要對得齊直行 */
.td-mono { font-family: var(--td-font-mono); font-variant-numeric: tabular-nums; }
.td-num  { font-family: var(--td-font-mono); font-variant-numeric: tabular-nums; text-align: right; }
.td-money { font: 600 var(--td-fs-money) var(--td-font-mono);
            font-variant-numeric: tabular-nums; text-align: right; color: var(--td-text); }
.td-dash { color: var(--td-empty); }          /* 空值「—」 */
.td-spacer { flex: 1; }
.td-vr { width: 1px; height: 20px; background: var(--td-hairline); flex: none; }

/* ══════════════════════════════════════════════════════════════════════════
   1 · 版面骨架（sidebar 218 + top bar 52）
   ══════════════════════════════════════════════════════════════════════════ */
.td-shell { display: flex; min-height: 100vh; }

/* ── sidebar ─────────────────────────────────────────────────────────── */
.td-side {
  width: var(--td-side-w); flex: none;
  background: var(--td-ink); color: var(--td-on-ink);
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh;
}
.td-side__brand {
  display: flex; align-items: center; gap: 9px;
  padding: 18px 18px 16px;
  border-bottom: 1px solid var(--td-on-ink-line);
  text-decoration: none; color: inherit;
}
.td-side__mark {
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  border: 2px solid var(--td-brand-ring-side);
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.td-side__mark img { width: 18px; height: 18px; object-fit: contain; display: block; }
.td-side__txt { min-width: 0; }
.td-side__name {
  display: block;
  font: 600 calc(12.5px * var(--td-scale))/1.2 var(--td-font); letter-spacing: var(--td-ls-brand);
  white-space: nowrap;
}
.td-side__sub { display: block; font: 400 var(--td-fs-micro)/1.4 var(--td-font); color: var(--td-on-ink-sub); }

.td-side__nav {
  flex: 1; display: flex; flex-direction: column; gap: 2px;
  padding: 14px 10px; overflow-y: auto; overflow-x: hidden;
}
.td-side__group {
  font: 600 var(--td-fs-navlabel)/1 var(--td-font-mono);
  letter-spacing: var(--td-ls-navlabel); color: var(--td-on-ink-label);
  padding: 16px 11px 7px;
}
.td-side__item {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 10px 7px 24px; border-radius: var(--td-r-control);
  color: var(--td-on-ink-2); font: 400 var(--td-fs-body)/1 var(--td-font);
  text-decoration: none;
}
.td-side__item:hover { background: var(--td-ink-hover); color: var(--td-on-ink-2); }
.td-side__item .td-side__ico { display: none; }        /* 全闊版唔出 icon（設計要求） */
.td-side__label { flex: 1; min-width: 0; }
.td-side__n {
  font: 500 var(--td-fs-micro) var(--td-font-mono); font-variant-numeric: tabular-nums;
  color: var(--td-on-ink-label);
}
.td-side__n--warn { color: var(--td-warn); }           /* 需要注意嘅數量 */
/* 第一項（我的工作台）冇分組、冇左邊縮排，active 有 5px 圓點 */
.td-side__item--top { padding-left: 10px; }
.td-side__item.is-active {
  background: var(--td-ink-active); color: var(--td-on-brand); font-weight: 500;
}
.td-side__dot { width: 5px; height: 5px; border-radius: 50%; background: var(--td-brand-ring-side); flex: none; }
.td-side__item:not(.is-active) .td-side__dot { visibility: hidden; }
.td-side__item:not(.td-side__item--top) .td-side__dot { display: none; }

/* 頂欄工具：深淺色 ＋ 文字大小（2026-08-30 由側欄底搬上嚟）
   Kelvin：「淺色深色可能放係頂上好似 TRUST GROUP 咁樣，有個光亮符號就夠，
   咁字型大小都放係上邊好似都幾好，細中大咁樣」。
   ⚠⚠ **搬位一定要換顏色**：呢批掣本來坐喺深色側欄，行 `--td-on-ink-*`；
      頂欄係 `--td-surface` 淺底 ⇒ 照搬就係淺底淺字，望落似冇咗粒掣
      （＝CLAUDE.md「改版面之前先睇嗰版載唔載得到你用嘅 class」同一個病）。
   ⚠ 深／淺兩粒掣**兩粒都寫喺 HTML**，靠 CSS 收起唔啱嗰粒 ⇒ 一撳就切、
      圖示自己跟住變，而且 zero JS 都出到正確嗰個圖示。 */
.td-tools { display: flex; align-items: center; gap: 6px; flex: none; }
.td-tool {
  width: 32px; height: 32px; padding: 0; flex: none; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--td-hairline); border-radius: var(--td-r-control);
  background: var(--td-surface); color: var(--td-text-2);
  font: 500 var(--td-fs-body-sm)/1 var(--td-font);
}
.td-tool:hover { background: var(--td-surface-hover); color: var(--td-text); }
:root[data-theme="dark"] .td-thm__b[data-theme-v="dark"] { display: none; }
:root:not([data-theme="dark"]) .td-thm__b[data-theme-v="light"] { display: none; }

/* 詳細版／簡潔版（TD-BRIEF-MODE-20261104）：簡潔版收起 `.td-explain`（純解說）。
   ⚠ !important：解說字好多有自己 display（inline style／版面 class），唔加會蓋唔到。
   ⚠ 掣闊跟字（兩個中文字），唔用 32px 方格。 */
.td-brief__b { width: auto; padding: 0 9px; white-space: nowrap; }
html.td-brief .td-brief__b[data-brief-v="1"], html:not(.td-brief) .td-brief__b[data-brief-v="0"] { display: none; }
html.td-brief .td-explain { display: none !important; }

/* 文字大小：一粒「Aa」撳開嘅細面板。用 `<details>` ⇒ 開關唔使 JS。
   ⚠ 面板 `position:absolute` 喺 `.td-fs` 入面；由 `.td-top` 一路上到 body
      冇任何 `overflow`（實測過），所以唔會被剪走。加新祖先容器要再驗。 */
.td-fs { position: relative; flex: none; }
.td-fs > summary { list-style: none; }
.td-fs > summary::-webkit-details-marker { display: none; }
.td-fs__pop {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 40;
  min-width: 168px; padding: 10px; display: flex; flex-direction: column; gap: 6px;
  background: var(--td-surface); border: 1px solid var(--td-hairline);
  border-radius: var(--td-r-card); box-shadow: 0 6px 18px rgba(19,36,34,.12);
}
.td-size__k {
  font: 600 var(--td-fs-navlabel)/1 var(--td-font-mono);
  letter-spacing: var(--td-ls-navlabel); color: var(--td-muted);
}
.td-size__g { display: flex; gap: 4px; }
/* ⚠ 三粒掣要撳得中：高 26px、最闊 40px 以上（`.td-menu__btn` 嗰個 23×15 嘅教訓）。 */
.td-size__b {
  flex: 1; min-width: 40px; height: 26px; padding: 0 6px; cursor: pointer;
  border: 1px solid var(--td-hairline); border-radius: var(--td-r-sm);
  background: transparent; color: var(--td-text-2);
  font: 400 var(--td-fs-body-sm)/1 var(--td-font);
}
.td-size__b:hover { background: var(--td-surface-hover); color: var(--td-text); }
.td-size__b[aria-pressed="true"] {
  background: var(--td-brand-fill); border-color: var(--td-brand-fill);
  color: var(--td-on-brand); font-weight: 500;
}

.td-side__user {
  padding: 12px 14px; border-top: 1px solid var(--td-on-ink-line);
  display: flex; align-items: center; gap: 9px;
}
.td-side__user b { display: block; font: 500 calc(11.5px * var(--td-scale))/1.3 var(--td-font); font-weight: 500; }
.td-side__user span { display: block; font: 400 calc(10px * var(--td-scale))/1.3 var(--td-font); color: var(--td-on-ink-sub); }
/* 抽屜底嘅「教學／登出」—— 淨係手機出（電腦版嗰兩條喺 top bar 右邊） */
.td-side__foot { display: none; gap: 16px; padding: 0 14px 14px; }
.td-side__foot a { font: 400 var(--td-fs-body-sm) var(--td-font); color: var(--td-on-ink-3); }
.td-side__foot a:hover { color: var(--td-on-ink); }

/* ── top bar ────────────────────────────────────────────────────────── */
.td-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.td-top {
  height: var(--td-top-h); flex: none;
  background: var(--td-surface); border-bottom: 1px solid var(--td-hairline);
  display: flex; align-items: center; gap: var(--td-s4); padding: 0 var(--td-s5);
  position: sticky; top: 0; z-index: 20;
}
.td-top__crumb { font: 400 var(--td-fs-body-sm)/1 var(--td-font); color: var(--td-muted); }
/* 返回掣（2026-08-05，Kelvin：「每一個 STEP 都要有返回鍵阿，好似入左去開單位又唔知點樣返回」）
   ⚠ 擺喺 sticky top bar 度，唔擺喺 `.td-head` —— 舊版面（`_td_legacy.html`）根本冇 `.td-head`，
     而且碌落去之後仲要返得到先算數。
   ⚠ **唔准跟 `.td-top__crumb` 一齊喺 ≤900px 隱藏** —— 手機先至最需要佢（sidebar 收咗做抽屜）。
     塞唔落就由 `max-width` 切字，唔准整走成粒掣。 */
.td-top__back { flex: none; max-width: 42vw; overflow: hidden; text-overflow: ellipsis; }
.td-top__search { flex: 1; max-width: 320px; margin-left: 8px; position: relative; }
/* ⭐ 頂欄「打字即出」結果框（2026-08-26 Kelvin：「直接我打咩下邊就出咩答案」） */
.td-findbox { position:absolute; top:calc(100% + 6px); left:0; right:0; min-width:280px;
              z-index:130; background:var(--td-surface); border:1px solid var(--td-hairline);
              border-radius:10px; box-shadow:0 12px 32px rgba(15,23,42,.16);
              max-height:60vh; overflow:auto; padding:6px; }
.td-findbox__g { font:600 var(--td-fs-label)/1.4 var(--td-font); color:var(--td-muted);
                 letter-spacing:var(--td-ls-label); padding:6px 10px 3px; }
.td-findbox__i { display:flex; gap:8px; align-items:baseline; padding:7px 10px;
                 border-radius:8px; text-decoration:none; min-width:0; }
.td-findbox__i b { font:600 var(--td-fs-body-sm)/1.4 var(--td-font); color:var(--td-text);
                   overflow-wrap:anywhere; }
.td-findbox__i span { font:400 var(--td-fs-meta)/1.4 var(--td-font); color:var(--td-muted);
                      overflow-wrap:anywhere; }
.td-findbox__i:hover { background:var(--td-surface-alt); }
.td-top__sync {
  display: flex; align-items: center; gap: 6px;
  font: 400 var(--td-fs-meta-sm) var(--td-font); color: var(--td-muted);
}
.td-top__link { font: 400 var(--td-fs-body-sm) var(--td-font); color: var(--td-muted); }
.td-top__link:hover { color: var(--td-text-2); }   /* ⚠ 登出唔用紅色 */

/* ⚠ content 要撐滿高度 —— mockup 嘅工作台／CRM 係「一屏到底」，
   卡片下面唔應該有一大橛空白。子元素冇 flex 就唔會被拉長，所以對短版面安全。 */
.td-content { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.td-page { flex: 1; }

/* 頁面 header（白底、底 hairline）＋ page title */
.td-head {
  background: var(--td-surface); border-bottom: 1px solid var(--td-hairline);
  padding: var(--td-pad-head) var(--td-pad-x) var(--td-pad-head-b);
}
.td-head--tabs { padding-bottom: 0; }               /* 下面有 tab 列嗰種 */
.td-head__row { display: flex; align-items: flex-start; gap: var(--td-s4); }
.td-crumb {
  display: flex; align-items: center; gap: 8px;
  font: 400 var(--td-fs-meta)/1 var(--td-font); color: var(--td-faint); margin-bottom: 9px;
}
.td-crumb a { color: var(--td-faint); }
.td-crumb a:hover { color: var(--td-text-3); }
.td-crumb__now { color: var(--td-text-2); }
.td-h1 {
  font: 600 var(--td-fs-page)/1.2 var(--td-font);
  letter-spacing: var(--td-ls-tight); color: var(--td-text);
}
.td-h1--greet { font-size: var(--td-fs-greet); }
.td-head__meta {
  display: flex; align-items: center; gap: var(--td-s2);
  font: 400 var(--td-fs-meta)/1.5 var(--td-font); color: var(--td-muted);
}
/* ⭐ 2026-08-24：內容區封頂 ＋ 置中（抄 SWIFT —— 佢個 shell 寫死 `max-w-7xl mx-auto`）。
   Kelvin：「無論放大縮細，佢個頁面都係可以保持晝面」。冇呢條，闊螢幕會將同一批內容
   一路扯到 2560px：表格每欄跟住扯闊、一行字長到要轉頭先讀完 —— 即係「每個闊度都唔同樣」。
   ⚠ 用 1440 唔用 SWIFT 個 1280 —— TD 表格欄數多（業務部 CRM 桌面 min-width 1290px），
     1280 會令佢即刻出橫捲。
   ⚠ `.td-page--wide`（甘特／月曆／排期板／單位檔案嗰批）照舊全闊 —— 嗰啲版面
     本身就係「愈闊愈有用」，封頂等於剪走佢哋嘅時間軸。 */
.td-page { padding: var(--td-pad-y) var(--td-pad-x); display: flex; flex-direction: column; gap: 16px;
           max-width: 1440px; margin-inline: auto; width: 100%; }
/* ⚠⚠⚠ 2026-08-25 Kelvin：「拉大拉細仲衰過之前，拉到某個位置啲嘢會拉長晒」。
   根因＝呢個 `--wide` 本來係 `max-width: none` ⇒ 工作台／單位列表／單位檔案／工作卡
   四版喺闊螢幕**一路扯到 2400px**：單位名同階段之間隔住成千 px 空白，KPI 卡撐到得個「10」
   浮喺一片白入面。SWIFT 由第一日就全站 `max-w-7xl mx-auto`（1280px），所以佢**點拉都唔變樣**。
   ⇒ `--wide` 改成封頂 1760（表多欄，比 `.td-page` 嘅 1440 鬆手），置中。
   ⚠ 真係要成個螢幕嘅（甘特／排期板／月曆——時間軸愈闊愈有用，封頂＝剪走時間）
     改用下面 `.td-page--canvas`。**唔准把 `--wide` 改返 `none`。** */
.td-page--wide { padding: 24px 28px; gap: var(--td-s5); max-width: 1520px; }
/* 時間軸類：愈闊愈有用，唔封頂 */
.td-page--canvas { padding: 24px 28px; gap: var(--td-s5); max-width: none; }

/* ══════════════════════════════════════════════════════════════════════════
   2 · Button（5 種）
   ══════════════════════════════════════════════════════════════════════════ */
.td-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: var(--td-ctl-h); padding: 0 12px;
  border: 1px solid var(--td-border-control); border-radius: var(--td-r-control);
  background: var(--td-surface); color: var(--td-text-2);
  font: 500 var(--td-fs-body-sm) var(--td-font); white-space: nowrap; cursor: pointer;
}
.td-btn:hover { background: var(--td-surface-hover); border-color: var(--td-border-control-hover); color: var(--td-text-2); }
/* ⚠ 鋪底一律 --td-brand-fill（兩模式共用深 teal）—— 深色模式 --td-brand 轉咗前景中調，
   白字坐佢上面唔夠對比。前景位（ghost 字／link／.td-bar>i）照舊 --td-brand，唔准跟住換。 */
.td-btn--primary {
  border-color: var(--td-brand-fill); background: var(--td-brand-fill); color: var(--td-on-brand);
}
.td-btn--primary:hover { background: var(--td-brand-fill-hover); border-color: var(--td-brand-fill-hover); color: var(--td-on-brand); }
.td-btn--ghost { border-color: transparent; background: transparent; color: var(--td-brand); }
.td-btn--ghost:hover { background: var(--td-brand-soft); border-color: transparent; color: var(--td-brand-text); }
/* ghost-outline：白底 + teal 邊 + teal 字（例：「由報價帶入」） */
.td-btn--outline { border-color: var(--td-brand); background: var(--td-surface); color: var(--td-brand-text); }
.td-btn--outline:hover { background: var(--td-brand-soft); border-color: var(--td-brand); }
.td-btn--danger { border-color: var(--td-danger-border); background: var(--td-surface); color: var(--td-alert-soft-text); }
.td-btn--danger:hover { background: var(--td-danger-hover); border-color: var(--td-danger-border); color: var(--td-alert-soft-text); }
.td-btn:disabled, .td-btn.is-disabled, .td-btn--disabled {
  border-color: var(--td-hairline-2); background: var(--td-surface-hover);
  color: var(--td-disabled); cursor: default; pointer-events: none;
}
/* 尺寸 */
.td-btn--lg { height: var(--td-btn-h); padding: 0 13px; font-size: var(--td-fs-body); }
.td-btn--sm { height: var(--td-btn-h-sm); padding: 0 10px; border-radius: var(--td-r-sm); font-size: var(--td-fs-meta); }
.td-btn--block { width: 100%; }
/* 深色底（sticky bar / toast）上面嘅掣 */
.td-btn--onink {
  border-color: var(--td-on-ink-border); background: transparent; color: var(--td-on-ink-2);
}
.td-btn--onink:hover { background: rgba(255,255,255,.06); border-color: var(--td-on-ink-border); color: var(--td-on-brand); }
.td-btn--accent {
  border: none; background: var(--td-brand-accent); color: var(--td-brand-accent-ink);
  font-weight: 600; padding: 0 14px;
}
.td-btn--accent:hover { background: var(--td-brand-accent); filter: brightness(1.06); color: var(--td-brand-accent-ink); }

/* ══════════════════════════════════════════════════════════════════════════
   3 · Input / Select / Search
   ══════════════════════════════════════════════════════════════════════════ */
.td-input, .td-select {
  height: var(--td-ctl-h); padding: 0 10px;
  border: 1px solid var(--td-hairline); border-radius: var(--td-r-control);
  background: var(--td-surface); color: var(--td-text);
  font: 400 var(--td-fs-body) var(--td-font); width: 100%;
}
.td-input::placeholder { color: var(--td-disabled-2); }
.td-input:focus, .td-select:focus { border-color: var(--td-brand); outline: none; }
.td-input--mono { font-family: var(--td-font-mono); font-variant-numeric: tabular-nums; }
.td-input--sm { height: var(--td-ctl-h-sm); padding: 0 9px; border-radius: var(--td-r-sm);
                font: 400 var(--td-fs-body-sm) var(--td-font-mono); font-variant-numeric: tabular-nums; }
.td-input--right { text-align: right; }
.td-input.is-dirty { border-color: var(--td-brand); background: var(--td-surface); color: var(--td-text); }
/* 低於底價：warn 邊 + alert 字（3a） */
.td-input.is-underprice { border-color: var(--td-warn-border); color: var(--td-alert-soft-text); }

.td-select {
  padding-right: 26px; cursor: pointer;
  font-size: var(--td-fs-body-sm); color: var(--td-text-2);
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23A8B3B0' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 10px center;
}
.td-select--toolbar { height: 30px; padding: 0 26px 0 11px; width: auto; }
.td-select--sm { height: var(--td-ctl-h-sm); border-radius: var(--td-r-sm); padding: 0 24px 0 9px; }

.td-textarea {
  min-height: 52px; padding: 8px 10px; width: 100%;
  border: 1px solid var(--td-hairline); border-radius: var(--td-r-control);
  background: var(--td-surface); color: var(--td-text);
  font: 400 var(--td-fs-body-sm)/1.6 var(--td-font); resize: vertical;
}
.td-textarea::placeholder { color: var(--td-disabled-2); }
.td-textarea:focus { border-color: var(--td-brand); outline: none; }

.td-field__label { font: 400 var(--td-fs-micro)/1 var(--td-font); color: var(--td-muted); margin-bottom: 5px; }

.td-search { position: relative; display: flex; align-items: center; }
.td-search input {
  height: 30px; width: 100%; padding: 0 44px 0 10px;
  border: 1px solid var(--td-hairline); border-radius: var(--td-r-control);
  background: var(--td-surface-hover); color: var(--td-text);
  font: 400 var(--td-fs-body-sm) var(--td-font);
}
.td-search input::placeholder { color: var(--td-faint); }
.td-search input:focus { border-color: var(--td-brand); background: var(--td-surface); outline: none; }
.td-search__hint {
  position: absolute; right: 10px; pointer-events: none;
  font: 500 var(--td-fs-micro-sm) var(--td-font-mono); color: var(--td-disabled-2);
}
.td-search--plain input { background: var(--td-surface); padding-right: 10px; }

/* 已選 filter chip（teal 邊 + 淺青底 + ×） */
/* ⚠ `flex:none` ＋ `nowrap` 唔准拆（2026-08-25）：chip 坐喺 `.td-toolbar` 個 flex row
   入面，冇呢兩句就會俾隔籬啲下拉逼到縮，「要跟進」四個字變**直行**（實測 1104px 中招）。
   塞唔落應該係成粒 chip 摺落第二行，唔係粒 chip 自己扭曲。 */
.td-chip {
  display: inline-flex; align-items: center; gap: 6px;
  flex: none; white-space: nowrap;
  height: 30px; padding: 0 11px;
  border: 1px solid var(--td-brand); border-radius: var(--td-r-control);
  background: var(--td-brand-soft); color: var(--td-brand-text);
  font: 500 var(--td-fs-body-sm) var(--td-font); cursor: pointer;
}
.td-chip__x { color: var(--td-brand); }

.td-toolbar {
  /* ⚠ `flex-wrap:wrap` 唔准拆（2026-08-25）：唔 wrap 嘅話工具列塞唔落就會**推到出界**，
     而 CLAUDE.md 硬規矩係「篩選唔准橫向捲／唔准剪走」—— 塞唔落一律摺落第二行。
     多咗嗰行高度，換返「一樣嘢都唔會靜靜唔見咗」。 */
  display: flex; flex-wrap: wrap; align-items: center; gap: 9px;
  padding: var(--td-s4) var(--td-pad-x);
  background: var(--td-surface); border-bottom: 1px solid var(--td-hairline-2);
}
.td-toolbar__count { font: 400 var(--td-fs-meta) var(--td-font); color: var(--td-faint); }

/* ══════════════════════════════════════════════════════════════════════════
   4 · Pill（狀態）／ badge ／ 質量方塊
   ══════════════════════════════════════════════════════════════════════════ */
.td-pill {
  display: inline-block; padding: 3px 8px; border-radius: var(--td-r-pill);
  font: 500 var(--td-fs-meta-sm) var(--td-font); white-space: nowrap;
}
.td-pill--brand   { background: var(--td-brand-soft);    color: var(--td-brand-text); }        /* 進行中 / 可帶入 */
.td-pill--neutral { background: var(--td-neutral-soft);  color: var(--td-neutral-soft-text); } /* 未開始 / 未上傳 */
.td-pill--warn    { background: var(--td-warn-soft);     color: var(--td-warn-soft-text); }    /* 要跟進 */
.td-pill--alert   { background: var(--td-alert-soft);    color: var(--td-alert-soft-text); }   /* 逾期 */
.td-pill--ok      { background: var(--td-ok-soft);       color: var(--td-ok-soft-text); }      /* 已完成 */
.td-pill--locked  { background: var(--td-locked);        color: var(--td-locked-text); }       /* 未開放 */
/* 計數 pill（card header 右邊嗰粒） */
.td-pill--count   { background: var(--td-neutral-soft); color: var(--td-muted);
                    padding: 3px 6px; border-radius: var(--td-r-chip);
                    font: 500 var(--td-fs-micro) var(--td-font-mono); }
.td-pill--count-warn { background: var(--td-warn-soft-2); color: var(--td-warn-text); }
/* 差異 badge（3a：新增／改價／已刪），mono 10.5/500 */
.td-diff { display: inline-block; padding: 2px 6px; border-radius: var(--td-r-chip);
           font: 500 var(--td-fs-micro) var(--td-font-mono); }
.td-diff--new     { background: var(--td-brand-soft); color: var(--td-brand-text); }
.td-diff--changed { background: var(--td-warn-soft);  color: var(--td-warn-soft-text); }
.td-diff--deleted { background: var(--td-alert-soft); color: var(--td-alert-soft-text); }

/* mono badge（SOURCE · NOTION） */
.td-badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 7px; border-radius: var(--td-r-chip);
  background: var(--td-neutral-soft); color: var(--td-neutral-soft-text);
  font: 500 var(--td-fs-label) var(--td-font-mono); letter-spacing: .02em;
}
.td-badge--admin { background: var(--td-ink); color: var(--td-on-brand); padding: 3px 7px; border-radius: var(--td-r-pill); }
.td-badge--brand { background: var(--td-brand-soft); color: var(--td-brand-text); padding: 3px 7px; border-radius: var(--td-r-pill); }

/* 質量方塊 A / B / C（17×17） */
.td-q {
  display: inline-block; width: 17px; height: 17px; border-radius: var(--td-r-chip);
  font: 600 var(--td-fs-micro-sm)/17px var(--td-font-mono); text-align: center;
}
.td-q--a { background: var(--td-alert-soft); color: var(--td-alert-soft-text); }
.td-q--b { background: var(--td-ok-soft);    color: var(--td-ok-soft-text-b); }
.td-q--c { background: var(--td-neutral-2);  color: var(--td-neutral-2-text); }
/* ⚠ Notion 真數據仲有 D 級（設計檔淨係畫咗 A/B/C）。同 C 一樣用中性色 ——
   質量級數係「參考」，唔屬於「要人處理」，唔應該着色搶眼。 */
.td-q--d { background: var(--td-neutral-2);  color: var(--td-neutral-2-text); }

/* 狀態圓點 */
.td-dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; flex: none; }
.td-dot--ok    { background: var(--td-ok); }
.td-dot--warn  { background: var(--td-warn); }
.td-dot--alert { background: var(--td-alert); }
.td-dot--idle  { background: var(--td-border-check); }
.td-dot--lg    { width: 7px; height: 7px; }
/* 顏色方塊（施工隊 / 商家；8px 圓角 2） */
.td-sq { display: inline-block; width: 8px; height: 8px; border-radius: 2px; flex: none; }
.td-sq--lg { width: 9px; height: 9px; }

/* ══════════════════════════════════════════════════════════════════════════
   5 · Toggle ／ Checkbox
   ══════════════════════════════════════════════════════════════════════════ */
.td-toggle {
  display: inline-block; position: relative; flex: none;
  width: var(--td-toggle-w); height: var(--td-toggle-h);
  border-radius: var(--td-r-toggle); background: var(--td-border-control);
  border: none; padding: 0; cursor: pointer; transition: background .12s;
}
.td-toggle::after {
  content: ''; position: absolute; top: 2px; left: 2px;
  width: var(--td-toggle-knob); height: var(--td-toggle-knob);
  border-radius: 50%; background: var(--td-on-brand); transition: left .12s;
}
.td-toggle.is-on { background: var(--td-brand-fill); }
.td-toggle.is-on::after { left: calc(100% - var(--td-toggle-knob) - 2px); }
.td-toggle:disabled { opacity: .5; cursor: default; }
.td-toggle--sm { width: 26px; height: 15px; border-radius: 8px; }
.td-toggle--sm::after { width: 11px; height: 11px; }

.td-check {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: var(--td-check); height: var(--td-check);
  border: 1.5px solid var(--td-border-check); border-radius: var(--td-r-chip);
  background: var(--td-surface); cursor: pointer; padding: 0;
  font: 600 calc(9px * var(--td-scale))/1 var(--td-font); color: transparent;
}
.td-check.is-on { background: var(--td-brand-fill); border-color: var(--td-brand-fill); color: var(--td-on-brand); }
.td-check--table { border-color: var(--td-border-control); }
.td-check--lg { width: var(--td-check-lg); height: var(--td-check-lg); }
.td-check:disabled { cursor: default; }
/* ADMIN 全開：唔可以逐項改，用 teal ● 表示 */
.td-perm-all { font: 400 var(--td-fs-body-sm) var(--td-font-mono); color: var(--td-brand); }

/* ══════════════════════════════════════════════════════════════════════════
   6 · Card
   ══════════════════════════════════════════════════════════════════════════ */
/* ⚠⚠ `overflow-x: auto` 唔係手機專用 —— 業務部 CRM 11 條欄合共 1096px，
   喺 **1280px**（218px sidebar 之後淨返 1062px）就已經塞唔落。原本 `overflow:hidden`
   會靜靜剪走最右嗰欄（「開設計檔」個掣直情唔見咗），冇任何提示。
   而家一律：塞得落就同以前一模一樣，塞唔落就捲。唔准改返 hidden。 */
.td-card {
  background: var(--td-surface); border: 1px solid var(--td-hairline);
  border-radius: var(--td-r-card); overflow: hidden; overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
/* --flush ＝ 入面有嘢要彈出 card 外（⋯ menu 以外嘅），所以唔准變捲軸容器 */
.td-card--flush { overflow: visible; }
.td-table { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.td-card__head {
  display: flex; align-items: center; gap: var(--td-s3);
  padding: 13px 16px; border-bottom: 1px solid var(--td-hairline-2);
}
.td-card__title { font: 600 var(--td-fs-section)/1 var(--td-font); color: var(--td-text); }
.td-card__title--sub { font-size: calc(13px * var(--td-scale)); }
.td-card__body { padding: 15px 16px; }
.td-card__foot {
  padding: 11px var(--td-pad-x); border-top: 1px solid var(--td-hairline-2);
  background: var(--td-surface-alt);
  display: flex; align-items: center; gap: var(--td-s3);
  font: 400 var(--td-fs-meta-sm) var(--td-font); color: var(--td-faint);
}
/* 需要處理嘅提示卡（warn 底 + warn 邊） */
.td-card--warn { background: var(--td-warn-row); border-color: var(--td-warn-border); }

/* KPI 卡 */
.td-kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--td-s4); }
.td-kpis--5 { grid-template-columns: repeat(5, 1fr); }
.td-kpi { background: var(--td-surface); border: 1px solid var(--td-hairline);
          border-radius: var(--td-r-card); padding: var(--td-s4) 15px; }
.td-kpi--warn { background: var(--td-warn-row); border-color: var(--td-warn-border); }
.td-kpi__k { font: 400 var(--td-fs-meta-sm)/1 var(--td-font); color: var(--td-muted); margin-bottom: 9px; }
.td-kpi__v { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
.td-kpi__n { font: 600 var(--td-fs-kpi)/1 var(--td-font-mono);
             font-variant-numeric: tabular-nums; letter-spacing: var(--td-ls-kpi); color: var(--td-text); }
.td-kpi__n--sm { font-size: var(--td-fs-kpi-sm); }
/* 全數金額（唔縮寫做 M／K）—— 見 tokens.css --td-fs-kpi-money */
.td-kpi__n--money { font-size: var(--td-fs-kpi-money); letter-spacing: 0; }
.td-kpi__u { font: 400 var(--td-fs-meta-sm) var(--td-font); color: var(--td-muted); }
.td-kpi__f { margin-top: var(--td-s4); font: 400 var(--td-fs-micro)/1 var(--td-font); color: var(--td-faint); }
.td-kpi__f--warn { color: var(--td-warn-text); }
.td-kpi__f--ok   { color: var(--td-ok); }
.td-kpi__f--tight { margin-top: var(--td-s2); }

/* ══════════════════════════════════════════════════════════════════════════
   7 · Table（用 grid 排欄；grid-template-columns 逐頁自己定）
   ══════════════════════════════════════════════════════════════════════════ */
.td-table { background: var(--td-surface); }
.td-thead, .td-row {
  display: grid; align-items: center; padding: 0 var(--td-pad-x);
}
.td-thead {
  height: var(--td-thead); background: var(--td-surface-alt);
  border-bottom: 1px solid var(--td-hairline);
  font: 600 var(--td-fs-label) var(--td-font-mono);
  letter-spacing: var(--td-ls-label); color: var(--td-muted);
}
.td-thead--sm { height: var(--td-thead-sm); }
.td-thead--md { height: var(--td-thead-md); }
/* ⚠⚠⚠ 行高一律 `min-height`，**永遠唔准寫死 `height`**（2026-08-25 Kelvin：
   「點解點做係創施系統都係 BUG BUG 地？但 SWIFT 系統係好好的」）。
   根因＝呢條 rule 本來寫 `height: var(--td-row)`（44px）＋ 上下 padding 0：
   單位名一長（例：「氹仔, 奧林匹克大馬路 66 號華寶花園T1-31E」）就換三行，
   撐出個框之後**同下一行文字疊晒埋一齊** —— 全系統 17 個用 `.td-row` 嘅版面都中。
   ⚠ 呢個病 2026-08-04 喺業務部 CRM 修過一次，但**淨係加咗 `.cs-t/.cs-ts` 兩個 class**，
     全域嗰條冇郁 ⇒ 之後每加一版就中多一版。今次由源頭改。
   ⚠ 實掃確認過**冇任何版面靠寫死行高做左右對齊**：甘特（`.gt-lane`）、排期板
     （`.bd-track`）、施工看板（`.sd-unit`／`.sd-lane`）全部用自己嗰套高度，唔關 `.td-row` 事。
   ⚠ `box-sizing:border-box`（本檔第 11 行）⇒ 單行行高照舊係 44px，只有真係換行先會長高。 */
.td-row {
  min-height: var(--td-row); border-bottom: 1px solid var(--td-hairline-3);
  padding-top: var(--td-row-pady); padding-bottom: var(--td-row-pady);
  font: 400 var(--td-fs-body) var(--td-font); color: var(--td-text-2);
}
.td-row:hover { background: var(--td-surface-hover); }
.td-row--batch   { min-height: var(--td-row-batch); }
.td-row--receipt { min-height: var(--td-row-receipt); }
.td-row--site    { min-height: var(--td-row-site); }
.td-row--emp     { min-height: var(--td-row-emp); }
.td-row--doc     { min-height: var(--td-row-doc); }
.td-row--group   { min-height: var(--td-row-group); background: var(--td-surface-group);
                   border-top: 1px solid var(--td-hairline); border-bottom: 1px solid var(--td-hairline); }
.td-row--group:hover { background: var(--td-surface-group); }
.td-row--last { border-bottom: none; }
/* 行狀態 */
.td-row.is-warn    { background: var(--td-warn-row); }              /* 需要處理 */
.td-row.is-warn:hover { background: var(--td-warn-row); }
.td-row.is-edited  { background: var(--td-edited-row); }            /* 已改 */
.td-row.is-off     { background: var(--td-surface-alt); }           /* 隱藏／停用 */
.td-row.is-off:hover { background: var(--td-surface-hover-2); }
.td-row.is-off .td-cell-main { color: var(--td-muted); font-weight: 400; }
.td-row.is-void    { background: var(--td-surface-alt); color: var(--td-disabled-2); }
.td-row.is-void .td-strike { text-decoration: line-through; }
.td-row.is-removed { background: var(--td-surface-alt); opacity: .62; }
.td-row.is-removed .td-strike { text-decoration: line-through; }
/* cell */
.td-cell-main { font-weight: 500; color: var(--td-text); }
.td-cell-sub  { font: 400 var(--td-fs-micro) var(--td-font); color: var(--td-muted); }
.td-cell-2    { font: 400 var(--td-fs-body-sm) var(--td-font-mono);
                font-variant-numeric: tabular-nums; color: var(--td-text-3); }
.td-cell-doc  { font: 500 calc(13px * var(--td-scale))/1.4 var(--td-font); color: var(--td-text); }
.td-cell-help { font: 400 var(--td-fs-meta-sm) var(--td-font); color: var(--td-muted); }
.td-actions   { display: flex; gap: 6px; justify-content: flex-end; }
/* 「另外 N 項…」 */
.td-more { font: 400 var(--td-fs-meta) var(--td-font); color: var(--td-faint); }
/* 揀檔上傳：原生 <input type=file> 個「選擇檔案 未選擇任何檔案」樣衰又對唔正，
   所以收埋佢，用 label 扮粒掣（揀完即刻 submit）。 */
.td-upload input[type=file] { position: absolute; width: 1px; height: 1px; opacity: 0; }
.td-upload > label { cursor: pointer; }

/* ⚠⚠ 2026-08-05 事故：`.td-upload` 入面**冇 <label>** 嘅話，上面條規則會將
   唯一嘅揀檔入口變成 1px 透明 —— 版面得返粒「上傳」掣，撳落去只彈一句
   「請選擇檔案」，但成版搵唔到邊度揀（Kelvin：「上傳即係邊度上傳先得 GA」）。
   簽約流程第 2／3 步就係咁中招。⇒ 冇 label 就唔准收埋，跌返做原生揀檔掣。
   ⚠ 加新上傳位一律用下面 `.td-filepick`，唔好再直接用 `.td-upload`。 */
.td-upload:not(:has(label)) input[type=file],
.td-filepick:not(:has(label)) input[type=file] {
  position: static; width: auto; height: auto; opacity: 1;
}

/* 揀檔案一行（新上傳位一律用呢個）：
     ① 粒 <label class="td-btn" for="…"> ＝ 揀檔入口（撳落去彈檔案總管／手機相簿）
     ② `.td-filepick__name` 即時出返揀咗邊個檔 —— 唔准得個掣冇任何回饋
     ③ 同一條 <form> 入面帶 `data-needs-file` 嘅送出掣，未揀檔前由 JS 禁用
   ⚠ 禁用狀態**唔准喺 HTML 寫死 disabled** —— JS 掛咗就會變成永遠撳唔到。
     後端本來就有「未揀檔案。」擋住，跌返做「撳得但會俾人鬧」係啱嘅方向。 */
.td-filepick { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.td-filepick input[type=file] { position: absolute; width: 1px; height: 1px; opacity: 0; }
.td-filepick > label { cursor: pointer; }
.td-filepick__name { font: 400 var(--td-fs-meta)/1.45 var(--td-font); color: var(--td-muted);
                     min-width: 0; overflow-wrap: anywhere; }
.td-filepick__name.is-set { color: var(--td-text); font-weight: 500; }

/* 上傳位嘅「檔案去咗邊 · 喺邊度睇返」——每個上傳位都要有一句，唔准淨係一粒掣 */
.td-filehint { font: 400 var(--td-fs-meta)/1.6 var(--td-font); color: var(--td-muted);
               margin-top: 7px; }
.td-filehint code { font-family: var(--td-font-mono); }

/* 表尾 dashed 快速加入列 */
.td-addrow {
  display: flex; align-items: center; gap: 7px; height: 30px; padding: 0 10px;
  border: 1px dashed var(--td-border-control); border-radius: var(--td-r-control);
  font: 400 var(--td-fs-body-sm) var(--td-font); color: var(--td-faint); background: var(--td-surface);
}
.td-addrow--boxed { padding: 7px 10px; height: auto; }

/* 進度 bar（成功率、轉化率） */
.td-bar { display: inline-block; width: 44px; height: 4px; border-radius: 2px;
          background: var(--td-hairline); overflow: hidden; flex: none; }
.td-bar > i { display: block; height: 4px; background: var(--td-brand); }
.td-bar > i.is-mid  { background: var(--td-brand-300); }
.td-bar > i.is-low  { background: var(--td-disabled); }
.td-bar--wide { width: 120px; }

/* ══════════════════════════════════════════════════════════════════════════
   8 · Tabs ／ Segmented
   ══════════════════════════════════════════════════════════════════════════ */
.td-tabs { display: flex; gap: 22px; margin-top: 16px; }
.td-tab {
  padding: 0 0 var(--td-s3); border-bottom: 2px solid transparent;
  font: 400 calc(13px * var(--td-scale)) var(--td-font); color: var(--td-muted); cursor: pointer;
}
.td-tab:hover { color: var(--td-text-2); }
.td-tab.is-active { border-bottom-color: var(--td-brand); font-weight: 600; color: var(--td-text); }
.td-tab__n { font: 500 var(--td-fs-meta-sm) var(--td-font-mono); color: var(--td-disabled-2); }
.td-tab.is-active .td-tab__n { color: var(--td-brand); }

.td-seg { display: flex; border: 1px solid var(--td-hairline);
          border-radius: var(--td-r-control); overflow: hidden; background: var(--td-surface); }
.td-seg > * {
  padding: 0 11px; height: 30px; line-height: 30px;
  font: 400 var(--td-fs-body-sm) var(--td-font); color: var(--td-muted);
  border-left: 1px solid var(--td-hairline); cursor: pointer; white-space: nowrap;
}
.td-seg > *:first-child { border-left: none; }
.td-seg > .is-active { background: var(--td-brand-soft); color: var(--td-brand-text); font-weight: 500; }

/* ══════════════════════════════════════════════════════════════════════════
   9 · Sticky bottom save bar ／ Toast
   ══════════════════════════════════════════════════════════════════════════ */
.td-savebar {
  position: sticky; bottom: 0; z-index: 15; flex: none;
  display: flex; align-items: center; gap: 12px;
  padding: 11px 16px; background: var(--td-ink);
}
.td-savebar--wide { padding: 11px var(--td-pad-x); gap: var(--td-s5); }
.td-savebar__msg { font: 500 var(--td-fs-body-sm) var(--td-font); color: var(--td-on-brand); }
.td-savebar__names { font: 400 var(--td-fs-meta) var(--td-font); color: var(--td-on-ink-3); }
.td-savebar__vr { width: 1px; height: 22px; background: var(--td-on-ink-line-2); }
.td-savebar__sums { display: flex; gap: 18px; font: 400 var(--td-fs-meta-sm)/1.5 var(--td-font); color: var(--td-on-ink-sub); }
.td-savebar__sums b { display: block; font: 500 calc(13px * var(--td-scale)) var(--td-font-mono);
                      font-variant-numeric: tabular-nums; color: var(--td-on-brand); font-weight: 500; }
.td-savebar__sums b.is-total { font-weight: 600; }
.td-savebar__sums b.is-delta { color: var(--td-on-ink-money); }
.td-savebar[hidden] { display: none; }

.td-toast {
  display: flex; align-items: center; gap: var(--td-s3);
  padding: 9px 14px; background: var(--td-ink); border-radius: 8px;
  font: 400 var(--td-fs-body-sm) var(--td-font); color: var(--td-on-ink);
}
.td-toast--float {
  position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%);
  z-index: 60; box-shadow: 0 6px 20px rgba(19,36,34,.22);
}
.td-toast--alert { background: var(--td-alert); }

/* ══════════════════════════════════════════════════════════════════════════
   10 · ⋯ menu（破壞性動作一律收埋喺呢度 + 二次確認）
   ══════════════════════════════════════════════════════════════════════════ */
.td-menu { position: relative; display: inline-block; }
.td-menu__btn {
  border: none; background: none; cursor: pointer; padding: 0 4px;
  font: 400 calc(13px * var(--td-scale)) var(--td-font); color: var(--td-disabled-2); line-height: 1;
}
.td-menu__btn:hover { color: var(--td-text-2); }
.td-menu__list {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: 40; min-width: 150px;
  background: var(--td-surface); border: 1px solid var(--td-hairline);
  border-radius: var(--td-r-control); padding: 4px;
  box-shadow: 0 6px 18px rgba(19,36,34,.12); display: none;
}
.td-menu.is-open .td-menu__list { display: block; }
.td-menu__item {
  display: block; width: 100%; text-align: left; border: none; background: none;
  padding: 7px 10px; border-radius: var(--td-r-sm); cursor: pointer;
  font: 400 var(--td-fs-body-sm) var(--td-font); color: var(--td-text-2);
}
.td-menu__item:hover { background: var(--td-surface-hover); color: var(--td-text-2); }
.td-menu__item--danger { color: var(--td-alert-soft-text); }
.td-menu__item--danger:hover { background: var(--td-danger-hover); color: var(--td-alert-soft-text); }
.td-menu__sep { height: 1px; margin: 4px 0; background: var(--td-hairline-3); }

/* ══════════════════════════════════════════════════════════════════════════
   11 · Avatar ／ 空狀態 ／ 斜紋
   ══════════════════════════════════════════════════════════════════════════ */
.td-avatar {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--td-brand-soft); color: var(--td-brand-text);
  font: 600 var(--td-fs-micro-sm)/1 var(--td-font); overflow: hidden;
}
.td-avatar { position: relative; }
.td-avatar__img { position: absolute; inset: 0; width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
.td-avatar--19 { width: 19px; height: 19px; font-size: calc(9px * var(--td-scale)); }
.td-avatar--22 { width: 22px; height: 22px; font-size: calc(9.5px * var(--td-scale)); }
.td-avatar--24 { width: 24px; height: 24px; }
.td-avatar--32 { width: 32px; height: 32px; font-size: calc(11px * var(--td-scale)); }
.td-avatar--ink { background: var(--td-ink); color: var(--td-avatar-on-ink); }
.td-avatar--side { background: var(--td-avatar-ink); color: var(--td-avatar-on-ink); font-size: var(--td-fs-micro); }
.td-avatar--none { background: transparent; border: 1px dashed var(--td-border-check); color: var(--td-disabled); }

/* 空狀態：一定要「講出現況 + 一個出路 link」 */
.td-empty-state { display: flex; flex-direction: column; align-items: center;
                  gap: var(--td-s3); padding: 32px 20px; text-align: center; }
.td-empty-state__ico {
  width: 44px; height: 44px; border-radius: 50%;
  border: 1px dashed var(--td-border-check); color: var(--td-disabled);
  display: flex; align-items: center; justify-content: center; font-size: calc(16px * var(--td-scale));
}
.td-empty-state__t { font: 500 calc(13px * var(--td-scale)) var(--td-font); color: var(--td-text); }
.td-empty-state__d { font: 400 var(--td-fs-meta)/1.7 var(--td-font); color: var(--td-muted); max-width: 420px; }

.td-ghost-bar { background: var(--td-ghost); border: 1px dashed var(--td-border-check); }
.td-photo-ph {
  background: var(--td-photo);
  display: flex; align-items: center; justify-content: center;
  font: 400 var(--td-fs-micro-sm) var(--td-font-mono); color: var(--td-disabled);
}

/* link（表格右邊嘅 next action） */
.td-link { font: 500 var(--td-fs-meta) var(--td-font); color: var(--td-brand); }
.td-link--sm { font-size: var(--td-fs-meta-sm); }

/* ══════════════════════════════════════════════════════════════════════════
   12 · Responsive（desktop-first）
       ≤1279px：sidebar 收成 56px icon rail
       ≤900px ：sidebar 變抽屜（漢堡掣開）、版面單欄、表格橫向捲

   ⚠⚠ 2026-08-01 Kelvin：「個系統執好左網頁版，但完全冇考慮過手機版」。
      設計檔本身寫明 desktop 為主（README「Target」），所以手機版**冇設計圖** ——
      呢度做嘅係「同一套 token 落手機」，唔係另畫一套：色、圓角、行高全部照用，
      只係改**排法**（欄數／padding／字級大細）。加新版面記得一齊補呢段。
   ══════════════════════════════════════════════════════════════════════════ */
/* ⭐⭐ 側欄收起／展開（2026-08-24 Kelvin：「好似內帳個邊咁樣，側欄可以做收起動作，
   咁好似使用起上黎方便D」）——**照抄內帳**（`TrustAccounting/static/neizhang.css:540`）。

   同日早前我做過一版「☰ 撳一撳變 56px icon rail」，佢睇完指名要內帳嗰種 ⇒ 換走：
     · 收起＝側欄**完全消失**（唔係剩返 icon）—— 畫面最闊
     · 兩粒掣都寫**中文字**（側欄「收起」／頂欄「選單」），唔用符號 —— 一睇就知做乜
     · 純 CSS checkbox（`#tdSideOff`）—— 冇 JS 都收得埋開得返；JS 只負責「記住」

   ⚠⚠ 同時保留咗嗰版嘅**最重要修正**：`@media (max-width:1279px)` 唔准再郁 sidebar。
     舊版一過 1279px 就自動由 218px 變 rail ⇒ 用家撳 Ctrl+ 放大，CSS viewport 縮細，
     一跨過就成個側欄自己變樣 ＝ 佢投訴嘅「放大縮細走樣」。而家闊窄一律由人手決定。
   ⚠ 手機（≤900px）唔受呢個 checkbox 影響 —— 嗰邊係抽屜（`body.nav-open`），
     收起態冇意義。所以下面兩條規則包喺 `@media (min-width: 901px)` 入面。 */
/* 預設兩粒都唔出（手機、同埋未收起嗰陣）。
   ⚠⚠ 一定要排喺下面個 @media **之前** —— specificity 一樣嘅時候後面嗰條贏，
     排喺後面就會蓋走 `display:inline-flex`，粒「收起」永遠唔出（實撞過）。 */
.td-side__hide, .td-top__show { display: none; }

@media (min-width: 901px) {
  #tdSideOff:checked ~ .td-shell .td-side { display: none; }
  /* 側欄入面粒「收起」 */
  .td-side__top { display: flex; align-items: stretch; border-bottom: 1px solid var(--td-on-ink-line); }
  .td-side__top .td-side__brand { flex: 1; min-width: 0; border-bottom: 0; }
  .td-side__hide {
    flex: none; align-self: center; cursor: pointer; user-select: none;
    display: inline-flex; align-items: center; margin-right: 12px;
    padding: 4px 9px; border-radius: 7px;
    font: 700 var(--td-fs-micro)/1 var(--td-font); color: var(--td-on-ink-3);
    background: var(--td-ink-hover); border: 1px solid var(--td-on-ink-line);
  }
  .td-side__hide:hover { background: var(--td-ink-active); color: var(--td-on-ink); }
  /* 收起之後喺 top bar 出返粒「選單」 */
  #tdSideOff:checked ~ .td-shell .td-top__show {
    display: inline-flex; align-items: center; flex: none; cursor: pointer;
    padding: 4px 11px; border: 1.5px solid var(--td-brand); border-radius: 99px;
    color: var(--td-brand-text); font: 700 var(--td-fs-meta-sm)/1.4 var(--td-font);
    user-select: none;
  }
  #tdSideOff:checked ~ .td-shell .td-top__show:hover {
    background: var(--td-brand-fill); color: var(--td-on-brand);
  }
}
@media (max-width: 1279px) {
  /* ⚠⚠ sidebar 嘅嘢**一條都唔准喺呢度出現** —— 呢個斷點淨係管表格。
     舊版喺呢度令側欄自動收成 icon rail，結果用家撳 Ctrl+ 放大就成個側欄自己變樣
     （Kelvin 2026-08-24 投訴嘅「走樣」）。收起／展開一律由人手掣話事（見上面）。 */
  .td-scroll-x { overflow-x: auto; }
  .td-scroll-x .td-thead, .td-scroll-x .td-row { min-width: 1100px; }
}


/* ── 漢堡掣：得手機先出（≥901px 用側欄入面粒「收起」／頂欄粒「選單」）────── */
.td-burger {
  display: none; flex: none;
  width: 32px; height: 32px; padding: 0; margin-left: -4px;
  align-items: center; justify-content: center;
  border: 1px solid var(--td-hairline); border-radius: var(--td-r-control);
  background: var(--td-surface); color: var(--td-text-2); cursor: pointer;
}
.td-burger:hover { background: var(--td-surface-hover); }
.td-backdrop { display: none; }

/* ══════════════════════════════════════════════════════════════════════════
   手機（≤900px）
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 900px) {
  /* 版面 padding／字級整體收細一級 —— 改 token 就成站一齊跟，唔使逐版執 */
  :root {
    --td-pad-x:        14px;
    --td-pad-y:        14px;
    --td-pad-head:     13px;
    --td-pad-head-b:   12px;
    /* ⚠ 手機呢幾級都要接返 --td-scale，唔係佢調大字級嗰陣手機唔會跟 */
    --td-fs-page:      calc(19px * var(--td-scale));
    --td-fs-greet:     calc(20px * var(--td-scale));
    --td-fs-kpi:       calc(22px * var(--td-scale));
    --td-fs-kpi-sm:    calc(21px * var(--td-scale));
    --td-fs-kpi-money: calc(16px * var(--td-scale));
  }

  /* ── sidebar → 抽屜。抽屜入面出返全名（唔用 icon rail），因為手機淨係
        撳開一陣，睇字快過認 icon。 ────────────────────────────────────── */
  .td-side {
    position: fixed; left: 0; top: 0; bottom: 0; z-index: 60;
    width: var(--td-side-w); height: 100%;
    transform: translateX(-100%); transition: transform .18s ease;
  }
  body.nav-open .td-side { transform: none; box-shadow: 0 12px 40px rgba(19,36,34,.38); }
  body.nav-open { overflow: hidden; }
  .td-backdrop { position: fixed; inset: 0; z-index: 55; background: rgba(19,36,34,.34); }
  body.nav-open .td-backdrop { display: block; }

  .td-side__brand { padding: 16px 16px 14px; justify-content: flex-start; }
  .td-side__brand .td-side__txt { display: block; }
  .td-side__group { font-size: var(--td-fs-navlabel); padding: 15px 11px 6px; text-align: left; }
  .td-side__group::after { display: none; }
  /* 手指撳得中：行高由 7px padding 加到 10px */
  .td-side__item { padding: 10px 10px 10px 24px; justify-content: flex-start; }
  .td-side__item--top { padding-left: 10px; }
  .td-side__item .td-side__ico { display: none; }
  .td-side__item .td-side__label, .td-side__item .td-side__n { display: block; }
  .td-side__item--top .td-side__dot { display: block; }
  .td-side__user { justify-content: flex-start; padding: 12px 14px; }
  .td-side__user div { display: block; }
  .td-side__foot { display: flex; }

  /* ── top bar：漢堡 ＋ 搜尋 ＋ 登出（麵包屑／同步時間／教學搬咗落抽屜）─── */
  .td-burger { display: inline-flex; }
  .td-top { padding: 0 12px; gap: var(--td-s3); }
  .td-top__crumb, .td-top__sync, .td-top .td-vr, .td-top__link--guide { display: none; }
  .td-top__search { margin-left: 0; max-width: none; }
  /* ⚠ 一定要寫 `.td-top .td-spacer`，唔准寫裸 `.td-spacer` —— 嗰個係全站共用
     （ui.css 上面嗰條），裸選擇器會剷親每一版 toolbar 嘅排版。
     點解要收：手機 `.td-top__search` 同 `.td-spacer` 兩個都 `flex:1`，平分剩餘位
     ⇒ 搜尋框得 93px，placeholder 一個字都睇唔到（實測 375px）。收咗變 196px。 */
  .td-top .td-spacer { display: none; }
  .td-search__hint { display: none; }          /* 手機冇 ⌘K */

  /* ── 通用排版 ─────────────────────────────────────────────────────── */
  .td-head__row { flex-wrap: wrap; }
  .td-head__row > .td-actions,
  .td-head__row .td-btn { flex-shrink: 0; }
  .td-toolbar { flex-wrap: wrap; gap: var(--td-s2); }
  .td-tabs { gap: 16px; overflow-x: auto; scrollbar-width: none; }
  .td-tabs::-webkit-scrollbar { display: none; }
  .td-tab { white-space: nowrap; }
  /* ⚠ `!important`：工作台個 KPI 列係 inline style 落欄數（repeat(N,1fr)，
     N 跟員工版塊變），inline 贏晒 stylesheet，唔加就永遠 4 欄逼喺 390px 度。 */
  .td-kpis, .td-kpis--5 { grid-template-columns: repeat(2, 1fr) !important; gap: var(--td-s3); }
  .td-kpi { padding: 12px 12px; }
  .td-kpi__k { margin-bottom: 6px; }

  /* ── 表格：手機一律撐到 720px 再橫向捲（好過逼到一個字一行）。
        逐版可以喺自己個 <style> 覆蓋 `--td-tmin`（排成卡片嗰啲寫 0）。
        捲嗰層喺上面 `.td-card` / `.td-table` 已經常設，呢度淨係落最闊值。 */
  .td-thead, .td-row { min-width: var(--td-tmin, 720px); }

  .td-savebar { flex-wrap: wrap; gap: var(--td-s3); padding: 10px 14px; }
  .td-savebar__sums { gap: 12px; }
  .td-toast--float { left: 12px; right: 12px; bottom: 12px; transform: none; }
  .td-menu__list { min-width: 170px; }
}

/* ⚠ 特登**唔**再收埋做一欄 —— KPI 一張張直落，真內容就會被推到screen 外面。
   183px 闊裝得落 17px mono 嘅 16,891,200。 */

/* 列印：改版版面唔會印，但保住唔好印出黑色 sidebar */
@media print {
  .td-side, .td-top, .td-savebar, .td-toast { display: none !important; }
  body.td { background: var(--td-surface); }
}

/* ── 面料採購清單（2026-09-07）：一行＝項目｜前置步驟｜狀態／動作 —— 看板同單位工序 tab 共用 ──
   ⚠ 塞唔落一律 wrap／摺行，唔准剪走；手機一格一行。全部行 token，唔准寫 hex。 */
.pol { display:grid; grid-template-columns:minmax(150px,1.1fr) minmax(220px,2fr) minmax(200px,1.1fr);
       gap:4px 14px; padding:10px 0; border-bottom:1px solid var(--td-hairline-4); align-items:start; }
.pol:last-child { border-bottom:0; }
.pol.is-warn { box-shadow:inset 3px 0 0 var(--td-alert); padding-left:8px; }
.pol--done .pol__nm { color:var(--td-muted); }
.pol__nm { font:600 var(--td-fs-body)/1.4 var(--td-font); color:var(--td-text); overflow-wrap:anywhere; }
.pol__sub { font:400 var(--td-fs-micro)/1.5 var(--td-font); color:var(--td-muted); }
.pol__late { color:var(--td-alert); font-weight:600; }
.pol__steps, .pol__st { display:flex; flex-wrap:wrap; gap:6px; align-items:center; }
.pol__st { justify-content:flex-end; }
.pol__inline { display:inline; }
.pol__note { grid-column:1 / -1; font:400 var(--td-fs-meta)/1.5 var(--td-font); color:var(--td-muted); }
.pol__more > summary { cursor:pointer; list-style:none; font:400 var(--td-fs-meta)/1.5 var(--td-font);
                       color:var(--td-brand-text); }
.pol__more > summary::-webkit-details-marker { display:none; }
.pol__more--btn > summary { display:inline-flex; align-items:center; min-height:28px; padding:0 10px;
                            border-radius:var(--td-r-control); background:var(--td-brand-soft); font-weight:600; }
.pol__more[open] > form { display:flex; flex-wrap:wrap; gap:6px; align-items:center; margin-top:4px; }
.pol__edit label { display:flex; flex-direction:column; gap:2px; flex:1 1 140px; min-width:0; }
.pol-g { border-bottom:1px solid var(--td-hairline); padding-bottom:8px; }
.pol-g:last-child { border-bottom:0; }
.pol-h { display:flex; flex-wrap:wrap; gap:8px 12px; align-items:center; padding:12px 0 4px; }
.pol-h__nm { font:600 var(--td-fs-body)/1.4 var(--td-font); color:var(--td-text); text-decoration:none; }
.pol-h__nm:hover { color:var(--td-brand-text); }
.pol-na, .pol-empty { font:400 var(--td-fs-meta)/1.6 var(--td-font); color:var(--td-muted); padding:6px 0; }
.pol-na__i { display:inline-flex; gap:6px; align-items:center; margin-right:10px; }
@media (max-width: 640px) {
  .pol { grid-template-columns:1fr; }
  .pol__st { justify-content:flex-start; }
  .pol .td-btn--sm { min-height:36px; }
}

/* 多頁工作（2026-11-02 TD-HIVE-TABS-20261102）：分頁入面嘅頁面只留內容、返回同頁名；
   選單、搜尋、通知、帳戶由外面殼頁負責。class 由 _td_head.html 喺畫之前落。 */
html.td-in-frame .td-side, html.td-in-frame .td-backdrop, html.td-in-frame .td-mobile-nav,
html.td-in-frame .td-burger, html.td-in-frame .td-top__show, html.td-in-frame .td-top__search,
html.td-in-frame .td-bell, html.td-in-frame .td-tools, html.td-in-frame .td-account,
html.td-in-frame .td-tabs-go { display: none !important; }
html.td-in-frame .td-shell { padding-bottom: 0 !important; }
.td-tool.td-tabs-go, .td-clarity .td-tool.td-tabs-go { width: auto; gap: 5px; padding: 0 10px; text-decoration: none;
  font: 600 var(--td-fs-meta-sm, 13px)/1 var(--td-font); color: var(--td-text-2, var(--td-text)); white-space: nowrap; }
@media (max-width: 900px) { .td-tool.td-tabs-go, .td-clarity .td-tool.td-tabs-go { display: none; } }
html.td-in-frame .td-top, html.td-in-frame .td-clarity .td-top { height: auto; min-height: 44px; padding-block: 6px; }
html.td-in-frame .td-top:not(:has(.td-top__back, .td-top__help, .td-top__crumb:not(:empty))) { display: none; }

/* 手機簡化（TD-MOBILE-SIMPLE-20261105，聰少）：手機（≤640 闊）簡潔版再收起 `.td-desk`（收款、排期、材料、
   詳細查驗、準備資料、驗收記錄等電腦版細項），只留工序／步驟粗體＋狀態。電腦版唔郁（簡潔只收 td-explain）。
   `.td-mobile-only` 只喺手機簡潔版出（例如「其餘細項用電腦或撳詳細」提示）。 */
.td-mobile-only { display: none; }
@media (max-width: 640px) {
  html.td-brief .td-desk { display: none !important; }
  html.td-brief .td-mobile-only { display: block; }
  html.td-brief .uw-wrap .up-check h4 { font-size: 16px; }
  html.td-brief .uw-wrap .up-check-meta.ck-st { font-size: 13px; }
}
/* 驗收步驟狀態牌：一眼分到完成／待驗收／未完成 */
.up-check-meta.ck-st { display: inline-block; padding: 2px 9px; border-radius: 999px; font-weight: 600; }
.ck-st--done { background: var(--td-brand-soft); color: var(--td-brand-text); }
.ck-st--wait, .ck-st--claim { background: var(--td-warn-soft); color: var(--td-warn-soft-text, var(--td-warn)); }
.ck-st--ng { background: var(--td-alert-soft); color: var(--td-alert-soft-text, var(--td-alert)); }
/* 一步上傳：成粒掣就係揀相，揀完自動上傳（JS）；冇 JS 時照用原本「上傳相片」掣 */
.up-pick { display: inline-flex; align-items: center; gap: 7px; min-height: 44px; padding: 8px 14px; border-radius: 22px;
  border: 1px solid var(--td-border-control); background: var(--td-brand-soft); color: var(--td-brand-text); font-weight: 600; cursor: pointer; }
.up-pick input[type=file] { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; }
.up-pick:focus-within { outline: 2px solid var(--td-brand-text); outline-offset: 2px; }
html.td-js-pick form:not(:has(.up-pick-pending)) .up-pick-submit { display: none; }
/* r5 P3：inline 刷新後 JS 提示行冇咗，但揀相欄仍標記未上傳 ⇒ 用 CSS 補返提示 */
html.td-js-pick form:has(.up-pick-pending):not(:has(.up-pick-wait))::after { content: "相片已揀好，未上傳。請撳「上傳相片」。"; display: block; margin-top: 6px; color: var(--td-warn-text, var(--td-warn)); font-size: 14px; }
/* 單位工作台師傅相片（TD-MOBILE-SIMPLE-20261105 批 2） */
.uw-wp-grid { list-style: none; padding: 0; margin: 12px 0 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 12px; }
.uw-wp { border: 1px solid var(--td-hairline); border-radius: 12px; padding: 10px; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.uw-wp.is-free { border-color: var(--td-warn-border, var(--td-warn)); }
.uw-wp img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: 8px; display: block; }
.uw-wp-meta b { display: block; font-size: 14px; overflow-wrap: anywhere; }
.uw-wp-meta span { display: block; color: var(--td-muted); font-size: max(12px, calc(11px * var(--td-scale, 1.14))); overflow-wrap: anywhere; }
.uw-wp-form { display: flex; flex-direction: column; gap: 6px; }
.uw-wp-form select { width: 100%; min-width: 0; min-height: 40px; }
/* 一頁篩選（批 4）：狀態牌、客人睇到剔格、確認／唔要 */
.uw-wp-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 6px; }
.uw-wp-album { display: flex; align-items: center; gap: 6px; font-size: 14px; min-height: 36px; }
.uw-wp-album input { width: 18px; height: 18px; }
.uw-wp-acts { display: flex; gap: 6px; }
.uw-wp-acts .btn { flex: 1; min-height: 40px; }
.uw-wp-rejected { margin-top: 12px; }
.uw-wp-rejected summary { cursor: pointer; color: var(--td-muted); }
.uw-wp-rejected .uw-wp { opacity: .7; }
/* 師傅相按工程類別篩選（TD-PHOTO-TRADE-FILTER-20261106） */
.uw-wp-filter { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 10px; }
.uw-wp-filter a { display: inline-flex; align-items: center; gap: 6px; flex: none; white-space: nowrap; min-height: 36px; padding: 0 12px;
  border: 1px solid var(--td-hairline); border-radius: 999px; color: inherit; text-decoration: none; font-size: 14px; background: var(--td-surface); }
.uw-wp-filter a b { font-weight: 600; color: var(--td-muted); font-variant-numeric: tabular-nums; }
.uw-wp-filter a:hover { border-color: var(--td-brand); }
.uw-wp-filter a.is-on { border-color: var(--td-brand); background: var(--td-brand-soft); color: var(--td-brand-text, var(--td-brand)); font-weight: 600; }
.uw-wp-filter a.is-on b { color: inherit; }
.uw-wp-meta small { display: block; color: var(--td-muted); font-size: 13px; overflow-wrap: anywhere; }
.uw-wp-step summary { cursor: pointer; color: var(--td-muted); font-size: 13px; min-height: 28px; display: flex; align-items: center; }
.uw-wp-step select { margin-top: 4px; }

/* 📱 TD-MOBILE-APP-20261106：頂欄「手機版」掣只喺手機闊度出 */
.td-m-go { display: none; }
@media (max-width: 640px) { .td-m-go { display: inline-flex; } }
