/* Material 3 的色役。**這個檔案是產生出來的，不要手改。**
 *
 *   node --import ./tools/esm-ext.mjs tools/gen-m3-colors.mjs > static/m3-colors.css
 *
 * 種子色 #5ED48B（品牌綠），配色變體 SchemeFidelity，對比等級 1（M3 的高對比；所有文字 ≥ 7:1，見產生器）。
 * 深色的 surface／background 強制純黑——見產生器裡的說明。
 * 名稱照 M3 的規範走 --md-sys-color-*，這樣對著官方文件看是同一組字。
 */

:root {
  --md-sys-color-primary: #003419;
  --md-sys-color-on-primary: #FFFFFF;
  --md-sys-color-primary-container: #00552D;
  --md-sys-color-on-primary-container: #FFFFFF;
  --md-sys-color-secondary: #07331B;
  --md-sys-color-on-secondary: #FFFFFF;
  --md-sys-color-secondary-container: #285136;
  --md-sys-color-on-secondary-container: #FFFFFF;
  --md-sys-color-tertiary: #4F1C07;
  --md-sys-color-on-tertiary: #FFFFFF;
  --md-sys-color-tertiary-container: #743821;
  --md-sys-color-on-tertiary-container: #FFFFFF;
  --md-sys-color-error: #600004;
  --md-sys-color-on-error: #FFFFFF;
  --md-sys-color-error-container: #98000A;
  --md-sys-color-on-error-container: #FFFFFF;
  --md-sys-color-background: #F5FBF2;
  --md-sys-color-on-background: #171D18;
  --md-sys-color-surface: #F5FBF2;
  --md-sys-color-on-surface: #000000;
  --md-sys-color-surface-variant: #D9E6D8;
  --md-sys-color-on-surface-variant: #000000;
  --md-sys-color-surface-dim: #B4BBB2;
  --md-sys-color-surface-bright: #F5FBF2;
  --md-sys-color-surface-container-lowest: #FFFFFF;
  --md-sys-color-surface-container-low: #ECF3EA;
  --md-sys-color-surface-container: #DEE4DC;
  --md-sys-color-surface-container-high: #D0D6CE;
  --md-sys-color-surface-container-highest: #C2C8C0;
  --md-sys-color-outline: #232F26;
  --md-sys-color-outline-variant: #404C42;
  --md-sys-color-shadow: #000000;
  --md-sys-color-scrim: #000000;
  --md-sys-color-inverse-surface: #2C322C;
  --md-sys-color-inverse-on-surface: #FFFFFF;
  --md-sys-color-inverse-primary: #67DD93;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --md-sys-color-primary: #BEFFCE;
    --md-sys-color-on-primary: #000000;
    --md-sys-color-primary-container: #63D98F;
    --md-sys-color-on-primary-container: #000F04;
    --md-sys-color-secondary: #CCFBD6;
    --md-sys-color-on-secondary: #000000;
    --md-sys-color-secondary-container: #A0CDAA;
    --md-sys-color-on-secondary-container: #000F04;
    --md-sys-color-tertiary: #FFECE6;
    --md-sys-color-on-tertiary: #000000;
    --md-sys-color-tertiary-container: #FFAF93;
    --md-sys-color-on-tertiary-container: #1C0400;
    --md-sys-color-error: #FFECE9;
    --md-sys-color-on-error: #000000;
    --md-sys-color-error-container: #FFAEA4;
    --md-sys-color-on-error-container: #220001;
    --md-sys-color-background: #000000;
    --md-sys-color-on-background: #DEE4DC;
    --md-sys-color-surface: #000000;
    --md-sys-color-on-surface: #FFFFFF;
    --md-sys-color-surface-variant: #3E4A40;
    --md-sys-color-on-surface-variant: #FFFFFF;
    --md-sys-color-surface-dim: #000000;
    --md-sys-color-surface-bright: #4B524B;
    --md-sys-color-surface-container-lowest: #000000;
    --md-sys-color-surface-container-low: #1B211C;
    --md-sys-color-surface-container: #2C322C;
    --md-sys-color-surface-container-high: #363D37;
    --md-sys-color-surface-container-highest: #424842;
    --md-sys-color-outline: #E6F4E6;
    --md-sys-color-outline-variant: #B9C6B9;
    --md-sys-color-shadow: #000000;
    --md-sys-color-scrim: #000000;
    --md-sys-color-inverse-surface: #DEE4DC;
    --md-sys-color-inverse-on-surface: #000000;
    --md-sys-color-inverse-primary: #00542C;
  }
}

:root[data-theme="dark"] {
  --md-sys-color-primary: #BEFFCE;
  --md-sys-color-on-primary: #000000;
  --md-sys-color-primary-container: #63D98F;
  --md-sys-color-on-primary-container: #000F04;
  --md-sys-color-secondary: #CCFBD6;
  --md-sys-color-on-secondary: #000000;
  --md-sys-color-secondary-container: #A0CDAA;
  --md-sys-color-on-secondary-container: #000F04;
  --md-sys-color-tertiary: #FFECE6;
  --md-sys-color-on-tertiary: #000000;
  --md-sys-color-tertiary-container: #FFAF93;
  --md-sys-color-on-tertiary-container: #1C0400;
  --md-sys-color-error: #FFECE9;
  --md-sys-color-on-error: #000000;
  --md-sys-color-error-container: #FFAEA4;
  --md-sys-color-on-error-container: #220001;
  --md-sys-color-background: #000000;
  --md-sys-color-on-background: #DEE4DC;
  --md-sys-color-surface: #000000;
  --md-sys-color-on-surface: #FFFFFF;
  --md-sys-color-surface-variant: #3E4A40;
  --md-sys-color-on-surface-variant: #FFFFFF;
  --md-sys-color-surface-dim: #000000;
  --md-sys-color-surface-bright: #4B524B;
  --md-sys-color-surface-container-lowest: #000000;
  --md-sys-color-surface-container-low: #1B211C;
  --md-sys-color-surface-container: #2C322C;
  --md-sys-color-surface-container-high: #363D37;
  --md-sys-color-surface-container-highest: #424842;
  --md-sys-color-outline: #E6F4E6;
  --md-sys-color-outline-variant: #B9C6B9;
  --md-sys-color-shadow: #000000;
  --md-sys-color-scrim: #000000;
  --md-sys-color-inverse-surface: #DEE4DC;
  --md-sys-color-inverse-on-surface: #000000;
  --md-sys-color-inverse-primary: #00542C;
}
/* oSUHA — 外殼樣式
 *
 * 色票逐一對應 Figma 的「色票」collection，值取自 iOS 端的資產目錄
 * （oSUHA/Assets.xcassets/*.colorset），兩邊是同一組數字，不是各寫各的。
 */

/* ---------- 色票 ---------- */

:root {
  /* ---- M3 的形狀階梯 ----
     M3 Expressive 的第一條戰術是「Use a variety of shapes」：混用圓與方
     製造張力，用「跳脫周圍的形狀」把注意力導到特定元素。
     所以這裡是一組**階梯**，不是單一個 --radius。 */
  --md-sys-shape-corner-none:        0px;
  --md-sys-shape-corner-extra-small: 4px;
  --md-sys-shape-corner-small:       8px;
  --md-sys-shape-corner-medium:     12px;
  --md-sys-shape-corner-large:      16px;
  --md-sys-shape-corner-extra-large:28px;
  --md-sys-shape-corner-full:     9999px;

  /* ---- M3 的字級 ---- */
  --md-sys-typescale-headline-small-size:  24px;
  --md-sys-typescale-title-large-size:     22px;
  --md-sys-typescale-title-small-size:     14px;
  --md-sys-typescale-title-medium-size:    16px;
  --md-sys-typescale-body-large-size:      16px;
  --md-sys-typescale-body-medium-size:     14px;
  --md-sys-typescale-label-large-size:     14px;
  --md-sys-typescale-label-medium-size:    12px;

  /* ---- M3 Expressive 的彈簧動態 ----
     spatial 給位移／大小（會過衝一點，像真的物體）；
     effects 給顏色與透明度（不過衝——過衝的顏色看起來像壞掉）。
     CSS 沒有彈簧函式，用 linear() 逼近（CSS Easing Level 2，
     Safari 17.2＋／Chrome 113＋）；不支援的會退回 ease，少一點回彈而已。 */
  --md-sys-motion-spring-default-spatial: linear(
    0, 0.006 1.1%, 0.024 2.3%, 0.098 4.7%, 0.42 11%, 0.72 15.6%, 0.89 19.8%,
    0.98 24.5%, 1.03 30%, 1.04 37%, 1.02 47%, 0.999 65%, 1);
  --md-sys-motion-spring-effects: linear(
    0, 0.02 2%, 0.16 6%, 0.44 12%, 0.72 19%, 0.9 27%, 0.98 38%, 1);
  --md-sys-motion-duration-short: 200ms;
  --md-sys-motion-duration-medium: 350ms;

  /* ---- M3 的色役對映到本專案原有的名字 ----
     完整的 --md-sys-color-* 由 tools/gen-m3-colors.mjs 產生，接在這份 CSS
     前面（見 build.py 的 concat_asset）。這一層是**別名**，不是第二套色票：
     舊名字全部指向 M3 的角色，所以既有的每一條規則不必改就換成 M3 的顏色，
     新寫的元件則直接用 --md-sys-color-* 那組官方名稱。

     > [!important] 淺／深不再寫在這裡
     > M3 那份自己帶了三個區塊（:root、prefers-color-scheme: dark、
     > [data-theme="dark"]），所以這裡只要對映一次。先前的兩個深色覆寫區塊
     > 已經刪掉——留著會蓋掉 M3 算出來的值。 */
  --bg:         var(--md-sys-color-surface);
  --surface:    var(--md-sys-color-surface-container-low);
  --sunk:       var(--md-sys-color-surface-container);
  --ink:        var(--md-sys-color-on-surface);
  --ink-2:      var(--md-sys-color-on-surface-variant);
  --ink-3:      var(--md-sys-color-on-surface-variant);
  --accent:     var(--md-sys-color-primary);
  --accent-ink: var(--md-sys-color-on-primary);
  --danger:     var(--md-sys-color-error);
  --line:       var(--md-sys-color-outline);
  --line-soft:  var(--md-sys-color-outline-variant);

  /* 浮動列的玻璃。M3 沒有「毛玻璃」這個角色，用 surface-container 加透明度
     自己調——底色跟著主題走，不再寫死 rgba。 */
  --bar-glass: color-mix(in srgb, var(--md-sys-color-surface-container) 72%, transparent);
  --bar-line:  var(--md-sys-color-outline-variant);

  /* 版面常數。全部量自 Figma / iOS 實機截圖，見〈Figma 分頁列照 iOS 26 重畫〉 */
  --pad:            14px;  /* 頁面左右留白，2026-08-16 本人指定 14–15 */
  --gap:             8px;  /* 卡間距與欄距 */
  --radius:          var(--md-sys-shape-corner-medium);  /* 舊名，指向 M3 的階梯 */
  /* > [!danger] 曲線上的邊框不能用 0.5px
   * > 這個值原本是 iOS 的 hairline 慣例（2x 螢幕上剛好一個實體像素）。
   * > 但在 3x 螢幕上 0.5px = 1.5 個實體像素——**非整數的描邊畫在曲線上**
   * > 一定會階梯化。本人回報「有些框邊會有鋸齒」，放大看就是那排
   * > border-radius: 999px 的膠囊。
   * >
   * > M3 的 outline 是 1dp，而我們已經在 M3 上了，本來就該跟著換。
   * > 直線邊框用 0.5 沒問題（沒有曲線可以階梯化），但為了一致也一起換——
   * > 兩種寬度混用只會讓人下次又選錯。 */
  --hairline:      1px;
  --capsule-h:      62px;
  --capsule-inset:  21px;  /* 膠囊左右內縮，也是它離螢幕底的距離 */
  --capsule-r:      31px;

  /* 分頁列離底多遠，以及內容要讓開多少——**這兩個必須由同一個值推導**。
     先前它們各算各的：`.tabbar` 的 bottom 帶了 `env(safe-area-inset-bottom)`，
     `body` 的 padding-bottom 沒帶。在有 home indicator 的 iPhone 上用瀏覽器開
     （env = 34），膠囊上緣離底 21+34+62 = 117，內容卻只讓 99——**捲到底仍被蓋 18px**。
     macOS 的 WKWebView 沒有安全區，所以這個 bug 在本機截圖上驗不出來。 */
  --tabbar-gap:    16px;
  --tabbar-bottom: calc(var(--capsule-inset) + env(safe-area-inset-bottom, 0px));
  --tabbar-clear:  calc(var(--tabbar-bottom) + var(--capsule-h) + var(--tabbar-gap));

  /* 導覽在頂部時，內容要往**上**讓——底部那個 clear 同時歸零。
     兩個方向各一個變數，是因為三個級別讓的方向不一樣：
       手機 ＋ 裝成 App   底部膠囊 → 讓下面
       手機瀏覽器、平板   頂部列   → 讓上面
       電腦              側邊欄   → 兩邊都不用讓，改讓左邊
     只有一個變數的話，切換級別時一定會有一邊算錯（先前 bottom 與
     padding-bottom 各算各的就踩過，見上面那段）。 */
  --topbar-h:      52px;
  --topbar-clear:   0px;
}


/* ---------- 基礎 ---------- */

*, *::before, *::after { box-sizing: border-box; }

/* > [!danger] `hidden` 屬性遇到有 display 的規則就失效
   > `[hidden]` 的 `display: none` 來自 UA 樣式表，而**作者樣式一律優先**。
   > 所以 `.home-search { display: flex }`、`.iconbtn { display: grid }` 這種
   > 規則會讓 `el.hidden = true` 完全沒有作用：DOM 上是 true、畫面照樣顯示。
   > 2026-09-07 一晚上撞了兩次（搜尋表單、搜尋鍵），所以在這裡一次擋掉。
   > 用 !important 是這一條的例外——它要贏過所有 display 宣告才有意義。 */
[hidden] { display: none !important; }

/* 鍵盤接力欄（見 shell.html）。看不見但**可以聚焦**——所以不能用
   display:none / visibility:hidden / hidden 屬性，那三種都不可聚焦。
   放在畫面外會讓 iOS 捲過去，所以留在原地、壓成 1×1 全透明。 */
.kbd-keeper {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 1px;
  height: 1px;
  padding: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
  z-index: -1;
}

/* > [!danger] 輸入欄的字級小於 16px，iOS 會在聚焦時自動放大整頁
   > 那是 Safari 的固定行為，跟版面無關——它只看**計算後**的 font-size。
   > 本人回報「點擊搜索框或輸入框不應該自動縮放聚焦」。
   >
   > 另一種「解法」是在 viewport 加 `maximum-scale=1, user-scalable=no`。
   > **不採用**：那會把使用者自己的雙指縮放一起關掉，違反 WCAG 1.4.4
   > （Resize text）。真正的修法就是把字級提到 16。
   >
   > `max(16px, 1em)`：1em 取的是父層的字級，所以小於 16 的一律撐到 16，
   > 本來就大於 16 的（例如放大字級的頁面）維持不變。
   >
   > > [!danger] 這一條**必須** !important，否則會被 `font: inherit` 蓋掉
   > > 表單控制項散布著約 30 條 `font: inherit`（讓控制項跟著頁面字體走的
   > > 常見重置）。那個**簡寫包含 font-size**，而它們都是 class 選擇器
   > > （0,1,0），一律贏過這條元素選擇器（0,0,1）。
   > > 換句話說：那些 `font: inherit` 正是輸入欄會掉到 15px 的原因，
   > > 而這一條的存在就是要贏過它們。第一版沒加 important，量出來
   > > post／tv／login 三頁的輸入欄仍然是 14／15px。 */
input, textarea, select { font-size: max(16px, 1em) !important; }

html { -webkit-text-size-adjust: 100%; }
/* Tab 鍵移動焦點、錨點跳轉、scrollIntoView 也要避開浮動分頁列，
   不然焦點落到被膠囊蓋住的地方，用鍵盤的人看不到自己在哪。 */
html {
  scroll-padding-bottom: var(--tabbar-clear);
  scroll-padding-top: var(--topbar-clear);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.5;
  /* 分頁列浮在內容上，捲到底時最後一張卡不能被它蓋住 */
  padding-bottom: var(--tabbar-clear);
  /* 頂部列同理，只是方向相反。不在頂部列的級別這個值是 0。 */
  padding-top: var(--topbar-clear);
  overscroll-behavior-y: none;
}

/* 焦點指示器。真瀏覽器稽核量到 solid 3px accent offset 2 對底色 9.54:1
   （2.4.13 AAA 只要 3:1）。用 :focus-visible 而不是 :focus，
   滑鼠點擊不該長出外框。 */
:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 10px 14px;
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--radius);
}
.skip:focus { left: var(--pad); top: 8px; }

/* 內容欄。手機是全寬；桌機不要拉成一條橫幅，
   收在 460 讓它維持「App 形狀」——這是手機版式，不是網站版式。 */
.wrap {
  max-width: 460px;
  margin: 0 auto;
  padding: 0 var(--pad);
  /* > [!important] 至少要有一個螢幕高，否則短頁面底下會露出一大塊黑
   * > 市集／訊息這類頁面的內容只有兩百多 px，`body` 根本到不了視窗底，
   * > 內容結束到導覽列之間就是一整片純黑——本人回報「市集、發佈、訊息、
   * > 我的切換時底部都會有黑塊」。
   * > 扣掉的是 body 自己的上下留白（頂部安全區與導覽列），剩下的正好是
   * > 內容可用的高度。用 dvh 而不是 vh：瀏覽器的網址列會伸縮。 */
  min-height: calc(100dvh - var(--topbar-clear) - var(--tabbar-clear));
}

/* 空狀態**就是**整頁的時候（市集、訊息、離線），把它擺在正中間，
   而不是靠在上方三分之一、底下留一大塊空白。
   `:has(> .empty)` 只挑「空狀態是 .wrap 的直接子節點」那幾頁——電視與
   稍後再看的空狀態是巢在別的內容裡的，那些不動。 */
.wrap:has(> .empty) { display: grid; align-content: center; }
.wrap:has(> .empty) > .empty { padding-top: 0; padding-bottom: 0; }

/* ---------- 頁首 ---------- */

.head {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  /* 上緣不留：頁面上方已經有系統的安全區（iPhone 17 Pro 是 59pt），
     再加 6 只會讓內容看起來離螢幕很遠。2026-09-07 量過本人的實機截圖：
     內容從 82.3pt 才開始，安全區 59 之外我們自己又多留了 23——本人回報
     「頂部太保守，留了很大的距離」。下緣從 16 收到 10。 */
  padding: 0 0 10px;
}
.head .spacer { margin-left: auto; }

.iconbtn {
  display: grid;
  place-items: center;
  width: 44px;              /* 2.5.5：可點目標不得小於 44 */
  height: 44px;
  border: 0;
  padding: 0;
  background: none;
  color: var(--ink);
  cursor: pointer;
  border-radius: 50%;
}
.iconbtn .ms { --ms-size: 22px; --ms-opsz: 24; }

/* 分段標題列（推薦／追蹤中） */
.segs {
  display: flex;
  gap: 18px;
  align-self: stretch;
  align-items: center;
}
.seg {
  border: 0;
  background: none;
  padding: 4px 0 6px;
  /* M3 的 primary tab 標籤是 TitleSmall——14sp／行高 20／字重 Medium。
     先前是 17px/700，那是 iOS 的 segmented control 語彙。
     值取自 PrimaryNavigationTabTokens.LabelTextFont → TypographyKeyTokens.TitleSmall。 */
  font-size: var(--md-sys-typescale-title-small-size);
  line-height: 20px;
  font-weight: 500;
  color: var(--ink-3);
  cursor: pointer;
  position: relative;
}
.seg[aria-selected="true"] { color: var(--ink); }
.seg[aria-selected="true"]::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  /* PrimaryNavigationTabTokens：ActiveIndicatorHeight 3dp、
     ActiveIndicatorShape RoundedCornerShape(3dp)。 */
  height: 3px;
  border-radius: 3px 3px 0 0;
  border-radius: 2px;
  background: var(--accent);
}

/* ---------- 首頁瀑布 ---------- */

/* 真正的瀑布不能用 flex-wrap：wrap 會把同列項目拉齊成該列最高者，
   於是永遠對齊、沒有錯落——那是網格不是瀑布。
   做法與 Figma／SwiftUI 端一致：兩條各自獨立的直欄。 */
.falls {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--gap);
  align-items: start;
}
.col { display: flex; flex-direction: column; gap: var(--gap); }

.card {
  background: var(--surface);
  border: var(--hairline) solid var(--line-soft);
  border-radius: var(--radius);
  overflow: hidden;
}
.card .body { padding: 8px 10px 10px; }
.card h3 {
  margin: 0 0 6px;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.4;
}
.card .meta {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: var(--ink-3);
}
.card .meta .who { margin-right: auto; }

/* 圖片佔位：這一輪沒有真圖，用 sunk 色塊撐出高度差，
   讓兩欄真的錯落。高度取 Figma 實測區間 142–212。 */
.ph { background: var(--sunk); display: block; }

.chip {
  display: inline-block;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 11px;
  font-weight: 700;
}

/* 置頂卡：整寬，放在瀑布之上。有金額、截止日、行動點，
   壓進 183pt 寬的瀑布卡會讀不到。 */
.pinned {
  background: var(--accent);
  color: var(--accent-ink);
  border-radius: var(--radius);
  padding: 12px 14px 14px;
  margin-bottom: var(--gap);
}
.pinned .badge {
  display: inline-block;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--accent-ink);
  color: var(--accent);
  font-size: 11px;
  font-weight: 700;
  margin-bottom: 8px;
}
.pinned .lack { font-size: 13px; opacity: 0.9; }
.pinned .amount { font-size: 26px; font-weight: 700; line-height: 1.2; }
.pinned .due { font-size: 13px; opacity: 0.9; }
.pinned h3 { margin: 8px 0 0; font-size: 15px; font-weight: 700; }

/* ---------- 空狀態 ---------- */

.empty {
  text-align: center;
  padding: 72px var(--pad) 0;
  color: var(--ink-3);
}
.empty .ms { --ms-size: 46px; --ms-opsz: 48; --ms-wght: 300; opacity: 0.55; }
.empty h2 { margin: 12px 0 4px; font-size: 17px; color: var(--ink); }
.empty p { margin: 0; font-size: 13px; }

/* ---------- 我的／建立 ---------- */
.me-page, .post-page { padding-bottom: 12px; }
.me-identity {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 2px 20px;
}
.me-avatar {
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 25px;
  font-weight: 700;
}
.me-identity h1 { margin: 0 0 2px; font-size: 19px; }
.me-identity p { margin: 0; color: var(--ink-3); font-size: 13px; }
.me-section { margin: 0 0 18px; }
.me-section h2 { margin: 0 0 7px 4px; color: var(--ink-3); font-size: 12px; font-weight: 600; }
.me-group { overflow: hidden; border: 1px solid var(--line-soft); border-radius: var(--radius); background: var(--line-soft); }
.me-row {
  min-height: 48px;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  background: var(--surface);
  color: var(--ink);
  text-decoration: none;
  font-size: 14px;
}
.me-row + .me-row { margin-top: 1px; }
.me-row:hover { background: color-mix(in srgb, var(--surface) 88%, var(--accent)); }
.me-value { margin-left: auto; color: var(--ink-3); font-size: 12px; text-align: right; }
.me-row[href] .me-value::after { content: "›"; margin-left: 8px; color: var(--ink-2); font-size: 16px; }
.me-signout { width: 100%; min-height: 44px; border: 1px solid var(--line-soft); border-radius: var(--radius); background: var(--surface); color: var(--danger); font: inherit; }

.post-mode { display: flex; gap: 4px; margin: 4px 0 18px; padding: 3px; border-radius: var(--radius); background: var(--sunk); }
.post-mode-btn { flex: 1; min-height: 40px; border: 0; border-radius: calc(var(--radius) - 2px); background: transparent; color: var(--ink-3); font: inherit; }
.post-mode-btn[aria-selected="true"] { background: var(--surface); color: var(--ink); font-weight: 700; }
.post-form { display: grid; gap: 16px; }
.post-form label { display: grid; gap: 7px; color: var(--ink-2); font-size: 13px; }
.post-form input, .post-form select, .post-form textarea { width: 100%; box-sizing: border-box; min-height: 44px; padding: 9px 11px; border: 1px solid var(--line-soft); border-radius: var(--radius); background: var(--surface); color: var(--ink); font: inherit; }
.post-form textarea { resize: vertical; }
.post-note { margin: -2px 0 0; color: var(--ink-3); font-size: 12px; line-height: 1.5; }
.post-submit { min-height: 44px; border: 0; border-radius: var(--radius); background: var(--accent); color: var(--accent-ink); font: inherit; font-weight: 700; }
.post-submit:disabled { cursor: not-allowed; opacity: .45; }

/* ---------- 分頁列 ---------- */

/* iOS 26 的浮動玻璃膠囊。規格量自實機截圖（iPhone 17 Pro，3x）：
 *   膠囊 360 × 62、左右各內縮 21、底距螢幕底 21、stadium r=31
 * 402 − 21×2 = 360，所以 inset 同時決定寬與位置，不必再寫死 360。
 */
.tabbar {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  width: min(360px, calc(100% - var(--capsule-inset) * 2));
  height: var(--capsule-h);
  border-radius: var(--capsule-r);
  background: var(--bar-glass);
  border: var(--hairline) solid var(--bar-line);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  display: flex;
  z-index: 50;

  /* Figma 的 21 是量到**螢幕真正的底**——iOS 26 的浮動分頁列本來就浮在
     home indicator 上方那塊，指示器畫在它之上。裝成 App（standalone）時
     照這個值才對得上設計稿。
     但在**瀏覽器裡**底部還有 Safari 的工具列，21 會被蓋掉，
     所以瀏覽器狀態下額外讓開安全區。 */
  bottom: var(--tabbar-bottom);
}
/* 裝成 App 時照實機的 21：覆寫**變數**而不是 `.tabbar` 的 bottom，
   這樣 `body` 的 padding-bottom 會跟著一起變，兩者不會再各說各話。 */
@media (display-mode: standalone) {
  :root { --tabbar-bottom: var(--capsule-inset); }
}

.tab {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  text-decoration: none;
  color: var(--ink-3);
  font-size: 11px;
  font-weight: 500;
  border-radius: var(--capsule-r);
  -webkit-tap-highlight-color: transparent;
}
.tab .ms { --ms-size: 25px; --ms-opsz: 24; }

/* 選中不換實心，五格一律外框。
   選取狀態的非顏色線索由藥丸的形狀與位置承擔，所以不牽動 WCAG 1.4.1。 */
.tab[aria-current="page"] { color: var(--accent); font-weight: 700; }

/* 選中的藥丸。四邊留白一律 4pt，半徑 31 − 4 = 27——與膠囊**同心**。

   > [!important] 不要用實機的絕對值去湊
   > iPhone 17 Pro 實機是藥丸 76 × 53.8、左右各留 0.4、上下各留 4.0，
   > 因為 iOS 的版面是「格子 76.8、間距 −8」，藥丸比節距還寬、相鄰重疊。
   > 我照那個比例算過一版（膠囊加 4pt 內距、藥丸 `inset: 4px -2.8px`），
   > 在 402 上確實量到 76.0 × 53.0，與實機逐項吻合——**但看起來是錯的**：
   > 藥丸幾乎貼著膠囊內緣，本人回報「左右太窄」。
   >
   > 教訓：對得上實機的數字，不等於在我們這個版面裡看起來對。
   > 我們的格子是 `flex: 1` 平分，沒有負間距，藥丸與膠囊是單純的同心關係，
   > 那就讓它同心——四邊都 4。改這個值請連 `border-radius` 一起改（31 − 留白）。 */
.tab[aria-current="page"]::before {
  content: "";
  position: absolute;
  inset: 4px;
  border-radius: 27px;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}
/* 絕對定位的 ::before 會畫在一般流程內容**之上**，會蓋掉圖示。
   把圖示與標籤也變成 positioned，它們在 DOM 順序在後，就會疊回上層。
   不用 z-index: -1——`.tabbar` 有 backdrop-filter，那會另開堆疊脈絡，
   負 z-index 的行為在那底下不好預測。 */
.tab .ms, .tab span { position: relative; }

/* ---------- 底部設定列（原型專用） ---------- */

/* 這一列**只存在於原型**。正式版的外觀走 cookie + 伺服器端 data-theme、
   語言走各自的網址，兩者都不需要這種浮在畫面上的切換器。
   留著是為了讓稽核與截圖能在同一台裝置上翻遍四種組合。 */
.protobar {
  display: flex;
  align-items: center;
  gap: 8px;
  justify-content: center;
  flex-wrap: wrap;
  padding: 20px var(--pad) 8px;
  font-size: 11px;
  color: var(--ink-3);
}
.protobar a, .protobar button {
  color: var(--ink-2);
  background: var(--sunk);
  border: var(--hairline) solid var(--line-soft);
  border-radius: 999px;
  padding: 6px 12px;
  font-size: 11px;
  text-decoration: none;
  cursor: pointer;
  min-height: 32px;
}
.protobar [aria-current="true"] { color: var(--accent); font-weight: 700; }

/* ============================================================
   桌面：分頁列變側邊欄
   ============================================================
   > [!important] 基準是本專案自己的 macOS App，不是隨便挑一個網站的版式
   > `MainWindowController.swift` 的 `NSSplitViewItem(sidebarWithViewController:)`：
   > `minimumThickness = 180`、`maximumThickness = 260`、目標固定 **200pt**、
   > 可收合；視窗 `minSize` 640×480、預設 900×700。
   > 所以這裡：側邊欄 200、斷點取 **900**（macOS 視窗的預設寬度）。
   >
   > 不新增 DOM：同一個 `<nav class="tabbar">` 換一套樣式就好。
   > 好處是 `check.py` 那條「分頁連結不能跳出當前語言」照樣守得到，
   > 也不必為桌面多維護一份導覽的 i18n。 */

/* 內容欄：平板與電腦都放寬到 640。
   手機的 460 在 768 或 1280 的視窗裡只有兩欄 212px 的卡片，空得像壞掉。
   640 剛好給瀑布三欄（(640 − 2×8) / 3 ≈ 208），卡片寬度與手機上的 212
   幾乎一樣——**放大的是版面，不是卡片**。
   平板與電腦共用同一個寬度是刻意的：分開的是**導覽**（頂部列 vs 側邊欄），
   不是卡片大小。卡片跟著螢幕一起變大，只會讓三個級別看起來像三個產品。 */
@media (min-width: 600px) {
  .wrap { max-width: 640px; }
}

/* 品牌名的基底是**不顯示**，由需要它的級別各自打開。
   > [!danger] 這條一定要寫在下面所有 media query **之前**
   > 同特異性時 CSS 靠源序決勝。寫在後面會把頂部列與側邊欄的
   > `display: block` 全部蓋掉——2026-08 踩過一次，2026-09-07 我又踩了
   > 同一次（把它挪到區塊尾巴），所以現在把警告寫在規則正上方。 */
.tabbar-brand { display: none; }

/* ============================================================
   三個級別，各自一種導覽
   ============================================================
   > [!important] 為什麼要分三種，而不是「小螢幕一種、大螢幕一種」
   > 2026-09-07 本人回報：在瀏覽器裡直接看時底部膠囊「很怪」。的確——
   > 那是 iOS 26 的浮動分頁列，是 **App 的**外觀；在 Safari 分頁裡它下面
   > 緊接著 Safari 自己的工具列，變成兩層底部列疊在一起，而且整頁沒有
   > 任何網站身份可言。接著本人指定「手機、平板、電腦都能區分開」。
   >
   >            手機 <600        平板 600–899     電腦 ≥900
   >   瀏覽器   頂部列（圖示）    頂部列（圖示＋字） 左側邊欄 200
   >   App      底部膠囊         頂部列（圖示＋字） 左側邊欄 200
   >
   > 手機是唯一兩種模式不同的級別，因為那正是衝突最大的地方。平板有寬度，
   > 但橫向導覽比側邊欄省下更多水平空間給內容，而且 iPad 的 App 本來就少用
   > 底部列。電腦維持側邊欄——基準是自家 macOS App 的 900×700 視窗。
   >
   > 「裝成 App 沒有」這個判斷由 `html[data-standalone]` 帶（見 shell.html
   > 的啟動腳本，它同時看 display-mode 與 iOS 的 navigator.standalone）。
   > 用屬性而不是直接寫 `@media (display-mode: ...)`，是因為 CSS 沒辦法把
   > 「平板」與「手機且非 App」OR 在一起——屬性選擇器可以跟寬度自由組合。

   排列順序有意義：頂部列寫在前面（涵蓋 <900 的多數情形），膠囊寫在後面
   用更窄的條件蓋回去。同特異性靠源序決勝，順序反了膠囊就永遠出不來。 */

/* ---- 頂部列：平板兩種模式，以及手機的瀏覽器 ---- */
/* 規格：高 64、surface 底、標題 title-large、動作圖示 24。
   平板與電腦不用它——M3 對那兩個尺寸級別指定的是 rail 與 drawer。 */
@media (max-width: 899px) {
  :root { --topbar-h: 64px; --topbar-clear: var(--topbar-h); --tabbar-clear: 0px; }

  .tabbar {
    top: 0;
    bottom: auto;
    left: 0;
    right: 0;
    transform: none;
    width: auto;
    height: var(--topbar-h);
    border-radius: 0;
    border: 0;
    /* M3 的 top app bar 沒有分隔線——層次靠色調，不靠框線。 */
    border-bottom: 0;
    background: var(--md-sys-color-surface);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    align-items: center;
    justify-content: flex-end;
    gap: 2px;
    padding: 0 var(--pad);
    /* 安全區：橫放的 iPhone 左右會有瀏海／圓角，內容不能貼邊。 */
    padding-left: max(var(--pad), env(safe-area-inset-left, 0px));
    padding-right: max(var(--pad), env(safe-area-inset-right, 0px));
  }

  /* 品牌名靠左、把導覽推到右邊。這是「這是一個網站」最便宜的訊號——
     在瀏覽器裡看的人需要知道自己在誰的站上，App 裡則不必（圖示就在主畫面）。 */
  .tabbar-brand {
    display: block;
    margin-right: auto;
    font-size: var(--md-sys-typescale-title-large-size);
    font-weight: 400;
    color: var(--ink);
    letter-spacing: .01em;
  }

  .tab {
    flex: 0 0 auto;
    flex-direction: row;
    gap: 6px;
    height: 36px;
    padding: 0 10px;
    border-radius: 9px;
    font-size: 13px;
  }
  .tab .ms { --ms-size: 21px; --ms-opsz: 20; }
  /* 頂部列沒有藥丸——那是膠囊的同心造型，貼在方形列上不對。 */
  .tab[aria-current="page"]::before { content: none; }
}

/* ---- 手機的頂部列：只留圖示，目前這頁才顯示文字 ---- */
@media (max-width: 599px) {
  /* > [!danger] 不要用 display: none 藏標籤
   * > 連結的無障礙名稱來自它的內容。`display: none` 會讓名稱變成空的，
   * > 螢幕閱讀器只會報「連結」——WCAG 2.4.4／4.1.2 直接不合格。
   * > 所以是視覺上移除、語意上保留。 */
  /* > [!danger] 只藏**標籤**，不要連圖示一起藏
   * > 圖示以前是 <svg>、標籤是 <span>，所以「藏 span」剛好只藏到標籤。
   * > 換成 Material Symbols 之後圖示也是 <span class="ms">——這條規則
   * > 把五個分頁的圖示全藏掉了，畫面上只剩目前那一頁看得見
   * > （因為下面那條 aria-current 又把它放回來）。用 :not(.ms) 排除。 */
  .tab span:not(.ms) {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0; border: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  /* 目前這頁把文字放回來。這同時是**非顏色**的選取線索：膠囊靠藥丸的形狀
     承擔，頂部列沒有藥丸，就由「只有這一項有字」承擔（WCAG 1.4.1）。 */
  .tab[aria-current="page"] span:not(.ms) {
    position: relative;
    width: auto; height: auto;
    margin: 0;
    overflow: visible;
    clip-path: none;
  }
  .tab { padding: 0 8px; gap: 5px; }
}

/* ---- compact（手機）＋ 裝成 App：M3 的 navigation bar ---- */
/* iOS 26 的浮動玻璃膠囊退場。M3 的 navigation bar 是**貼齊底邊的整條**，
   選取狀態靠圖示後面那顆 active indicator，不是把整格圈起來。

   > [!important] 用 flexible 版的預設尺寸，不是 tall，也不是 baseline
   > M3 的規格頁寫著：baseline navigation bar「is no longer recommended,
   > and should be replaced by the flexible nav bar, **which is shorter**」。
   > 第一版我照 baseline 做了 80 高、指示器 64 寬，本人回報「還是太高」。
   >
   > 數值取自 Compose Material3 的權杖（規格頁的量測只存在圖裡，抓不到文字）：
   >
   >     NavigationBarTokens.ContainerHeight                 64dp   ← 預設
   >     NavigationBarTokens.TallContainerHeight             80dp   ← 我原本用的
   >     NavigationBarTokens.ItemActiveIndicatorIconLabelSpace 4dp
   >     NavigationBarVerticalItemTokens.ActiveIndicatorHeight 32dp
   >     NavigationBarVerticalItemTokens.ActiveIndicatorWidth  56dp  ← 原本用 64
   >     NavigationBarVerticalItemTokens.IconSize              24dp
   >
   > 本人先要求「做到最緊湊」（一度拿掉可見標籤收到 44），之後要求把標籤
   > 加回來。有標籤的話 64 就是下限，而且那不是挑出來的數字：
   >
   >     指示器 32 ＋ 間距 4 ＋ 標籤行高 16 = 52
   >     52 ＋ 上下各 6 = 64  ← 正好是 ContainerHeight
   >
   > 已經沒有多餘的內距可以砍。horizontal item 的配置也不行：5 格在 375px 上
   > 每格只有 75px，而 horizontal 要「圖示 24 ＋ 標籤 24 ＋ 指示器左右各 16」
   > ＝ 84，塞不下——那正是 M3 只把它用在 medium 視窗的原因。
   > 先前那組（膠囊 62、藥丸與膠囊同心、半徑 31−4）是 iOS 的語彙。
   > 兩套規格不能混——混出來哪一邊都不像。

   底部的 scroll edge effect 也一起移除：那是為了**浮動**膠囊與螢幕底之間
   那 21px 的縫。M3 的列貼齊底邊且不透明，沒有縫，也就不需要漸進模糊。 */
@media (max-width: 599px) {
  :root[data-standalone] {
    /* black-translucent 之下 web view 從螢幕最上方開始，所以**我們**要讓開
       狀態列的高度——不然「推薦／追蹤中」一開始就壓在時間底下。
       讓開的是 padding，不是 margin：捲動時卡片仍然會經過那一段，
       那正是多出來的 59pt 可見內容。 */
    --topbar-clear: env(safe-area-inset-top, 0px);
    --tabbar-clear: calc(64px + env(safe-area-inset-bottom, 0px));
  }

  :root[data-standalone] .tabbar {
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    transform: none;
    width: auto;
    height: calc(64px + env(safe-area-inset-bottom, 0px));
    /* > [!important] 內距**不要**再加 6，那是重複算
     * > 先前寫 `padding-bottom: calc(6px + env(...))`，於是圖示上面 6pt、
     * > 下面 40pt，1 比 6.7——本人回報「安全區不對吧，多了好像」。總高沒錯
     * > （78pt，比 iOS 原生分頁列的 83 還矮），錯的是**分配**。
     * >
     * > 現在：44 的那一段是可點的列，指示器在裡面上下各留 6 置中；
     * > 安全區的 34 純粹是系統的地盤，不放任何東西，也不再疊我們的內距。
     * >
     * >     6  上
     * >     32 指示器
     * >     4  間距（ItemActiveIndicatorIconLabelSpace）
     * >     16 標籤
     * >     6  下
     * >     34 home indicator（系統的） */
    /* > [!danger] 底部安全區不能砍——那 34pt 是系統上滑手勢的地盤
     * > 我一度只留「指示器自己那一條」（有指示器的機器留 20pt），理由是
     * > 「實際被遮住的只有約 20」。那個推論是錯的：被**遮住**的確實只有 20，
     * > 但系統的上滑手勢吃掉的比視覺上的指示器寬。本人實測回報「會誤觸手勢
     * > 了」——在只有圖示的狀態最明顯，因為那時可點區的下緣就是圖示本身。
     * >
     * > 兩種狀態的下緣間距其實**一樣**（都是離螢幕底 20pt），所以不是只補
     * > 圖示那一種，是整個還回去。iOS 自己的分頁列也保留整個安全區
     * > （49 ＋ 34 = 83）——那不是保守，是那塊本來就不屬於 App。
     * >
     * > 要更緊湊的槓桿是**標籤開關**（64 → 44），那個沒有代價。 */
    padding: 0 8px env(safe-area-inset-bottom, 0px);
    border: 0;
    border-radius: 0;
    background: var(--md-sys-color-surface-container);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    align-items: stretch;
    justify-content: space-around;
    gap: 0;
  }
  :root[data-standalone] .tabbar-brand { display: none; }

  :root[data-standalone] .tab {
    flex: 1 1 0;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;   /* ItemActiveIndicatorIconLabelSpace */
    /* 可點高度不能低於 WCAG 2.5.5 的 44。有標籤時內容是 52，本來就夠；
       這條是保險——將來若又把標籤拿掉，不會靜靜地掉到 32。 */
    min-height: 44px;
    height: 100%;
    padding: 0;
    border-radius: 0;
    font-size: var(--md-sys-typescale-label-medium-size);
    color: var(--md-sys-color-on-surface-variant);
  }
  :root[data-standalone] .tab[aria-current="page"] {
    color: var(--md-sys-color-on-surface);
  }
  /* 指示器是圖示的背景，所以圖示要有自己的框。 */
  :root[data-standalone] .tab .ms {
    --ms-size: 24px;
    --ms-opsz: 24;
    display: grid;
    place-items: center;
    width: 56px;
    height: 32px;
    border-radius: var(--md-sys-shape-corner-full);
  }
  :root[data-standalone] .tab[aria-current="page"] .ms {
    background: var(--md-sys-color-secondary-container);
    color: var(--md-sys-color-on-secondary-container);
  }
  :root[data-standalone] .tab[aria-current="page"]::before { content: none; }

  /* 標籤顯示。手機的頂部列那邊會把 span 視覺隱藏，這裡要放回來。 */
  :root[data-standalone] .tab span:not(.ms),
  :root[data-standalone] .tab[aria-current="page"] span:not(.ms) {
    position: static;
    width: auto; height: auto;
    margin: 0;
    overflow: visible;
    clip-path: none;
    line-height: 16px;
  }
}

  /* ---- 標籤的開關（設定 → 偏好 → 分頁列標籤）----
     隱藏時列從 64 收到 44——那是 WCAG 2.5.5 的觸控下限，不能再低。
     標籤只是視覺上移除：DOM 與無障礙名稱都留著，選取狀態仍由指示器的
     形狀承擔，所以關掉之後螢幕閱讀器與 WCAG 1.4.1 都不受影響。

     > [!danger] 這一段一定要放在「標籤顯示」**後面**
     > 兩邊的特異性完全相同（都是 0,4,2），同特異性靠源序決勝。
     > 第一版我把隱藏寫在前面，於是後面那條含 aria-current 的「顯示」
     > 又把**目前這一頁**的標籤放了回來——本人回報「隱藏那個，按下的頁面
     > 還是有標籤」。這是這個檔案第三次踩源序：先前是 .tabbar-brand，
     > 再前面是桌面側邊欄的 display: block。 */
  :root[data-standalone][data-navlabels="hide"] {
    --tabbar-clear: calc(44px + env(safe-area-inset-bottom, 0px));
  }
  :root[data-standalone][data-navlabels="hide"] .tabbar {
    height: calc(44px + env(safe-area-inset-bottom, 0px));
  }
  :root[data-standalone][data-navlabels="hide"] .tab span:not(.ms) {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0; border: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  :root[data-standalone][data-navlabels="hide"] .tab { gap: 0; }


/* ---- expanded（電腦 ≥900）：M3 的 navigation drawer ---- */
/* M3 對三個視窗尺寸級別各有指定的元件：
     compact  <600   navigation bar（底部）
     medium   600–899 navigation rail（左側窄欄，只有圖示）
     expanded ≥900   navigation drawer（左側寬欄，圖示＋文字）
   剛好對得上先前定的三層，只是換成 M3 的語彙。
   斷點 900 沿用原本的理由：自家 macOS App 的預設視窗寬度。 */
@media (min-width: 900px) {
  :root {
    --sidebar-w: 240px;   /* M3 的 drawer 是 360，這個 App 的內容欄只有 640，
                             360 會把版面壓掉一半。240 是折衷。 */
    --tabbar-clear: 24px;
    --topbar-clear: 0px;
  }

  body { padding-left: var(--sidebar-w); }

  .tabbar {
    left: 0;
    top: 0;
    bottom: 0;
    right: auto;
    transform: none;
    width: var(--sidebar-w);
    height: auto;
    border: 0;
    border-radius: 0;
    background: var(--md-sys-color-surface-container-low);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    flex-direction: column;
    justify-content: flex-start;
    align-items: stretch;
    gap: 4px;
    padding: 12px;
  }

  .tabbar-brand {
    display: block;
    padding: 16px 16px 20px;
    font-size: var(--md-sys-typescale-title-large-size);
    font-weight: 400;
    color: var(--md-sys-color-on-surface);
  }

  /* drawer item：56 高、圓角 full、選取時整條染成 secondary-container。
     這跟 navigation bar 不同——那邊的指示器只包住圖示。 */
  .tab {
    flex: 0 0 auto;
    flex-direction: row;
    justify-content: flex-start;
    align-items: center;
    gap: 12px;
    height: 56px;
    padding: 0 16px;
    border-radius: var(--md-sys-shape-corner-full);
    font-size: var(--md-sys-typescale-label-large-size);
    color: var(--md-sys-color-on-surface-variant);
  }
  .tab .ms { --ms-size: 24px; --ms-opsz: 24; flex: 0 0 auto; }
  .tab[aria-current="page"] {
    background: var(--md-sys-color-secondary-container);
    color: var(--md-sys-color-on-secondary-container);
  }
  .tab[aria-current="page"]::before { content: none; }
  .tab span:not(.ms) {
    position: static;
    width: auto; height: auto;
    margin: 0; overflow: visible; clip-path: none;
  }

  .protobar { justify-content: flex-start; }
}

/* ---- medium（平板 600–899）：M3 的 navigation rail ---- */
/* 只有圖示的窄欄，靠左。文字用 visually-hidden 留給輔助技術；
   目前所在那一頁靠指示器（而不是文字）表示，跟 navigation bar 一致。 */
@media (min-width: 600px) and (max-width: 899px) {
  :root { --rail-w: 80px; --topbar-clear: 0px; --tabbar-clear: 24px; }

  body { padding-left: var(--rail-w); padding-top: 0; }

  .tabbar {
    left: 0;
    top: 0;
    bottom: 0;
    right: auto;
    transform: none;
    width: var(--rail-w);
    height: auto;
    border: 0;
    border-radius: 0;
    background: var(--md-sys-color-surface);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    flex-direction: column;
    justify-content: flex-start;
    align-items: center;
    gap: 12px;
    padding: 44px 0 0;
  }
  .tabbar-brand { display: none; }

  .tab {
    flex: 0 0 auto;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    width: 100%;
    height: auto;
    padding: 0;
    border-radius: 0;
    font-size: var(--md-sys-typescale-label-medium-size);
    color: var(--md-sys-color-on-surface-variant);
  }
  .tab .ms {
    --ms-size: 24px;
    --ms-opsz: 24;
    display: grid;
    place-items: center;
    width: 56px;
    height: 32px;
    border-radius: var(--md-sys-shape-corner-full);
  }
  .tab[aria-current="page"] { color: var(--md-sys-color-on-surface); }
  .tab[aria-current="page"] .ms {
    background: var(--md-sys-color-secondary-container);
    color: var(--md-sys-color-on-secondary-container);
  }
  .tab[aria-current="page"]::before { content: none; }
  .tab span:not(.ms) {
    position: static;
    width: auto; height: auto;
    margin: 0; overflow: visible; clip-path: none;
  }
}

/* ---------- Material Symbols ---------- */

/* > [!important] 自架子集字型，不是連 Google Fonts
 * > CSP 是 `font-src 'self'`／`style-src 'self'`，本來就連不出去；而且 PWA
 * > 要能離線開，外部字型一斷線圖示就全沒了。
 * >
 * > 這一份是從 Google Fonts 的 `icon_names=` 取回來的**子集**：32 個圖示、
 * > 40.8 KB，含 FILL／wght／GRAD／opsz 四個可變軸。
 * > 它取代的是原本內嵌在**每一個 HTML 檔**裡的 SVG 精靈——那份 8 KB × 27 頁
 * > ＝ 216 KB，佔全部 HTML 的一半以上。
 * >
 * > 要加圖示：改 tools/fetch-symbols.sh 的名單重新抓，不要手改這個檔案。
 */
/* @font-face 由 build.py 產生後**附加在 app.css 後面**——字型檔名帶內容
   指紋，而這個檔案不經模板處理，注入不進來。見 build.py 的 font_face()。 */

.ms {
  font-family: "Material Symbols Rounded";
  font-weight: normal;
  font-style: normal;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  display: inline-block;
  white-space: nowrap;
  word-wrap: normal;
  direction: ltr;
  -webkit-font-feature-settings: "liga";
  font-feature-settings: "liga";
  -webkit-font-smoothing: antialiased;
  /* opsz 跟著字級走是 M3 的規定：小尺寸要粗一點才看得清楚。 */
  font-variation-settings: "FILL" var(--ms-fill, 0), "wght" var(--ms-wght, 400),
                           "GRAD" var(--ms-grad, 0), "opsz" var(--ms-opsz, 24);
  font-size: var(--ms-size, 24px);
  /* 圖示的方框就是字級，別讓行高把它撐開。 */
  height: 1em;
  width: 1em;
  overflow: hidden;
  /* 使用者選取時不要把 ligature 的原文（home、search…）也選進去 */
  user-select: none;
  -webkit-user-select: none;
}

/* 填滿版。M3 的導覽列用 FILL 0→1 表示選取，而且它是**可以補間的軸**，
   不是換一個圖示——所以會平滑地填起來。 */
.ms.is-filled, [aria-current="page"] > .ms, [aria-selected="true"] > .ms {
  --ms-fill: 1;
}

/* ---------- M3 Expressive：字級與動態 ---------- */

/* 「Use emphasized text styles to draw attention to important UI elements,
   like headlines and actions.」——同一階字級，字重拉高。 */
.m3-emphasized { font-weight: 700; letter-spacing: -0.01em; }

/* 會動的東西一律走彈簧：位移／大小用 spatial，顏色／透明度用 effects。 */
.tab, .tab .ms, .seg, .iconbtn {
  transition:
    color var(--md-sys-motion-duration-short) var(--md-sys-motion-spring-effects),
    background-color var(--md-sys-motion-duration-short) var(--md-sys-motion-spring-effects),
    font-variation-settings var(--md-sys-motion-duration-medium) var(--md-sys-motion-spring-default-spatial);
}

/* > [!important] 使用者說不要動就不要動
   > prefers-reduced-motion 之下把過渡收成幾乎瞬間——但**不是 0s**：
   > 完全沒有過渡會讓某些狀態變化難以察覺。WCAG 2.3.3 要的是「非必要的動態
   > 可以關掉」，不是「不准有回饋」。 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---------- 頂部的 scroll edge effect（裝成 App 時） ----------
   內容捲到狀態列底下之後，卡片會從時間與電量圖示後面經過。
   跟先前底部那層同一個機制、同一份 HIG 依據：

     "a scroll edge effect provides a visual separation between certain
      interface elements, such as toolbars, and the scrolling content area
      behind them. If you use custom bars, you might want to add this effect
      manually"

   不做的話狀態列會與卡片的字疊在一起，兩邊都讀不清楚。 */
:root[data-standalone] body::before {
  content: "";
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  height: env(safe-area-inset-top, 0px);
  pointer-events: none;
  z-index: 40;              /* 內容之上、導覽列（50）之下 */
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 55%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 55%, transparent 100%);
  background: linear-gradient(
    to bottom,
    color-mix(in srgb, var(--md-sys-color-surface) 86%, transparent) 0%,
    color-mix(in srgb, var(--md-sys-color-surface) 58%, transparent) 50%,
    transparent 100%);
}


/* ==== lib: static/lib/ui.css ==== */
/* osuha.ui 的樣式。接在 core app.css 後面、features 前面。
 * 度量對齊 iOS 的 insetGrouped：一組一張卡、列高 48、左右內距 12、
 * section 之間 18、header 12px 次要色——與 .me-* 同一套數字。 */

/* ---------- 只給輔助技術 ---------- */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- 按鈕 ---------- */
.ui-btn {
  min-height: 44px;                       /* 2.5.5：可點目標不得小於 44 */
  padding: 8px 16px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}
.ui-btn.is-primary { border-color: transparent; background: var(--accent); color: var(--accent-ink); font-weight: 700; }
.ui-btn.is-danger { color: var(--danger); }
.ui-btn:disabled { cursor: not-allowed; opacity: .45; }

/* ---------- toast ---------- */
/* 浮在分頁膠囊上方；視覺上的這一份 aria-hidden，播報交給 .sr-only 的即時區 */
.ui-toast {
  position: fixed;
  left: 50%;
  bottom: calc(var(--capsule-inset) + var(--capsule-h) + 12px + env(safe-area-inset-bottom, 0px));
  transform: translate(-50%, 8px);
  max-width: min(360px, calc(100% - var(--capsule-inset) * 2));
  padding: 10px 16px;
  border-radius: 999px;
  background: var(--ink);
  color: var(--bg);
  font-size: 13px;
  line-height: 1.4;
  text-align: center;
  opacity: 0;
  pointer-events: none;
  z-index: 60;
  transition: opacity .18s ease, transform .18s ease;
}
.ui-toast.is-on { opacity: 1; transform: translate(-50%, 0); }
@media (display-mode: standalone) {
  .ui-toast { bottom: calc(var(--capsule-inset) + var(--capsule-h) + 12px); }
}

/* ---------- sheet（原生 <dialog>） ---------- */
/* 手機：從底部升起的面板，圓角只在上緣；寬螢幕：置中的卡。
   iOS 的 .sheet 就是這兩種形狀（iPhone 底部、iPad／Mac 置中）。 */
dialog.ui-sheet {
  width: 100%;
  max-width: 460px;
  margin: auto 0 0;                       /* 靠底 */
  max-height: calc(100% - 48px);
  padding: 0;
  border: 0;
  border-radius: 16px 16px 0 0;
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 -8px 40px rgba(0, 0, 0, .25);
  overflow: auto;
}
@media (min-width: 600px) {
  dialog.ui-sheet { margin: auto; border-radius: 14px; }
}
dialog.ui-sheet::backdrop { background: rgba(0, 0, 0, .45); }
.ui-sheet-head {
  position: sticky;
  top: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 8px 8px var(--pad);
  background: var(--surface);
  border-bottom: var(--hairline) solid var(--line-soft);
}
.ui-sheet-title { margin: 0; flex: 1; font-size: 17px; font-weight: 700; }
.ui-sheet-body { padding: var(--pad); }
.ui-sheet-body > p { margin: 0; color: var(--ink-2); }
.ui-sheet-actions {
  display: grid;
  gap: var(--gap);
  padding: 0 var(--pad) calc(var(--pad) + env(safe-area-inset-bottom, 0px));
}
@media (min-width: 600px) {
  .ui-sheet-actions { grid-auto-flow: column; justify-content: end; padding-bottom: var(--pad); }
}

/* ---------- 分組清單（GroupedContainer ＋ LabeledRow／SubtitleRow） ---------- */
.grouped-section { margin: 0 0 18px; }
.grouped-header {
  margin: 0 0 7px 4px;
  color: var(--ink-3);
  font-size: 12px;
  font-weight: 600;
}
.grouped-footer {
  margin: 7px 4px 0;
  color: var(--ink-3);
  font-size: 12px;
  line-height: 1.5;
}
.grouped-group {
  overflow: hidden;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--line-soft);           /* 1px 的間隔露出來就是分隔線 */
}
.grouped-row {
  width: 100%;
  min-height: 48px;
  display: flex;
  align-items: center;
  gap: 12px;                              /* LabeledRow 的 Spacer(minLength: 12) */
  padding: 8px 12px;
  border: 0;
  background: var(--surface);
  color: var(--ink);
  text-decoration: none;
  text-align: left;
  font: inherit;
  font-size: 14px;
  cursor: default;
}
.grouped-row + .grouped-row { margin-top: 1px; }
a.grouped-row, button.grouped-row { cursor: pointer; }
a.grouped-row:hover, button.grouped-row:hover { background: color-mix(in srgb, var(--surface) 88%, var(--accent)); }
.grouped-row[aria-disabled="true"], .grouped-row:disabled { opacity: .45; cursor: not-allowed; }
.grouped-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }   /* SubtitleRow spacing: 2 */
.grouped-sub { color: var(--ink-3); font-size: 12px; }                              /* footnote ＋ 次要色 */
.grouped-value { margin-left: auto; color: var(--ink-3); font-size: 12px; text-align: right; }
.grouped-chevron { color: var(--ink-2); font-size: 16px; margin-left: 4px; }
.grouped-row.is-danger { color: var(--danger); }

/* ---------- 三態容器 ---------- */
/* loading／empty／error 三態互斥：面板在時，容器裡其他子節點一律不顯示。
   「載入更多」那種局部指示不走這裡——那是 content 態的事。 */
[data-state="loading"] > :not(.state-panel),
[data-state="empty"] > :not(.state-panel),
[data-state="error"] > :not(.state-panel) { display: none; }
.state-panel {
  grid-column: 1 / -1;                    /* 容器是 grid（例如 .falls）時佔滿一列 */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 48px var(--pad) 16px;
  color: var(--ink-3);
  font-size: 13px;
  text-align: center;
}
.state-text { margin: 0; }
.state-panel.is-error .state-text { color: var(--danger); }
.state-spinner {
  width: 22px; height: 22px;
  border: 2px solid var(--line-soft);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: ui-spin .8s linear infinite;
}
@keyframes ui-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .state-spinner { animation: none; border-top-color: var(--line-soft); }
  .ui-toast { transition: none; }
}

/* 產生的：Material Symbols 的子集字型，見 build_font()。 */
@font-face {
  font-family: "Material Symbols Rounded";
  font-style: normal;
  font-weight: 100 700;
  font-display: block;
  src: url("fonts/material-symbols-rounded.2a8528fc.woff2") format("woff2");
}
