/* qlink-theme-rules.css v1.0.0 · git@git_ai:Qlink-TP/qlink_theme.git · 產生檔，請勿手改 */

/* =========================================================================
   qlink-theme-rules.css — 共用的樣式「決定」，不是「值」。產生檔，請勿手改。
   載入順序：qlink-theme.css → qlink-theme-packs.css → 這支 → 專案自己的樣式。

   為什麼獨立一支：token 只能統一「顏色是什麼」，各專案還是各自寫 th {},
   照樣會漂。規則住進共用檔，改一次才會全部跟著動。
   但也因為它會真的動到版面，所以跟 token 分開 —— 專案可以只要 token。
   ========================================================================= */

/* ---- 字級 -------------------------------------------------------------
   六階住在 qlink-theme.css（--fs-1 … --fs-6），這裡只決定「誰用哪一階」。

   ⚠️ 這層只是預設值，權重很低（body/h1/th 都是 (0,0,1)）。專案裡任何
   `table.month-matrix { font-size: 13px }` 這種 (0,1,0) 的規則都壓得過它，
   而且**不會報錯**——檔案有載入，字級卻一動也不動。要讓主題真的管到字，
   專案端的硬編碼 px 必須拿掉，換成 var(--fs-n)。見 docs/adopting.md。

   單位刻意用 px 不用 rem：這幾個介面是密集資料表，欄寬與 nowrap 都跟
   絕對字級綁著。rem 會被使用者的瀏覽器基準字級放大，表格會橫向爆開。
   要跟隨使用者設定的專案，自己覆寫 --fs-* 即可（它們就是為此存在）。
   ---------------------------------------------------------------------- */
body {
  font-size: var(--fs-3);
  line-height: 1.55;
}

h1 { font-size: var(--fs-6); }
h2 { font-size: var(--fs-5); }
h3 { font-size: var(--fs-4); }
h4, h5, h6 { font-size: var(--fs-3); }

/* 標題行高單獨壓低：1.55 是給連續內文的，套到標題會鬆散。 */
h1, h2, h3, h4, h5, h6 { line-height: 1.25; }

small, caption, figcaption { font-size: var(--fs-2); }

/* ---- 表格 -------------------------------------------------------------
   欄位名稱置中、數字靠右。CSS 無法自己判斷哪一欄是數字，
   所以數字欄要在 <td> 上掛 class="num"，這是本層的唯一契約。
   ---------------------------------------------------------------------- */
table {
  border-collapse: collapse;
  background: var(--bg-surface);
  color: var(--text-primary);
}

th {
  text-align: center;              /* 欄位名稱一律置中 */
  font-weight: 600;
  background: var(--bg-surface-2);
  color: var(--text-secondary);
}

th, td {
  padding: 6px 10px;
  line-height: 1.35;                /* 密集排版，比內文的 1.55 緊 */
  border-bottom: 1px solid var(--border-grid, var(--border-subtle));
}

/* 數字靠右 + 等寬數字。少了 tabular-nums，比例字體下各列位數對不齊，
   靠右也還是掃不動 —— 這兩件事要一起做才有意義。 */
td.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* th.num 刻意不右對齊：欄位名稱一律置中，見上。 */

/* ---- 可互動的資料格 ---------------------------------------------------
   契約：`<td class="cell" tabindex="0">`，選中掛 aria-selected、
   不可編輯掛 aria-disabled。跟 td.num 可以並用（`class="cell num"`）。

   三件事是這一組存在的理由：

   1. **透明的 3px 前緣一開始就佔位。** 選中才長出邊框的話，整欄會在點選當下
      橫移 3px —— 而那是使用者眼睛正盯著的那一格。
   2. **選中同時走底色與色條兩個通道。** 資料表常常整片同色，只靠底色的話
      「選中」與「某種狀態底色」在灰階下無法區分。
   3. **tabindex 是契約的一部分，不是可選項。** 沒有它，格子只有滑鼠點得到，
      而焦點框（本檔最後一段）也永遠不會出現。CSS 這邊給不了鍵盤可達性。

   刻意**不提供**任何語意狀態色（已確認、待處理、延用之類）。那是領域語彙，
   主題對領域一無所知；要的話用 --success / --warning / --danger 自己接。
   ---------------------------------------------------------------------- */
td.cell {
  border-inline-start: 3px solid transparent;
  cursor: pointer;
}

td.cell:hover { background: var(--bg-surface-3); }

td.cell[aria-selected="true"] {
  border-inline-start-color: var(--accent);
  background: var(--bg-surface-3);
  font-weight: 600;
}

td.cell[aria-disabled="true"] {
  color: var(--text-disabled);
  cursor: not-allowed;
}

td.cell[aria-disabled="true"]:hover { background: none; }

/* 斑馬紋。--bg-stripe 來自加購包；沒載就是透明，不會壞。
   :where() 把 :nth-child 的權重歸零，整條只剩 (0,0,2) —— 這樣專案自己的
   語意色帶（tr.week-start 之類，(0,1,0)）一定壓得過斑馬紋。
   沒有這層保護的話，色帶只在「剛好落在單數列」時才看得到，落在偶數列
   就靜默地變回一條普通斑馬紋。CAI_Exchange_Rate 正式站上就是這個狀況：
   它的斑馬紋規則權重比 tr.avg 高，週平均列的底色時有時無，取決於那一列
   排在第幾筆 —— 而資料一多就沒有人分得出來那是規則還是巧合。 */
tbody tr:where(:nth-child(even)) { background: var(--bg-stripe, transparent); }

/* 合計列：跟資料列拉開，但不靠顏色（列印與色盲都要成立）。
   排在 tbody tr:last-child 之前，是因為它權重較低 —— 兩者目標互斥
   （tfoot vs tbody），順序不影響結果，只影響 lint 的可讀性檢查。 */
tfoot td {
  border-top: 2px solid var(--border-strong);
  border-bottom: 0;
  font-weight: 600;
}

tbody tr:last-child td { border-bottom: 0; }

/* ---- 勾選框與選項鈕 ---------------------------------------------------
   用 accent-color，不用 appearance:none 自己重畫。原生控制項的勾勾會由
   瀏覽器依背景自動選對比色，還保有 indeterminate 狀態、高對比模式與
   系統的 focus 行為 —— 自己重畫等於把這些全部退掉，換來的只是形狀。
   ---------------------------------------------------------------------- */
input[type="checkbox"], input[type="radio"] {
  accent-color: var(--accent);
  inline-size: 1.05em;
  block-size: 1.05em;
  margin: 0;
}

/* 標籤與框對齊。用 :has 才不必要求專案改 HTML 結構。 */
label:has(> input[type="checkbox"], > input[type="radio"]) {
  display: inline-flex;
  gap: 0.5em;
  align-items: center;
  cursor: pointer;
}

/* ---- 頁籤 -------------------------------------------------------------
   狀態掛在 aria-selected，不掛 class。這樣「看起來被選中」與「真的被選中」
   無法各自漂移 —— 少了 aria-selected 就不會亮，補上就自動亮。
   選中同時用顏色與 2px 底線兩個通道，色盲與灰階列印都還讀得出來。

   這裡不包 :where()。焦點框包，是因為它是安全網，專案該蓋得掉；
   元件不是 —— 專案只要有一條通用的 button {} 就會贏過權重 0 的規則，
   頁籤靜默地退回成普通按鈕。[role="tab"] 是 (0,1,0)，壓得過 button 的
   (0,0,1)，而專案想覆寫時一個 class 就夠。
   ---------------------------------------------------------------------- */
[role="tablist"] {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--border-subtle);
}

[role="tab"] {
  appearance: none;
  padding: 8px 14px;
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;                 /* 專案通用 button 的圓角會讓底線兩端翹起來 */
  margin-bottom: -1px;              /* 壓在 tablist 的底線上 */
  background: transparent;
  color: var(--text-secondary);
  font: inherit;

  /* font 簡寫會把 font-size 重設掉，所以字級要排在它後面。
     第一層是主要導覽，用 --fs-4（那一階的定義就是「強調、頁籤、區塊標題」）；
     第二層在下面降回 --fs-3。兩層必須真的差一階 —— 一開始這裡是 font:inherit，
     結果兩層都落在 --fs-3，註解寫著「小一號」而畫面上一模一樣。 */
  font-size: var(--fs-4);
  cursor: pointer;
}

/* ---- 巢狀頁籤 ---------------------------------------------------------
   不需要新的契約：**巢狀本身就是訊號**。第二層頁籤就是長在 tabpanel 裡面的
   頁籤，所以選擇器是 `[role="tabpanel"] [role="tab"]`（(0,2,0)，壓得過第一層
   的 (0,1,0)）。加一個 .subtab 或 data-level="2" 只是把已經在 DOM 裡的事實
   再抄一遍，而抄本會漂。

   視覺上刻意沿用同一套語言，只換兩件事：小一號，而且選中是**填色的框**不是
   底線。兩層都畫底線的話畫面會出現兩條平行線，沒有人分得出哪條屬於哪層；
   換成填色則兩層一眼可辨，而且填色是形狀通道不是顏色通道 —— 灰階列印
   與色盲都還讀得出來，跟第一層的 2px 底線同一個標準。
   ---------------------------------------------------------------------- */
[role="tabpanel"] [role="tablist"] {
  gap: 6px;
  border-block-end: 0;              /* 第一層已經有一條，兩條就是噪音 */
}

[role="tabpanel"] [role="tab"] {
  padding: 6px 14px;
  border: 1px solid transparent;
  border-radius: 6px;
  margin-block-end: 0;              /* 不必再壓在誰的底線上 */
  font-size: var(--fs-3);
}

[role="tab"]:hover:not([aria-selected="true"]) {
  background: var(--bg-surface-2);
  color: var(--text-primary);
}

[role="tab"][aria-selected="true"] {
  border-bottom-color: var(--accent);
  color: var(--text-primary);
}

[role="tabpanel"] [role="tab"][aria-selected="true"] {
  border-color: var(--border-subtle);
  background: var(--bg-surface-2);
  font-weight: 600;
}

[role="tabpanel"] { padding-block: 14px; }


/* ---- 側邊欄版面 -------------------------------------------------------
   契約有兩個:外層掛 `.app-shell`(通常就是 <body>),側欄是 `nav.side`。

   為什麼是 opt-in 的 class 而不是直接寫 `body { display: flex }`:
   共用檔裡對 body 下版面是把整個專案綁死 —— 沒有側欄的頁面會無聲地被攤平。
   掛 class 才是「這一頁要側欄版面」的明示,跟 `td.num` 同一個形狀。

   為什麼側欄與卡片**同一個面**(--bg-surface),靠框線分界:
   側欄是版面角色,不是深度層級。舊碼把它塞進深度階梯(側欄一階、卡片一階、
   頁底一階),結果三個面視覺等距而且全部太近 —— 明明沒有任何一塊真的「浮」在
   另一塊上面。深度階梯要留給真的有上下關係的東西(選單、彈窗、hover)。
   ---------------------------------------------------------------------- */
.app-shell {
  display: flex;
  align-items: flex-start;
  min-block-size: 100vh;
}

/* 寬度用 em 不用 px —— 側欄裝的是文字,字級一動它就得跟著動。
   寫死 px 的話,改字級時側欄不會變寬,項目名稱開始折行,而且不會報錯。
   （CAI_Exchange_Rate 的日期欄就是這樣折的,寬度寫死 66px 綁在 11px 字級上。）
   14em 在基準字級下是 196px。要別的寬度就覆寫 --side-width,不要改這條。 */
nav.side {
  flex: 0 0 var(--side-width, 14em);
  align-self: stretch;
  padding-block: 26px;
  background: var(--bg-surface);
  border-inline-end: 1px solid var(--border-subtle);
}

/* 側欄標題不是內容標題 —— 它是一個標籤,所以不吃 h1..h3 的字級階梯。 */
nav.side h1, nav.side h2, nav.side .side-title {
  margin: 0 0 14px;
  padding-inline: 20px;
  font-size: var(--fs-3);
  font-weight: 600;
  color: var(--text-secondary);
}

nav.side a {
  display: block;
  padding: 10px 20px;
  border-inline-start: 3px solid transparent;
  color: var(--text-primary);
  font-size: var(--fs-4);
  text-decoration: none;
}

nav.side a:hover { background: var(--bg-surface-3); }

/* 目前頁面掛在 aria-current,不掛 class —— 跟頁籤的 aria-selected 同一個理由:
   「看起來在這一頁」與「真的在這一頁」不能各自漂移。
   同時用底色、左側 3px 色條與字重三個通道,灰階列印與色盲都還讀得出來。 */
nav.side a[aria-current="page"] {
  border-inline-start-color: var(--accent);
  background: var(--bg-surface-3);
  font-weight: 600;
}

/* min-inline-size: 0 是必要的,不是保險:flex item 的預設 min-width 是 auto,
   所以一張寬表格會把 main 撐開、把側欄擠掉,而不是自己出現橫向捲軸。 */
.app-shell > main {
  flex: 1 1 auto;
  min-inline-size: 0;
}

/* ---- 漢堡鈕（窄螢幕的側欄開關）---------------------------------------
   契約：按鈕排在 nav.side **前面**，兩者同為 .app-shell 的子元素。

   <button class="side-toggle" aria-expanded="false" aria-controls="sidenav">選單</button>
   <nav class="side" id="sidenav"> … </nav>

   狀態掛在 aria-expanded，跟頁籤的 aria-selected、側欄的 aria-current 同一條
   原則：「看起來開著」與「真的開著」不能各自漂移。所以 CSS 讀的就是那個屬性
   （`[aria-expanded="false"] ~ nav.side`），專案只要負責翻轉它：

     btn.onclick = () => btn.setAttribute(
       'aria-expanded', btn.getAttribute('aria-expanded') !== 'true');

   少了那一行 JS，抽屜就打不開 —— 這是**刻意**的：CSS-only 的做法（隱藏的
   checkbox、<details>）會把一個按鈕變成一個不是按鈕的東西，鍵盤與螢幕閱讀器
   拿到的語意都不對。真按鈕 + 一個屬性，是唯一兩邊都成立的組合。

   圖示用 box-shadow 疊出三條槓，不用圖檔也不用圖示字型 —— 共用 CSS 不該
   夾帶二進位資產，而 url() 裡放不了 var()，塞圖等於在主題外面多養一組顏色。
   ---------------------------------------------------------------------- */
.side-toggle {
  display: none;                    /* 寬螢幕不需要 —— 側欄本來就在 */
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  border: 0;
  border-block-end: 1px solid var(--border-subtle);
  background: var(--bg-surface);
  color: var(--text-primary);
  font: inherit;
  font-size: var(--fs-3);
  cursor: pointer;
}

/* 三條槓：本體是中間那條，上下兩條用 box-shadow 疊出來。 */
.side-toggle::before {
  content: "";
  inline-size: 18px;
  block-size: 2px;
  background: currentcolor;
  box-shadow: 0 -6px 0 currentcolor, 0 6px 0 currentcolor;
}

.side-toggle[aria-expanded="true"] {
  background: var(--bg-surface-3);
  font-weight: 600;
}

/* 窄螢幕：側欄收成抽屜，由漢堡鈕開關。 */
@media (width <= 640px) {
  .app-shell { display: block; }

  .side-toggle { display: flex; }

  nav.side {
    inline-size: auto;
    padding-block: 8px;
    border-inline-end: 0;
    border-block-end: 1px solid var(--border-subtle);
  }

  .side-toggle[aria-expanded="false"] ~ nav.side { display: none; }
}

/* ---- 下拉選單 ---------------------------------------------------------
   刻意不設 appearance:none。核心 token 已經宣告 color-scheme，瀏覽器會用
   對的模式畫箭頭與展開後的原生選單；一旦關掉外觀就得自己塞一張箭頭圖，
   而 url() 裡放不了 var()，等於在主題外面多養一組寫死的顏色。
   ---------------------------------------------------------------------- */
select {
  padding: 5px 8px;
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  background: var(--bg-surface-2);
  color: var(--text-primary);
  font: inherit;
}

/* Windows / Linux 的展開清單不繼承 select 的顏色，要另外給。 */
select option {
  background: var(--bg-surface-2);
  color: var(--text-primary);
}

/* 停用狀態排在最後：它是狀態修飾，要蓋在各元件的基本樣式之上。 */
input:disabled, select:disabled, label:has(> input:disabled) {
  color: var(--text-disabled);
  cursor: not-allowed;
}

/* ---- 焦點框 -----------------------------------------------------------
   :where() 讓專案的選擇器一定蓋得過去（權重為 0）。
   ---------------------------------------------------------------------- */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}
