/* ---------- 好拼友 CIS：陶土橘＋旅途墨綠＋鼠尾草綠＋紙感米白＋暖灰綠 ---------- */
:root {
  --bg: #F7F4EC;            /* 紙感米白：大面積背景 */
  /* 頁首（.top）目前的實際高度。給個人展示頁的主視覺照片拿去往上延伸到
     頁面最頂端用（見 .profile-display 的說明）——寫成變數而不是各處各自
     猜一個數字，改 .top-inner 的 padding 時只要改這裡一個地方。 */
  --top-h: 68px;
  --panel: #FFFFFF;
  --ink: #21302A;           /* 深墨綠黑，內文主色 */
  --muted: #6D766B;         /* 暖灰綠：次要文字、分隔線 */
  --line: #E4DECE;          /* 米白系細邊線 */
  --brand: #D9642A;         /* 陶土橘：主要行動／金額重點，只用在關鍵處 */
  --brand-soft: #FBE7D8;
  --deep: #295747;          /* 旅途墨綠：導覽／主標／信任資訊 */
  --deep-soft: #E4EBE6;
  --sage: #7E9481;          /* 鼠尾草綠文字版（成團狀態用，跟 --sage-soft 背景配） */
  --sage-soft: #E7EEE3;     /* 鼠尾草綠：成團狀態、地圖、次要背景 */
  --warn: #9C6B1F;          /* 暖金褐：提醒用，跟品牌橘明確區分開，橘色留給行動／金額 */
  --warn-soft: #F3EBD8;
  --danger: #A8493D;
  --danger-soft: #F3E2DE;
  --ok: #3E6854;
  --ok-soft: #E7EEE3;
  /* 團的識別色：專用 token，不借用 --brand（§5.3 規定橘色只給金額）也不借用 --ok/--warn
     （那些有狀態語意，拿來當識別色會讓「第 2 團」看起來像是出了什麼問題）。
     栗棕跟品牌橘刻意拉開飽和度，不會被誤認成金額。 */
  --team-a: #295747;
  --team-a-soft: #E4EBE6;
  --team-b: #8A5A3C;
  --team-b-soft: #F1E6DC;
  --radius: 12px;
  --shadow: 0 1px 2px rgba(41, 48, 42, .04), 0 6px 16px rgba(41, 48, 42, .05);
  --font-serif: "Noto Serif TC", "Cormorant Garamond", "Songti TC", serif;
  /**
   * 灰白：使用者原話「不要米色背景要灰白色」。跟 --bg（暖米白紙感，§5.3 的
   * 全站基準）刻意不同，是給「滿版主視覺 + 內容」這種沉浸式版面用的中性底。
   *
   * ⚠️ 2026-09-02 從 `#tripDetailDlg` 提升到 :root。原本那裡寫著「只給這一個
   * 對話框用、不要拿去用在別的地方」——現在個人展示頁也用同一個灰白，所以它
   * 已經是第二個使用者，繼續藏在某個 id 底下就會變成兩份各自寫死的顏色
   * （§11.1 那種坑）。這是刻意的擴大，不是忘記那句話。
   * 🚫 但它仍然**不是** --bg 的替代品：一般頁面照舊用紙感米白，只有沉浸式
   * 版面（行程詳情、個人展示頁）才用這個。
   */
  /* ⚠️ 2026-09-04 使用者實機反映「個人」分頁的背景太灰——原本 #F3F4F2
     跟純白對比明顯，尤其主視覺照片下方那一大片素色區域最明顯。這裡調亮
     成 #F8F8F7（灰階分量微幅上調 5），跟白色的差距收窄，但仍保留一點點
     灰階讓它跟純白卡片（--panel）分得出層次——完全用白色會讓沉浸式版面
     跟一般卡片版面無法區分。🚫 這個變數是共用 token（查看此團、個人展示頁、
     個人卡片對話框都用它做漸層終點與底色，見上面的說明），改這裡三個地方
     會一起變亮，這是刻意的、也是必要的——只改其中一處會讓漸層終點跟底色
     對不上，重新出現接縫（這個坑在 §6.3.15 已經踩過一次）。 */
  --detail-surface: #F8F8F7;

  /**
   * ---------- 字級：6 階，只能從這裡挑 ----------
   *
   * 2026-09-03 整理。整理前有 **21 種**字級（10 / 10.5 / 11 / 11.5 / 12 / 12.5 /
   * 13 / 13.5 / 14 / 14.5 / 15 / 16 / 17 / 19 / 21 / 22 / 23 / 24 / 26 / 27 / 32），
   * 其中六種擠在 12–14.5px 這 2.5px 的範圍裡、佔了 65% 的用量——差距只有 4%，
   * 人眼分不出來，所以那六階沒有在傳達任何訊息，只是不同時間各自調出來的結果。
   *
   * 這套 6 階衍生自 **Apple HIG（iOS）的字級表**，不是憑空發明的：整理前用最多的
   * 11 / 13 / 15 / 17 / 22px 本來就跟那套表一樣，147 個宣告裡有一半以上早就落在
   * 上面，所以這是「把零頭歸位」不是「換一套新系統」。選 iOS 那套的理由：使用者
   * 是台灣手機族、站上本來就有 platform-ios 的專屬樣式，而且中文筆畫比拉丁字母密，
   * 小字需要 11 / 13 這種細分（Material 直接從 12 跳到 14，中文的小字會不夠用）。
   *
   * 🚫 **不要再寫死 px 字級，也不要新增第 7 階。** 需要一個「介於中間」的大小時，
   * 那幾乎一定代表這個元件的角色沒想清楚——先問它是哪一種角色，再挑對應的階。
   * 這就是為什麼字級寫成變數而不是註解一句「請用 13px」（CLAUDE.md §14.1）。
   */
  --text-tag: 11px;     /* 徽章、贊助標記、時間戳、最小的輔助字 */
  --text-sub: 13px;     /* 標籤（限女生／找室友）、說明文字、灰字副標 */
  --text-body: 15px;    /* 內文。body 的預設值就是這一階 */
  --text-head: 17px;    /* 對話框標題、分區小標 */
  --text-title: 22px;   /* 頁面標題、大數字 */
  --text-hero: 28px;    /* 只給首屏大標 */

  /**
   * ---------- 行高：3 階 ----------
   *
   * 同一次整理。整理前有 1.3 / 1.4 / 1.45 / 1.5 / 1.55 / 1.6 / 1.7 / 1.85 八種。
   * 中文沒有降部、字面又比拉丁字母滿，行距要比英文版面鬆一點才不會糊成一團，
   * 所以三階都刻意偏鬆（1.4 已經是「緊」的那一端）。
   *
   * ⚠️ 這三個是**排版用**的行高。另外還有幾處 `line-height: 0` / `1` /
   * `15px` / `16px` 是拿來做圖示對齊與徽章垂直置中的**版面手法**，不是排版，
   * 不要一起改成這裡的值。
   */
  --leading-tight: 1.4;   /* 單行的標題、列表標籤 */
  --leading-body: 1.6;    /* 內文、訊息（body 的預設值） */
  --leading-loose: 1.75;  /* 長段落說明、要慢慢讀的文字 */
}

* { box-sizing: border-box; }

/* 全域隱藏捲軸、但捲動功能不變（2026-09-04 使用者要求）。原本只有
   `.chat-composer textarea`／`.filter-bar`／`.ad-row`／`.approval-q-presets`
   各自關掉自己的捲軸，這次直接放到 `*`，那幾個 scoped 規則變成多餘但無害
   （同一組屬性再寫一次不會衝突），懶得清是刻意的——沒有壞處，拿掉純粹是
   多做工。 */
* { scrollbar-width: none; -ms-overflow-style: none; }
*::-webkit-scrollbar { display: none; width: 0; height: 0; }

body {
  margin: 0;
  /* 🔴 2026-09-04 稍晚：使用者要求把疊色整個拿掉，只留原本的紋理圖片本身
     ——三版調色（半透明白 55%→半透明米白 82%→半透明棕褐 82%）輪流被打回票
     之後，使用者的結論是「不要調色，我要的就是原本紋理的樣子」。
     🚫 不要再回頭疊任何顏色層——這條路已經連續三次被否決，下次有人（含 AI）
     覺得「這個紋理偏灰藍綠，應該疊一層調色」，先看這段歷史，不要重蹈覆轍。
     `bg-texture.jpg` 本身偏灰藍綠（實測取樣 rgb(177,211,205)）是它原本的
     樣子，不是需要修正的瑕疵。 */
  background-image: url('/img/bg-texture.jpg');
  background-repeat: repeat;
  background-attachment: fixed;
  background-size: 700px 700px;
  color: var(--ink);
  /* ⚠️ 拆成三行寫，不用 `font:` 簡寫——簡寫裡放 var() 在部分瀏覽器解析會失敗
     （字級跟行高會整個掉回預設值），而這是全站的基準字級，錯了整站都跟著錯。 */
  font-family: -apple-system, "PingFang TC", "Noto Sans TC", "Microsoft JhengHei", sans-serif;
  font-size: var(--text-body);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
  padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px));
}

a { color: var(--brand); }
h1, h2 { margin: 0; letter-spacing: .01em; font-family: var(--font-serif); color: var(--deep); font-weight: 600; }
h1 { font-size: var(--text-hero); }
h2 { font-size: var(--text-title); }
h3 { margin: 0; letter-spacing: -.01em; font-size: var(--text-head); font-weight: 600; color: var(--ink); }

.wrap { max-width: 1180px; margin: 0 auto; padding: 0 16px 64px; }
.wrap.narrow { max-width: 720px; }

/* ---------- 開場動畫 ---------- */
/* 只在旅客端第一次造訪時蓋在畫面最上層，播完或逾時就淡出、從此不再出現（見 guest.js initSplash()）。 */
#splash {
  transition: opacity .5s ease;
}
#splash.hide {
  opacity: 0;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  #splash { transition: none; }
}

/* ---------- 頁首 ---------- */
/**
 * 2026-09-02：從「實心白底＋一條 1px 底線」改成**透白毛玻璃＋漸層收邊**。
 *
 * 原本那條 `border-bottom` 是一條明確的邊，頁首跟內容之間被硬切成兩塊；改成
 * 半透明白＋`backdrop-filter`，捲動時內容會從頁首底下透出來、被模糊掉，
 * 頁首自己再用一段漸層淡出，兩邊就接起來了。跟個人展示頁的霧化是同一套語言。
 *
 * ⚠️ **底部的淡出用 `::after` 疊在頁首外面，不是把漸層做進 `background`**：
 * 頁首本身要維持夠高的不透明度（文字要讀得到），淡出的那一段必須發生在
 * 頁首**下方**、疊在內容上，才不會把 logo 跟鈴鐺一起淡掉。
 *
 * ⚠️ 停點用緩動的多段值，不是兩點——長距離低對比的漸層在 8-bit 色深下會出現
 * 帶狀邊（跟個人展示頁那次是同一個問題，見 .profile-hero-tint 的說明）。
 * 這裡距離短，但同一份寫法沿用下去比較不會有人之後改壞。
 *
 * ⚠️ 這是 `public/style.css`，**三個入口共用**（旅客端／後台／旅行社端，見
 * CLAUDE.md §3）。三邊的頁首結構一樣，改這裡三邊都會變——這是刻意的，
 * 不是只想改旅客端卻波及到另外兩個。
 */
/**
 * 🔴 **關鍵：疊上去的顏色＝頁面自己的底色，不是白色。**
 *
 * 前面試過「白色半透明＋毛玻璃」「白色漸層」「遞減的模糊」，全部都留下一條
 * 看得見的橫帶，原因是同一個：**只要在內容上疊一層「跟頁面底色不一樣」的東西，
 * 它結束的地方就是一條邊**。疊白色蓋在深色廣告上就是一條亮帶；疊模糊，模糊
 * 停止的地方就是一條清晰度的落差。怎麼調停點都只是在改那條帶的形狀。
 *
 * 換成用 `--bg`（頁面本來的底色）之後，這個問題**在結構上消失**：
 * 頁首下緣淡出時，是「頁面底色」淡進「頁面底色」——在米白區域上根本沒有東西
 * 在變化，看不到就是看不到。只有真的有內容（廣告圖、卡片）在底下時才會看到
 * 它從頁面底色裡浮出來，而那正是「自然過渡」該有的樣子。
 *
 * 🚫 不要改回半透明白＋`backdrop-filter`。那個做法比較「潮」，但它跟「不要有
 * 橫線」這個需求是互相衝突的——使用者連續三輪指出同一條線，就是那個衝突。
 * 🚫 也不要加 `backdrop-filter`：模糊的邊界一樣是一條線（實測拿掉它線就消失）。
 */
.top {
  /* ⚠️ 實心的那一段只留到 34%（原本 58%），剩下三分之二都在淡出——使用者
     要的是「看得出來在淡」，不是一條實心bar底部收個邊。實心區只要夠襯住
     logo 跟鈴鐺就好。
     ⚠️ 2026-09-03 使用者要求頁首不要是米白（`--bg`），要灰白色——改用
     `--detail-surface`（#F3F4F2，個人展示頁那個「灰白底」已經在用的同一個
     顏色，不是另外發明一個新色號）。🚫 不要改回 `--bg`。 */
  background: linear-gradient(to bottom,
    var(--detail-surface) 0%,
    var(--detail-surface) 34%,
    rgba(243, 244, 242, .97) 45%,
    rgba(243, 244, 242, .92) 55%,
    rgba(243, 244, 242, .84) 64%,
    rgba(243, 244, 242, .73) 72%,
    rgba(243, 244, 242, .60) 80%,
    rgba(243, 244, 242, .45) 87%,
    rgba(243, 244, 242, .30) 93%,
    rgba(243, 244, 242, .18) 100%);
  border-bottom: 0;
  position: sticky;
  top: 0;
  z-index: 20;
}
/**
 * 往下捲時把頁首收起來、往回捲再放出來（2026-09-02）。
 *
 * ⚠️ 用 `transform` 不是 `display`／`height`：`position: sticky` 的元素改變高度
 * 會讓底下的內容跟著跳，捲動中看起來像畫面在抽動。transform 只動繪製、不動版面。
 * ⚠️ `.top::after`（底部的漸層淡出）是 `.top` 的子層、`top: 100%` 定位，
 * 會跟著一起被推上去，不用另外處理。
 */
.top { transition: transform .28s ease; will-change: transform; }
.top.top-hidden { transform: translateY(-100%); }
/* 會暈的人不該被這個動畫掃到——直接切換，不做位移動畫 */
@media (prefers-reduced-motion: reduce) {
  .top { transition: none; }
}

/**
 * 🔴 **頁首下緣那條線是 `backdrop-filter` 的邊，不是顏色的邊。**
 *
 * 這件事花了一輪才找對方向：一開始一直在調白色漸層的停點，但 `backdrop-filter`
 * 只作用在**元素自己的範圍內**，到了 `.top` 的下緣就**整個停掉**——上面是模糊的、
 * 下面是清楚的，這個落差本身就是一條線，白色再怎麼調都蓋不掉它（實測把
 * `backdrop-filter` 拿掉，那條線立刻消失，換成整片清楚，這才確定兇手是誰）。
 * 而且白色壓得愈淡，這條線愈明顯——「調淡」跟「這條線」是互相衝突的兩件事。
 *
 * **解法：讓模糊也漸漸收掉，不是說停就停。** 底下接兩段，模糊度跟白色一起遞減
 * （22 → 12 → 4 → 0），每一段自己的下緣雖然還是有落差，但落差小到看不出來——
 * 跟個人展示頁的漸進霧化是同一招（見 `.profile-haze-*`）。
 *
 * 🚫 不要把這兩段合併成一個純漸層的 `::after`：那就退回原本「模糊說停就停」的
 * 狀態，線會回來。
 * ⚠️ 每一段的白色起點必須接上一段的終點（.60 → .40 → 0），中間斷開就會多一階色差。
 */
/**
 * 頁首下方再接一小段，把剩下的 .22 收乾淨。
 * ⚠️ 用的是同一個 `--bg`（頁面底色），起點接上面的終點（.22）——顏色一致，
 * 所以在米白區域上完全看不到這一段的存在。
 * ⚠️ **不要在這裡加 `backdrop-filter`**：模糊一旦在這裡開始或結束，就會多出
 * 一條清晰度的邊，等於把剛消滅掉的問題請回來。
 */
.top::after {
  content: '';
  position: absolute; left: 0; right: 0; top: 100%; height: 40px;
  pointer-events: none;
  /* 顏色跟 .top 自己的收尾色一致（灰白，見上面 2026-09-03 的說明），
     不然這一段接不上 .top 的終點，會多一階色差。 */
  background: linear-gradient(to bottom,
    rgba(243, 244, 242, .18) 0%,
    rgba(243, 244, 242, .145) 25%,
    rgba(243, 244, 242, .105) 45%,
    rgba(243, 244, 242, .066) 63%,
    rgba(243, 244, 242, .034) 79%,
    rgba(243, 244, 242, .012) 91%,
    rgba(243, 244, 242, 0) 100%);
}
.top-inner {
  max-width: 1180px; margin: 0 auto; padding: 12px 16px;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
}
.brand { display: flex; align-items: center; gap: 8px; font-weight: 700; }
.brand .dot { width: 10px; height: 10px; border-radius: 50%; background: var(--brand); }
/* 品牌 logo 圖檔。用 height 定高、width:auto 保持比例，才不會在不同頁首高度下變形 */
.brand-logo { height: 34px; width: auto; display: block; }
/* 後台／旅行社端的頁首字比較長，logo 後面還要接文字說明，稍微縮小一點 */
.brand-logo.sm { height: 22px; }
.spacer { flex: 1; }

.tabs { display: flex; gap: 4px; overflow-x: auto; padding: 8px 0 0; }
.tabs button {
  border: 0; background: transparent; padding: 8px 12px; border-radius: 8px;
  font: inherit; color: var(--muted); cursor: pointer; white-space: nowrap;
}
.tabs button:hover { background: var(--bg); color: var(--ink); }
.tabs button.on { background: var(--deep-soft); color: var(--deep); font-weight: 600; }

/* ---------- 元件 ---------- */
.card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 16px; box-shadow: var(--shadow); margin-bottom: 14px;
}
.card-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; flex-wrap: wrap; }

/* 表單內分組用（例如對話框裡「勾選要開哪些拼團」），只是分隔區塊，不是獨立卡片，所以不加陰影 */
.fieldset {
  border: 1px solid var(--line); border-radius: 10px; padding: 10px; margin-bottom: 8px; background: var(--bg);
}
.fieldset:last-child { margin-bottom: 0; }

/* 團（同一群人）：把「這些項目要徵同一群人」這件事變成看得見的容器。
   一個團一個顏色，同一團在建立表單、瀏覽卡、管理頁都是同一色，使用者不用記編號。 */
.team-box {
  border: 1.5px solid var(--team-a); background: var(--team-a-soft);
  border-radius: 12px; padding: 12px; margin-bottom: 12px;
}
.team-box[data-team-tone="b"] { border-color: var(--team-b); background: var(--team-b-soft); }
/* 開新行程精靈裡的 team-box（data-team-block）現在只有一個、不用再靠顏色分辨
   「這是哪一團」（拿掉「＋新增一批人」之後，一次只服務一群人），綠框＋綠底
   在這裡只是多餘的裝飾，拿掉讓分頁／卡片自己的邊框當視覺邊界就好。 */
.team-box[data-team-block] { border: none; background: none; padding: 0; margin-bottom: 0; }
.team-box-hint { margin: 0 0 10px; font-size: var(--text-sub); color: var(--muted); line-height: var(--leading-loose); }
.team-box-hint strong { color: var(--ink); }
/* 選擇器刻意重複一次類別名：`.row > *` 有 flex:1 + min-width:120px，會把這個圓形徽章
   拉成橢圓（跟 .avatar 一模一樣的坑，§14.1 記過，這是第五次）。單一類別跟 `.row > *`
   同權重、誰寫在後面誰贏，靠檔案順序太脆弱；提高權重才是放在哪都不會壞。 */
.team-badge.team-badge {
  width: 24px; height: 24px; min-width: 24px; max-width: 24px; border-radius: 50%;
  background: var(--team-a); color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--text-sub); font-weight: 700; flex: 0 0 auto;
}
.team-box[data-team-tone="b"] .team-badge { background: var(--team-b); }
/* 團標題（「15:22 一起住宿＋聚餐＋搭車＋玩」這種）跟徽章／按鈕擠在同一個 .row
   裡，字長的時候會被擠到只剩 min-width:120px 就換行——同一個坑，同樣要蓋掉
   `.row > *` 的 min-width（見上面 .team-badge.team-badge 的說明），改成單行
   超出用 ... 省略，不要斷成兩行。 */
.team-headline.team-headline {
  min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.team-add-row { display: flex; gap: 8px; flex-wrap: wrap; }
/* 行程管理看板的標題（h2）跟模式標籤（自由行／跟團）擠在同一個 .row 裡，
   同一個坑（見上面 .team-headline 的說明）——標題長一點就會蓋到旁邊的標籤上，
   不是換行也不是截斷，是整段文字疊在標籤底下（實測過，見 CLAUDE.md §5.2）。 */
.board-title.board-title {
  min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 標題＋「編輯」同一行（2026-09-28）：標題吃剩下的寬度、太長就截斷，按鈕固定寬不被擠掉。
   ⚠️ 不用 .row——那條 `.row > * { min-width: 120px }` 已經咬過標題一次（見上面）。 */
.board-title-row { display: flex; align-items: center; gap: 10px; }
.board-title-row .board-title { flex: 1 1 auto; }
.board-edit-btn.board-edit-btn { flex: 0 0 auto; min-height: 0; display: inline-flex; align-items: center; gap: 4px; color: var(--deep); }
/* 虛線＝「還沒開，可以加」，跟實心的既有名額卡明顯區分開 */
.team-add-btn {
  flex: 1 1 auto; min-height: 0; padding: 8px 12px; border-radius: 9px;
  border: 1.5px dashed var(--team-a); color: var(--team-a); background: var(--panel);
  font-size: var(--text-sub); font-weight: 600; cursor: pointer; font-family: inherit;
}
.team-box[data-team-tone="b"] .team-add-btn { border-color: var(--team-b); color: var(--team-b); }

/* 開新行程第 2 步（2026-08-20 八改版）：拼房／拼餐／拼車／拼活動改成分頁切換
   （.kind-tabs-row 選分頁，.kind-panel 是各分頁的內容，一次只顯示一個），不是
   四類同時攤開要一路往下滑。切走的分頁只是 hidden，資料還在（見 guest.js
   setActiveKindTab）。「＋新增」放在每個分頁卡片列表最下面（沿用 .team-add-btn
   樣式），不是跟分頁鍵放一起。每個類別可以加不只一筆，同一類別底下依日期自動
   排序（見 sortKindInstances）。卡片標題（.kind-instance-head）只顯示日期本身，
   類別已經由分頁講過，不重複講一次。
   分頁鍵改成純文字加底線（不是實心按鈕）：四個字都要放得下同一行，實心按鈕
   的框線＋內距在手機寬度會擠到「拼活動」斷行；文字加底線更省寬度，用
   flex:1 平分寬度、justify-content:center 置中，強制 nowrap 不換行。
   ⚠️ 不要加背景色——試過灰底長條，畫出來是一整塊實心的色塊墊在文字下面，
   視覺太重，使用者不要。分頁鍵就是純文字＋選中那格的綠色底線，背景維持
   透明，直接吃外層 .team-box 的底色。
   ⚠️ 全站 `button, .btn` 有預設的白底／圓角／hover 灰底（style.css 183 行），
   `.kind-tab` 的 class 選擇器規則上贏得了 `button` 元素選擇器，但贏不了
   `button:hover`（多一個 pseudo-class，優先度更高）——不特別覆寫 :hover，
   滑過／點過一次就會跑出白底，這個坑踩過一次了。 */
.team-date-row { margin-bottom: 4px; }
.kind-tabs-row {
  display: flex; flex-wrap: nowrap; border-bottom: 1px solid var(--line); margin: 4px 0 8px;
}
.kind-tab, .kind-tab:hover, .kind-tab.active, .kind-tab.active:hover {
  flex: 1 1 0; min-width: 0; min-height: 0; display: flex; justify-content: center; align-items: center; gap: 4px;
  border: none; border-bottom: 2px solid transparent; border-radius: 0; background: none;
  padding: 6px 2px; font-size: var(--text-sub); white-space: nowrap; cursor: pointer;
  font-family: inherit; color: var(--muted);
}
.kind-tab.active, .kind-tab.active:hover { color: var(--ink); font-weight: 700; border-bottom-color: var(--deep); }
/* 分頁旁邊「已經加了幾筆」的數字，0 筆時 textContent 是空字串，:empty 直接不顯示 */
.kind-tab-count {
  min-width: 15px; height: 15px; padding: 0 3px; border-radius: 999px; background: var(--brand-soft);
  color: var(--brand); font-size: var(--text-tag); font-weight: 700; line-height: 15px; text-align: center;
}
.kind-tab-count:empty { display: none; }
/* 出發日／回程日沒填完之前鎖住（見 guest.js updateKindTabsEnabled）：文字整個
   淡掉、游標改叉叉，讓人一眼看出「現在點不了」而不是以為壞了。 */
.kind-tab:disabled, .kind-tag-add:disabled, .kind-add-btn:disabled {
  opacity: .4; cursor: not-allowed;
}
.kind-panel { margin-bottom: 8px; }
/* 行程詳情彈窗裡的名額分頁（TripDetailSplits.tsx）。
   ⚠️ 這個 margin-top 不能加在 .kind-tabs-row 上——那組樣式是開新行程精靈
   第二步共用的，那裡的分頁緊接在出發日／回程日底下，只需要 4px。
   詳情彈窗這裡上面是「希望旅伴有／謝絕」那一整區標籤，只隔 4px 會讓分頁看
   起來像那區的一部分；這裡是換一個主題（要看名額了），所以留白要明顯拉開。 */
.trip-detail-splits { margin-top: 24px; }
/* 行程詳情頁的公開留言區（TripComments.tsx，2026-08-28）。跟上面的名額分頁
   同一個道理，用明顯留白隔開「這是換一個主題」；留言列沿用 .thread-item
   那套「整列、列與列之間用上邊框分隔」的既有慣例，不是另外發明一套卡片樣式。 */
.trip-comments { margin-top: 24px; }
.trip-comments-title { margin: 0 0 8px; font-size: var(--text-body); }
.trip-comment-form { display: flex; gap: 8px; }
.trip-comment-form input { flex: 1; }
/* 沒登入時取代整個表單的登入導引——2026-08-29 使用者要求合併成一段話、
   而且要能點（導向登入），不是純文字說明。長得像連結，不是滿版按鈕，
   因為這裡只是「順便提示」，不是這個畫面的主要動作。
   `.ask-login-cta`（快問板，AskBoard.tsx）是同一套處理，共用同一份樣式。 */
.trip-comment-login-cta, .ask-login-cta {
  display: inline-block; padding: 0; border: 0; background: none;
  font: inherit; font-size: var(--text-sub); font-weight: 600; color: var(--deep);
  text-decoration: underline; cursor: pointer; min-height: 0;
}
.trip-comments-list { margin-top: 12px; }
.trip-comment { padding: 12px 4px; border-top: 1px solid var(--line); }
.trip-comment:first-child { border-top: 0; }
.trip-comment-head { display: flex; align-items: baseline; gap: 8px; }
.trip-comment-body { margin: 4px 0 0; font-size: var(--text-body); line-height: var(--leading-body); white-space: pre-wrap; word-break: break-word; }
.kind-add-btn { width: 100%; margin-top: 4px; }
.kind-instance + .kind-instance { margin-top: 8px; }
/* 2026-08-20 九改版：拿掉深綠實心底＋箭頭圖示——手機上點下去會吃到瀏覽器
   自己的 focus 高亮（背景跟文字都被蓋成同一個藍色，字整個看不見，這個環境
   沒辦法可靠關掉那個原生高亮，見 CLAUDE.md §5.2 這類「試過關不掉」的坑）。
   改成跟卡片同色系的淺色，不管有沒有被原生 focus 蓋過都還看得到字；收合
   狀態不用箭頭表示，卡片本身有沒有接在下面就是最直接的指示。

   🔴 這裡曾經手滑打成 HTML 註解的收尾符號，不是 CSS 註解該用的收尾符號，
   CSS 沒有那種寫法，註解會一路吃到下一個「星號加斜線」才結束——結果把
   .kind-instance-head 自己、.team-kind-card、.grid、.stat，連 button/.btn
   跟 button.primary 全域按鈕樣式都吞掉了，整個對話框的按鈕跟卡片瞬間變回
   瀏覽器預設樣式。改 CSS 註解時要注意：連在解釋這件事的註解文字裡，都不能
   直接打出「星號加斜線」這幾個字元連在一起，那樣寫本身就會把註解提前關掉
   ——這份說明改到第二次才真的改對，第一次的版本自己就犯了同一種錯。 */
.kind-instance-head, .kind-instance-head:hover, .kind-instance-head:focus, .kind-instance-head:active {
  display: flex; align-items: center; justify-content: space-between; width: 100%;
  border: 1px solid var(--line); border-bottom: none; border-radius: 10px 10px 0 0;
  background: var(--bg); color: var(--ink);
  font-weight: 600; font-size: var(--text-sub); padding: 8px 12px; cursor: pointer; font-family: inherit;
}
.kind-instance-head.collapsed, .kind-instance-head.collapsed:hover,
.kind-instance-head.collapsed:focus, .kind-instance-head.collapsed:active {
  border-radius: 10px; border-bottom: 1px solid var(--line);
}
.kind-tag-remove { margin-left: 8px; font-size: var(--text-tag); opacity: .7; padding: 2px; color: var(--muted); }
.kind-tag-remove:hover { opacity: 1; }
.team-kind-card {
  width: 100%; border: 1px solid var(--line); border-top: none; border-radius: 0 0 10px 10px;
  padding: 10px; background: var(--panel);
}
.team-kind-card .field:last-child { margin-bottom: 0; }

.grid { display: grid; gap: 12px; }
.grid.k2 { grid-template-columns: repeat(2, 1fr); }
.grid.k3 { grid-template-columns: repeat(3, 1fr); }
.grid.k4 { grid-template-columns: repeat(4, 1fr); }

.stat { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px; }
.stat .label { color: var(--muted); font-size: var(--text-sub); }
.stat .value { font-size: var(--text-hero); font-weight: 700; letter-spacing: -.02em; margin-top: 2px; }
.stat .sub { font-size: var(--text-sub); color: var(--muted); }

button, .btn {
  font: inherit; border-radius: 9px; border: 1px solid var(--line); background: var(--panel);
  color: var(--ink); padding: 8px 13px; cursor: pointer; transition: .12s;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 38px;
}
/* 🔴 **hover 只在真的有游標的裝置上生效。** 手機沒有游標，瀏覽器會在「點一下」
   時把 :hover 套上去而且**黏住不放**（sticky hover），直到你點別的地方為止——
   使用者實機看到的「按下底部導覽會出現白底」就是這條的近白色背景黏在那裡
   （2026-09-06）。`@media (hover: hover)` 是查詢「這個裝置有沒有游標」，
   不是查螢幕寬度，所以平板、觸控筆電都判斷得正確。
   🚫 不要改回沒有包 media query 的寫法——那會讓全站每一顆按鈕在手機上
   點完都留著一層 hover 樣式。 */
@media (hover: hover) {
  button:hover, .btn:hover { border-color: #cbd5e1; background: #fbfbfc; }
}
button.primary, .btn.primary { background: var(--deep); border-color: var(--deep); color: #fff; font-weight: 600; }
button.primary:hover { filter: brightness(1.08); }
button.ghost { border-color: transparent; background: transparent; color: var(--muted); }
button.danger { color: var(--danger); border-color: #f2c9c9; }
button.sm { padding: 4px 9px; font-size: var(--text-sub); min-height: 30px; }
button:disabled { opacity: .5; cursor: not-allowed; }

/* 觸控裝置：加大熱區到接近 44px，滑鼠操作為主的密集表格（admin）維持原本尺寸不膨脹 */
@media (pointer: coarse) {
  button, .btn { min-height: 44px; }
  button.sm { min-height: 38px; padding: 7px 12px; }
}

/* ---------- 全站唯一的箭頭 ----------
   🔴 **一種箭頭只能有一份做法，而且是墨綠色**（2026-09-21 使用者：「全域的箭頭
   icon 要統一，以綠色的為主」）。整理前同一個「往下展開」的箭頭有 **5 種** 各自
   獨立的做法：原生 select 的灰三角、`.wizard-advanced` 與 `.kind-detail` 的文字
   glyph `⌄`、`.trait-select` 用 border 拼出來旋轉 45° 的方塊、`.pick-field-chev`
   的 inline SVG——同一個手勢在同一個精靈裡長成四五個樣子（§5.4 第 5 點）。
   往右的「點進去」箭頭則是硬編碼的 `#B9BFB7`，連 token 都不是。

   做法收斂到 `.pick-field-chev` 本來就在用的那一種：**JSX 裡放一個真的
   `<Icon name="chevron-down">`**，顏色靠 `currentColor` 跟著這個 class 走。
   🚫 不要用 `mask-image` 上色——§5.2 記過那個屬性在這個環境完全不渲染。 */
.ui-chev {
  flex: 0 0 auto; display: inline-flex; color: var(--deep);
  transition: transform .15s ease;
}
/* 展開時轉 180°。⚠️ 選擇器寫成「任何 [open] 的祖先」會誤傷巢狀的 details，
   所以每一種收合區自己指名一次。 */
.pick-field[open] > summary .ui-chev,
.trait-select[open] > summary .ui-chev,
.wizard-advanced[open] > summary .ui-chev,
.kind-detail[open] > summary .ui-chev { transform: rotate(180deg); }

input, select, textarea {
  font: inherit; width: 100%; padding: 8px 10px; border: 1px solid var(--line);
  border-radius: 9px; background: #fff; color: var(--ink);
}
/* 🔴 勾選框與單選鈕不吃上面那條的 width:100%／padding（2026-10-08，通知設定實機回報
   「勾選跟選項有遮擋」）。桌機 Chrome 對 checkbox 會忽略那兩個屬性，所以瀏覽器看起來
   正常；**iOS WebView 不會**——勾選框被撐寬、蓋住旁邊的「Email」字。之前是每個元件
   各自補 `.checkbox input { width: auto }`，漏一個就壞一個，這裡一次收掉（§14.1）。 */
/* ⚠️ 用 `input:where(...)`：權重只等於上面那條 `input`（排在它後面所以贏），
   但輸給任何 class——`.prep-check`、`.sr-only` 這類自己畫外觀的勾選框不受影響。 */
input:where([type="checkbox"], [type="radio"]) {
  width: auto; padding: 0; flex: 0 0 auto;
}
/* 點一下展開時 iOS 會在整塊收合列上畫一圈藍色焦點框（對話框打開時第一列也會）。
   只有用鍵盤操作時才需要那圈框，觸控／滑鼠點擊時拿掉。 */
summary:focus:not(:focus-visible) { outline: none; }
/* `<select>` 的箭頭：拿掉原生的（各 OS 長得都不一樣、而且是灰的），換成跟
   `.ui-chev` 同一個雪佛龍、同一個墨綠。
   🔴 **這是全站唯一一處把 `--deep` 的色碼寫死的地方**：`background-image` 裡的
   SVG 吃不到 `currentColor`，而唯一的替代方案 `mask-image` 在這個環境不渲染
   （§5.2）。改 `--deep` 時這裡要跟著改。
   ⚠️ `min-height` 不能省——`appearance: none` 會一併拿掉原生控制項自帶的高度，
   跟 §5.2 那條 `input[type="date"]` 的修法是同一個補償（那次在 iOS 上塌成一條
   空橢圓）。⚠️ `padding-right` 要留給箭頭，不然長選項的文字會壓在箭頭上。 */
select {
  -webkit-appearance: none; appearance: none; min-height: 42px;
  padding-right: 34px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23295747' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 10px center;
}
/* ⚠️ 瀏覽器預設是 `resize: both`——沒設過這個屬性，任何一個 <textarea> 都能被
   拖成比對話框還寬（使用者 2026-08-28 實測：「寬度能拉到無限遠」）。全站找不到
   一個地方故意需要橫向縮放，改成只能拉高、不能拉寬——高度給彈性（打長一點的
   說明還是有地方放），寬度固定跟著版面走，不會被拖破版。`.chat-composer textarea`
   已經自己覆寫成 `resize: none`（高度交給 JS 自動跟著內容長），不受這條影響。 */
textarea { resize: vertical; }
input:focus, select:focus, textarea:focus { outline: 2px solid var(--brand-soft); border-color: var(--brand); }
/* 🔴 **iOS 的 `<input type="date">` 有一個 `width:100%` 壓不下去的原生最小寬度。**
   2026-09-07 使用者實機圈出「開新行程有些欄位會過長導致整個版面水平位移」——
   模擬器上量到「報名截止日」那格比容器右緣多出約 45px，兩個日期欄位並排時更明顯。
   桌機 Chrome 完全看不出來（那邊 `width:100%` 就是最終寬度），所以這是 §5.2 那類
   「只在 App 裡發生」的坑，靠瀏覽器測不出來。
   `-webkit-appearance: none` 拿掉原生外觀之後它才會照一般輸入框的規則排版；
   **點下去仍然叫得出 iOS 原生的日期滾輪**，只是不再自己決定要多寬。
   ⚠️ `min-width: 0` 不能省：這個欄位有時候是 flex 項目（`.row > *`），
   沒有它一樣會撐開那一格。 */
input[type="date"], input[type="time"], input[type="datetime-local"] {
  -webkit-appearance: none; appearance: none; min-width: 0;
  /* 🔴 **`min-height` 是 `-webkit-appearance: none` 的必要補償，不是裝飾**
     （2026-09-16 使用者手機實機回報「報名截止日／用餐時間／出發時間／找拍友
     開始時間的欄位變一個橢圓了，字都不見，點擊了才出現」）。
     上面那行拿掉原生外觀的同時，也把 iOS 原生控制項**內建的高度**一起拿掉了
     ——桌機 Chrome 不受影響（那邊 `appearance:none` 之後仍然是 44px，實測過），
     但 iOS 上這個盒子會塌成很矮的一條，配上 `border-radius: 9px` 看起來就是
     一顆圓角的空橢圓。42px 是對齊一般文字輸入框的實測高度（同一份 padding
     ＋ 行高算出來就是這個數字），不是隨手挑的。 */
  min-height: 42px;
}
/* 拿掉原生外觀之後，值會靠左但預設仍是 inline 置中，補一行讓它跟其他輸入框對齊。
   🔴 **`min-height` 這行才是「字都不見」真正的修法**：iOS 會把這個 pseudo-element
   （日期／時間的「值」那塊文字）壓成 0 高，於是欄位裡明明有值卻什麼都看不到，
   要等點下去叫出原生滾輪、focus 之後才重新繪出來——正好是使用者描述的症狀。
   桌機上這個盒子本來就有高度，加了不會改變任何東西。
   ⚠️ 三種型別都要寫：`time`／`datetime-local` 跟 `date` 用的是同一個 pseudo，
   只寫 `date` 的話「用餐時間／出發時間／開始時間」那幾格照樣是空的。 */
input[type="date"]::-webkit-date-and-time-value,
input[type="time"]::-webkit-date-and-time-value,
input[type="datetime-local"]::-webkit-date-and-time-value {
  text-align: left; min-height: 1.4em;
}


label { display: block; font-size: var(--text-sub); color: var(--muted); margin-bottom: 4px; }
.field { margin-bottom: 10px; }

/* 密碼欄位的眼睛切換（PasswordField.tsx，2026-09-22）。
   ⚠️ 這裡曾經寫著「不能排成 flex，不然輸入框會撞到 `.row > *` 的 min-width」
   ——**那個顧慮是搞錯層級了**：`.row > *` 只作用在 `.row` 的直接子元素，也就是
   外面那層 `.field`，碰不到 `.pw-field` 裡面的輸入框。
   🔴 **真正不能做的是相反的那件事：眼睛不能用絕對定位疊在輸入框上面，在 iOS 上
   會點不到。**
   2026-09-22 實機抓到：桌機 Chrome 完全正常（絕對定位的元素在非定位內容之上，
   命中判定也照這個順序），但 iPhone 模擬器上不管點眼睛的哪裡，拿到焦點的都是
   底下那個 `<input>`，圖示一次都沒被按到——WebKit 對表單控制項的命中判定會贏過
   疊在它上面、沒有自己 stacking context 的兄弟節點。跟 §5.2 記的那批一樣，是
   「只在 iOS 上發生、靠瀏覽器測不出來」的那一類。
   修法不是補 `z-index` 賭 WebKit 會聽話，是**讓兩者根本不重疊**（§14.1）：
   外層這個 div 變成看得見的那個框，輸入框自己不畫邊框、只佔左邊那一段，眼睛是
   它的兄弟節點各佔各的空間。視覺上完全一樣，但「誰吃到這一下」不再有懸念。 */
.pw-field {
  display: flex; align-items: center;
  border: 1px solid var(--line); border-radius: 9px; background: #fff;
}
/* 邊框與底色都交給外層了，這裡只負責文字。⚠️ `min-width: 0` 不能省——flex 項目
   預設不能縮到內容寬度以下，少了它長密碼會把眼睛擠出框外（§5.2 那條老坑）。 */
.pw-field input { flex: 1; min-width: 0; border: 0; background: none; }
/* 全域有一條 `input:focus { outline: 2px solid ... }`，留著會在框裡再畫一圈。
   聚焦的回饋改由外層整個框給（下面那條 `:focus-within`），視覺跟其他欄位一致。 */
.pw-field input:focus { outline: none; }
.pw-field:focus-within { outline: 2px solid var(--brand-soft); border-color: var(--brand); }
.pw-eye {
  flex: 0 0 auto; margin-right: 4px;
  /* 🔴 全域有一條 `button { min-height: 44px }`，而 **min-height 永遠贏過
     height**——不歸零的話這顆會把自己撐成 44px、比輸入框還高。這個坑站上
     已經踩到第六次了（.profile-settings-btn／.avatar-btn／.trip-cover-owner
     .avatar／篩選鈕／.idcard-photo-btn）。 */
  min-height: 0; height: 34px; width: 34px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: none; border: none; color: var(--muted); cursor: pointer;
}
.pw-eye:hover { color: var(--deep); }
/* 按下去（正在顯示密碼）時用主色，讓「現在是看得到的」這件事有視覺回饋 */
.pw-eye[aria-pressed="true"] { color: var(--deep); }
/* 規則說明／錯誤。跟 `.opt` 同一種角色（欄位底下的補充），所以同一個字級 */
.pw-hint { display: block; margin-top: 4px; font-size: var(--text-sub); color: var(--muted); }
.pw-hint.bad { color: var(--danger); }
.row { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; }
.row > * { flex: 1; min-width: 120px; }
.row > .fixed { flex: 0 0 auto; min-width: 0; }
.checkbox { display: flex; align-items: center; gap: 8px; font-size: var(--text-body); }
.checkbox input { width: auto; }
.checkbox:has(input:disabled) { opacity: .5; cursor: not-allowed; }

table { width: 100%; border-collapse: collapse; font-size: var(--text-body); }
th, td { text-align: left; padding: 9px 8px; border-bottom: 1px solid var(--line); vertical-align: middle; }
th { font-size: var(--text-sub); color: var(--muted); font-weight: 600; }
tr:last-child td { border-bottom: 0; }
.num { text-align: right; font-variant-numeric: tabular-nums; }
.scroll-x { overflow-x: auto; }
.scroll-x table { white-space: nowrap; }

.tag {
  display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: var(--text-sub);
  background: var(--bg); color: var(--muted); border: 1px solid var(--line);
}
.tag.ok { background: var(--ok-soft); color: var(--ok); border-color: transparent; }
.tag.warn { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
.tag.danger { background: var(--danger-soft); color: var(--danger); border-color: transparent; }
.tag.brand { background: var(--brand-soft); color: var(--brand); border-color: transparent; }

.muted { color: var(--muted); }
.small { font-size: var(--text-sub); }
.mono { font-variant-numeric: tabular-nums; }
.empty { color: var(--muted); text-align: center; padding: 28px 12px; }

.alert { padding: 10px 12px; border-radius: 10px; margin-bottom: 8px; font-size: var(--text-body); }
.alert.info { background: var(--deep-soft); color: var(--deep); }
.alert.warn { background: var(--warn-soft); color: var(--warn); }
.alert.danger { background: var(--danger-soft); color: var(--danger); }
.pending-review-box { padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px; }
/* 一筆待審核申請的區塊間距——統一在這裡管，不要散在元件裡各自補
   margin-top（CLAUDE.md 記過這個坑：零散的 inline 間距很容易漏掉某一塊，
   結果整卡擠成一團）。8px 是這張卡目前用的間距單位，跟 .pending-review-box
   自己的 padding 一致。 */
.pending-review-item {
  display: flex; flex-direction: column; gap: 8px;
  margin-top: 10px; padding-top: 10px; border-top: 1px solid rgba(41, 48, 42, .1);
}
.pending-review-who { display: flex; align-items: center; gap: 8px; }
.pending-review-actions { display: flex; justify-content: flex-end; gap: 8px; }

/* 進度條：房間集人狀態 */
.bar { height: 8px; background: var(--line); border-radius: 999px; overflow: hidden; }
.bar > i { display: block; height: 100%; background: var(--deep); border-radius: 999px; transition: width .3s; }

.seats { display: flex; gap: 6px; }
/* ⚠️ 位子一律是**圓形**，跟已入座的頭像同一個形狀（2026-09-06 使用者實機
   圈出）。這裡原本是 `border-radius: 8px`（圓角方形），於是同一排會出現
   「圓形頭像 ＋ 方形空位」兩種形狀——但它們是同一種東西（一個位子），
   只差有沒有人，長得不一樣就是 §5.4 第 5 點說的那種不一致。
   🚫 不要改回方形。已入座是 `.avatar`（`border-radius: 50%`，見那條規則），
   要嘛兩邊都圓、要嘛兩邊都方，而圓形才讀得出「這是一個人的位子」。 */
.seat {
  width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center;
  font-size: var(--text-sub); background: var(--bg); border: 1px dashed var(--line); color: var(--muted);
}
.seat.taken { background: var(--deep-soft); border: 1px solid transparent; color: var(--deep); font-weight: 600; }

/* 位子圓點後面那顆「＋ 加人」（2026-09-23，管理看板）。刻意跟 .seat 同尺寸、
   同圓形——它讀起來要像「這排位子的下一格」，不是一顆插在旁邊的按鈕。
   🔴 `min-height: 0` 不能省：全域 `button { min-height: 44px }`，而
   **min-height 永遠贏過 height**，30×30 會被撐成 30×44 變成橢圓。
   這個坑已經踩到第七次（.profile-settings-btn／.avatar-btn／
   .trip-cover-owner .avatar／篩選鈕／.idcard-photo-btn／.pw-eye）。
   🔴 **選擇器寫成重複類別**（2026-09-24 補）：全域 `button:hover` 會把背景
   蓋成近白色（權重 0,1,1 贏過單一類別的 0,1,0），滑鼠移上去就變成白底白字
   的「＋」。跟 `.board-back-btn` 同一天發現的同一個陷阱，見那邊的說明。 */
.seat-add.seat-add {
  width: 30px; height: 30px; min-height: 0; padding: 0; border-radius: 50%;
  display: grid; place-items: center; flex: 0 0 auto;
  background: var(--deep); border: 1px solid var(--deep); color: #fff;
  font-size: var(--text-body); line-height: 1; cursor: pointer;
}
.seat-add.seat-add:hover { background: var(--deep); border-color: var(--deep); filter: brightness(1.08); }
/* 「還有 N 個位子沒畫出來」——瀏覽列表的整團卡片位子太多時收成這一格
   （見 SeatDots 的 max）。字比位子小一號，讓它讀起來像註記而不是又一個位子。 */
.seat.seat-more { font-size: var(--text-tag); }
.seat-avatar { width: 30px; height: 30px; font-size: var(--text-sub); flex: 0 0 auto; }
.seat-avatar.user-link { cursor: pointer; text-decoration: none; }
/* 2026-08-27 使用者在 mockup 畫布上直接改的決定：座位頭像跟一般 .avatar
   （個人頁／聊天室／訊息列表）不一樣，改用品牌深綠，橘色留給金額／截止日
   這類真正的行動與急迫提示。只改 .seat-avatar 這個更窄的選擇器，不動
   .avatar 本身——但 `.avatar` 本身的規則排在後面，單一類別選擇器跟它
   權重相同會被蓋掉，寫成重複類別 `.avatar.seat-avatar` 拉高權重
   （CLAUDE.md §5.2 記過的既有寫法，不是新發明）。 */
.avatar.seat-avatar { background: var(--deep-soft); color: var(--deep); }

/* 對話框 */
dialog {
  border: 0; border-radius: 14px; padding: 0; width: min(560px, calc(100vw - 32px));
  box-shadow: 0 24px 60px rgba(0, 0, 0, .25);
}
dialog::backdrop { background: rgba(15, 23, 42, .45); }
/**
 * 🔴 **對話框一律是「固定的頭 ＋ 會捲的身體 ＋ 固定的腳」，不能整個對話框自己捲。**
 *
 * 2026-09-03 使用者定的全站規則：「上方的常用操作欄不管怎麼滑動都要看得見」、
 * 「禁止只有捲到最底才有關閉」。這條規則之所以會被違反，不是誰忘了寫，是
 * **舊寫法在結構上就會發生**：`.dlg-body` 只給自己設 `max-height`，沒有把
 * `.dlg-head` 跟 `.dlg-foot` 的高度算進去，所以內容一長，
 * 「頭＋身體上限＋腳」就超過視窗高度，超出的部分變成**對話框自己在捲**——
 * 頭跟腳就跟著捲走了。實機在「通知」清單上量到過（`dialog.scrollHeight >
 * dialog.clientHeight`）。
 *
 * 解法是讓對話框成為固定高度的 flex 容器，身體是唯一會縮的那一塊：
 *   - `flex: 0 1 auto`：**不要用 `1 1 auto`**，那會讓內容很短的對話框也被撐高。
 *   - `min-height: 0`：flex 子元素預設不能縮到內容高度以下，少了這行 `overflow`
 *     永遠不會生效（這是 flex 最常見的一個坑）。
 * ⚠️ `display` 一定要限定 `[open]`，否則會蓋掉瀏覽器內建的
 * `dialog:not([open]) { display: none }`，對話框會關不掉（§5.2）。
 */
dialog[open] { display: flex; flex-direction: column; }
/* 🔴 頭／身體／腳包在 `<form>` 裡的對話框（通知設定、報名、註冊…大部分表單）：上面那條
   flex 只管得到對話框的**直接**子元素，中間多一層 form 就失效——整個對話框自己捲、
   標題跟著捲走（2026-10-08 通知設定實機抓到）。讓 form 也當一層會縮的 flex column。 */
dialog[open] > form { display: flex; flex-direction: column; flex: 0 1 auto; min-height: 0; }
.dlg-head { padding: 14px 18px; border-bottom: 1px solid var(--line); font-weight: 700; font-size: var(--text-head); letter-spacing: -.01em; }
.dlg-body { padding: 16px 18px; max-height: 68vh; overflow: auto; flex: 0 1 auto; min-height: 0; }
.dlg-head, .dlg-foot { flex: 0 0 auto; }
/* 旅伴偏好問卷的「1 / 4」步數（TraitsQuiz.tsx）。⚠️ 靠 `float: right`
   而不是把 `.dlg-head` 改成 flex——那個 class 是全站每個對話框標題共用的，
   為了一個元件把它改成 flex 容器，其他幾十個對話框的標題排版會一起被動到
   （CLAUDE.md §5.5 第 7 點那類全域樣式衝突）。 */
.onboard-step-count { float: right; font-weight: 500; font-size: var(--text-sub); color: var(--muted); }
.dlg-foot { padding: 12px 18px; border-top: 1px solid var(--line); display: flex; gap: 8px; align-items: center; justify-content: flex-end; }

/* 開新行程分步表單：頂端步驟條，只有必填欄位在第 1 步 */
.wizard-steps { display: flex; padding: 10px 18px 0; gap: 2px; border-bottom: 1px solid var(--line); }
/* ⚠️ 2026-08-28 修正：「費用與名額」5 個字比另外兩個標籤（各 4 個字）長，
   窄螢幕上會被 `flex:1` 分到的寬度擠到換行，變成三個高度不一致的分頁籤。
   `white-space: nowrap` 只是防止換行，真正讓它塞得下的是把水平內距與
   字級都收窄一點——量過 320px 寬的對話框，三個籤加起來還有餘裕。 */
.wizard-step-tab {
  flex: 1; display: flex; align-items: center; justify-content: center; gap: 4px;
  padding: 8px 2px 10px; font-size: var(--text-sub); color: var(--muted); border-bottom: 2px solid transparent;
  white-space: nowrap;
}
.wizard-step-tab.on { color: var(--deep); font-weight: 600; border-bottom-color: var(--deep); }
.wizard-step-tab.done { color: var(--deep); }
.wizard-step-num {
  width: 18px; height: 18px; border-radius: 50%; background: var(--line); color: var(--muted);
  font-size: var(--text-tag); display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
}
.wizard-step-tab.on .wizard-step-num, .wizard-step-tab.done .wizard-step-num { background: var(--deep); color: #fff; }
.wizard-panel { display: flex; flex-direction: column; gap: 0; }
/* 開新行程第 2 步依序解鎖：還沒輪到的那一關淡化＋不能互動（見 guest.js
   updateTripStep2Locks），讓使用者一眼看出「現在該填哪一格」。 */
/* 依序解鎖：前一格還沒填完的欄位是暗的、也點不動（CLAUDE.md §5.5 第 1 條）。
   ⚠️ 名字刻意不帶步驟編號——2026-09-05 那次把第 2／3 步對調就證明了位置會變，
   叫 `step2-locked` 之後只會變成一個說謊的名字。 */
.wizard-locked { opacity: .4; pointer-events: none; transition: opacity .15s ease; }
[hidden] { display: none !important; }
.req { font-size: var(--text-tag); font-weight: 600; color: var(--brand); background: var(--brand-soft); border-radius: 999px; padding: 1px 8px; vertical-align: middle; }
.opt { font-size: var(--text-tag); font-weight: 400; color: var(--muted); vertical-align: middle; }

/* 由下往上展開的面板（篩選用）：貼在螢幕底部，只有上緣圓角，從下滑入 */
dialog.sheet {
  position: fixed; inset: auto 0 0 0; margin: 0 auto; top: auto;
  width: 100%; max-width: 560px; border-radius: 16px 16px 0 0; animation: sheet-up .18s ease-out;
}
/* ⚠️ 上限給**對話框整體**，不是只給身體——身體的上限如果是 92vh，加上頭跟腳
   就會超過視窗，變成整個對話框自己捲（見上面 `dialog[open]` 那段的完整說明）。
   身體改成沒有自己的上限，由 flex 去分剩下的高度。 */
dialog.sheet[open] { max-height: 92vh; max-height: 92dvh; }
dialog.sheet .dlg-body { max-height: none; padding-top: 8px; }
/* ⚠️ 2026-09-03 使用者實機要求：「查看此團要全螢幕佔滿，不要還留上方區塊」。
   原本是 `min-height: 88vh`——內容撐不高時逼它佔視窗的 88%，但代價是頂端
   永遠留一道看得到底下頁面（頁首、其他卡片）的縫。改成固定 `height`（不是
   `min-height`）就直接佔滿整個視窗高度，不再看內容多寡。用 dvh 不是 vh
   （見下面 1109 行那段動態視窗高度的說明，同一個理由：手機瀏覽器的網址列
   收合／展開時 vh 是固定值，dvh 會跟著縮，不會被工具列蓋住底部內容）。 */
#tripDetailDlg.sheet[open] {
  height: 100vh; height: 100dvh;
  /* ⚠️ 瀏覽器對 `<dialog>` 用 `showModal()` 開啟時，內建樣式表自己會加一條
     `max-height: calc(100% - 36px)`（縮小留白，避免對話框整個貼齊視窗邊緣）
     ——這條 UA 預設值不是被我們的 `height` 覆蓋掉，是**疊在後面**繼續生效：
     `max-height` 會夾住算出來的高度，不管 `height` explicit 設多少。實測過：
     只改 `height` 不改 `max-height`，畫面上還是會在頂端留一道 36px 的縫、
     露出底下的頁面——正是使用者這次要拿掉的東西。要全螢幕就要兩個屬性
     一起蓋掉。 */
  max-height: 100vh; max-height: 100dvh;
  display: flex; flex-direction: column;
}
/* 詳情頁本身就是一個獨立畫面，裡面不需要再包一張卡：.trip-card 的邊框＋陰影＋16px 內距
   疊在對話框的內距上，變成卡片疊卡片，橫向空間被吃掉兩層（名額卡因此被擠得很窄）。
   這裡把外層那張卡攤平，內距也收窄，把寬度讓給真正有內容的名額卡。 */
#tripDetailDlg.sheet .dlg-body { flex: 1; padding-left: 12px; padding-right: 12px; }
#tripDetailBody .trip-card {
  background: none; border: 0; border-radius: 0; padding: 0; margin-bottom: 0; box-shadow: none;
}

/* ---------- 行程詳情頭圖（2026-09-02 改版，見 TripDetailDialog.tsx 開頭的完整說明） ----------
   使用者拿旅遊 app 的參考截圖來回定案：頭圖圓角放大、關閉／分享改毛玻璃圓鈕、
   標題區改置中、背景換成專屬的灰白（不是全站 --bg）。 */

/* ⚠️ 只在 #tripDetailDlg 這個 id 覆寫，不動 `dialog.sheet` 的共用值——
   篩選面板／通知清單／請旅行社協助三個對話框也共用 .sheet，不該被這次
   改動波及。灰白（--detail-surface）刻意不等於站上 --bg（暖米白紙感）——
   使用者原話「不要米色背景要灰白色」。
   ⚠️ 2026-09-02 那個變數已經從這裡提升到 :root（個人展示頁也用同一個灰白），
   定義與適用範圍見 :root 那段說明。這裡只留 background 的指定。
   ⚠️ **`overflow: hidden` 是這次修 bug 才加的**：拿掉它、改成靠圖片自己的
   `border-radius` 跟對話框的 `border-radius` 兩邊各自湊出同一個數字（28px），
   實機在使用者的裝置上看得到頂端兩角有一道很細的斷層——兩個各自獨立算出來的
   圓角，就算數字寫得一模一樣，瀏覽器對兩者的邊緣抗鋸齒運算是分開跑的，
   在某些縮放比例下就會露出一條細縫。加了 `overflow: hidden` 之後，圓角只由
   **這一個元素**決定，圖片跟漸層層都不再各自畫圓角、只負責被裁切——同一個
   圓角只算一次，斷層在結構上就不可能出現，不是「數字對就好」那種脆弱的
   對齊（CLAUDE.md §14.1）。 */
#tripDetailDlg.sheet {
  border-radius: 28px 28px 0 0;
  overflow: hidden;
  background: var(--detail-surface);
}

/* 滿版頂圖：封面填滿卡片頂端、貼齊左右與上緣，用負 margin 抵銷
   #tripDetailBody 的 8px/12px padding。⚠️ 圖片本身**不再需要自己的
   border-radius**——頂端的圓角完全交給 #tripDetailDlg 的 overflow:hidden
   裁切，圖片只要乖乖填滿方形範圍即可（見上面那段「單一圓角來源」的說明）。 */
.trip-cover-hero { position: relative; margin: -8px -12px 0; }
.trip-cover-hero #tripDetailCover {
  display: block; width: 100%; height: 300px; object-fit: cover;
}
/* 頂端壓一層淡淡的漸層，讓毛玻璃圓鈕在亮色照片上也看得清楚——不是裝飾，
   拿掉的話遇到淺色系封面（例如白牆房間）兩顆按鈕會幾乎融進照片裡。
   同上，不自己畫圓角，靠 #tripDetailDlg 的 overflow:hidden 裁切。 */
.trip-cover-hero::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to bottom, rgba(20,26,22,.28) 0 18%, rgba(20,26,22,0) 40%);
}
/* ⚠️ 2026-08-31：Unsplash 署名小標籤（.trip-cover-credit）已依使用者明確要求移除，
   見 public/guest.js renderTripCard 那段的說明（違反 Unsplash 條款但為使用者知情的
   決定）。這裡刻意留一行註記，避免下一個人看到「有 hero 沒 credit」以為是漏掉。 */

/* 關閉／分享：浮在照片上的毛玻璃圓鈕，取代原本標題旁的實心白底圓鈕
   （`.sheet-close`／`.trip-detail-share-btn`，這兩個 class 已經沒有呼叫端，
   跟著這次改版一起刪掉）。
   ⚠️ 2026-09-03：關閉／分享兩顆從 `.trip-cover-hero`（照片內、隨內容捲動）
   搬成 `<dialog>` 自己的直接子元素——使用者實機要求「常駐在上方」，捲動
   內文時這兩顆不能跟著頭圖一起滾出畫面。`position: absolute` 的容納區塊
   （containing block）是**最近一個有 `position` 的祖先**：搬家前是
   `.trip-cover-hero`（`position: relative`），搬家後是 `#tripDetailDlg`
   本身（`.sheet` 給的 `position: fixed`）——同一個 class、同一組
   `top/left/right` 數字，只是換了一個祖先，`top:14px` 就從「貼著照片頂端」
   變成「貼著整個對話框（=整個螢幕，見上面 100dvh 那段）頂端」，不需要另外
   寫一份定位邏輯。收藏鈕留在 `.trip-cover-hero` 裡沒有搬——使用者只點名
   「關閉和分享」，收藏跟著照片一起捲動是可以接受的次要動作。 */
.trip-cover-glass {
  position: absolute; top: 14px; z-index: 2;
  width: 40px; height: 40px; min-height: 0; padding: 0; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  /* 🔴 深色玻璃，理由跟 `.profile-settings-btn` 完全一樣（見那裡的完整說明）：
     關閉／分享是常駐在上面的控制項，捲過封面照之後底下就變成灰白的內容區，
     白玻璃在那上面幾乎看不見。深色在兩種底色上都讀得到。 */
  background: rgba(20, 26, 22, .42); border: 1px solid rgba(255, 255, 255, .22);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  color: #fff; cursor: pointer;
}
.trip-cover-glass svg { stroke: #fff; }
.trip-cover-glass.left { left: 14px; }
.trip-cover-glass.right { right: 14px; }
/* 右邊第二顆（收藏，2026-09-02）。14 + 40（第一顆寬）+ 8（間距） */
.trip-cover-glass.right-2 { right: 62px; }

/* 主揪頭貼（2026-09-07）：首圖左下角，點下去開他的個人卡片。
   使用者在三個 mockup 裡選了「只有頭貼、沒有名字」這一版。

   🔴 **`bottom` 不是 0，是 26px，這個數字有來源不要亂調**：`.trip-detail-head`
   有 `margin-top: -30px`（波浪線疊在照片下緣 30px 上），而波浪的路徑在最左邊
   是 `y=17`（viewBox 高 34）——換算成實際位置，左側邊緣的實色從照片底部往上
   13px 就開始了。頭貼真的貼齊左下角的話，左下那一角會被波浪切掉。26px 讓
   整顆圓完全落在照片上，跟波浪之間還留得下一點呼吸。

   ⚠️ 白色描邊不是裝飾：頭貼會疊在使用者自己上傳的任何照片上，沒有描邊時
   深色頭貼配深色照片會整顆糊掉。跟 `.trip-cover-glass` 那三顆用深色玻璃是
   同一個理由的另一種解法——那三顆裡面是白色圖示，這裡裡面是一張圖，蓋深色
   玻璃反而會把頭貼本身壓暗。

   ⚠️ 全域 `button { min-height: 44px }` 會把 40×40 撐成 40×44 變橢圓，一定要
   歸零——`.profile-settings-btn`／`.avatar-btn` 都踩過同一個坑（§6.4.0a）。 */
.trip-cover-owner.trip-cover-owner {
  position: absolute; left: 14px; bottom: 40px;
  /* 2026-09-07 使用者要求放大——改回站上的標準頭貼尺寸（見 `.avatar` 本身，
     1363 行），不是另外挑一個數字。同一天稍晚使用者再要求「往上一點」，
     `bottom` 從 26px 加到 40px——往上調沒有下限風險（波浪危險區只到照片
     底部往上 13px，見上面那段註解，26px 早就有安全距離，40px 只是更寬裕），
     照片本身 300px 高（見 `#tripDetailCover`），上面關閉／分享兩顆常駐控制項
     是 `top:14px` 高 40px，離這裡還很遠，往上調不會撞到。 */
  width: 56px; height: 56px; min-height: 0; padding: 0;
  border: 0; border-radius: 50%; background: none;
  -webkit-tap-highlight-color: transparent; cursor: pointer;
}
/* 🔴 **底色一定要改成不透明的。** `.avatar` 預設是 `rgba(41,87,71,.12)`
   半透明墨綠——那是刻意的，讓它在白卡片／灰白個人頁／紙紋快問卡三種底色上
   都跟著背後那層走、不像一塊補丁（§5.3）。但這裡背後是**使用者自己上傳的
   照片**，半透明等於沒有底：實測疊在夕陽色的封面上，12% 的綠幾乎消失，
   墨綠色的姓名首字也糊進背景，整顆看起來像一個空的圈。
   #DBE1DD 就是那個半透明色疊在 `--detail-surface` 上算出來的實際顏色——
   換句話說，這裡看到的跟個人頁上看到的是同一個顏色，只是不再跟著背後變。
   （沒上傳頭貼才會看到底色；有照片的話 `img` 會蓋滿整顆，這行不影響。） */
.trip-cover-owner .avatar {
  /* 尺寸跟字級不用自己設——56px／var(--text-title) 正是 `.avatar` 本身的
     預設值（1363 行），這裡只需要覆寫底色跟白框，那兩個才是這個位置專屬的。 */
  background: #DBE1DD;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, .85);
}

/* 頭圖點下去要能放大——本身是可互動元素，不是純裝飾照片 */
.trip-cover-hero #tripDetailCover { cursor: zoom-in; }

/* 圖片放大檢視——外層對話框自己的固定定位子元素，**不是**第二個 `<dialog>`
   （見 TripDetailDialog.tsx 的 viewerOpen 說明：兩個同時開著的 modal
   `<dialog>` 在這個瀏覽器環境會互相牽連，關掉一個另一個也會被原生關掉，
   實測證實過）。`position:fixed` 相對的是視窗，`z-index` 只要蓋過同一個
   `<dialog>`（top layer）裡的其餘內容即可，不需要也搶不到另一層 top layer。
   全螢幕黑底＋圖片置中等比縮放，跟站上其他對話框的白卡片樣式刻意不同——
   這裡的目的是讓照片本身佔滿注意力，不需要卡片外框。 */
.trip-cover-viewer {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(10, 12, 10, .96);
  display: flex; align-items: center; justify-content: center;
}
.trip-cover-viewer img {
  max-width: 100vw; max-height: 100vh; width: auto; height: auto;
  object-fit: contain; cursor: zoom-out;
}
.trip-cover-viewer-close { top: 18px; left: 18px; }

/* 地區／標題：置中，跟照片之間用一條連續的波浪 SVG 銜接（使用者拿參考圖
   圈出來的形狀——不對稱、中間凹、靠右揚，不是對稱的圓角矩形疊照片）。
   ⚠️ 這是第三版才做對，見 TripDetailDialog.tsx 那段「圓弧」的完整沿革。
   ⚠️ 這裡原本還有一顆「還缺 N 人／已成團」的單一資訊 pill，使用者實機看過
   之後要求拿掉（跟名額卡本身重複到不需要再講一次），2026-09-02 移除，
   pill 相關的 CSS 與 TripDetailDialog.tsx 的 needSummary() 一起刪掉了。 */
/* ⚠️ 一定要跟 .trip-cover-hero 用同一組負 margin（-12px 左右）——這裡漏寫過
   一次：波浪／內容區沒有抵銷 .dlg-body 的左右 12px 內距，寬度比照片窄了
   24px，兩側露出底下沒被蓋住的照片（使用者實機圈出來的那個缺口）。 */
.trip-detail-head { position: relative; margin: -30px -12px 0; }
/* SVG 疊在照片下緣：viewBox 0-34 的座標系裡，y=34 是波浪的最低點（跟下面
   .trip-detail-head-body 的背景齊平），y 越小代表那個 x 位置的波浪切進照片
   越深。fill 用跟內容區同一個 --detail-surface，波浪以下看起來就是同一塊
   底色，波浪以上露出照片——效果等同「照片被挖出一道不規則的缺口」。
   ⚠️ preserveAspectRatio="none" 讓寬度貼齊卡片、高度固定 34px 不跟著縮放，
   這是裝飾用的分隔線，不是要維持长寬比的圖示。 */
.trip-detail-wave { display: block; width: 100%; height: 34px; }
.trip-detail-wave path { fill: var(--detail-surface); }
.trip-detail-head-body {
  background: var(--detail-surface); margin-top: -1px; /* 蓋掉 SVG 與下面實色區塊間的次像素縫隙 */
  padding: 0 16px 14px; text-align: center;
}
.trip-detail-eyebrow {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--text-tag); letter-spacing: .14em; text-transform: uppercase;
  color: var(--sage); margin-bottom: 6px;
}
.trip-detail-eyebrow svg { stroke: var(--sage); width: 13px; height: 13px; }
.trip-detail-title { font-size: var(--text-title); margin: 0; text-wrap: balance; }
/* 下面（說明／旅伴條件／此團屬性／名額／留言）維持原本的靠左排版，
   只有頭圖到標題這一段（身分辨識用的最上層資訊）改成置中。 */
#tripDetailBody .trip-detail-head ~ * { text-align: left; }

/* 「希望旅伴有」／「謝絕」的段落標題——2026-09-01 第一版跟「這團的風格」
   （.trip-style-tags-head）走同一套「帶 icon、粗體」語言，取代普通的
   .small.muted 小字。
   ⚠️ 2026-09-02 使用者實機用過後再校正一次：icon 拿掉、字放大兩級
   （13px → 17px，跟 .dlg-head 的標題字級一致）——份量改成全靠文字本身撐，
   不是靠 icon。三個標題（這團的風格／希望旅伴有／謝絕）一起改，字級跟著
   .trip-style-tags-head 對齊，不要各寫各的數字。 */
/* ⚠️ 2026-09-04 校正：margin-bottom 從 6px 補到 10px——跟 .seek-detail-label
   同一種「17px 粗體標題後面接內容」樣式，6px 太擠，標題會貼著底下的標籤列
   （使用者實機圈出來，見 .seek-detail-label 的說明）。 */
.trip-detail-subhead {
  font-size: var(--text-head); font-weight: 700; color: var(--ink); margin-bottom: 10px;
}

/* 團的外框在詳情頁裡是第二層，內距再收窄一點，名額卡才有寬度可用 */
#tripDetailBody .team-box { padding: 10px 8px; }
@keyframes sheet-up { from { transform: translateY(100%); } to { transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) { dialog.sheet { animation: none; } }

/* 登入／註冊：全螢幕背景，表單卡片浮在上面。
   ⚠️ 2026-08-28 使用者釐清過：**只有註冊視窗**（RegisterDialog，沒有
   `<AuthVideo>`）拿掉背景影片，改用跟全站 body 同一套紙感材質貼圖
   （/img/bg-texture.jpg，底色換成藍綠）；**登入視窗**（LoginDialog）維持
   原本的背景影片，這裡的材質貼圖只當它的載入前備援色／備援材質，
   不是要取代影片。兩個對話框共用同一個 `.auth-dlg` class，差異只在
   JSX 裡有沒有渲染 `<AuthVideo/>`。 */
dialog.auth-dlg {
  padding: 0; max-width: none; width: 100vw; max-height: none; height: 100vh;
  background: #b2c2bb url('/img/bg-texture.jpg') repeat;
  background-size: 700px 700px;
}
/* 卡片靠下對齊而不是置中：背景上方留白放品牌 logo，置中的話卡片上緣會蓋過去。
   ⚠️ 手機版維持這樣（水平置中＋貼底）就是對的，不要動——桌機版的例外在下面。
   🔴 **`flex-direction: row` 這行不能拿掉，拿掉會讓下面兩個屬性的意思整個對調。**
   全站有一條 `dialog[open] { display: flex; flex-direction: column; }`（593 行，
   2026-09-03 那次「對話框固定頭尾」的結構修正加的）。這裡原本只重設了
   `display: flex`、沒有重設 `flex-direction`，於是繼承到 `column`：
     - `justify-content: center` 的主軸變成**垂直** → 卡片變成垂直置中
     - `align-items: flex-end` 的交錯軸變成**水平** → 卡片變成貼齊右邊
   結果就是「垂直置中＋貼右」，跟上面這行註解寫的意圖剛好相反，而且**沒有任何
   錯誤訊息**——CSS 不會告訴你「你以為的軸跟實際的軸不一樣」。2026-09-04
   使用者實機截圖圈出「登入白色區域要靠下置中」才發現，那條全域規則加上去的
   那天起就一直是壞的。 */
dialog.auth-dlg[open] {
  display: flex; flex-direction: row;
  align-items: flex-end; justify-content: center; overflow: hidden;
  padding-bottom: 4vh;
}
/* 🔴 2026-09-04 稍晚：桌機版改成**跟手機一樣置中靠下**，同一天稍早那版
   「靠右但不貼邊」已作廢（使用者實機看過後改主意：「不要擋 logo／登入白色
   區域放置中」）。所以這裡不再覆寫 `justify-content`，桌機直接沿用上面那條
   基準規則；🚫 不要再把 `flex-end` ＋ `padding-right: 6vw` 加回來。

   ⚠️ **光把卡片移到中間會直接壓在品牌 logo 上**，兩個要求看起來互相衝突，
   實際上不是——衝突的是「卡片的位置」跟「logo 的位置」，而後者是可以移的。
   實測（1280×800）：影片素材是直式 1080×1920，`cover` 在寬螢幕上由**寬度**
   決定縮放（1280/1080 = 1.185 倍），高度被放大到 2276px、只看得到最上面
   35%，而 logo 剛好落在那一段的中間（螢幕 y 約 333–640），卡片上緣在 378
   ——置中就一定重疊。
   **解法是把裁切窗口往下移，讓 logo 在畫面上往上升**：`object-position` 的
   第二個值從 `top`（＝ 0%，裁切窗口貼齊影片頂端，logo 上面留一大段空白紋理）
   改成 20%，logo 整段升到畫面上緣附近，卡片下方那一半就空出來了。
   🚫 這條只能寫在桌機斷點裡：手機（直式）的 `cover` 是由**高度**決定縮放、
   垂直方向完全看得到，`object-position` 的垂直值根本不會生效，但平板橫放
   之類的中間尺寸會，寫成全域等於在沒量過的尺寸上亂動已經正確的畫面。 */
@media (min-width: 861px) {
  /* ⚠️ 選擇器刻意比下面那條 `.auth-video` 更明確：這段 media query 排在
     `.auth-video` 的**前面**（它要接著上面那段對話框對齊的說明一起讀），
     同權重的話後寫的會贏，`object-position` 會被打回 `center top` 而且
     完全不會報錯——實測踩過一次。 */
  dialog.auth-dlg .auth-video { object-position: center 20%; }
}
/* ⚠️ 2026-09-04 使用者實機指出：桌機寬螢幕上「好拼友」品牌字不見了。
   根因是影片素材是直式構圖（品牌字在畫面上半部），`object-fit: cover`
   預設 `object-position: center` 在寬螢幕（16:10 那種）會把高度大幅放大
   來填滿容器，垂直方向因此被裁掉一大段，裁切窗口置中在影片正中央，
   品牌字所在的上半部反而被裁掉了。改成 `object-position: center top`，
   裁切窗口固定貼齊影片頂端，品牌字所在的那一段永遠留在畫面裡，不會因為
   螢幕比例改變而消失。 */
.auth-video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: center top; z-index: 0;
}
/* ⚠️ 2026-09-14 使用者實機圈出「建立帳號的白頁面太窄了」——420px 比全站
   其他對話框的標準寬度（見上面 `dialog { width: min(560px, …) }`）窄了一截，
   而且沒有任何文件記過為什麼要故意窄，看起來只是當初隨手挑的數字。改成跟
   全站一致的 560px，姓名／性別那個 `.row` 也有更多空間，不會擠成一團。 */
.auth-card {
  position: relative; z-index: 1; background: var(--panel); border-radius: 14px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .3); width: min(560px, calc(100vw - 32px));
  max-height: 68vh; display: flex; flex-direction: column;
}
/* 卡片變矮了，內容改成在卡片內部捲動，不要把卡片撐高回去蓋住 logo */
.auth-card .dlg-body { flex: 1; max-height: none; }
.auth-card .dlg-body { flex: 1; }

/* Google／訪客登入：圖示並列，不是各佔一整列的文字按鈕（2026-08-27 使用者
   明確要求）。跟頭首通知鈴鐺 .bell-btn 同一個 44px 圓形圖示按鈕慣例——
   那顆已經是「觸控最小熱區 44px、桌機手機都是正圓」踩過的坑，直接照抄。 */
.auth-icon-btn {
  flex: 0 0 auto; width: 44px; height: 44px; min-width: 0;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; border: 1px solid var(--line); border-radius: 999px;
  background: var(--panel); color: var(--ink); cursor: pointer;
}
.auth-icon-btn:hover { border-color: var(--deep); color: var(--deep); }

/* 提示／警示訊息一律出現在**畫面上方**（2026-09-06 使用者要求：「全域所有的
   提示和警示訊息改放上方，目前是在下方會擋到操作」）。
   🚫 不要改回 `bottom`：底部是底部導覽（活動／訊息／發布／管理／個人）跟各種
   對話框「送出／確認」按鈕的所在，訊息浮在那裡剛好蓋住使用者**下一步要按的
   東西**，而它還會停留兩秒多。上方只有時間與電量，蓋不到任何操作。
   ⚠️ `env(safe-area-inset-top)` 直接寫在共用樣式裡、不另外在 App 那份複本
   覆寫一次：瀏覽器沒有安全區時它算出來就是 0，網頁版行為完全不變，而 App 裡
   自動讓開瀏海／動態島——同一件事寫兩個地方才是之後會改一邊忘一邊的來源。
   ⚠️ `max-width` ＋ 左右留白：訊息文字長度不固定（有的是一句話），不設上限
   在窄螢幕上會整條貼齊兩邊、讀起來像一條橫幅而不是提示。 */
.toast {
  position: fixed; left: 50%; top: calc(12px + env(safe-area-inset-top, 0px));
  transform: translateX(-50%);
  max-width: min(420px, calc(100vw - 32px));
  background: var(--deep); color: #fff; padding: 10px 16px; border-radius: 10px;
  font-size: var(--text-body); z-index: 100; box-shadow: var(--shadow);
}

/* ---------- 旅客端 ---------- */
.hero { padding: 28px 0 10px; }
.hero h1 { font-size: var(--text-hero); letter-spacing: -.02em; }
.trip-card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 16px; margin-bottom: 12px; box-shadow: var(--shadow); }
/* 用分隔線隔開區塊，不要每個區塊都包一層邊框變成卡片疊卡片 */
.divider { border: none; border-top: 1px solid var(--line); margin: 14px 0; }
.trip-card h2 { margin-bottom: 4px; }
/* 行程截圖縮圖列（最多 5 張，見 guest.js renderTripCard）：橫向排、點哪張主圖換哪張。
   選中的那張用綠框標出來，不用另外做輪播套件或指示點。 */
.trip-cover-thumbs { display: flex; gap: 6px; overflow-x: auto; }
.trip-cover-thumb {
  width: 56px; height: 56px; object-fit: cover; border-radius: 6px; flex: 0 0 auto;
  border: 2px solid transparent; cursor: pointer; opacity: .7;
}
.trip-cover-thumb.active { border-color: var(--deep); opacity: 1; }
/* 開新行程第1步「行程截圖」的縮圖列（上傳中，最多 5 張，見 guest.js renderCoverPreviews）——
   跟上面 .trip-cover-thumb（行程詳情頁、唯讀）是不同情境，這裡每張要能按 ✕ 移除。 */
/* ⚠️ 這排現在一定有內容（縮圖或新增格），不會是空的——`:empty` 那條規則
   已經是死碼，拿掉。 */
/* 縮圖與新增格 2026-09-21 從 64px 放大到 96px（使用者：「圖片上傳的 ui 大一點」）。
   ⚠️ 96 是量過的上限，不是隨手挑的：375px 寬的對話框裡內容區約 311px，
   96×3 ＋ gap 8×2 = 304 剛好一排放得下三格；再大一格就只剩兩格一排，
   五張圖會變成三排。要再改先量過。 */
.cover-preview-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.cover-preview-item { position: relative; width: 96px; height: 96px; }
.cover-preview-item img { width: 100%; height: 100%; object-fit: cover; border-radius: 8px; border: 1px solid var(--line); }
.cover-preview-remove {
  position: absolute; top: -6px; right: -6px; width: 20px; height: 20px; min-height: 0; padding: 0;
  border-radius: 999px; border: 1px solid var(--line); background: var(--panel); color: var(--muted);
  font-size: var(--text-tag); line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center;
}
/* 新增格──跟縮圖同尺寸排在同一排，虛線＋圖示取代原生「選擇檔案」按鈕
   （2026-08-28，理由見 TripDialog.tsx 那段註解）。虛線＋米色底沿用
   `.avatar-step .avatar:empty` 那個「這裡可以放東西」的既有視覺語言，
   不是另外發明一種空格子樣式。 */
.cover-add-tile {
  width: 96px; height: 96px; min-height: 0; padding: 0; flex: 0 0 auto;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  border-radius: 8px; border: 1px dashed var(--line); background: var(--bg); color: var(--muted);
  font-size: var(--text-tag); cursor: pointer;
}
.cover-add-tile:hover { border-color: var(--deep); color: var(--deep); }
/* 名額卡＝票券感：右邊鋸齒撕邊，滑鼠移上去浮起＋淡陰影 */
.room-line {
  position: relative;
  display: flex; flex-direction: column; gap: 12px; padding: 10px 22px 10px 14px;
  background: var(--panel); border: 1px solid var(--line);
  margin-top: 10px;
  clip-path: polygon(
    100% 0%, 97% 6.25%, 100% 12.5%, 97% 18.75%, 100% 25%, 97% 31.25%,
    100% 37.5%, 97% 43.75%, 100% 50%, 97% 56.25%, 100% 62.5%, 97% 68.75%,
    100% 75%, 97% 81.25%, 100% 87.5%, 97% 93.75%, 100% 100%, 0% 100%, 0% 0%
  );
  transition: transform .15s ease, filter .15s ease;
}
.room-line:hover {
  transform: translateY(-3px);
  filter: drop-shadow(0 10px 16px rgba(41, 48, 42, .16));
}
/* 卡片右側的系統預設圖：用不透明漸層蓋在圖片上，讓它往左淡出融進卡片底色。
   （這個環境的 mask-image 不會渲染，所以不能用遮罩，必須用同色漸層疊上去。）
   --card-bg 讓漸層跟著所在容器的底色走，卡片放在哪都不會出現一條色差邊。 */
.room-line { --card-bg: var(--panel); }
.room-line-bg {
  position: absolute; top: 0; right: 0; height: 100%; width: 46%;
  object-fit: cover; z-index: 0; pointer-events: none; opacity: .42;
}
.room-line-bg-fade {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(90deg,
    var(--card-bg) 0%, var(--card-bg) 54%,
    rgba(255, 255, 255, .86) 70%, rgba(255, 255, 255, .6) 86%, rgba(255, 255, 255, .45) 100%);
}
/* 內容一律站在圖片與漸層之上 */
/* 🔴 **排除清單一定要包在 `:where()` 裡。** `:where()` 的權重是 0，所以這條
   規則永遠是 (0,1,0)——不管之後再加幾個要排除的元素都不會變。
   ⚠️ 這不是美化，是修一個真的發生過的 bug（2026-09-06）：原本寫成一串
   `:not(.a):not(.b)`，每個 `:not()` 都會加權重，加到第三個時整條變成 (0,4,0)，
   **反超了 `.room-line-share.room-line-share.room-line-share`(0,3,0)**——那顆
   分享鈕的 `position: absolute` 被這裡的 `relative` 蓋掉，於是它從卡片右上角
   掉回排版流的左上角（`right: 16px` 還在，只是對 relative 沒有作用），
   而且完全不會報錯。使用者實機圈出「分享怎麼跑去左上角」。
   🚫 不要改回 `:not(...):not(...)` 串接——那等於把這個坑重新挖開。 */
.room-line > *:not(:where(.room-line-bg, .room-line-bg-fade, .room-line-veil)) { position: relative; z-index: 2; }
/* 已經截止的行程，在活動頁蓋一層灰綠半透明遮罩（2026-09-06 使用者要求）。
   ⚠️ 顏色用 `--muted`（#6D766B，:root 註解本來就寫著「暖灰綠」）——不要
   另外調一個新的灰綠，站上已經有這個角色的顏色了（§5.3）。
   ⚠️ **一定要寫進上面那條 `:not()` 鏈**：那條規則會把 `.room-line` 的每個
   子元素都設成 `position: relative; z-index: 2`，遮罩被它接住就會變成排版
   流裡的一個空元素、蓋不到任何東西，而且完全不會報錯。
   ⚠️ `pointer-events: none`：它只是一層顏色，不能吃掉觸控——使用者還是要
   點得到「查看此團」去看這團當初是什麼內容。
   ⚠️ z-index 要比內容（2）高才蓋得住；不用管 `clip-path`，那是畫在
   `.room-line` 自己身上的，子元素會一起被裁成票根形狀。 */
.room-line-veil {
  position: absolute; inset: 0; z-index: 3; pointer-events: none;
  background: rgba(109, 118, 107, .34);
}
/* ⚠️ 2026-08-28 使用者實機指出：截止日標籤原本用 flex-wrap 讓它「空間夠就跟日期
   擠在同一行、空間不夠才換行」——寬螢幕跟窄螢幕看到的相對位置不一樣，位置飄忽。
   改成直向排列，截止日**一律**在日期下面一行，不再看螢幕寬度決定。 */
.room-line-top { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
/* 出遊日期是這張卡片上「要不要點進去」最先被讀的一行（§5.4 第 1 點），
   2026-09-18 使用者實機要求大一級：13px → 15px（§5.3 的六階往上一階，
   不是自己挑一個數字）。⚠️ 旁邊的截止標籤刻意維持 13px——兩個都放大就沒有
   主次了，而截止日已經有 --brand 橘色在搶注意力。 */
.room-line-top strong { font-size: var(--text-body); }
.room-line-top-badges { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.room-line-status {
  display: inline-block; align-self: flex-start; font-size: var(--text-sub); font-weight: 600; color: var(--deep);
  background: var(--deep-soft); border-radius: 999px; padding: 4px 10px; white-space: nowrap;
}
/* 類別／性別限制：跟左右兩顆徽章同一組尺寸（13px／600／4px 10px），只有顏色最低調。
   先前直接用通用的 .tag（12px、一般字重、多一圈邊框），三顆並排時中間那顆明顯不同掛。 */
.room-line-kind {
  display: inline-block; align-self: flex-start; font-size: var(--text-sub); font-weight: 600; color: var(--muted);
  background: var(--bg); border-radius: 999px; padding: 4px 10px; white-space: nowrap;
}
.room-line-mid { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.room-line-mid .price { font-size: var(--text-sub); color: var(--muted); }
.room-line-action { display: flex; gap: 8px; }
.room-line-action button { flex: 1; width: auto; }
.room-line-mates { font-size: var(--text-sub); color: var(--muted); padding-top: 4px; border-top: 1px dashed var(--line); }
.split-feed-trip-link {
  align-self: flex-start; background: none; border: none; padding: 0; min-height: 0;
  font: inherit; font-size: var(--text-head); font-weight: 600; color: var(--deep); cursor: pointer; display: inline-flex; align-items: center; gap: 8px;
}
.split-feed-trip-link:hover { text-decoration: underline; }
/* 卡片右上角的分享圖示——使用者 2026-08-28 實機要求瀏覽卡片跟「查看此團」
   彈窗都要有。浮在背景圖之上（`.room-line-bg-fade` 蓋在 z-index:1，這裡沒有
   額外設 z-index 是靠 §506 那條「內容一律站在圖片與漸層之上」的通用選擇器）。
   半透明白底＋圓形，跟卡片右緣的鋸齒撕邊留一點距離，不會被 clip-path 切到。
   ⚠️ 實機一驗就踩到 CLAUDE.md §5.2 那條老坑的新變種：`.room-line > *:not(...):not(...)`
   （§506，用來讓內容站在背景圖之上）用了兩個 `:not()`，選擇器權重是 3 個
   class，蓋掉了這裡單一 class 的 `position: absolute`，變成 `position: relative`，
   分享按鈕因此擠在卡片左上角的文字流位置，不是浮在右上角。跟 `.avatar`／
   `.team-badge` 那次同一個解法：選擇器寫成重複類別拉高權重，不要用
   `!important`（那條規則本身沒有錯、之後還會有新元件撞到，權重要贏得乾脆）。
   ⚠️ 重複兩次（權重打平）試過還是輸——`.team-badge.team-badge` 那次贏是因為
   對方只有 1 個 class、打平之後靠寫在後面贏；這裡對方本身就有 3 個 class，
   打平沒有用，只能真的權重更高，所以重複到三次。 */
.room-line-share.room-line-share.room-line-share {
  position: absolute; top: 10px; right: 16px;
  width: 30px; height: 30px; min-height: 0; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: 999px; border: 1px solid var(--line);
  background: rgba(255, 255, 255, .82); color: var(--deep); cursor: pointer;
}
.room-line-share:hover { background: #fff; }

/* 團卡：同一團的房／餐／車／活動列在同一張卡裡，一行一項。
   z-index 是必要的——.room-line-bg-fade 蓋在 z-index:1，內容不墊高就會被那層漸層蓋住。 */
.team-feed-lines { position: relative; z-index: 2; display: flex; flex-direction: column; gap: 6px; }
.team-feed-line { display: flex; align-items: center; gap: 6px; font-size: var(--text-sub); min-width: 0; }
.team-feed-line svg { flex: 0 0 auto; color: var(--deep); }
.team-feed-kind { font-weight: 600; flex: 0 0 auto; }
.team-feed-detail {
  flex: 1; min-width: 0; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 團卡裡每一行各自的加入按鈕——2026-08-26 第二版改寫：拿掉「整團一起加入」之後，
   同一團底下的每筆名額還是要各自可以按加入，不是共用外層那顆「查看此團」。
   縮小尺寸是因為一行裡要同時放圖示、類別、狀態、按鈕，不能跟主卡片一樣大。 */
.team-feed-line button.sm { flex: 0 0 auto; width: auto; padding: 3px 10px; min-height: 26px; font-size: var(--text-sub); }

/* ---------- 特質標籤 ---------- */
/* 🔴 `position: relative` 不能拿掉（2026-09-30）：每一格裡藏著一個 `.sr-only`
   的 checkbox（position: absolute）。捲動容器自己沒有 position 的話，那些
   checkbox 的定位基準會一路往上找到 `<dialog>`，而它們的原始位置在捲動區
   下面好幾百 px——於是整個對話框被撐出一截可以捲的空白（使用者看到「下一步
   那一欄下方多了一塊空間」）。容器有 position，它們就被框在捲動區裡。 */
.trait-grid {
  position: relative;
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 4px 10px;
  max-height: 220px; overflow-y: auto; border: 1px solid var(--line); border-radius: 9px;
  padding: 8px 10px; background: #fbfbfc;
}
.trait-grid label.checkbox { margin: 0; padding: 3px 0; font-size: var(--text-sub); }

/* 勾選格＝跟選完之後的摘要同一種卡片（2026-09-23 使用者要求：「怎麼沒有放上
   新設計的牛皮紙卡片」）。紙紋／內層淡色／右下角底紋都跟 .trait-card 同一套
   ——那是同一批資料的兩個狀態（要選的 vs 選好的），長成兩種樣子只會讓人以為
   是兩種東西。

   🔴 **但它仍然是「一列」，不是方格。** 2026-09-18 使用者收回過一版「勾選格
   也畫底紋」，理由是方格會把 21 個選項拉成三倍長、選的時候掃不完（下面那條
   🚫 講的就是這件事）。所以高度維持一行字，兩欄照舊——換的是視覺語言，
   不是版面密度。🚫 不要因為「卡片就該大一點」改成方格。

   ⚠️ `.trait-grid` 的底色跟著改成透明：底下每一格自己有紙紋了，外面再墊一層
   #fbfbfc 會變成「灰底上鋪一排米色卡片」，兩種紙感打架。 */
.trait-grid:has(.tag-card) { background: transparent; gap: 7px; padding: 7px; }
.tag-card {
  display: block; border-radius: 9px; padding: 4px; cursor: pointer;
  background: #F7F4EC url('/img/paper-cream.jpg') repeat; background-size: 300px 300px;
  box-shadow: 0 1px 2px rgba(33, 48, 42, .07), 0 0 0 1px rgba(33, 48, 42, .05);
}
.tag-card-in {
  display: flex; align-items: center; gap: 6px;
  position: relative; overflow: hidden; border-radius: 6px; padding: 7px 8px;
  background: rgba(41, 87, 71, .07); color: var(--deep);
}
.tag-card.danger .tag-card-in { background: rgba(168, 73, 61, .07); color: var(--danger); }
/* 選中：內層加深＋勾勾填實。⚠️ **不要只靠勾勾**——一整格 21 個選項掃過去時，
   一個 16px 的勾勾分不出「選了幾個」，整塊底色的深淺才看得出來。 */
.tag-card.selected .tag-card-in { background: rgba(41, 87, 71, .18); }
.tag-card.danger.selected .tag-card-in { background: rgba(168, 73, 61, .18); }
.tag-card-art {
  position: absolute; right: -14px; bottom: -16px; width: 52px; height: 52px;
  opacity: .15; pointer-events: none;
}
/* position: relative 不是多寫的：底紋是 absolute，文字不墊起來會被蓋住。 */
.tag-card-text {
  position: relative; flex: 1; min-width: 0;
  font-size: var(--text-sub); font-weight: 700; color: var(--ink);
  line-height: var(--leading-body);
}
/* 勾勾平常只有一圈框（暗示「這裡可以點」），選中才填實。
   ⚠️ flex: 0 0 auto——不加的話長標籤會把它擠扁成橢圓。 */
.tag-card-check {
  position: relative; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 17px; height: 17px; border-radius: 50%;
  background: rgba(255, 255, 255, .7); color: transparent;
  box-shadow: inset 0 0 0 1px rgba(33, 48, 42, .18);
}
.tag-card.selected .tag-card-check { background: var(--deep); color: #fff; box-shadow: none; }
.tag-card.danger.selected .tag-card-check { background: var(--danger); }
/* 2026-09-15：每個選項前面的圖示色塊，見 TraitPicker.tsx 檔頭的完整說明
   （表情符號版本上線同一天就被使用者要求拿掉，改成 icon＋色塊）。
   顏色沿用既有的語意 token（--sage 系一般選項、--danger 系謝絕條件），
   不是新發明的色票——跟 .trait-chip／.danger-chip 是同一套顏色語言。 */
.pick-icon-block {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 7px;
  background: var(--sage-soft); color: var(--sage);
}
.pick-icon-block.danger { background: var(--danger-soft); color: var(--danger); }
/* TraitChips（唯讀標籤）裡的縮小版——chip 本身字級就小，色塊跟著縮小才不會
   比文字還顯眼，跟 checkbox 那個 22px 版本共用同一組顏色規則。 */
.pick-icon-block-sm { width: 15px; height: 15px; border-radius: 4px; margin-right: 3px; vertical-align: -3px; }
/* 「清單沒有？自己填」那一列：跨兩欄鋪滿，跟上面的 checkbox 格子拉開一點距離 */
.pick-custom-add { grid-column: 1 / -1; display: flex; gap: 6px; margin-top: 4px; padding-top: 6px; border-top: 1px dashed var(--line); }
.pick-custom-input { flex: 1; min-width: 0; font-size: var(--text-sub); padding: 4px 8px; border: 1px solid var(--line); border-radius: 6px; }
.pick-custom-add-btn { flex: 0 0 auto; font-size: var(--text-sub); padding: 4px 10px; border: 1px solid var(--line); border-radius: 6px; background: var(--panel); color: var(--deep); cursor: pointer; }
.pick-custom-add-btn:hover { background: var(--bg); }

/* 通用「視覺隱藏但保留可存取性」——跟 `display:none`／`hidden` 不同，
   螢幕報讀器與鍵盤操作都還讀得到、找得到。給 .pick-card 底下真正的
   checkbox 用（視覺上被卡片取代），之後其他地方需要同樣的隱藏 checkbox／
   radio 樣式也可以直接用這個，不用各自重寫一次。 */
.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;
}

/* 照片卡片（2026-09-15，icon＋色塊的下一版，見 TraitPicker.tsx 檔頭）。
   取代 .trait-grid 的 checkbox 列表，只在選項清單真的有照片時啟用。 */
.pick-card-grid {
  position: relative; /* 同 .trait-grid：框住裡面的 .sr-only checkbox */
  display: grid; grid-template-columns: repeat(auto-fill, minmax(92px, 1fr)); gap: 10px;
  max-height: 340px; overflow-y: auto; padding: 4px 2px;
}
.pick-card {
  position: relative; display: flex; flex-direction: column; cursor: pointer;
  border-radius: 12px; overflow: hidden; border: 2px solid transparent;
  background: var(--panel); box-shadow: 0 1px 2px rgba(0, 0, 0, .06);
}
/* 選中狀態用主色描邊，跟站上其他「這個被選中了」的語言一致（§5.3 --deep）。 */
.pick-card.selected { border-color: var(--deep); }
.pick-card-photo {
  position: relative; aspect-ratio: 1 / 1; background-size: cover; background-position: center;
  display: flex; align-items: center; justify-content: center;
}
/* 沒有照片（特質／地雷還在等搜尋詞的過渡狀態，或自訂值）退回色塊＋圖示，
   跟舊版 icon＋色塊同一套顏色語意，不是另外發明的樣式。 */
.pick-card-photo-fallback { background: var(--sage-soft); color: var(--sage); }
.pick-card-photo-fallback.danger { background: var(--danger-soft); color: var(--danger); }
/* 🚫 2026-09-18 這裡曾經有一組 .pick-card.art-card／.pick-card-art（勾選格也
   畫底紋），同一天被使用者收回——選的時候要能一眼掃完清單，方格會把 21 個
   選項拉成三倍長。底紋只留給選完之後的摘要卡片（.trait-card）。不要再加回來，
   完整理由見 TraitPicker.tsx 裡 usePhotoCards 上面那段。 */
/* 右上角的選中勾勾——平常是半透明白底、看不出勾（暗示「這裡可以點」），
   選中時才填滿主色。放在照片裡面（不是照片外面另起一塊），是因為這是
   「這張照片被選中了」的標記，語意上屬於照片本身。 */
.pick-card-check {
  position: absolute; top: 6px; right: 6px; width: 20px; height: 20px; border-radius: 50%;
  background: rgba(255, 255, 255, .82); color: transparent;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .25);
}
.pick-card.selected .pick-card-check { background: var(--deep); color: #fff; }
/* Unsplash 署名——硬性要求，不是裝飾（見 pickPhoto.ts 檔頭）。疊在照片
   底部、深色漸層墊底才能在任何照片上都讀得到，字級刻意很小（這是輔助
   資訊，不是這張卡片的主要內容，太搶眼反而模糊了「這是哪個國家」的重點）。 */
.pick-card-credit {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 10px 6px 3px;
  font-size: 9px; line-height: 1.3; color: rgba(255, 255, 255, .92); text-decoration: none;
  background: linear-gradient(transparent, rgba(0, 0, 0, .58));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pick-card-credit:hover { text-decoration: underline; }
.pick-card-label { padding: 6px 6px 7px; font-size: var(--text-sub); text-align: center; color: var(--ink); }

/* 收納起來的特質選單：平常看起來像一個下拉欄位，點開才展開複選格 */
.trait-select {
  border: 1px solid var(--line); border-radius: 9px; background: var(--panel); margin-bottom: 10px;
}
.trait-select summary {
  list-style: none; cursor: pointer; padding: 10px 12px; font-size: var(--text-body);
}
.trait-select summary::-webkit-details-marker { display: none; }
/* 箭頭跟標題文字自己包成一個不會換行的橫排（.trait-select-label 自己是一個
   flex row），箭頭固定貼右側，不管標題文字多長、要不要自己換兩行都不受影響——
   之前箭頭是掛在 summary::after，靠 flex-wrap 排最後一個，標題文字一長就會
   把箭頭一起擠到下一行，變成「箭頭跑到文字下面」。收合摘要（.trait-select-count）
   維持獨立一行，放在這個橫排下面。 */
/* ⚠️ 不要用 justify-content: space-between——標題本身可能不只一個 <span>
   （例如「希望對方有這些特質」＋「選填」），space-between 會把它們跟箭頭一起
   拆成三個位置，「選填」就浮到中間去了（使用者 2026-08-31 實機發現）。改成
   標題群組靠左自然排、箭頭用 margin-left:auto 推到最右。 */
.trait-select-label { display: flex; align-items: flex-start; gap: 8px; }
/* 箭頭改用 JSX 裡的 `.ui-chev`（2026-09-21 全站統一），這裡只負責把它推到最右。 */
.trait-select-label .ui-chev { margin-left: auto; }
.trait-select-count {
  display: block; color: var(--deep); font-size: var(--text-sub); font-weight: 600; margin-top: 4px;
}
.trait-select-count:empty { display: none; }
.trait-select .trait-grid { border: none; border-top: 1px solid var(--line); border-radius: 0; max-height: 180px; margin: 0; }
/* ⚠️ 2026-09-03 使用者實機指出「找哪些人」那排標籤水平間距太窄——4px 只夠當
   同一顆藥丸內文字的呼吸空間，兩顆藥丸之間用同樣的數字會讓它們看起來快黏在
   一起。改成 8px，這是全站共用的 class（見上面的說明），5 個地方一起變寬。 */
.trait-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
/* 2026-08-26 使用者反饋橘色太刺眼——而且這裡本來就不該用 --brand：
   §5.3／上面第 20 行早就寫明橘色只留給行動／金額，特質標籤誤用了它。
   改用 --sage（鼠尾草綠，次要資訊用色），跟品牌橘拉開。 */
/* 2026-09-18 使用者實機要求「像例圖那種有設計感的卡片」，不是藥丸也不是
   掛 icon（那批 icon 同一次被拿掉，見 TraitPicker.tsx 的 TraitChips）。
   卡片感來自三個數字，不是新素材——**這裡沒有任何圖片或插畫**：
   1. 圓角 999px → 12px：藥丸讀起來是「一個小標記」，方一點的圓角才讀得像
      「一張小卡」。⚠️ 12 是卡片本來就在用的圓角，不是新發明的數字。
   2. padding 1px 8px → 7px 12px：藥丸的 1px 上下留白是「貼著字」，卡片要有
      自己的面積才站得住。
   3. 字色 --sage → --deep：--sage 壓在 --sage-soft 上本來對比就偏淡，面積
      變大之後更顯得糊。⚠️ 底色**不變**，語意還是「次要資訊用鼠尾草綠系」
      （§5.3：橘色只留給行動／金額）。
   🚫 不要在這裡加 border 或 box-shadow：一排 5～10 顆標籤各自帶框線會變成
   一片網格線，卡片感是靠底色色塊做的，加框只是把雜訊補回來。 */
.trait-chip {
  display: inline-block; padding: 7px 12px; border-radius: 12px; font-size: var(--text-sub);
  font-weight: 600; line-height: var(--leading-tight);
  background: var(--sage-soft); color: var(--deep);
}
.trait-chip.danger-chip { background: var(--danger-soft); color: var(--danger); }
/* 「沒有特別偏好／限制」這種空狀態——2026-08-26 使用者反饋「怎麼只列出字」，
   要跟其他徽章（.room-line-kind／.room-line-status）一樣是圓角底色的 TAG，
   不是純文字。用中性的 --bg／--muted，跟「有選填」的兩種顏色都分得開。 */
.trait-chip.empty-chip { background: var(--bg); color: var(--muted); }

/* 特質／謝絕條件的**卡片**（2026-09-18，取代原本的 .trait-chip 藥丸；
   .trait-chip 本身留著給不在那 36 項裡的標籤用）。使用者原話：「我要的是
   像例圖這種有設計感的卡片設計」，前後改了六版才定案，每個數字都是實機
   看過決定的，不要隨手調：

   四層由淡到深疊上去，層次不是靠一塊色堆出來的：
     1. .trait-card      白底 ＋ 髮絲框線 ＋ 極淺陰影（厚度）
     2. 6px padding      露出一圈白邊（使用者要求「四周間隔」）
     3. .trait-card-in   7% 的綠／紅（**比圖示的 15% 更淡**，這是使用者指定
                         的順序：內層要比圖示更看不見）
     4. .trait-card-art  15% 的底紋，沉在右下角、刻意被切掉一角
     5. .trait-card-text 13px／700／--ink，唯一有對比的東西

   ⚠️ .trait-card 是 flex、.trait-card-in 是 flex:1：同一列的卡片會被最高的
   那張拉到等高（grid 的預設，而且等高才不會參差），內層不跟著長高的話底下
   會露出一條白的——使用者實機圈出來過。

   ⚠️ 內層的綠／紅寫成 rgba() 而不是 --sage-soft／--danger-soft：那兩個 token
   是**不透明**的色票，疊在白卡上會是一塊實色，做不出「更淡一層」的效果。

   🚫 不要在 .trait-card 上加 border：髮絲框線是用 box-shadow 的第二層做的
   （0 0 0 1px），加 border 會讓卡片實際尺寸變大、跟旁邊的 gap 對不齊。 */
/* 2026-09-18 使用者實機要求「往右滑，不是往下堆疊」：改成一列橫向捲動。
   ⚠️ 卡片要 flex: 0 0 auto ＋ 固定寬度，不能用 flex: 1——後者會把它們壓成
   一排擠在容器寬度裡，就沒有「右邊還有」這件事了。
   ⚠️ 捲軸本身全站已經隱藏（見 :root 附近那條 *::-webkit-scrollbar），被切一半
   的那張卡就是「右邊還有」的提示，跟 .filter-bar／常用題目那排同一個做法。
   🚫 不要加 flex-wrap: wrap——那等於把「往下堆疊」換個寫法又做回來。 */
.trait-cards { display: flex; flex-wrap: nowrap; gap: 9px; margin-top: 4px; overflow-x: auto; }
/* 桌機滑鼠拖曳捲動（dragScroll.ts）——游標明示「這裡抓得動」，不然滑鼠使用者
   根本不會知道可以拖。⚠️ 拖曳中要關掉文字選取，不然整排標籤會被反白。 */
.trait-cards { cursor: grab; }
/* 旅遊風格卡片上方那行小字（是哪一組），見 TripStyle.tsx 的 TripStyleCards */
.trait-card-kicker { display: block; margin-bottom: 2px; font-size: var(--text-tag); font-weight: 500; color: var(--muted); }
.trait-cards.is-dragging { cursor: grabbing; user-select: none; }
/* 2026-09-18 使用者實機要求「白底改成例圖那樣」——例圖是快問板的發問小卡，
   那張刻意用米色紙紋（見 .ask-item 上面的說明）。這裡用同一張貼圖、同一個
   background-size，不是另外挑一張像的圖，兩處的紙紋才會對得起來。
   ⚠️ 底下墊 #F7F4EC 不能省：貼圖還沒載進來時要有底色，不然會先閃一下透明。 */
/* ⚠️ 2026-09-18 使用者問「為何卡片不直接適配文字長度」——原本寫死 138px，
   短的（早睡型）留一大片空白、長的（常大聲講電話／外放影音）擠成三行。
   ⚠️ **校正（同一天稍晚）：不是每張都縮到字寬。** 第一版拿掉 width 之後
   「早睡型」縮成 73px、跟旁邊 125px 的卡片高低胖瘦全部不一樣，整排看起來很亂
   ——使用者原話：「前提是以超出目前大小被擠到下排的字會要做的，不是代表每個
   卡片都要」。所以是 **min-width（底線）＋ max-width（上限）＋ width auto**：
     - 短標籤（早睡型）→ 停在 138px 的底線，整排維持同一個基準寬度
     - 放不下而會折到第二行的（會開車／有駕照）→ 自己長寬，字留在同一行
     - 真的很長的（常大聲講電話／外放影音）→ 撞到上限才折行
   🚫 不要改成 flex: 1，那會把卡片拉去平分容器寬度，又變回全部一樣寬。
   ⚠️ max-width 不能省：沒有上限的話最長那張會撐成一條、一張卡佔滿整個
   可視寬度，旁邊一張都看不到，等於沒有「右邊還有」的提示。 */
.trait-card {
  display: flex; border-radius: 10px; padding: 6px;
  flex: 0 0 auto; min-width: 138px; max-width: 62vw;
  background: #F7F4EC url('/img/paper-cream.jpg') repeat; background-size: 300px 300px;
  box-shadow: 0 1px 2px rgba(33, 48, 42, .07), 0 0 0 1px rgba(33, 48, 42, .05);
}
.trait-card-in {
  flex: 1; position: relative; overflow: hidden; border-radius: 6px; padding: 12px 11px;
  background: rgba(41, 87, 71, .07); color: var(--deep);
}
.trait-card.danger .trait-card-in { background: rgba(168, 73, 61, .07); color: var(--danger); }
/* 底紋吃 .trait-card-in 的 color（currentColor），所以綠卡紅卡共用同一份 path，
   見 apps/guest-react/src/traitArt.tsx 的說明。 */
.trait-card-art {
  position: absolute; right: -19px; bottom: -21px; width: 68px; height: 68px;
  opacity: .15; pointer-events: none;
}
/* position: relative 是必要的，不是多寫的：底紋是 absolute，文字不墊起來就會
   被蓋住（同層後來的元素才贏，但 absolute 已經脫離文件流）。 */
.trait-card-text {
  position: relative; margin: 0; font-size: var(--text-sub); font-weight: 700;
  color: var(--ink); line-height: var(--leading-body);
}
.trait-card-no { color: var(--danger); }


/* 管理看板：一筆報名（BoardCards.tsx 的 BoardBookingRow）——2026-09-02
   使用者實機指出原本身分／狀態／特質／備註／動作全部黏在一起，重新規劃過
   層級，見那個元件開頭的完整說明。 */
.board-booking-list { margin-top: 14px; }
.board-booking-row { padding: 10px 0; border-top: 1px solid var(--line); }
/* ⚠️ 2026-09-04：聯絡方式／特質標籤／備註原本各自用零散的 marginTop（3／6）
   決定間距，使用者實機指出「元素間距太擠」——跟 §6.3.12 PendingReviewBox
   同一種坑，改成容器 gap 統一管理，不要再加零散 marginTop。 */
.board-booking-sub { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
/* 使用者自己寫的備註，用引號＋左邊一條細線跟系統產生的文字（特質標籤）
   區分開，不會被誤讀成系統訊息。 */
.board-booking-note {
  font-size: var(--text-sub); color: var(--ink); font-style: italic;
  padding-left: 10px; border-left: 2px solid var(--line);
}
/* ⚠️ 2026-09-07：待審核卡片（`PendingReviewBox`）的「他說」區塊——見
   `MyTrips.tsx` 的說明。原本備註（引言樣式）跟問答（`justify-content:
   space-between` 撐開兩端的一列）是兩種不同的視覺語彙硬接在一起，寬螢幕上
   問答那一行會被拉出一條誰都沒預期到的空白。改成兩者收進同一個淺底面板，
   不再需要撐開卡片寬度的排法——問答的「問題／答案」用行內小 gap 並排，
   不是 space-between，容器本身有多寬都不會被撐開。 */
.pending-review-says {
  display: flex; flex-direction: column; gap: 6px;
  /* `--detail-surface`：站上既有的「灰白底」token（§5.3），不是另外調色。 */
  background: var(--detail-surface); border-radius: 8px; padding: 10px 12px;
}
.pending-review-says-label {
  font-size: var(--text-tag); color: var(--muted); font-weight: 700; letter-spacing: .02em;
}
.pending-review-says-note { font-size: var(--text-sub); color: var(--ink); }
.pending-review-says-qa { display: flex; flex-direction: column; gap: 3px; }
.pending-review-says-qa-row {
  display: flex; gap: 6px; flex-wrap: wrap; font-size: var(--text-sub);
}
.pending-review-says-qa-row .muted { color: var(--muted); }
/* 來源標記（2026-09-06）：這個人是從哪個社團／場次來的。
   ⚠️ 刻意做成**低調的膠囊**，不是徽章：它是主揪自己的歸因筆記，不是這個人的
   屬性，也不影響任何判斷——做得跟「手動登記」那種狀態標籤一樣醒目會搶掉
   聯絡方式與特質的注意力。用 --muted 而不是任何狀態色，理由同上。 */
.board-booking-from {
  align-self: flex-start;
  font-size: var(--text-tag); color: var(--muted);
  background: rgba(41, 87, 71, .06);
  border-radius: 999px; padding: 2px 10px;
}

/* 收款／移除此人是「對這筆報名做什麼」，跟上面「這筆報名是什麼」用一條
   虛線＋間距隔開，不會因為擠在同一排而分不清哪個是狀態、哪個是按鈕。 */
.board-booking-actions {
  display: flex; justify-content: flex-end; gap: 8px;
  margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--line);
}

/* 「＋ 新增找室友／找飯友…」——管理看板每個分類底下的新增入口。
   ⚠️ 2026-09-04 使用者實機指出外框線太淡：原本用 `var(--line)`（#E4DECE，
   米白系細邊線，是給「兩個區塊之間的分隔線」用的淡色），但這顆按鈕是整個
   分類目前唯一的動作入口（沒有名額時整個分類只有這一顆），份量不該跟分隔線
   一樣淡。改用 `var(--muted)`（暖灰綠，字色本來就用它）、寬度加到 1.5px，
   跟 `.profile-wish-chip`（同樣是「可以按的虛線外框」）同一組數字。 */
.board-add-split-btn {
  width: 100%; padding: 16px; border-radius: var(--radius); border: 1.5px dashed var(--muted);
  background: transparent; box-shadow: none; color: var(--muted); cursor: pointer;
  text-align: center; display: block; margin-bottom: 14px;
}
.board-add-split-btn:hover { border-color: var(--deep); color: var(--deep); }

/* 發布選擇器的兩個大選項（尋旅友貼文／開新行程，見 PublishFlow.tsx） */
.publish-choice {
  /* ⚠️ 通用 `button` 規則（262 行附近）設了 `justify-content: center`，這裡沒有
     覆寫的話圖示＋標題＋說明整組會被橫向置中在按鈕裡，看起來離左邊緣很遠
     （使用者 2026-08-31 實機發現）。CSS 是逐屬性疊加，不是整條規則二選一，
     沒明講 justify-content 就會被通用規則吃掉。 */
  display: flex; align-items: center; justify-content: flex-start; gap: 12px; width: 100%; text-align: left;
  padding: 14px; border: 1px solid var(--line); border-radius: 12px; background: var(--panel);
  cursor: pointer; margin-bottom: 10px;
}
.publish-choice:hover { border-color: var(--deep); background: var(--deep-soft); }
.publish-choice strong { display: block; font-size: var(--text-body); color: var(--ink); }
.publish-choice .small { display: block; margin-top: 2px; }
.publish-choice-icon {
  flex: 0 0 auto; width: 44px; height: 44px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--deep-soft); color: var(--deep);
}
/* 快問板裡的尋旅友需求卡：整張可點看詳細 */
.seek-item { cursor: pointer; }
.seek-item:hover { border-color: var(--deep); }
/* 目的地／時間／模式隔行排版，每行自己的間距，不會被硬接成一行後從中間折斷 */
.seek-meta-lines { margin: 3px 0 8px; line-height: var(--leading-body); }
.seek-meta-lines > div { margin-bottom: 2px; }
/* 尋旅友詳細對話框的「想找什麼樣的旅程」／「找哪些人」——2026-09-03 使用者
   實機要求「大兩級並粗體」：原本是 `.small muted`（13px，跟旁邊的欄位說明
   字級太接近，分不出這是標題）。跳到 17px 粗體，跟 `.wizard-section-head`
   同一組數字（13→17 是這個站上「大兩級」固定的做法，見那裡的說明）。
   ⚠️ 2026-09-04 校正：margin-bottom 從 6px 補到 10px——字級跳到 17px 之後
   6px 顯得太擠，標題像是黏在底下的標籤列上（使用者實機圈出來）。跟
   `.trip-detail-subhead`／`.trip-style-tags-head` 是同一種「17px 粗體標題
   後面接內容」的樣式，三個一起校正到同一個數字，見那兩處的說明。 */
.seek-detail-label { display: block; font-size: var(--text-head); font-weight: 700; color: var(--ink); margin-bottom: 10px; }
/* 風格標籤：這裡刻意不用 .trait-chip 預設的圓角（999px），改方角跟卡片本身的
   直角風格一致——使用者 2026-08-31 實機指出圓角在這裡太「軟」。只在需求卡的
   標籤上覆寫，不動 .trait-chip 本身（個人卡片等其他地方還是要圓角）。 */
.seek-tag { border-radius: 4px; }
/* 「看詳細」移到右邊（跟整張卡片可點的動作語意一致，箭頭本來就暗示往右／
   往下一層），並且跟上面標籤的距離拉開。用 padding-top（不是 margin-top）
   疊加在 .ask-item-foot 既有的 margin-top:auto 上，兩者不會互相蓋掉。 */
.seek-foot { padding-top: 14px; text-align: right; }

/* 開新行程精靈第 3 步的分區小標（配對訊號一區、其他一區）。用細上框線分段，
   跟卡片其他 label 拉開層級，但不搶戲。 */
/* 開新行程第 1 步的模式三選一：一行一個。⚠️ 不用 `.row`——那個會 flex-wrap，
   短標籤會兩個擠在同一行、第三個掉下去，讀起來像分成兩組，但三者是平行的
   三選一（使用者 2026-09-03 指出）。間距統一交給容器的 gap（不要各自補
   margin，見 CLAUDE.md 那條「卡片間距用 gap 統一管理」）。 */
.wizard-mode-list { display: flex; flex-direction: column; gap: 10px; }

.wizard-section-head {
  /* ⚠️ 2026-09-03 使用者要求「大兩級」（原本 13px，跟旁邊 label 的字級太接近，
     分不出這是分區標題還是普通欄位說明）——跳到 17px，跟 .dlg-head 對話框
     標題同一個字級，讓它在這一步裡真的有標題的份量。 */
  font-size: var(--text-head); font-weight: 600; color: var(--ink);
  margin: 4px 0 10px; padding-top: 12px; border-top: 1px solid var(--line);
}
.wizard-section-head:first-child { padding-top: 0; border-top: none; }
.wizard-section-head .opt { font-weight: 400; }
/* 🔴 **「進階設定」裡面的分區標題小一級、而且完全沒有分隔線**（2026-09-21 使用者：
   「誰可以加入、要不要旅行社／同行成員／想找什麼樣的旅伴 字小一級；有長輩同行那一行
   下方有分隔線要拿掉」）。兩個理由：
   1. **層級**：`summary`（進階設定）本身也是 `.wizard-section-head`，它跟它底下的三個
      小標同一個字級的話，讀起來像四個平行的東西，看不出哪些收在哪裡面。17→15 之後
      「進階設定」才是那一層的標題。
   2. **分隔線本來就不一致**：那三個裡只有「想找什麼樣的旅伴」畫得出線——另外兩個一個
      是 `:first-child`（被上面那條歸零）、一個包在 `.trip-style` 裡不是直接子元素，所以
      根本沒吃到 `border-top`。留著等於三個標題有一條隨機出現的線。全部拿掉才一致。
   ⚠️ `:not(summary)` 不能省：少了它「進階設定」自己也會被縮小、也會掉分隔線，
   上面第 1 點的層級就不存在了。 */
.wizard-advanced .wizard-section-head:not(summary) {
  font-size: var(--text-body); padding-top: 0; border-top: none;
  /* ⚠️ 上方間距交給 `.wizard-advanced` 的 gap，這裡歸零——留著 4px 的話「區塊之間」
     會變成「gap ＋ 這個 margin」，而區塊裡面的第一個標題又吃不到，兩邊就不一樣了。 */
  margin-top: 0;
}
/* 🔴 **三個大項的間距由容器統一給，不是各元素自己的 margin 疊出來的**
   （2026-09-21 使用者：「為什麼這三個區塊間距不一樣？平均拉開一點，現在太近」）。
   改版前量到「需要旅行社協助」→「同行成員」是 10px、「同行成員」→「想找什麼樣的
   旅伴」只有 4px——因為相鄰的是誰、誰身上帶多少 margin，決定了那一條縫有多寬。
   包成 `.wizard-block` 之後「區塊之間」（gap）跟「區塊裡面」（各自的 margin）
   是兩件事，不會再互相影響（§6.3.12 記過同一條）。
   🔴 **flex 一定要下在 `.wizard-blocks` 上，不能下在 `<details>` 本身**：`<details>`
   會把 `<summary>` 以外的內容塞進一個匿名容器（Chrome 的 `::details-content`），
   設在 `<details>` 上的話真正的 flex 項目只有「summary」跟那個匿名盒子兩個，
   三個區塊分不到 gap。⚠️ 這件事不報錯也看不出來——`getComputedStyle` 讀到的
   display/gap 都是對的，量出來的間距卻沒變（§5.2 那類環境陷阱，2026-09-21 踩到）。 */
.wizard-blocks { display: flex; flex-direction: column; gap: 24px; }
/* 區塊裡面維持原本的節奏（標題 10px 接內容、欄位各自的 margin），不要在這裡加東西。 */
.wizard-block { display: block; }

/* 開新行程第 2 步的「進階設定」收合容器（2026-09-16，見 TripDialog.tsx 那段說明）。
   ⚠️ `summary` 同時也是 `.wizard-section-head`，所以字級／分隔線都跟其他分區標題
   一致——它讀起來要像「第三個分區」，不是一顆外觀不同的按鈕。
   ⚠️ 收合狀態下 `<summary>` 是唯一看得到的東西，箭頭要自己畫（拿掉原生三角形，
   兩邊瀏覽器畫得不一樣），跟 `.trait-select`／`.pick-field` 同一個做法。 */
.wizard-advanced > summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.wizard-advanced > summary::-webkit-details-marker { display: none; }
/* 展開之後，裡面第一個分區標題緊接在 summary 下面會出現兩條分隔線疊在一起 */
.wizard-advanced[open] > .wizard-section-head:nth-of-type(1) { padding-top: 0; border-top: none; }

/* 名額卡裡的「詳細設定」（2026-09-21 使用者要求把描述性欄位與均分價收起來）。
   ⚠️ 外觀沿用 `.wizard-advanced` 那一套（無標記、右邊一個會轉的 ⌄），只是小一號
   ——同一種「收合區」在同一個精靈裡長成兩個樣子會讀起來像兩種東西（§5.4 第 5 點）。
   🚫 不要用 `.wizard-advanced` 本身：那個的 summary 是分區標題的字級，放在卡片
   裡面會比卡片自己的標題還大。 */
.kind-detail { border-top: 1px solid var(--line); margin-top: 4px; padding-top: 10px; }
.kind-detail > summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  font-size: var(--text-sub); font-weight: 600; color: var(--muted);
}
.kind-detail > summary::-webkit-details-marker { display: none; }
.kind-detail[open] > summary { margin-bottom: 10px; }

/* ---------- 此團屬性（開新行程精靈輸入 + 詳情頁顯示，見 TripStyle.tsx） ----------
   選中狀態一律用 --deep／--deep-soft（§5.3：主色墨綠給選中狀態），不用品牌橘。 */
.trip-style { display: flex; flex-direction: column; gap: 20px; }
.trip-style-label {
  display: flex; align-items: center; gap: 6px;
  font-size: var(--text-sub); font-weight: 600; color: var(--ink); margin-bottom: 10px;
}
.trip-style-label svg { color: var(--muted); }
.trip-style-label .opt { font-weight: 400; }

/* 詳情頁顯示：一列一個群組（標題 + 已選標籤） */
/* 跟上面「希望旅伴有」／「謝絕」那區拉開距離——貼太近會讓「謝絕」的紅標籤
   跟風格標籤看起來像同一區（使用者 2026-08-31 實機指出）。
   ⚠️ 2026-09-01 附記：當初這是**跨渲染路徑的接縫**（上半 vanilla、下半 React
   島，中間沒有共同容器），只能靠這裡自己補。行程詳情彈窗整個搬進 React
   （TripDetailDialog.tsx）之後接縫消失了，這行留著純粹是正常的區塊間距。 */
.trip-style-tags { margin-top: 20px; }
/* ⚠️ 2026-09-02 使用者實機要求拿掉 icon、字放大兩級（13px → 17px，跟
   .dlg-head 的標題字級一致）——icon 拿掉之後份量全靠文字本身撐，字級
   要跟著補。跟 .trip-detail-subhead 是同一批一起改的，理由寫在那邊。 */
/* ⚠️ 2026-09-04 校正：margin-bottom 從 8px 補到 10px，跟 .seek-detail-label／
   .trip-detail-subhead 統一成同一個數字（見 .seek-detail-label 的說明）。 */
.trip-style-tags-head {
  font-size: var(--text-head); font-weight: 700; color: var(--ink); margin-bottom: 10px;
}
/* ⚠️ 2026-09-03：key 跟 vals 之間、vals 裡每顆標籤之間都太窄（8px／4px），
   使用者實機指出來——「行程節奏」跟後面的「隨性 P」黏在一起，不像兩個獨立
   的東西。跟上面 `.trait-chips` 同一次一起放寬，這三個 class 是同一組視覺
   語言（key/value 配對＋標籤），數字要一起變才會維持節奏一致。 */
.trip-style-tags-row { display: flex; gap: 14px; align-items: baseline; margin-bottom: 8px; }
.trip-style-tags-key {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--text-sub); color: var(--muted); min-width: 72px;
}
.trip-style-tags-key svg { color: var(--muted); }
.trip-style-tags-vals { display: flex; flex-wrap: wrap; gap: 8px; }

/* ---------- 個人資料圖卡／私訊 ---------- */
/* min-width: 0 是必要的，不是多寫的：`.row > *` 有 min-width:120px，
   放進 .row 裡的頭貼會被撐成 120×56 的橢圓（實測過）。這裡自己擋掉，
   就不用每個使用它的地方都記得加 .fixed。 */
/* ⚠️ 2026-09-03：預設頭貼（沒上傳照片時顯示姓名首字）從陶土橘底橘字改成
   半透明墨綠底綠字。理由是 §5.3 那條「--brand 只用在金額」——一個人的
   預設頭貼跟金額無關，而且它是全站用得最廣的元件之一（每個沒放照片的人
   都是），橘色用在這裡等於把「這是錢」這個訊號稀釋掉。
   ⚠️ **底色刻意用半透明的 --deep，不是實色的 --deep-soft**：頭貼會出現在
   白卡片、灰白個人頁、紙紋快問卡、對話框好幾種底色上，實色淺綠貼上去在
   非白底的地方會像一塊補丁；半透明的話它會跟著背後那層走，四種底色上都
   自然。（對比度實測 7.0:1，遠高於 4.5:1 的門檻。） */
.avatar {
  width: 56px; height: 56px; min-width: 0; border-radius: 50%; display: grid; place-items: center;
  background: rgba(41, 87, 71, .12); color: var(--deep); font-weight: 700; font-size: var(--text-title);
  flex: 0 0 auto; overflow: hidden;
}
.avatar.sm { width: 32px; height: 32px; font-size: var(--text-body); }
/* 訊息信箱列表用——比 sm 大，讓頭貼在一份靠臉認人的清單裡真的看得清楚
   （使用者 2026-08-28：「頭貼佔比太小」）。 */
.avatar.md { width: 44px; height: 44px; font-size: var(--text-head); }
/* 看板／待審核清單裡的小頭貼——比 .sm 稍微大一點，跟旁邊的姓名同一行時
   比例比較對，但不需要 .md 那麼大（那是訊息列表用的）。 */
.board-avatar { width: 36px; height: 36px; font-size: var(--text-body); }
/* 頭貼包成按鈕時，把全域 button 的樣式（padding／邊框／min-height）全部清掉，
   只留頭貼本身的圓形——不然按鈕的方形邊框會把圓形頭貼框成一個方塊。 */
.avatar-btn.avatar-btn {
  padding: 0; border: 0; background: none; min-height: 0; border-radius: 50%;
  flex: 0 0 auto; display: block; cursor: pointer;
}
.avatar-btn.avatar-btn:hover .avatar { filter: brightness(0.94); }
/* 有上傳頭貼時，圖片要填滿圓形且不變形 */
.avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* 🚫 .avatar-picker 已經刪除（2026-09-21）：唯一的呼叫端是「我的資料」的頭貼欄位，
   那裡改成旅人證上的 .idcard-photo（照片框＋換頭貼膠囊）之後就沒有人用了。
   註冊精靈那一步用的是 .avatar-step，不受影響。 */
/* 還沒選圖的空預覽框：虛線提示這裡可以放東西 */
.avatar-step .avatar:empty { border: 1px dashed var(--line); background: var(--bg); }
/* 註冊精靈把頭貼獨立成一步（2026-08-27）：置中、預覽圖放大，跟這一步
   其他表單欄位（打字填）視覺語言不同，這步只有一件事——選一張圖。 */
.avatar-step {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: 12px; padding: 20px 0 8px;
}
.avatar-step-hint { line-height: var(--leading-body); }
/* 真人驗證的拍照介面（VerifyPhotoCapture.tsx，2026-09-22 抽成全站唯一一份）。
   🚫 前身是 .verify-photo-example／-preview（84px 的小方塊，2026-09-14），
   那兩條已經沒有呼叫端、連同刪除，不留死碼。
   ⚠️ 範例照片從 84px 放大到 132px 是刻意的：使用者要求「沉浸式」，而這張圖
   就是唯一的指引——小到看不出手勢比在哪裡，等於沒有指引。
   ⚠️ 例圖與拍好的照片**共用同一個尺寸**：兩個狀態切換時版面高度不變，不會
   因為「拍完」讓底下的按鈕跳位置。 */
.verify-capture {
  display: flex; flex-direction: column; align-items: center; text-align: center; gap: 10px;
}
.verify-capture-example, .verify-capture-shot { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.verify-capture-example img, .verify-capture-shot img {
  width: 132px; height: 132px; object-fit: cover; border-radius: 12px; border: 1px solid var(--line);
}
/* 拍好的那張用主色描邊：那是「這件事完成了」在站上一貫的訊號（選中狀態＝--deep） */
.verify-capture-shot img { border-color: var(--deep); border-width: 2px; }
.verify-capture-gesture { font-size: var(--text-body); font-weight: 700; color: var(--deep); }
.verify-capture-hint { font-size: var(--text-sub); color: var(--muted); margin: 0; }

/* 審核狀態（2026-09-22）。三種狀態用三種顏色，因為它們要使用者做的事完全不同：
   等待＝什麼都不用做、通過＝好了、退回＝要回去重拍。⚠️ 退回那一種要能容納
   管理員寫的原因，所以不是單行的 tag 而是一段文字。 */
.verify-capture-status {
  margin: 0; font-size: var(--text-sub); font-weight: 600;
  line-height: var(--leading-body); text-align: center;
}
.verify-capture-status.is-pending { color: var(--muted); }
.verify-capture-status.is-approved { color: var(--deep); }
/* 🔴 退回的原因本身是一般字重的內文，不要跟著標題一起變粗——整段都粗的話
   使用者反而讀不出「哪一句是結論、哪一句是要改的地方」。 */
.verify-capture-status.is-rejected { color: var(--danger); font-weight: 400; }
/* 指引：兩句話，編號跟著走。⚠️ 用 padding-left 而不是 list-style: none ＋ 自己畫
   數字——那兩個數字是真的順序（先比手勢、再一起拍進去），交給瀏覽器畫就好。 */
.verify-capture-steps {
  margin: 0; padding-left: 20px; text-align: left;
  font-size: var(--text-sub); color: var(--muted); line-height: var(--leading-body);
}
/* 沒拍過時那顆是這一步唯一的動作，給它整排寬度——按鈕小一顆會讓人以為
   還有別的地方要先填（§5.5 第 5 點：畫面要老實反映當下該做什麼） */
.verify-capture-go { align-self: stretch; }

/* 報名前的準備頁（JoinDialog.tsx）。兩件事各自一塊，靠 gap 隔開——
   ⚠️ 不要各自寫 marginTop：其中一塊會依「缺不缺」整個不渲染，靠自己的 margin
   會忽大忽小（CLAUDE.md §6.3.12 記過的同一條）。 */
.join-prep { display: flex; flex-direction: column; gap: 20px; }
.join-prep-item { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
.join-prep-item h3 { margin: 0; font-size: var(--text-head); font-weight: 600; color: var(--ink); }
.join-prep-item p { margin: 0; line-height: var(--leading-body); }
/* 準備頁裡的拍照介面靠左那塊要撐滿，不然置中的範例圖跟左對齊的標題對不起來 */
.join-prep-item .verify-capture { align-self: stretch; }

/* 「我的資料」裡的真人驗證卡（ProfileDialog.tsx 的 ProfileForm） */
/* 2026-10-13：8px 太擠（標題、說明、按鈕黏在一起），跟編輯頁其他區塊同一級的間距 */
.profile-verify { display: flex; flex-direction: column; gap: 14px; }
.profile-verify > .profile-sec-title { margin: 0; }
.profile-verify p { margin: 0; line-height: var(--leading-body); }
.user-link { color: var(--ink); font-weight: 600; cursor: pointer; text-decoration: underline dotted; }
.user-link:hover { color: var(--brand); }

.thread-list { display: flex; flex-direction: column; }
/* 對話清單改成一般聊天 App 的整列樣式：整列可點、列與列之間用分隔線，
   不是一顆一顆有邊框的卡片（卡片式在只有幾則對話時看起來像「選項」，
   不像信箱列表）。 */
/* ⚠️ 2026-08-28 修正一個真的 CSS bug：全域 `button` 規則（本檔案前段）給every
   button 設了 `border: 1px solid var(--line); border-radius: 9px;`。這裡只覆寫
   `border-top`，左／右／下三邊繼承那個全域設定，殘留成一個完整的方框——
   每一列因此看起來像自己有邊框的卡片，不是分隔線（使用者實測截圖抓到，
   底部還多出一段被邊框圍住的空白）。修法是先歸零整個 border／border-radius，
   再單獨補上 `border-top` 當作列與列之間唯一的分隔線。 */
/* 好拼友佈告欄（訊息頁）。2026-10-13 重排資訊層級（使用者：「這一區 ui 很醜、資訊層級不清」）：
   以前卡片標題、公告標題、「跟好拼友說」膠囊、實心連結按鈕四樣東西一樣重，眼睛不知道先看哪裡。
   現在由輕到重只有一個主角：
     1. 「好拼友佈告欄」是 15px 的區塊標籤（2026-10-13 第二輪：11px 被嫌太小；仍比公告標題的 17px 小一級）
     2. 公告標題是唯一的粗大字——主角
     3. 內文淡色、連結是文字連結（不是實心按鈕，避免跟主角搶）
     4. 「跟好拼友說」是卡片底部獨立一列，跟公告用分隔線分開——它是另一件事（回饋管道），不是公告的一部分
   🚫 不要把連結改回實心膠囊、也不要把「跟好拼友說」搬回標題旁邊。
   ⚠️ 不能用 --brand（陶土橘只給金額與截止日，§5.3）。
   ⚠️ 選擇器寫成重複類別，壓過全域 `a`／`button` 的預設樣式與 hover（§6.3.35）。 */
.bulletin { padding-bottom: 0; overflow: hidden; }
/* 沒有公告時卡片只剩「跟好拼友說」一列：拿掉上方留白與分隔線，不然上面多一條空白帶 */
.bulletin.bulletin-empty { padding-top: 0; }
.bulletin-empty .bulletin-talk.bulletin-talk { border-top: 0; }
/* 2026-10-13 第三輪：標題搬出卡片、改用跟「我的訊息」同一個 .card-head h2，.bulletin-head 已刪 */
.bulletin-list { display: flex; flex-direction: column; gap: 16px; padding-bottom: 16px; }
.bulletin-item { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.bulletin-item + .bulletin-item { padding-top: 16px; border-top: 1px solid var(--line); }
.bulletin-title { font-size: var(--text-head); font-weight: 700; line-height: var(--leading-tight); color: var(--ink); }
.bulletin-body { margin: 0; font-size: var(--text-sub); line-height: var(--leading-body); color: var(--muted); }
.bulletin-link.bulletin-link {
  display: inline-flex; align-items: center; gap: 4px; margin-top: 2px;
  color: var(--deep); font-size: var(--text-sub); font-weight: 600; text-decoration: none;
}
.bulletin-link.bulletin-link:hover { color: var(--deep); text-decoration: underline; }
.bulletin-link svg { stroke: var(--deep); }
/* 底部那一列：整列都是按鈕，左邊說它是什麼、右邊是動作 */
.bulletin-talk.bulletin-talk {
  display: flex; align-items: center; gap: 10px; width: calc(100% + 32px); margin: 0 -16px;
  min-height: 0; padding: 14px 16px; border: 0; border-top: 1px solid var(--line); border-radius: 0;
  background: var(--detail-surface); text-align: left; cursor: pointer;
}
.bulletin-talk.bulletin-talk:hover { background: var(--detail-surface); filter: brightness(.98); }
.bulletin-talk-icon { flex: 0 0 auto; display: inline-flex; width: 32px; height: 32px; border-radius: 50%;
  align-items: center; justify-content: center; background: var(--deep); color: #fff; }
.bulletin-talk-icon svg { stroke: #fff; }
.bulletin-talk-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.bulletin-talk-title { font-size: var(--text-body); font-weight: 600; color: var(--ink); }
.bulletin-talk .ui-chev { color: var(--deep); flex: 0 0 auto; }
/* 「跟好拼友說」對話框：身體是對話串（會捲），底下固定輸入區 */
/* 對話框本身是白底，官方那邊的泡泡要有自己的底色才看得出是一則訊息 */
.official-chat .chat-row.theirs .chat-bubble { background: var(--bg); }
.official-cats { display: flex; flex-wrap: wrap; gap: 6px; }
.official-cat.official-cat {
  flex: 0 0 auto; min-height: 0; padding: 6px 12px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--panel); color: var(--ink); font-size: var(--text-sub);
}
.official-cat.official-cat.on { border-color: var(--deep); background: var(--deep-soft); color: var(--deep); font-weight: 600; }
.official-chat-compose { flex: 0 0 auto; display: flex; flex-direction: column; gap: 8px; padding: 12px 18px; border-top: 1px solid var(--line); }

.thread-item {
  display: flex; align-items: center; gap: 12px; padding: 16px 4px; cursor: pointer;
  border: 0; border-radius: 0; border-top: 1px solid var(--line);
  background: none; text-align: left; width: 100%;
}
.thread-item:first-child { border-top: 0; }
.thread-item:hover { background: var(--bg); }
.thread-item.unread .thread-name { font-weight: 700; }
/* 使用者 2026-08-28：「有新訊息的話字是黑色粗體，已讀過的話就呈現灰色」。
   已讀的預設樣式就是 `.small.muted`（灰字），這裡只需要補未讀時的黑字＋粗體。 */
.thread-item.unread .thread-last { color: var(--ink); font-weight: 700; }
.thread-last { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 2026-08-26 使用者反映列跟列擠在一起。原因不只是 padding 太小——三行字
   （標題、幾人在群組、最後一則）本來全是同樣的 13px、同樣的行高，一列裡面
   的行距跟列與列之間一樣寬，整份清單就變成分不出段落的九行字。
   所以除了把上下內距從 12px 拉到 16px，還要讓標題明顯比下面兩行大且重
   （每一列有一個視覺錨點），並把兩行副標的行高收緊，讓一列的內容先黏成
   一塊，列與列的分隔才讀得出來。 */
.thread-item .thread-name { font-size: var(--text-body); font-weight: 600; line-height: var(--leading-body); }
.thread-item .small.muted { line-height: var(--leading-tight); }

/* ---------- 對話視窗（chatDlg）---------- */
/* 從右側推入的全高面板。⚠️ 蓋掉 <dialog> 的 display 一定要限定 [open]，
   否則會蓋掉瀏覽器內建的 dialog:not([open]){display:none}，對話框關不掉（§5.2）。 */
/* 高度用 dvh（動態視窗高度）：手機叫出虛擬鍵盤時視窗會變矮，100vh 是固定值、
   不會縮，輸入框會被鍵盤蓋住。dvh 會跟著縮，輸入框永遠貼在鍵盤上方。 */
dialog.chat-dlg {
  position: fixed; inset: 0 0 0 auto; margin: 0; height: 100vh; height: 100dvh; max-height: none;
  width: min(480px, 100vw); max-width: none; border-radius: 0; box-shadow: -8px 0 30px rgba(0, 0, 0, .18);
  background: var(--panel);
}
dialog.chat-dlg[open] { display: flex; flex-direction: column; animation: chat-slide-in .2s ease-out; }
@keyframes chat-slide-in { from { transform: translateX(100%); } to { transform: none; } }
@media (prefers-reduced-motion: reduce) { dialog.chat-dlg[open] { animation: none; } }

.chat-head {
  display: flex; align-items: center; gap: 8px; padding: 10px 12px;
  border-bottom: 1px solid var(--line); flex: 0 0 auto;
}
.chat-back {
  border: 0; background: none; padding: 4px; min-height: 0; cursor: pointer; color: var(--ink);
  display: flex; align-items: center; flex: 0 0 auto;
}
.chat-back:hover { background: var(--bg); border-radius: 8px; }
.chat-head-who { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; }
.chat-head-name { font-weight: 700; font-size: var(--text-body); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-head-sub { font-size: var(--text-tag); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 群組聊天室標頭點下去看「所有旅伴」（2026-09-05）——蓋掉全域 button 的
   邊框／底色／置中，讓它看起來還是原本那塊資訊列，不是一顆按鈕 */
.chat-head-who-btn {
  border: 0; background: none; padding: 0; min-height: 0; cursor: pointer;
  text-align: left; justify-content: flex-start;
}
/* 「所有旅伴」清單：沿用 .chat-scroll 的捲動＋內距，取代訊息串顯示在同一個
   對話框裡（不開第二個 dialog，見 ChatDialog.tsx 的說明） */
.chat-members-list { gap: 4px; }
.chat-member-row {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 8px; border-radius: 10px; background: var(--panel);
}
.chat-member-who {
  border: 0; background: none; padding: 0; min-height: 0; cursor: pointer;
  display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1;
  text-align: left; justify-content: flex-start;
}

.chat-scroll {
  flex: 1; min-height: 0; overflow-y: auto; padding: 12px; background: var(--bg);
  display: flex; flex-direction: column;
}
/* 日期分隔：同一天的訊息只在第一則前面標一次日期，不是每一則都印完整年月日時分 */
.chat-day { align-self: center; margin: 10px 0 14px; }
.chat-day span {
  font-size: var(--text-tag); color: var(--muted); background: var(--panel);
  border-radius: 999px; padding: 3px 12px;
}
/* 系統提示（安全提醒）：長得像對話裡的系統訊息，不是常駐佔一塊的段落 */
.chat-system {
  align-self: center; text-align: center; font-size: var(--text-tag); color: var(--muted);
  background: var(--panel); border-radius: 10px; padding: 6px 12px; margin-bottom: 12px; max-width: 90%;
}
/* 一列＝一則訊息。同一個人連續傳的訊息（.cont）間距收緊、頭貼只出現在第一則，
   時間只印在這一串的最後一則——這是 LINE/WhatsApp 那類成熟聊天室的做法，
   每一則都印一次完整時間會讓畫面充滿重複的字。 */
.chat-row { display: flex; align-items: flex-end; gap: 6px; margin-bottom: 10px; }
/* 🔴 關掉原生長按選字（2026-10-08）：跟訊息自己的長按選單是同一個手勢，兩個一起跑時
   iOS 會把選取擴到整頁。複製改走選單裡的「複製」，見 ChatDialog.tsx 的 MessageRow。 */
.chat-row { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
.chat-row.cont { margin-bottom: 2px; }
.chat-row.mine { flex-direction: row-reverse; }
.chat-bubble {
  max-width: 72%; padding: 8px 12px; border-radius: 16px; font-size: var(--text-body); line-height: var(--leading-body);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.chat-row.theirs .chat-bubble { background: var(--panel); color: var(--ink); border-bottom-left-radius: 5px; }
.chat-row.mine .chat-bubble { background: var(--deep); color: #fff; border-bottom-right-radius: 5px; }
.chat-row .avatar { width: 28px; height: 28px; min-width: 0; font-size: var(--text-sub); flex: 0 0 auto; }
.chat-avatar-gap { width: 28px; flex: 0 0 auto; }
/* 群組聊天室：泡泡上面標發言者是誰（1 對 1 不需要，只有兩個人）。
   包一層 wrap 是因為名字要跟泡泡對齊成一欄、又不能撐開整列的寬度。 */
.chat-bubble-wrap { display: flex; flex-direction: column; min-width: 0; max-width: 72%; }
.chat-row.mine .chat-bubble-wrap { align-items: flex-end; }
.chat-bubble-wrap .chat-bubble { max-width: 100%; }
.chat-sender { font-size: var(--text-tag); color: var(--muted); margin: 0 0 2px 4px; }
/* 群組在清單與對話頂端用的圖示（沒有單一頭貼可以代表一群人） */
/* 44px，跟 .avatar.md 同尺寸——同一份信箱列表，群組跟個人對話的頭像
   大小要一致，不然使用者會覺得是兩種不同層級的東西。 */
.chat-group-icon {
  width: 44px; height: 44px; min-width: 0; flex: 0 0 auto; border-radius: 50%;
  display: grid; place-items: center; background: var(--deep); color: #fff;
}
.chat-time { font-size: var(--text-tag); color: var(--muted); flex: 0 0 auto; padding-bottom: 2px; }

/* ---------- 長按回覆（2026-09-05）---------- */
/* 「你在回覆哪一句」。⚠️ 這塊在泡泡**裡面**（見 ChatDialog.tsx 的說明）：放在
   泡泡外面的話，自己傳的訊息會變成白字印在米色背景上，幾乎看不見。
   左邊那條直線是全站唯一核准的「左側色條」用法——§5.3 禁止的是**卡片**用
   border-left 當強調（那是視覺噪音），引用區塊的直線則是全世界聊天軟體共用的
   語彙，換成別的畫法反而看不懂。 */
/* 🔴 左側直線 2026-10-08 拿掉（使用者要求）。跟下面的回覆內容靠「小一號＋淡色字」
   分開，多留 2px 間距；🚫 不要把直線加回來。 */
.chat-quote {
  padding: 1px 0; margin-bottom: 6px; max-width: 100%; overflow: hidden;
}
.chat-quote-who { font-size: var(--text-tag); color: var(--muted); font-weight: 600; }
.chat-quote-text {
  font-size: var(--text-tag); color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 我自己傳的回覆：泡泡是深綠底白字，引用區塊要跟著換成白色系才讀得到 */
.chat-row.mine .chat-quote-who,
.chat-row.mine .chat-quote-text { color: rgba(255, 255, 255, .8); }
/* 純圖片訊息沒有文字泡泡可以放引用區塊，自己撐一個出來（下面接著就是圖片泡泡） */
.chat-quote-only { padding-bottom: 6px; }
.chat-quote-only .chat-quote { margin-bottom: 0; }

/* 輸入框上方的「正在回覆」列 */
.chat-reply-bar {
  display: flex; align-items: center; gap: 8px; margin-bottom: 8px;
  padding: 6px 8px; border-radius: 10px; background: var(--bg);
  border-left: 3px solid var(--deep);
}
.chat-reply-bar-body { flex: 1; min-width: 0; }
.chat-reply-who { font-size: var(--text-tag); color: var(--deep); font-weight: 600; }
.chat-reply-text {
  font-size: var(--text-tag); color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.chat-reply-cancel {
  border: 0; background: none; padding: 0 4px; min-height: 0; cursor: pointer;
  font-size: var(--text-head); line-height: 1; color: var(--muted); flex: 0 0 auto;
}

/* 長按叫出來的動作選單（回覆／檢舉）。⚠️ 刻意不是第二個 <dialog>，是這個對話框
   自己的絕對定位子元素——兩個同時開著的 modal dialog 在這個環境會互相牽連
   （§5.2）。底那層是一顆透明按鈕，點哪裡都能關掉。 */
.chat-msg-menu-backdrop {
  position: absolute; inset: 0; z-index: 5;
  border: 0; background: rgba(20, 26, 22, .18); padding: 0; min-height: 0; cursor: pointer;
}
.chat-msg-menu {
  position: absolute; left: 50%; transform: translateX(-50%); bottom: 92px; z-index: 6;
  background: var(--panel); border-radius: 14px; padding: 6px;
  box-shadow: 0 8px 28px rgba(20, 26, 22, .18); min-width: 180px;
}
.chat-msg-menu-item {
  display: block; width: 100%; border: 0; background: none; padding: 10px 14px;
  border-radius: 10px; text-align: left; justify-content: flex-start;
  font-size: var(--text-body); color: var(--ink); cursor: pointer;
}
.chat-msg-menu-item:hover { background: var(--bg); }
.chat-msg-menu-item.danger { color: var(--danger); }

/* 底部留出 iOS home indicator 的高度，不然輸入框會被那條橫線壓到（跟底部導覽列同一個做法） */
.chat-foot {
  flex: 0 0 auto; border-top: 1px solid var(--line); background: var(--panel);
  padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
}
/* ---------- 聊天室圖片（2026-08-26）---------- */
/* 圖片自己一個泡泡：把 .chat-bubble 的內距與底色拿掉，讓圖片鋪滿圓角。
   ⚠️ 用 <a> 而不是 <div>，點一下用新分頁開原圖——對話視窗最寬只有 480px，
   要看細節一定得放大。所以這裡也要蓋掉全站 a 的顏色與底線。 */
.chat-bubble.image {
  padding: 0; background: none; overflow: hidden; line-height: 0;
  display: block; text-decoration: none; cursor: zoom-in;
}
.chat-bubble.image img {
  display: block; width: 100%; height: auto; max-height: 320px;
  object-fit: cover; border-radius: 16px;
}
/* 圖片跟文字都有的時候，兩個泡泡之間要有一點縫，不然黏成一塊 */
.chat-bubble.image + .chat-bubble { margin-top: 3px; }

/* 送出前的預覽：讓使用者確認挑對圖了，右上角可以反悔 */
.chat-image-preview { position: relative; align-self: flex-start; margin-bottom: 8px; }
.chat-image-preview img {
  display: block; max-height: 96px; width: auto; border-radius: 10px; border: 1px solid var(--line);
}
.chat-image-remove {
  position: absolute; top: -7px; right: -7px; width: 22px; height: 22px; min-height: 0;
  padding: 0; border-radius: 50%; border: none; background: var(--ink); color: #fff;
  font-size: var(--text-body); line-height: 1; cursor: pointer; display: grid; place-items: center;
}
.chat-image-btn {
  flex: 0 0 auto; border: none; background: none; padding: 6px; min-height: 0;
  color: var(--muted); cursor: pointer; display: flex; align-items: center;
}
.chat-image-btn:hover { color: var(--deep); background: var(--bg); border-radius: 50%; }
.chat-image-btn:disabled { opacity: .4; cursor: not-allowed; }

.chat-composer-wrap { display: flex; flex-direction: column; }
.chat-composer { display: flex; align-items: flex-end; gap: 8px; }
/* 多行輸入（會自動長高，見 guest.js autoGrowChatInput）——單行 input 沒辦法換行，
   稍長一點的訊息就看不到自己打了什麼。 */
/* 不顯示捲軸：高度由 JS 跟著內容長（autoGrowChatInput），長到 max-height 之前
   永遠剛好裝得下，捲軸只會佔掉輸入區的寬度又什麼都不做。超過上限之後仍然可以
   捲（overflow:auto），只是把捲軸本身藏起來。 */
.chat-composer textarea {
  flex: 1; min-width: 0; resize: none; max-height: 40vh; overflow-y: auto;
  border-radius: 18px; padding: 9px 14px; font-size: var(--text-body); line-height: var(--leading-tight);
  scrollbar-width: none; /* Firefox */
}
.chat-composer textarea::-webkit-scrollbar { display: none; } /* Chrome/Safari */
.chat-composer button { flex: 0 0 auto; border-radius: 18px; }

/* ⚠️ 一律**水平捲動**，不要 `flex-wrap: wrap`（使用者 2026-08-28）。
   標籤換行堆疊會讓篩選列在窄螢幕上長成三四行、把下面真正的內容推出畫面；
   橫向捲動則永遠只佔一行，而且被切一半的那顆本身就在暗示「右邊還有」。
   🚫 不要為了「全部看得到」改回 wrap——那是拿版面高度換一次性的可見度。 */
.filter-bar {
  display: flex; gap: 8px; align-items: center; margin: 16px 0;
  flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
  /* 捲動時左右不要被容器邊界切齊，留一點呼吸；負 margin 讓它跟其他區塊對齊 */
  padding-bottom: 2px;
}
.filter-bar::-webkit-scrollbar { display: none; }
/* 子元素不准被壓縮——沒有這行，chip 會為了塞進一行而被擠扁（跟 §5.2 的
   `.row > *` 是同一類問題，只是方向相反） */
.filter-bar > * { flex: 0 0 auto; }
.filter-bar select { width: auto; min-width: 160px; }
.chip-btn {
  border: 1px solid var(--line); background: var(--panel); border-radius: 999px; padding: 6px 14px;
  font-size: var(--text-sub); cursor: pointer; color: var(--muted);
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
}
.chip-btn.on { background: var(--deep); border-color: var(--deep); color: #fff; font-weight: 600; }
.chip-btn.icon-only { padding: 6px 10px; gap: 4px; }
/* 快問板的建議提問（"3 月想去日本" 這排）字級比一般篩選 chip 小一級，
   這排是輔助性的範例文字，不是操作用的篩選按鈕，不需要跟其他 chip 搶視覺份量 */
.ask-templates .chip-btn { font-size: var(--text-sub); }
.filter-divider { width: 1px; align-self: stretch; background: var(--line); margin: 2px 2px; }

/* 篩選鈕＋國家下拉合併成一個欄位（2026-09-21 使用者要求，篩選在左）。
   外框刻意跟 .date-range 用同一組數字（白底／1px --line／9px 圓角／46px 高），
   那兩個在版面上是同一種角色：一列可以點的欄位。
   ⚠️ overflow: hidden 不能省——裡面的 <select> 是原生控制項，沒有裁掉的話
   它自己的方角會戳出圓角外框。 */
.filter-combo {
  display: flex; align-items: center; gap: 8px;
  border: 1px solid var(--line); border-radius: 9px; background: var(--panel);
  padding: 5px 6px; overflow: hidden;
}
.filter-combo:focus-within { border-color: var(--deep); }
/* 🔴 **這顆是圓的，不是橢圓的**（2026-09-21 使用者問「為何篩選是橢圓的外型
   而不是圓的？」）。原因是全域規則 button { min-height: 44px }，而 **min-height
   永遠贏過 height**——舊的 .chip-btn.icon-only 只有 padding 決定寬度（約 36px），
   高度卻被撐到 44，就是一顆橢圓。這個坑在 .profile-settings-btn／.avatar-btn／
   .trip-cover-owner 已經各踩過一次（§6.4.0a／§6.3.15a）：**自訂尺寸的圓形按鈕
   都要記得把全域的 min-height 歸零**，不然它永遠是個橢圓。 */
.filter-combo-btn {
  flex: 0 0 auto; width: 34px; height: 34px; min-height: 0; padding: 0;
  border: 1px solid var(--line); border-radius: 50%; background: var(--bg);
  color: var(--muted); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
/* 有任何條件生效就填實心——條件收進面板之後，這顆鈕是唯一看得到的線索
   （見 BrowseFilter.tsx 的 anyFilterActive）。 */
.filter-combo-btn.on { background: var(--deep); border-color: var(--deep); color: #fff; }
/* 下拉自己不要邊框：外框已經由 .filter-combo 畫了，兩層框會變成框中框。
   🔴 **一定要寫 `background-color` 不能用 `background` 簡寫**：簡寫會把
   `select` 那條規則設好的 `background-image`（墨綠箭頭）一起重設掉，結果是
   這一格完全沒有箭頭、而且不會有任何錯誤（2026-09-21 統一箭頭時當場踩到）。
   ⚠️ `padding-right` 要留給箭頭，覆寫 `select` 的 34px 之前先確認箭頭還在。 */
.filter-combo-select {
  flex: 1; min-width: 0; border: none; background-color: transparent;
  padding: 0 28px 0 4px; min-height: 34px; font-size: var(--text-body); color: var(--ink);
  background-position: right 4px center;
}
.filter-combo-select:focus { outline: none; }

/* 旅遊期間：一個欄位，點開是自製月曆（一次選完整段）。
   沒有標籤，靠按鈕本身的文字說明要做什麼，所以上下 padding 對稱。 */
.date-range {
  position: relative; display: flex; align-items: center; gap: 4px; flex-wrap: nowrap;
  border: 1px solid var(--line); border-radius: 9px; background: var(--panel);
  padding: 10px;
}
.date-range:focus-within { border-color: var(--deep); }
/* 區間欄位裡那顆「看起來是輸入框、其實是按鈕」的文字。原本寫在 BrowseFilter
   的 inline style 裡，2026-09-07 開新行程也要用同一個外觀時收成 class——
   同一種角色的元件不要兩邊各寫一份（§5.4 第 5 點）。
   ⚠️ `justify-content: flex-start` 不能省：通用的 `button { justify-content: center }`
   會把文字推到正中間，看起來就不像輸入框了（§6.3.11 記過同一個坑）。 */
.date-range-btn {
  flex: 1; min-width: 0; border: none; background: none; padding: 0; min-height: 0;
  justify-content: flex-start; text-align: left; font: inherit;
  font-size: var(--text-body); cursor: pointer;
}
.date-range-btn:disabled { cursor: not-allowed; }
.date-range-clear {
  flex: 0 0 auto; border: none; background: none; color: var(--muted);
  padding: 0 2px; min-height: 0; cursor: pointer;
}

/* 🚫 2026-09-18 拿掉「手機塌成一欄」那條覆寫（原本是 max-width 860px 時把
   .trait-grid 的 grid-template-columns 改成 1fr）。
   使用者實機：「不要一整列視窗展開要一直往下滑很累」——21 個選項排成一欄
   就是 21 列，展開之後整頁被推長一倍。同一批把選項前面的圖示色塊也拿掉了，
   所以兩欄放得下最長的「不路痴／認路強」（實機量過 320px 也不破版）。 */

@media (max-width: 860px) {
  .grid.k4 { grid-template-columns: repeat(2, 1fr); }
  .grid.k3, .grid.k2 { grid-template-columns: 1fr; }
}

/* ⚠️ .site-footer 那組樣式 2026-08-26 隨頁尾一起刪掉了（見 index.html 的說明）。 */

/* ---------- 關於頁／常見問題頁 ---------- */
/* faq.html 直接沿用 .about-page（同一種「靜態說明頁」），只多了 .faq-q 這一層
   問題標題。⚠️ 這兩頁都不載入 guest.js／icons.js，只吃這份 style.css。 */
.about-page { max-width: 640px; margin: 0 auto; padding: 48px 20px 64px; }
.about-page h1 { font-size: var(--text-hero); letter-spacing: -.02em; margin-bottom: 4px; }
.about-page .lede { color: var(--muted); margin: 0 0 28px; }
.about-page section { margin-bottom: 24px; }
.about-page h2 { font-size: var(--text-head); margin-bottom: 8px; }
.about-page p { margin: 0 0 12px; }
.about-page .back { display: inline-flex; align-items: center; gap: 6px; margin-bottom: 24px; font-size: var(--text-body); }
/* 問題標題：比 h2（分類）小、比內文重，中間留白讓一長串問答不會糊成一片。
   常見問題頁的分類標題也因此要跟內文拉開，所以 h2 多給一點上緣留白。 */
.about-page h3.faq-q { font-size: var(--text-body); font-weight: 600; color: var(--deep); margin: 20px 0 6px; }
.about-page section h2 { padding-bottom: 8px; border-bottom: 1px solid var(--line); }

/* ---------- 廣告刊位（頁首下方，可導向外部連結） ---------- */
/* 圖片鋪滿整張卡，上面壓一層墨綠漸層讓文字讀得清楚；
   漸層用品牌的旅途墨綠而不是純黑，卡片才不會從整站的視覺系統裡跳出來。 */
/* #adBanner 現在跟 #app 一樣是 .wrap，兩者 padding／max-width 相同，
   所以這裡不能再自己設 max-width/margin:auto——那樣會跟卡片邊緣對不齊。
   直接填滿 .wrap 的內容寬度，左右邊界就會跟下面的卡片切齊。 */
.ad-row {
  display: flex; align-items: stretch; gap: 10px; padding-top: 12px;
  overflow-x: auto; scrollbar-width: none;
}
.ad-row::-webkit-scrollbar { display: none; }
.ad-banner {
  position: relative; overflow: hidden; isolation: isolate;
  display: flex; align-items: center; gap: 12px; flex: 1 1 380px; min-width: 300px;
  padding: 20px 22px; min-height: 108px; border-radius: 12px;
  background: var(--deep); color: #fff; text-decoration: none; cursor: pointer;
  transition: transform .15s ease, box-shadow .15s ease;
}
.ad-banner:hover { transform: translateY(-2px); box-shadow: 0 10px 22px rgba(41, 48, 42, .18); }
.ad-banner-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: -2; }
.ad-banner::after {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(100deg, rgba(41, 87, 71, .92) 0%, rgba(41, 87, 71, .74) 30%, rgba(41, 87, 71, .34) 62%, rgba(41, 87, 71, .06) 100%);
}
/* 沒有圖片的刊位：純墨綠底就夠對比了，不需要漸層，右側放一個放大的類別圖示當底紋 */
.ad-banner.is-plain::after { display: none; }
/* 放大並讓它從右緣溢出，當成底紋而不是一個元件；「前往」按鈕會蓋在上面，看起來是有意的層次 */
.ad-banner-mark {
  position: absolute; right: -14px; top: 50%; transform: translateY(-50%) rotate(-8deg); z-index: 0;
  opacity: .16; color: #fff; pointer-events: none; line-height: 0;
}
.ad-banner-mark svg { width: 132px; height: 132px; }
/* 文字與按鈕要明確站在底圖與底紋之上，不要靠預設堆疊順序碰運氣 */
.ad-banner-body { flex: 1; min-width: 0; position: relative; z-index: 1; }
.ad-banner-cta { position: relative; z-index: 1; }
.ad-banner-title {
  font-size: var(--text-head); font-weight: 600; margin: 0; color: #fff; text-shadow: 0 1px 2px rgba(0, 0, 0, .25);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 廣告揭露：不用方框，改成標題後面一個很淡的小字，看得到但不搶戲 */
.ad-banner-sponsored {
  margin-left: 8px; font-size: var(--text-tag); font-weight: 400; letter-spacing: .05em;
  color: rgba(255, 255, 255, .62); text-shadow: none; vertical-align: 1px;
}
.ad-banner-desc {
  font-size: var(--text-sub); margin: 4px 0 0; color: rgba(255, 255, 255, .88);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-shadow: 0 1px 2px rgba(0, 0, 0, .25);
}
.ad-banner-cta {
  flex: 0 0 auto; font-size: var(--text-sub); font-weight: 600; color: var(--deep); background: #fff;
  border-radius: 999px; padding: 8px 16px; display: inline-flex; align-items: center; gap: 4px;
}
.ad-link-card {
  position: relative; overflow: hidden; isolation: isolate;
  flex: 0 0 160px; border-radius: 12px;
  display: flex; align-items: flex-end; padding: 12px;
  background: var(--deep); color: #fff; text-decoration: none; cursor: pointer;
  transition: transform .15s ease, box-shadow .15s ease;
}
.ad-link-card:hover { transform: translateY(-2px); box-shadow: 0 10px 22px rgba(41, 48, 42, .18); }
.ad-link-card-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: -2; }
.ad-link-card::after {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg, rgba(41, 87, 71, .1) 0%, rgba(41, 87, 71, .9) 100%);
}
.ad-link-card-title {
  font-size: var(--text-body); font-weight: 600; text-shadow: 0 1px 2px rgba(0, 0, 0, .25);
}
.ad-link-card-tag {
  display: block; margin-top: 2px; font-size: var(--text-tag); font-weight: 400;
  letter-spacing: .05em; color: rgba(255, 255, 255, .68); text-shadow: none;
}
@media (max-width: 520px) {
  /* 主刊位在手機上吃滿整個容器寬度。原本是 260px 固定寬，標題與說明都被
     截掉——刊位在賣什麼看不出來，對廣告來說等於完全失效。 */
  .ad-banner { min-height: 92px; padding: 16px; flex: 0 0 100%; min-width: 0; }
  .ad-banner-title { font-size: var(--text-body); }
  /* 「前往」的文字視覺隱藏、只留箭頭圖示——把 74px 的按鈕縮成 36px，
     那 38px 全部還給標題。整張 banner 本來就可點，文字是重複的。
     用 clip-path 而不是 display:none：螢幕閱讀器還讀得到。 */
  .ad-cta-text {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  .ad-banner-cta { padding: 8px 12px; }
  .ad-banner-mark svg { width: 104px; height: 104px; }
  .ad-link-card { flex-basis: 130px; }
}

/* ---------- 旅遊期間日曆 ---------- */
.cal-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.cal-week { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; margin-bottom: 4px; }
.cal-week span { text-align: center; font-size: var(--text-sub); color: var(--muted); padding: 4px 0; }
.cal-days { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.cal-day {
  border: none; background: none; border-radius: 0; min-height: 0; padding: 9px 0;
  font-size: var(--text-body); color: var(--ink); cursor: pointer;
}
.cal-day:disabled { color: var(--line); cursor: default; }
.cal-day.is-empty { visibility: hidden; }
/* 區間用連續的底色表示，頭尾兩天用實心圓角標出來，中間留白條讓整段看起來是連的 */
.cal-day.in-range { background: var(--deep-soft); }
.cal-day.is-start, .cal-day.is-end { background: var(--deep); color: #fff; font-weight: 600; }
.cal-day.is-start { border-radius: 8px 0 0 8px; }
.cal-day.is-end { border-radius: 0 8px 8px 0; }
.cal-day.is-start.is-end { border-radius: 8px; }
.cal-day:hover:not(:disabled):not(.is-start):not(.is-end) { background: var(--bg); }

/* ---------- 快問板 ---------- */
/* 四邊都有的微鋸齒撕邊：上下左右各走一圈，振幅約 1.5%，間距刻意不等距，
   等距會變成規律的鋸齒圖案（像郵票），不等距才像手撕的紙。
   目前只用在發問小卡上；發問輸入框維持一般欄位樣式。 */
.paper-edge {
  /* ⚠️ 形狀寫成變數是為了**重用**：「所有問題」那一頁的卡片要在同一張紙底下
     再墊一張（見 .ask-all-list），那張墊紙用的是同一個撕邊，不是另外抄一份。 */
  --paper-clip: polygon(
    2% 0.8%, 8% 0%, 14% 1.2%, 21% 0.3%, 28% 1.5%, 35% 0.2%, 42% 1.1%, 49% 0%,
    56% 1.4%, 63% 0.4%, 70% 1.2%, 77% 0.1%, 84% 1.5%, 91% 0.5%, 97% 1%,
    100% 3%, 98.6% 9%, 100% 16%, 98.8% 23%, 100% 30%, 98.5% 37%, 100% 44%,
    98.9% 51%, 100% 58%, 98.6% 65%, 100% 72%, 98.7% 79%, 100% 86%, 98.8% 93%, 100% 97%,
    97% 99.2%, 91% 100%, 84% 98.8%, 77% 99.7%, 70% 98.5%, 63% 99.9%, 56% 98.9%,
    49% 100%, 42% 98.6%, 35% 99.6%, 28% 98.8%, 21% 100%, 14% 98.5%, 8% 99.5%, 2% 99.2%,
    0% 97%, 1.4% 91%, 0% 84%, 1.2% 77%, 0% 70%, 1.5% 63%, 0% 56%, 1.1% 49%,
    0% 42%, 1.4% 35%, 0% 28%, 1.2% 21%, 0% 14%, 1.5% 8%, 0% 3%
  );
  clip-path: var(--paper-clip);
}

/* ---------- 快問板跑馬燈 ---------- */
/* 發問橫向排列並自動往左跑，省掉直式清單的高度。
   會停下來的三個時機：滑鼠移上去、鍵盤 focus 到裡面、手指按住（JS 加 is-paused）——
   會動的東西如果不能停，使用者就沒辦法好好讀完或按到「回覆」。 */
/* 2026-09-03：標題字大兩級（跟 .wizard-section-head 同一組數字），
   箭頭鈕見 AskBoard.tsx 的說明。 */
.ask-card-title { font-size: var(--text-head); }
.ask-all-btn.ask-all-btn { padding: 2px 4px; min-height: 0; color: var(--muted); }

/* 「所有問題」列表（AllQuestionsDialog）：跑馬燈的 .ask-item 是給水平跑動
   用的固定寬度卡（236px）＋兩行截斷，這裡改直向堆疊、撐滿對話框寬度，
   而且不截斷內文——跑馬燈截斷是為了讓卡片高度一致，這個頁面的目的恰好
   相反（「一次看完所有問題」），截斷反而違背目的。 */
.ask-all-list { display: flex; flex-direction: column; gap: 44px; }

/* 🔴 這一頁的卡片是**兩層**：底下一張微微歪掉的撕邊紙，上面一張正的白卡
   （2026-09-22 使用者：「要疊一個白色卡在紙材質上方」「不要角度一樣的放，
   要微交錯做出設計感但不要影響閱讀」）。

   🔴 **歪的只有底下那張紙，白卡永遠是正的。** 內容全部在白卡上，轉一度都會讓
   整段文字跟著斜——那正是「影響閱讀」。交錯感是靠底紙左右各歪一邊做出來的，
   不是靠把字轉歪。

   ⚠️ **兩層都做在 pseudo-element 上，不用負的 z-index。** 有 stacking context
   的元素會先畫自己的背景、才畫負 z-index 的孩子，那張紙會反過來蓋在白卡上面。
   改成 ::before（紙）→ ::after（白卡）→ 內容（z-index:1）由下往上疊，順序固定。
   ⚠️ 所以卡片自己不能有背景，也不能留著 .paper-edge 的 clip-path——白卡不撕邊，
   撕邊是底下那張紙的事；而且 clip-path 會把白卡的陰影切掉（§5.2）。 */
.ask-all-list .ask-item {
  width: 100%; position: relative; isolation: isolate;
  padding: 14px 16px; background: none; clip-path: none;
}
.ask-all-list .ask-item > * { position: relative; z-index: 1; }
/* 底下那張撕邊紙：四邊都露一點出來，看得出是墊在下面的另一張紙。
   🔴 **陰影一定要用 filter: drop-shadow()，不能用 box-shadow**——這個元素有
   非矩形的 clip-path，box-shadow 會**連同陰影一起被裁掉**、完全看不到（§5.2
   記過的環境陷阱）。drop-shadow 是照裁切後的形狀畫，撕邊的鋸齒也會跟著有影子。
   ⚠️ **2026-09-22 調亮過一次**：第一版壓到 4%／6%，在手機上（而且底下就是紙紋
   背景）根本看不出來——使用者回報「陰影你沒做啊」，實際上是做了但看不見。
   淡到看不見的陰影等於沒做，該留的是「看得出紙有厚度」而不是「技術上有一層」。 */
.ask-all-list .ask-item::before {
  content: ""; position: absolute; inset: -7px -6px -9px -8px; z-index: 0;
  background: #F7F4EC url('/img/paper-cream.jpg') repeat; background-size: 300px 300px;
  clip-path: var(--paper-clip);
  /* 三層各有各的工作，少一層就看不出來：
     ① 0 偏移的細描邊——牛皮紙跟頁面的紙紋背景色溫太接近，光靠投影只會糊成一片，
        要一圈貼著撕邊的暗線才切得開（使用者：「感覺都跟背景混在一起了」）。
     ② 短距離的接觸陰影——紙「貼在」背景上的那一道。
     ③ 長距離的擴散陰影——厚度感。 */
  /* ⚠️ **這組數字是「維持米色」這個決定的代價，不要再往下調**（2026-09-22 使用者
     在「換成牛皮紙色」與「維持米色、陰影加更重」之間選了後者）：底紙跟頁面背景
     的紙紋明度太接近，顏色分不開就只能靠陰影分——所以描邊壓到 45%，比站上任何
     一張卡都重。🚫 想「順手調淡一點比較清爽」之前先回頭看這行。 */
  filter:
    drop-shadow(0 0 1px rgba(33, 48, 42, .45))
    drop-shadow(0 3px 3px rgba(33, 48, 42, .30))
    drop-shadow(0 12px 18px rgba(33, 48, 42, .28));
}
/* 上面那張白卡 */
.ask-all-list .ask-item::after {
  content: ""; position: absolute; inset: 0; z-index: 0;
  /* ⚠️ 圓角刻意比站上其他卡片小（2026-09-22 使用者要求）：底下那張是**手撕的紙**，
     上面壓一張圓滾滾的卡會像兩種材質硬湊；6px 讀起來才像一張裁過的紙。 */
  background: var(--panel); border-radius: 6px;
  box-shadow: 0 1px 2px rgba(33, 48, 42, .05), 0 10px 20px -16px rgba(33, 48, 42, .5);
}
/* ⚠️ 角度刻意一正一反、幅度也不一樣——兩張都歪同一邊會看起來像整疊沒放正，
   左右交替才是「一疊隨手疊起來的紙」。
   ⚠️ **2026-09-22 從 1.15°／1.5° 加大到 2.2°／2.6°**：原本那個幅度在手機上幾乎
   看不出來（使用者回報「底下的紙也要微歪斜」，實際上是有歪、但歪得看不出來）。
   🔴 **加大角度就要一起加大 .ask-all-list 的 gap**：343px 寬的卡片轉 2.6°，兩個
   上下角各會抬起約 8px，加上 inset 的 9px 就快吃到下一張了——所以 gap 18→22px。
   兩個數字是綁在一起的，只改一邊會讓相鄰的紙角疊在一起。 */
.ask-all-list .ask-item:nth-child(odd)::before { transform: rotate(-2.2deg) translateX(-3px); }
.ask-all-list .ask-item:nth-child(even)::before { transform: rotate(2.6deg) translateX(3px); }
@media (prefers-reduced-motion: reduce) {
  /* 這是靜態的傾斜、不是動畫，但偏好「減少動態」的人通常也不想要歪掉的版面 */
  .ask-all-list .ask-item::before { transform: none; }
}
.ask-all-list .ask-item-body { -webkit-line-clamp: unset; overflow: visible; }

.ask-marquee { overflow: hidden; margin-top: 10px; }
.ask-track { display: flex; gap: 10px; width: max-content; animation: ask-scroll 40s linear infinite; }
.ask-marquee:hover .ask-track,
.ask-marquee:focus-within .ask-track,
.ask-marquee.is-paused .ask-track { animation-play-state: paused; }
@keyframes ask-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
/* 發問小卡也是撕邊紙：clip-path 會把邊框切掉，所以不畫 border，改用紙質紋理＋內縮 padding 撐出紙感 */
/* 這張小卡刻意用米色紙紋（paper-cream），不跟頁面背景同色系——
   同色系會讓卡片融進背景、看不出是一張獨立的紙。 */
.ask-item {
  flex: 0 0 auto; width: 236px; padding: 12px 16px;
  background: #F7F4EC url('/img/paper-cream.jpg') repeat; background-size: 300px 300px;
  display: flex; flex-direction: column;
}
.ask-item-body {
  margin: 4px 0 6px; font-size: var(--text-body); line-height: var(--leading-body);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* 上面內容長短不一，卡片高度又會被同一排最高的那張撐開，回覆按鈕那排要固定貼在卡片底部，不要跟著內容高度上上下下 */
.ask-item-foot { margin-top: auto; }
.ask-item-reply { font-size: var(--text-sub); color: var(--muted); }
/* 只有一則的時候不跑，跑起來會一直閃 */
.ask-marquee.is-single .ask-track { animation: none; }
@media (prefers-reduced-motion: reduce) {
  .ask-track { animation: none; }
  .ask-marquee { overflow-x: auto; }
}

/* ---------- 底部導覽 ---------- */
.bottom-nav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  display: flex; align-items: stretch;
  /* 🔴 高度要「64px ＋ 安全區域」，不能只寫 64px。
     全站是 `box-sizing: border-box`（style.css 開頭那條），所以 padding 是
     **算在 height 裡面**的——寫死 64px 的話，iPhone 那 34px 的 Home Indicator
     安全距離會從這 64px 裡扣掉，圖示＋文字只剩 29.7px 可用（實機量到的數字），
     整條導覽被壓扁。
     ⚠️ 它還會讓高度**看起來忽高忽低**：叫出鍵盤時安全區域變成 0，那 34px 就
     還回來、圖示突然變大，收鍵盤又縮回去。使用者回報的「不同頁面高度不同」
     就是這個——不是各頁真的不一樣，是同一條在不同狀態下自己在變。
     🚫 不要改回寫死的高度；也不要拿掉 padding-bottom 改用 margin，那樣毛玻璃
     背景會在 Home Indicator 那一段斷掉，露出底下的內容。
     ⚠️ 網頁版 `env()` 算出來是 0，`calc(64px + 0px)` 就是 64px——**跟改動前
     一模一樣**，這條不是只給 App 的分支，是同一份定義在兩邊各自算對。 */
  height: calc(64px + env(safe-area-inset-bottom, 0px));
  padding-bottom: env(safe-area-inset-bottom, 0px);
  border-top: 1px solid var(--line);
}
.bottom-nav-item {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  background: none; border: none; border-radius: 0; padding: 6px 4px; min-height: 0;
  color: var(--muted); font-size: var(--text-tag); cursor: pointer; position: relative;
}
.bottom-nav-item svg { width: 22px; height: 22px; }
.bottom-nav-item.on { color: var(--brand); font-weight: 600; }
/* ⚠️ 這一排是「圖示＋文字」不是一般按鈕，任何狀態都不該有底色或邊框——
   選中的效果只靠顏色（橘）與字重（2026-09-06 使用者要求：「不要白底，
   只要橘色 icon 效果就好」）。
   ⚠️ 權重要比全域的 `button:hover`(0,1,1) 高，所以寫成 `.bottom-nav-item:hover`
   (0,2,0)——不寫的話上面那條的近白色底會贏。
   ⚠️ `-webkit-tap-highlight-color` 另外關掉 iOS 點擊時的灰色閃爍，那是
   系統畫的，跟 CSS 背景是兩件事。 */
.bottom-nav-item, .bottom-nav-item:hover, .bottom-nav-item:active, .bottom-nav-item:focus {
  background: none; border-color: transparent; -webkit-tap-highlight-color: transparent;
}
/* 「按下去了、資料還在路上」的分頁（2026-09-04，見 store.ts 的 `navPending`）：
   已經上色了，再用一點透明度的呼吸表示「還沒到」。🚫 不要換成轉圈圈的
   spinner——那要在圖示的位置塞一個新元素，切頁瞬間圖示會跳一下。 */
.bottom-nav-item.going { opacity: .55; }
@media (prefers-reduced-motion: no-preference) {
  .bottom-nav-item.going { animation: nav-going 1s ease-in-out infinite alternate; }
}
@keyframes nav-going { from { opacity: .45; } to { opacity: 1; } }

/* 切頁時的頂端進度條（App.tsx）。⚠️ 是 `fixed` 不是 `sticky`：它要浮在
   頁首**上面**，而頁首自己就是 sticky 的；用 sticky 會被排進文件流，
   內容整個往下推一條線的高度，切頁時畫面會抖一下。 */
.nav-progress {
  position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 60;
  background: linear-gradient(90deg, transparent, var(--brand), transparent);
  background-size: 40% 100%; background-repeat: no-repeat;
  animation: nav-progress-sweep 1.1s ease-in-out infinite;
}
@keyframes nav-progress-sweep {
  from { background-position: -40% 0; }
  to { background-position: 140% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .nav-progress { animation: none; background: var(--brand); opacity: .5; }
}
/* ⚠️ 2026-09-01 從 #unreadBadge 改成 class：底部導覽搬進 React 之後
   （BottomNav.tsx），用 id 綁樣式等於逼元件去掛一個全域唯一的 id。 */
.bottom-nav-item .bottom-nav-badge {
  position: absolute; top: 4px; right: calc(50% - 20px); min-width: 16px; height: 16px; line-height: 16px;
  padding: 0 4px; border-radius: 999px; font-size: var(--text-tag); text-align: center;
}
.bottom-nav-publish-icon {
  width: 40px; height: 40px; margin-top: -22px; border-radius: 50%; background: var(--brand); color: #fff;
  display: flex; align-items: center; justify-content: center;
  /* ⚠️ 2026-09-03 使用者要求暈影改橘色——原本是 rgba(15,118,110,.35)，
     一個跟按鈕本身（--brand 陶土橘）完全不搭的墨綠色殘留，暈影跟按鈕
     背景不同色系看起來像貼錯了。改用 --brand 自己的 rgb 值。 */
  box-shadow: 0 4px 12px rgba(217, 100, 42, .35);
}
.bottom-nav-publish-icon svg { width: 22px; height: 22px; }
.bottom-nav-publish.on { color: var(--muted); font-weight: 400; }

/* iOS：原生玻璃擬態，毛玻璃透光 + 細髮絲線，符合 iOS tab bar 的視覺語言 */
body.platform-ios .bottom-nav {
  background: rgba(255, 255, 255, .72);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border-top: .5px solid rgba(0, 0, 0, .12);
}
body.platform-ios .bottom-nav-item.on { color: var(--brand); }

/* Android：Material 風格，實色底 + elevation 陰影，不用毛玻璃 */
body.platform-android .bottom-nav {
  background: var(--panel);
  box-shadow: 0 -2px 10px rgba(16, 24, 40, .1);
  border-top: none;
}
body.platform-android .bottom-nav-item.on::before {
  content: ''; position: absolute; top: 2px; left: 50%; transform: translateX(-50%);
  width: 32px; height: 3px; border-radius: 999px; background: var(--brand);
}

/* 桌機／其他平台預設：接近 Android 的實色風格，不用毛玻璃（效能與相容性考量） */
body:not(.platform-ios):not(.platform-android) .bottom-nav {
  background: var(--panel);
  box-shadow: 0 -1px 6px rgba(16, 24, 40, .08);
}

@media (prefers-color-scheme: dark) {
  body.platform-ios .bottom-nav { background: rgba(28, 28, 30, .72); border-top-color: rgba(255, 255, 255, .12); }
}

/* ---------- 通知鈴鐺與通知中心 ---------- */
/* ⚠️ 重複類別（.bell-btn.bell-btn）是刻意的：這顆按鈕會出現在 .top-inner 這種
   flex 容器裡，而專案裡 `.row > *` 有 `flex: 1; min-width: 120px`（見 CLAUDE.md §5.2，
   那個坑踩過五次）。把 min-width: 0 / flex: 0 0 auto 寫進元件自己的規則，
   並用重複類別拉高權重，這樣它放在哪個容器裡都不會被撐開。 */
.bell-btn.bell-btn {
  position: relative;
  flex: 0 0 auto;
  min-width: 0;
  /* 44px 不是隨便挑的：觸控裝置上有一條 `button { min-height: 44px }`（見上面的
     @media (pointer: coarse)），那是無障礙的最小觸控目標。如果這裡寫 40px，
     桌機會是正圓、手機卻被撐成 40x44 的橢圓。與其去對抗那條規則（它是對的），
     不如直接用 44——差 4px 看不出來，而且兩種裝置上都是正圓。 */
  width: 44px; height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; margin-right: 6px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--panel); color: var(--ink);
  cursor: pointer;
}
.bell-btn.bell-btn:hover { border-color: var(--deep); color: var(--deep); }
/* ⚠️ 頁首改成透白毛玻璃之後（見 .top），鈴鐺原本的實心白底會變成漂在上面的
   一塊不透明白色方塊——整條頁首就它一個地方是實心的，特別顯眼。在頁首裡
   改成透明底、框線也降到很淡，讓它融進同一層玻璃。
   🚫 只在 `.top` 裡面改，不動 `.bell-btn` 本身——那顆按鈕的樣式是通用的，
   之後如果別的地方（非毛玻璃底）也用到，實心白底才是對的。 */
.top .bell-btn.bell-btn {
  background: transparent;
  border-color: rgba(33, 48, 42, .14);
}
.top .bell-btn.bell-btn:hover { border-color: var(--deep); }

/* 未讀只用一個點，不顯示數字：通知不像訊息需要「還有幾則沒回」，
   使用者要知道的只是「有沒有新東西」。數字反而會製造清空的壓力。 */
.bell-dot.bell-dot {
  position: absolute; top: 6px; right: 6px;
  width: 9px; height: 9px; min-width: 0;
  border-radius: 999px;
  background: var(--danger);
  border: 2px solid var(--panel);
}

.notif-item {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 12px 4px;
  border-top: 1px solid var(--line);
  cursor: pointer;
}
.notif-item:first-child { border-top: none; }
.notif-item:hover { background: var(--bg); }
/* 未讀的用左側小圓點標示。⚠️ 不要改成 border-left 色條——
   CLAUDE.md §5.3 明訂卡片不准有左側色條，已全站移除過一次。 */
.notif-item .notif-unread-dot {
  flex: 0 0 auto; min-width: 0;
  width: 8px; height: 8px; margin-top: 6px;
  border-radius: 999px; background: var(--deep);
}
.notif-item.read .notif-unread-dot { background: transparent; }
.notif-item-main { min-width: 0; flex: 1; }
.notif-item-title { font-weight: 600; font-size: var(--text-body); }
.notif-item.read .notif-item-title { font-weight: 500; color: var(--muted); }

/* 通知設定的送達管道（展開後的兩個勾選）。2026-09-18 從「一列三塊擠在同一個
   flex row」改成收合的複選，沿用 .trait-select（見 NotificationBell.tsx 的
   NotifKindRow）。🚫 舊的 .notif-pref-row／.notif-pref-label 已經沒有呼叫端，
   整組刪掉，不留死碼。 */
.notif-pref-channels { display: flex; gap: 18px; padding: 0 12px 12px; }
.notif-pref-channels label {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--text-sub); color: var(--ink); white-space: nowrap;
}

.danger-zone {
  margin-top: 18px;
  padding-top: 14px;
  border-top: 0.5px solid var(--line);
}

/* ---------- 個人卡片：他在募集的團 ---------- */
/* ⚠️ 刻意不用 .row：.row > * 有 flex:1 + min-width:120px，會把標題擠成一列一個字（§5.2） */
.profile-trips { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.profile-trip {
  display: flex; flex-direction: column; gap: 4px; align-items: flex-start;
  width: 100%; padding: 10px 12px; text-align: left; cursor: pointer;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  font: inherit; color: inherit;
}
.profile-trip:hover { border-color: var(--deep); }
.profile-trip-title { font-weight: 600; }
.profile-trip-need-row { display: flex; margin-top: 2px; }
.profile-trip-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }

/* ---------- 個人頁（2026-08-26 改版） ---------- */
/* 頁面中的分段標題。用襯線體＋墨綠跟 h2 同一家族，但小一號——它標的是
   「下面這一整區」，不是一個物件的名字，跟卡片裡的 h2 要分得出來。 */
.section-label {
  font-family: var(--font-serif); color: var(--deep); font-size: var(--text-body); font-weight: 600;
  margin: 20px 2px 8px;
}
.verified-tag {
  display: inline-flex; align-items: center; font-size: var(--text-sub); font-weight: 600;
  color: var(--ok); background: var(--sage-soft); border-radius: 999px; padding: 2px 8px; margin-top: 3px;
}
/* ==========================================================================
   個人展示頁（2026-09-02 改版）
   ==========================================================================
   使用者拿常見 App 的個人頁當例圖要求：先是一個展示頁，右上角一顆設定圖示，
   點下去才進到「我的資料」跟整份設定清單（見 Profile.tsx 的說明）。

   🔴 這一區的視覺經過六輪修正，最後定案的做法跟前五版都不一樣，重點只有兩句：

   1. **「斷層感」的根源是「畫面上有好幾個分開的元素」，不是漸層停點沒調好。**
      前五版一直在調停點，但只要還存在「照片元素」「色塊元素」「退回底色的
      那一層」三個東西，它們之間就一定有交界。定案的做法是**一個元素、一張
      背景圖、一道漸層**：照片是這一區的背景（只鋪上半部），霧化的終點直接
      用頁面自己的底色，所以照片在哪裡結束看不出來，也不需要第二個過渡。
      🚫 不要為了「好讀」把它拆回幾個元素各自負責一段，那條交界會馬上回來。

   2. **長距離、低對比的漸層在 8-bit 色深下一定會有色階斷裂（banding）**，
      那是格式的極限、加停點補不掉，要靠一層極淡的雜訊打散（`.profile-hero-grain`
      用站上原本的紙紋）。
   ========================================================================== */

/* 展示頁整頁換成灰白底（不是站上的紙感米白）——霧化的終點就是它，
   照片才能直接溶進頁面，不需要第二個過渡。負 margin 是為了吃掉 .wrap 的
   左右內距，讓主視覺真的滿版到兩側。 */
/* ⚠️ 負的 margin-bottom 是要吃掉 .wrap 自己的 64px 下內距——不吃掉的話，
   灰白只鋪到內容結束，底下會露出一條頁面本來的米白紙紋，看起來像沒鋪滿。
   內距再自己加回來，內容才不會被固定在底部的導覽列蓋住。 */
/**
 * ⚠️ 2026-09-03 使用者要求：「首圖要靠頂部靠齊」——主視覺照片原本從頁首
 * 底下（y = --top-h）才開始，頁首淡出的那段會露出 `body` 的紙紋貼圖
 * （`bg-texture.jpg`），紙紋本身偏灰藍，襯在頁首那條窄窄的漸層帶上很明顯，
 * 使用者形容成「藍色底圖」。
 *
 * 解法：把整個展示頁往上拉 `--top-h`，讓主視覺從頁面最頂端（y=0）開始、
 * 延伸到頁首**底下**——頁首本來就是半透明淡出的漸層，浮在照片上方剛好是
 * 玻璃感的效果（跟頭圖右上角那顆設定齒輪的毛玻璃是同一個語言）。
 * 🚫 不要以為這條線多餘就拿掉——拿掉之後主視覺會退回頁首下方，中間那段
 * 紙紋帶又會跑出來。
 */
.profile-display {
  margin: calc(-1 * var(--top-h)) -16px -64px; padding-bottom: 88px;
  background: var(--detail-surface); min-height: calc(100vh - 56px);
}
/* ⚠️ 2026-09-07：`body.profile-topless .profile-display` 補回來了，沿革見
   main.tsx 那段「個人頁的頂部導覽列，只在登入時才藏起來」的完整說明——
   2026-09-06 曾經整套刪掉（那時頁首一律顯示，不需要補償），這次使用者要求
   「已登入才藏」，`.profile-display` 上面那段
   `margin-top: calc(-1 * var(--top-h))` 就只在**登入時**（頁首被藏起來、
   不占版面高度）多拉了一層，需要靠這條覆寫抵消回來。 */
body.profile-topless .profile-display { margin-top: -16px; }
.profile-display-body { padding: 0 16px; }

/* ⚠️ 2026-09-03 使用者實機發現：看別人的個人卡片（`ProfileCardDialog`）在
   `.profile-hero` 底下有一道看得出來的接縫。根因不是漸層本身沒做好——
   漸層確實有淡到 `var(--detail-surface)`（見上面 .profile-hero-tint 的說明），
   問題是**漸層淡進的目標色，跟它下面那塊的底色不一樣**：「個人」分頁
   （`.profile-display`）自己有 `background: var(--detail-surface)`，漸層
   淡進淡出都在同一個顏色的世界裡，看不出接縫；這個對話框是普通
   `dialog.sheet`，沒有另外設過背景，退回瀏覽器 `<dialog>` 的預設白——
   灰白（#F3F4F2）漸層淡完，緊接著是純白，兩色中間那條線就是使用者說的
   「白色色塊」。修法只有一行：讓這個對話框也用同一個 `--detail-surface`，
   跟「個人」分頁共用同一份底色，不需要重算漸層停點。 */
/**
 * 看別人的個人卡片。
 *
 * 🔴 **捲動一定要發生在裡面的 `.dlg-body`，不能讓 `<dialog>` 自己捲**
 * （2026-09-03 使用者定的全站規則：上方的常用操作欄不管怎麼滑動都要看得見）。
 * 對話框自己捲的話，`position: absolute` 的返回／更多兩顆會跟著內容一起被捲走
 * ——那正是使用者實機圈出來的問題。改成「對話框是固定高度的 flex 容器、
 * 內容在 `.dlg-body` 裡捲」之後，那兩顆的容納區塊是不動的對話框本身。
 * 跟 `#tripDetailDlg` 的關閉／分享是同一招。
 */
#profileCardDlg.sheet {
  background: var(--detail-surface);
  overflow: hidden;   /* 捲動交給裡面的 .dlg-body，這一層不捲 */
}
#profileCardDlg.sheet[open] { display: flex; flex-direction: column; max-height: 92vh; }
/* 主視覺要貼齊對話框左右與上緣，所以這個捲動區不能有內距（`.dlg-body` 預設有）。
   🔴 **這裡不能寫 `flex: 1`，寫了整個對話框會在 iOS App 裡變成 0 高度**
   （2026-09-06 實機重現：背景變暗、內容一個字都沒有，量出來 dlg 是 402×0）。
   `flex: 1` 等於 `flex-basis: 0%`，而這個對話框只有 `max-height`、沒有明確的
   `height`，**而且返回／更多兩顆是絕對定位、不在流內，所以這個捲動區是唯一的
   流內子元素**——整個對話框的高度完全靠它撐，它的 basis 是 0 就沒有東西可以撐。
   兩邊的引擎在「高度不定的直向 flex 容器該拿什麼當內在高度」上算出不同答案：
   App 的 WebView（WebKit）算出 0，專案內建瀏覽器（Chrome）拿子元素的內容去撐。
   **所以這個 bug 在瀏覽器裡怎麼測都是正常的，只有 App 會壞**——查的時候不要
   因為瀏覽器重現不出來就以為不是 CSS。
   ⚠️ 站上另外兩處也寫 `flex: 1`，都沒事，原因各不相同，**不要順手一起改**：
   `#tripDetailDlg` 有明確的 `height: 100dvh`（高度是確定的）；`.auth-card`
   有一個帶內容的 `.dlg-head` 兄弟撐著，不是唯一的流內子元素（App 實測登入
   畫面正常）。會壞的條件是「高度不定」＋「唯一的流內子元素 basis 是 0」兩個
   同時成立。
   通用的 `.dlg-body { flex: 0 1 auto; min-height: 0 }` 本來就是對的（§5.2
   那條「頭固定、身體會捲、腳固定」的結構解法），這裡只覆寫內距與上限，
   不要再去動 flex。 */
#profileCardDlg .profile-card-body { max-height: none; padding: 0; }

/* 區塊之間的距離交給容器的 gap，不要每個區塊各自寫 margin-top——
   那是「有的 10、有的 12、有的 16」的來源，也是使用者說「間距太近」的原因。
   22px 跟「個人」分頁的 `.profile-sec` 同一個數字，兩邊節奏一致。 */
.profile-card-sections {
  display: flex; flex-direction: column; gap: 22px;
  padding-top: 18px; padding-bottom: 24px;
}

/* ⚠️ 2026-09-03 使用者要求把主視覺拉高——padding-top 300→380、照片
   420→500（各 +80px），是同一組數字的兩端，不能只改其中一個：photo 的
   下緣要落在霧化已經完全不透明的區間裡（見 .profile-hero-tint 的說明），
   兩個一起變、連同下面 tint 的終點 62%→68%，才維持這個關係不被打破。 */
.profile-hero { position: relative; overflow: hidden; padding: 380px 18px 4px; }
/**
 * 主視覺照片。
 *
 * ⚠️ **一定要 `background-size: cover`，不能寫成 `100% <高度>`**（2026-09-02 修）。
 * `100% 420px` 是「寬度撐滿、高度硬設成 420」——那是兩個各自獨立的尺寸，等於
 * 直接把圖片拉變形。手機寬度不固定（375／390／430…），變形程度還會跟著裝置跑，
 * 在開發機上看起來「還好」，換一支手機就明顯壓扁。`cover` 才是等比放大到蓋滿、
 * 多的部分裁掉。
 *
 * ⚠️ 照片獨立成一層（而不是鋪在 `.profile-hero` 自己身上）是為了讓 `cover` 有一個
 * **固定高度的框**可以算——鋪在 hero 上的話，cover 會以整塊（含底下的文字區）
 * 的高度去算，主體會被放到超出畫面。
 * 這一層的下緣（420px）落在霧化**已經完全不透明**的區間裡（見 .profile-hero-tint
 * 的 62%），所以它在哪裡結束看不出來——這正是那條「多一個元素沒關係，只要它的
 * 邊界被蓋住」的判準。
 */
.profile-hero-photo {
  position: absolute; top: 0; left: 0; right: 0; height: 500px; pointer-events: none;
  background-position: center 30%; background-repeat: no-repeat; background-size: cover;
}
/* 霧化：透明 → 灰白。⚠️ 一定要在照片下緣（500/整塊高度）**之前**就到 100%，
   而且要用依緩動曲線算出的十幾個中間停點——兩三個停點的線性漸層會在「開始
   變色」的位置留下一條帶狀邊。
   ⚠️ 2026-09-03 主視覺拉高之後（padding-top 300→380、照片 420→500），終點
   從 62% 往後挪到 68%——不挪的話，text 內容開始的位置（padding-top）會落在
   終點**之前**，還沒完全不透明。中間那串緩動停點维持原樣不動：只是把
   「到達全不透明」那一刻往後延，51.6%→68% 之間的線性內插本來就很平緩
   （92.85%→100%），不會因為區間變寬而冒出新的色階。 */
.profile-hero-tint {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to bottom,
    rgba(243,244,242,0) 8%,
    rgba(243,244,242,.0086) 12.6%,
    rgba(243,244,242,.0335) 16.8%,
    rgba(243,244,242,.0736) 20.7%,
    rgba(243,244,242,.1276) 24.4%,
    rgba(243,244,242,.1946) 28%,
    rgba(243,244,242,.2732) 31.3%,
    rgba(243,244,242,.3626) 34.6%,
    rgba(243,244,242,.4614) 37.9%,
    rgba(243,244,242,.5685) 41.2%,
    rgba(243,244,242,.6829) 44.5%,
    rgba(243,244,242,.8033) 48%,
    rgba(243,244,242,.9285) 51.6%,
    var(--detail-surface) 68%,
    var(--detail-surface) 100%);
}
/* 漸進模糊，讓霧真的有霧感。這個環境不能用 mask-image（§5.2：完全不會渲染），
   所以用幾片模糊度遞增的 backdrop-filter 疊出來。
   ⚠️ 這幾片**一定要壓在「灰白已經六七成」的位置**：放在照片還清楚的上半部時，
   每一片的上緣都會是一條「上面銳利、下面糊掉」的線，非常明顯。
   ⚠️ 2026-09-03 跟著 tint 的終點一起從 62%→68% 往後挪 6 個百分點，維持
   「壓在灰白已經六七成的位置」這個關係。 */
.profile-haze { position: absolute; left: 0; right: 0; pointer-events: none; }
.profile-haze-1 { top: 48%; height: 6%; backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px); }
.profile-haze-2 { top: 54%; height: 6%; backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); }
.profile-haze-3 { top: 60%; height: 8%; backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); }
.profile-haze-4 { top: 68%; height: 10%; backdrop-filter: blur(26px); -webkit-backdrop-filter: blur(26px); }
.profile-hero-grain {
  position: absolute; inset: 0; pointer-events: none;
  background: url('/img/bg-texture.jpg') repeat; background-size: 380px; opacity: .07;
}
.profile-hero-body { position: relative; z-index: 1; }

/* 展示頁右上角的圖示鈕：沿用站上頭圖那顆毛玻璃圓鈕（.trip-cover-glass）的
   同一份 recipe——底下是照片，玻璃才透得出東西。
   ⚠️ 2026-09-03：從「固定一顆」改成「一叢」（`.profile-top-actions`）——
   自己看自己只有一顆（設定），看別人是兩顆（檢舉＋封鎖，見 ProfileHero.tsx
   的說明）。定位交給外層 flex 容器，個別按鈕本身不再各自 `position:absolute`。 */
/* ⚠️ 2026-09-03：`top` 從 `calc(var(--top-h) + 14px)` 改回單純的 14px。
   那個 --top-h 補償本來是給「個人」展示頁用的（主視覺往上延伸到 sticky 頁首
   底下，圖示不讓開就會被蓋住）——但現在展示頁的右上角已經沒有任何圖示了
   （設定齒輪 2026-09-03 拿掉，見 ProfileDisplay.tsx），這一叢**只剩看別人的
   個人卡片在用**，而那是浮動對話框、上面根本沒有頁首要閃。使用者實機圈出
   「位置太低」就是這多出來的 68px。 */
.profile-top-actions {
  position: absolute; top: 14px; right: 14px; z-index: 3;
  display: flex; gap: 8px;
}
/* 返回：對稱放在左上角，跟右上角那顆共用同一份毛玻璃 recipe。 */
.profile-top-back { position: absolute; top: 14px; left: 14px; z-index: 3; }
.profile-settings-btn.profile-settings-btn {
  /* 🔴 `min-height: 0` 不能省：全域有一條 `button { min-height: 44px }`（觸控
     目標尺寸），而 **min-height 永遠贏過 height**——少了這一行，40×40 的圓鈕
     會被撐成 40×44 的**橢圓**（使用者實機圈出來的就是這個）。跟 `.avatar-btn`
     那次是同一個坑：自訂尺寸的圓形按鈕都要記得把全域的 min-height 歸零。 */
  flex: 0 0 auto; min-width: 0; min-height: 0; width: 40px; height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; border-radius: 999px;
  /* 🔴 **深色玻璃，不是白色玻璃**（2026-09-03 改）。這幾顆現在是「不管怎麼捲
     都固定在上面」的常駐控制項，所以它們底下的東西是會變的：一開始是主視覺
     照片，捲下去之後是灰白的內容區。原本的白玻璃（rgba(255,255,255,.20)）
     只在照片上成立，捲過去就變成「淺色的圈圈疊在淺色底上」，實機幾乎看不見
     ——那等於沒有滿足「不管怎麼滑動都能看見」。深色玻璃在兩種底色上都讀得到，
     也是各家 App 把控制項疊在未知內容上時的標準做法。
     🚫 不要改回白玻璃，那只有在「按鈕永遠待在照片上」的前提下才成立，
     而那個前提已經不存在了。 */
  background: rgba(20, 26, 22, .42); border: 1px solid rgba(255, 255, 255, .22);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  color: #fff; cursor: pointer;
}
.profile-settings-btn.profile-settings-btn svg { stroke: #fff; }

/**
 * 「更多」收納選單（檢舉／封鎖）。
 *
 * 🔴 **刻意不是第二個 `<dialog>`**，是這個對話框自己的一個絕對定位子元素——
 * 兩個同時開著的 modal `<dialog>` 在這個環境會互相牽連（關掉一個，瀏覽器會把
 * 另一個也原生關掉），`TripDetailDialog` 的圖片檢視器踩過同一個坑並且實測證實
 * 過，見那個檔案的說明。外層對話框本來就活在 top layer，這一層只要 z-index
 * 蓋過同一個對話框裡的其餘內容就好。
 */
.profile-more-menu {
  position: absolute; top: 62px; right: 14px; z-index: 4;
  min-width: 132px; padding: 6px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: var(--shadow);
  display: flex; flex-direction: column; gap: 2px;
}
.profile-more-menu button.profile-more-item {
  width: 100%; min-height: 0; padding: 9px 12px; border: 0; border-radius: 8px;
  background: none; color: var(--ink); font-size: var(--text-sub); font-weight: 500;
  text-align: left; justify-content: flex-start; gap: 8px; cursor: pointer;
}
.profile-more-menu button.profile-more-item:hover { background: var(--bg); }
.profile-more-menu button.profile-more-item.danger { color: var(--danger); }

/* 看別人的個人卡片裡的中標題（旅伴特質／飲食禁忌／謝絕…）。
   ⚠️ 2026-09-03 使用者要求「大一級並粗體」：原本是 `.small muted`（13px 灰字），
   改成沿用「個人」分頁同一個角色本來就在用的 `.profile-sec-title`
   （15px／600／墨綠襯線）——同一種語意層級用同一套視覺語言，不要為了差不多的
   東西再發明第三種標題樣式（§5.4 第 5 點）。這裡只補 `<label>` 需要的排版。 */
.profile-card-label { display: block; margin-bottom: 6px; }
.profile-card-label + .trait-chips,

.profile-id { text-align: center; }
.profile-name {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  font-family: var(--font-serif); font-size: var(--text-title); font-weight: 600; color: var(--ink);
}
.profile-name svg { color: var(--ok); flex: 0 0 auto; }
.profile-sub { font-size: var(--text-sub); color: var(--muted); margin-top: 2px; }

/* 真人驗證＝名字前面的綠勾（2026-10-08 起取代灰色「已驗證」標章）。顏色吃 .profile-name svg 的 --ok。 */
.profile-verified-check { display: inline-flex; }

.profile-stats { display: flex; margin-top: 16px; }
.profile-stat { flex: 1; text-align: center; min-width: 0; }
.profile-stat-label { font-size: var(--text-sub); color: var(--muted); }
.profile-stat-value {
  font-family: var(--font-serif); font-size: var(--text-title); font-weight: 600; color: var(--deep);
  margin-top: 1px; font-variant-numeric: tabular-nums;
}

/* 旅伴特質膠囊：淺灰底深字的實心膠囊。
   ⚠️ 不要用毛玻璃——底色是灰白，白色半透明的玻璃在淺底上等於看不見。 */
.profile-chips { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-top: 16px; }
.profile-chip {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--text-sub); font-weight: 500; padding: 7px 14px; border-radius: 999px;
  background: rgba(33,48,42,.06); border: 1px solid rgba(33,48,42,.09); color: var(--ink);
}

.profile-actions { display: flex; align-items: center; gap: 10px; margin-top: 14px; }
.profile-actions .act-circle.act-circle {
  width: 46px; height: 46px; flex: 0 0 auto; min-width: 0; border-radius: 999px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--panel); border: 1px solid var(--line); color: var(--ink);
}
.profile-actions .act-pill.act-pill {
  flex: 1; min-width: 0; height: 46px; border-radius: 999px;
  background: var(--deep); border-color: var(--deep); color: #fff; font-weight: 600;
}
/* 看別人的卡片：封鎖用次要樣式（文字＋外框），跟私訊那顆實心主色分出主次
   （2026-09-03，ProfileCardDialog 併入 ProfileHero 時新增——原本封鎖鍵是
   dlg-foot 裡的 ghost danger 按鈕，搬進這排之後要有對應的次要樣式）。 */
.profile-actions .act-pill.ghost {
  flex: 0 0 auto; min-width: 0; padding: 0 18px;
  background: var(--panel); border-color: var(--line); color: var(--ink); font-weight: 600;
}
.profile-actions .act-pill.ghost.danger { color: var(--danger); border-color: var(--danger); }

/* ---------- 展示頁的各區塊 ---------- */
.profile-sec { margin-top: 22px; }
.profile-sec-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 10px; }
.profile-sec-title { font-family: var(--font-serif); font-size: var(--text-body); font-weight: 600; color: var(--deep); }
.profile-sec-count { font-size: var(--text-sub); color: var(--muted); }
/* 自我介紹等區塊只用一條細線分隔，不畫框——灰白底上再畫一個白框會浮出來 */
.profile-block { border-top: 1px solid var(--line); padding-top: 14px; }
.profile-block-title { font-size: var(--text-sub); color: var(--muted); margin-bottom: 4px; }

/* 🎫 旅遊足跡＝護照戳章。資料是推導的（走完的行程→目的地國家），
   會隨著使用者真的用這個產品長出來。每一枚略為歪斜，才像蓋上去的。 */
/* 🔴 一列水平滑動，不換行（2026-10-08 使用者要求）。上下留 padding 是因為戳章有旋轉，
   overflow 容器會把轉出去的角切掉；左右負 margin 讓第一枚對齊內容邊。 */
.profile-stamps {
  display: flex; flex-wrap: nowrap; gap: 12px;
  overflow-x: auto; padding: 6px 4px; margin: -6px -4px;
  -webkit-overflow-scrolling: touch;
}
.profile-stamps > .profile-stamp { flex: 0 0 auto; }
/* 未來想去＝還沒蓋到的戳章：外框虛線、顏色淡一階。實線＝去過、虛線＝還想去。 */
.profile-stamps.is-wish .profile-stamp { border-style: dashed; color: var(--muted); border-color: var(--muted); opacity: .7; }
.profile-stamps.is-wish .profile-stamp::before { display: none; }
.profile-stamp {
  /* min-width 而不是固定寬：「印尼／峇里島」這種長名字固定 82px 會貼邊 */
  position: relative; min-width: 82px; padding: 0 10px; height: 62px; box-sizing: border-box; white-space: nowrap;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  border: 1.5px solid var(--sage); border-radius: 8px; color: var(--sage);
  font-size: var(--text-sub); font-weight: 600; opacity: .78;
}
.profile-stamp:nth-child(odd) { transform: rotate(-2.5deg); }
.profile-stamp:nth-child(even) { transform: rotate(1.8deg); }
.profile-stamp::before {
  content: ''; position: absolute; inset: 3px; border: 1px dashed var(--sage); border-radius: 5px; opacity: .5;
}
.profile-stamp-year { font-size: var(--text-tag); font-weight: 500; letter-spacing: .06em; opacity: .85; }


/* 行程列：跟自我介紹同一套「只用細線分隔、不畫框」。
   ⚠️ 全域 `button` 有 border-radius:9px／四邊框線／justify-content:center／
   min-height:38px，整列可點的列表列四項都要明確蓋掉（站上 .set-row 踩過同一個坑）。 */
.profile-trip-row.profile-trip-row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 11px 2px; min-height: 0;
  background: none; border: 0; border-top: 1px solid var(--line); border-radius: 0;
  justify-content: flex-start; text-align: left;
}
.profile-trip-row.profile-trip-row:first-of-type { border-top: 0; }
.profile-trip-row.profile-trip-row:hover { background: rgba(33,48,42,.03); }
.profile-trip-main { flex: 1; min-width: 0; display: block; }
.profile-trip-title { display: block; font-size: var(--text-body); color: var(--ink); font-weight: 500; }
.profile-trip-meta { display: block; font-size: var(--text-sub); color: var(--muted); margin-top: 1px; }
.profile-trip-need {
  flex: 0 0 auto; font-size: var(--text-sub); font-weight: 600; padding: 3px 9px; border-radius: 999px;
  color: var(--brand); background: var(--brand-soft);
}
.profile-trip-chev { flex: 0 0 auto; color: var(--deep); display: inline-flex; }
.profile-empty { font-size: var(--text-sub); color: var(--muted); }

/* 設定清單。沿用 .thread-item 的「整列可點、列間分隔線、
   不是一列一張卡」慣例（CLAUDE.md §5.3：不要一列包一個框）。
   ⚠️ 2026-08-28 兩次校正：
   第一次，分組原本是靠「一組一張 .card」表達的（三張各自獨立的白色卡片，
   中間空一段），使用者覺得太破碎，要求拿掉分組、七個項目拉平成一份連續清單。
   第二次（真正的根因）：拉平之後每一列還是看起來像獨立的圓角方塊——
   原因是 `.set-row` 是 `<button>`，繼承全域 `button { border-radius: 9px }`
   卻沒有覆寫成 0，`:hover` 的背景色因此在每一列都畫出圓角方塊，變成
   「每個項目都是圓角、上面還有線裝飾」。跟 `.thread-item` 那次是同一類
   bug（CLAUDE.md §14.1）：全域 `button` 規則設的東西，沒有被子類別完整
   覆寫，殘留的部分就會在特定狀態（這裡是 hover 而不是預設狀態）冒出來。
   修法：`.set-row` 明確 `border-radius: 0`，不再有任何分組手段。 */
.set-card { padding: 2px 16px; }
.set-row {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 13px 0; border: 0; border-radius: 0; border-top: 1px solid var(--line);
  background: none; text-align: left; font: inherit; color: inherit; cursor: pointer;
  min-height: 0;
}
.set-row:first-child { border-top: 0; }
.set-row:hover { background: var(--bg); }
.set-row-icon { flex: 0 0 auto; color: var(--deep); display: inline-flex; }
.set-row-label { flex: 1; min-width: 0; font-size: var(--text-body); }
.set-row-value { flex: 0 0 auto; font-size: var(--text-sub); color: var(--muted); }
.set-row-chev { flex: 0 0 auto; color: var(--deep); display: inline-flex; }

/* 登出／刪除帳號。刻意在卡片外面、跟設定清單隔一段距離——這兩個是
   「離開」動作，混進設定列表裡會被誤按。 */
.account-exit { display: flex; flex-direction: column; gap: 10px; margin-top: 22px; }
.account-exit .block { width: 100%; }
.account-exit .danger-text { border-color: transparent; background: none; color: var(--danger); font-weight: 500; }

/* 有未儲存的變更才出現的儲存列（見 ProfileDialog.tsx 的「存檔時機」）。
   負邊距是為了讓它貼齊卡片邊緣、看起來像卡片的頁尾，而不是卡片裡的一塊。 */
/* 🔴 固定浮在畫面底部、貼在底部導覽上方（2026-09-29）。以前它排在特質卡的最底部，
   改最上面的手機／LINE 時根本看不到「儲存」，使用者以為存好了、重新整理就全部消失。
   🚫 不要改回卡片裡的一般區塊，也不要用 sticky（sticky 只在父層範圍內有效）。
   ⚠️ bottom 要讓開導覽列（64px ＋ 安全區域）再加上「發布」圓鈕凸出去的那一截，
   z-index 要比導覽列（30）高。寬度跟 .view-column 同一個上限，桌機上不會拉成一整條。 */
.profile-savebar {
  position: fixed; z-index: 31;
  left: 50%; transform: translateX(-50%);
  bottom: calc(64px + env(safe-area-inset-bottom, 0px) + 24px);
  width: min(688px, calc(100vw - 32px));
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px 10px 16px;
  background: #FFFDF8; border: 1px solid var(--line); border-radius: 14px;
  box-shadow: 0 6px 20px rgba(16, 24, 40, .16);
}
/* 儲存列浮起來之後會蓋住頁面最後一截，墊一塊同樣高的空白讓最後一組特質點得到。 */
.profile-savebar-spacer { height: 84px; }
.profile-savebar-note { flex: 1; min-width: 0; font-size: var(--text-sub); color: var(--warn); }

/* 「我的資料」底下那組特質自己包成一張卡（最外層那張 .card 2026-09-22 拿掉了，
   見 ProfileDialog.tsx）。第一組的上緣線要收掉——它本來是「跟上一個欄位之間的
   分隔線」，變成卡片的第一個孩子之後就只是貼著卡片邊緣的第二條線。 */
.profile-traits > .pick-field:first-child { border-top: 0; padding-top: 0; }

/* 行程詳情的說明文字（TripDetailDialog）。⚠️ 用 .trip-detail-notes 不是 .small：
   那個 class 是全站的 13px 補充說明，這裡是一整段要讀完的內容。 */
/* 🔴 white-space: pre-wrap 不能省：主揪是分段打的（【行程】一段、【住宿】一段），
   HTML 預設會把換行當成空白吃掉，整篇黏成一大段——那不是他送出去的東西。
   跟 .trip-comment-body 用的是同一招。 */
.trip-detail-notes {
  margin: 0; font-size: var(--text-body); line-height: var(--leading-loose);
  white-space: pre-wrap; word-break: break-word;
}

/* 收合式特質欄位（TraitPicker.tsx 的 TraitField）。
   ⚠️ 收合時看得到的只有 <summary> 的內容，所以已選標籤是放在 summary 裡的。 */
/* 🔴 間距（2026-10-13 使用者：「標題和內裡 ui 垂直間距要拉開，太擠了」）：
   每一組上下 18px；組內（標題 → 已選的卡片／戳章 → 展開的勾選區）一律交給 summary 的 gap
   與 [open] 的 margin，子元素自己的 margin-top 在這裡歸零，不要各帶各的數字（§6.3.12 那條容器 gap 規則）。 */
.pick-field { border-top: 1px solid var(--line); padding: 18px 0; }
.pick-field > summary { list-style: none; cursor: pointer; display: flex; flex-direction: column; gap: 12px; }
.pick-field > summary > .trait-cards,
.pick-field > summary > .trait-chips { margin-top: 0; }
.pick-field[open] > summary { margin-bottom: 18px; }
/* 展開後的「我的旅遊風格」：一組一組（行程節奏、景點密度…）之間要比組內鬆 */
/* 旅遊風格選項（編輯個人資料／開新行程／尋旅友）＝跟特質勾選格同一套 .tag-card（兩欄、一行字高） */
.trip-style-cardgrid { position: relative; display: grid; grid-template-columns: repeat(2, 1fr); gap: 7px; }
.pick-field > summary::-webkit-details-marker { display: none; }
.pick-field-top { display: flex; align-items: center; gap: 8px; }
.pick-field-label { flex: 1; min-width: 0; font-size: var(--text-body); }
.pick-field-chev { flex: 0 0 auto; color: var(--deep); display: inline-flex; transition: transform .15s ease; }
.pick-field[open] .pick-field-chev { transform: rotate(180deg); }
/* ⚠️ 2026-09-18 推翻上一版的「直接讓它長完，跟著頁面一起捲」——那句話對
   「一次只有一組展開」是對的，但這個頁面有四組特質欄位疊在一起，加上飲食
   禁忌就是 60 幾個選項，展開一組整頁就多推一個螢幕，使用者要一直往下滑才
   找得到儲存列（他原話：「很累」）。給一個高度上限、讓它自己捲。
   ⚠️ 280px 是「大約 8 列 ＋ 半列」：最後一列被切一半就是「底下還有」的提示，
   跟 .filter-bar／常用題目那排同一個做法，不用另外畫箭頭。 */
.pick-field .trait-grid { max-height: 280px; }

/* ---------- 加入名額前的特質比對（compat.ts / JoinDialog.tsx） ----------
   排版依使用者 2026-08-27 改過的 mockup：先講合得來的、再講要注意的，
   兩塊都是「圖示＋標題＋一句話」的同一個結構，不用兩套版面。
   🚫 不要在這裡加相容度分數或百分比條——見 compat.ts 開頭的說明。 */
.compat {
  border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px; margin-bottom: 12px;
  background: var(--panel);
}
.compat-title {
  font-family: var(--font-serif); font-weight: 600; color: var(--deep);
  font-size: var(--text-body); margin-bottom: 10px;
}
.compat-group { display: flex; gap: 10px; align-items: flex-start; }
.compat-group + .compat-group { margin-top: 10px; }
/* 用 flex: 0 0 auto 而不是靠 .row：這裡本來就不在 .row 裡，
   但圓形圖示被拉成橢圓是這個專案踩過五次的坑（§5.2），先寫死不讓它縮。 */
.compat-icon {
  flex: 0 0 auto; width: 18px; height: 18px; border-radius: 50%;
  display: grid; place-items: center; color: #fff; font-size: var(--text-tag); font-weight: 700;
  margin-top: 2px;
}
.compat-icon.good { background: var(--ok); }
.compat-icon.note { background: var(--warn); }
.compat-group h4 { margin: 0 0 2px; font-size: var(--text-sub); font-weight: 700; }
.compat-group p { margin: 0; font-size: var(--text-sub); color: var(--muted); line-height: var(--leading-loose); }
.compat-group p b { color: var(--ink); }
.compat-group .trait-chip { margin-right: 4px; }

/* ---------- 把行程傳給家人（ShareTripDialog.tsx） ----------
   入口列的排版依使用者 2026-08-27 改過的 mockup。
   🚫 `.safety-row` 是 flex 容器但**不是** `.row`——不要改成 `.row`，
   那個類別的 `min-width: 120px` 會把說明文字擠成一列一個字（§5.2 踩過五次）。 */
/* 管理看板「回我的行程」——2026-09-24 使用者實機指出它原本貼在白色卡片
   背景上，讀起來像卡片本身的一部分（「不應該在白底」）。現在是 `.card`
   外面的頁面層級元素，落在紙紋背景上；純色 `--deep` 底＋白字，不是
   ghost（透明底在淺色紙紋上太淡，「確保足夠明顯」）。跟 BoardHeader.tsx
   那段說明是同一件事的兩面。
   🔴 **選擇器一定要寫成重複類別**（`.board-back-btn.board-back-btn`，
   權重 0,2,0）。全域有一條 `@media (hover:hover) { button:hover { background:
   #fbfbfc } }`，權重 0,1,1——單一類別的 `.board-back-btn`（0,1,0）**贏不過
   它**，滑鼠移上去背景會被蓋成近白色、字仍然是白的，整顆按鈕在懸停時看不見
   （使用者實機回報）。跟 `.split-danger-del.split-danger-del` 是同一種陷阱，
   差別是那邊撞的是 `button.danger`（(0,1,1)），這裡撞的是 `button:hover`
   （同樣 (0,1,1)）——**任何自訂底色的按鈕都要檢查有沒有贏過這條全域 hover
   規則**，不只是危險色按鈕會中獎。 */
.board-back-btn.board-back-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 14px 7px 12px; border-radius: 999px; border: none;
  background: var(--deep); color: #fff; font-size: var(--text-sub); font-weight: 600;
  min-height: 0; cursor: pointer;
}
.board-back-btn.board-back-btn svg { stroke: #fff; }
.board-back-btn.board-back-btn:hover { background: var(--deep); border-color: var(--deep); filter: brightness(1.08); }

/* 看板標頭的「行程截圖」——2026-09-24 使用者實機二度指出：原本跟「希望旅伴有」
   擠在同一塊、縮圖只有 56px，讀起來像貼在旅伴條件下面的附屬品。改成獨立一節，
   跟上傳介面同一個尺寸（96px，見 `.cover-preview-item`），上下都留出呼吸空間。 */
.board-cover-sec { margin-top: 14px; }
.board-cover-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.board-cover-img { width: 96px; height: 96px; border-radius: 8px; object-fit: cover; border: 1px solid var(--line); }
/* 截圖跟底下的旅伴條件之間也要有一樣的呼吸空間，不能只有截圖自己上緣有 */
.board-trait-sec { margin-top: 16px; }

/* 看板標頭的一行事實（模式／狀態／目的地／截止日）。2026-09-24 使用者圈出
   「目的地＋截止日」原本孤零零夾在特質卡上面、位置很怪——那兩件是這趟的基本
   事實，跟模式／狀態標籤是同一層，放同一行。用 gap 管間距（不是各自 margin）。 */
.board-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  margin-top: 6px;
}
.board-meta > .small { display: inline-flex; align-items: center; gap: 3px; }

.safety-row {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 13px 0; margin-top: 4px;
  border: none; border-top: 1px solid var(--line);
  background: none; text-align: left; cursor: pointer; font: inherit; color: inherit;
}
.safety-ico { flex: 0 0 auto; color: var(--deep); display: inline-flex; }
.safety-lbl { flex: 1; min-width: 0; font-size: var(--text-body); }
.safety-sub { display: block; font-size: var(--text-sub); color: var(--muted); }
.safety-chev { flex: 0 0 auto; color: var(--deep); display: inline-flex; }

/* ---------- 行前功課清單（PrepBox.tsx） ----------
   收合列刻意跟 `.safety-row` 長得幾乎一樣：兩個都是行程卡底部的「還有一件事
   可以做」入口，同語意層級的元素要長一樣（§5.4 第 5 條）。差別只有右邊的
   箭頭會轉——那是在講「這個會就地展開」，不是「會跳到別的地方」。
   🚫 一樣不要改成 `.row`（`min-width: 120px` 會把說明文字擠爆，§5.2）。 */
.prep-box { border-top: 1px solid var(--line); margin-top: 4px; }
.prep-head {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 13px 0; border: none; background: none;
  text-align: left; cursor: pointer; font: inherit; color: inherit;
}
.prep-head-ico { flex: 0 0 auto; color: var(--deep); display: inline-flex; }
.prep-head-lbl { flex: 1; min-width: 0; font-size: var(--text-body); font-weight: 600; }
.prep-head-sub { display: block; font-size: var(--text-sub); color: var(--muted); font-weight: 400; }
.prep-head-chev {
  flex: 0 0 auto; color: var(--deep); display: inline-flex;
  transition: transform .18s ease;
}
.prep-head-chev.is-open { transform: rotate(90deg); }

.prep-body { padding: 2px 0 12px; }
.prep-group + .prep-group { margin-top: 16px; }
.prep-group-title { margin: 0 0 7px; font-size: var(--text-sub); font-weight: 700; color: var(--deep); }
.prep-list { list-style: none; margin: 0; padding: 0; }
.prep-item {
  /* ⚠️ 2026-09-22 從 `align-items: center` 改成 `flex-start`：加了打勾之後
     一列可能有兩三行（項目＋結論），置中會讓勾勾浮在中間、對不到第一行的文字。 */
  display: flex; align-items: flex-start; gap: 10px;
  padding: 7px 0; border-top: 1px solid rgba(41, 48, 42, .07);
}
/* 打勾（§6.3.7b）。用真的 checkbox 只換外觀——鍵盤與螢幕報讀器原生就處理好了。
   ⚠️ `margin-top` 是為了讓 19px 的方框對齊 13px 文字的第一行，不是隨手加的。 */
.prep-check {
  appearance: none; -webkit-appearance: none;
  flex: 0 0 auto; width: 19px; height: 19px; margin: 1px 0 0;
  border: 1.5px solid var(--muted); border-radius: 5px;
  background: var(--panel); cursor: pointer; position: relative;
}
.prep-check:checked { background: var(--deep); border-color: var(--deep); }
.prep-check:checked::after {
  content: ''; position: absolute; left: 6px; top: 2px; width: 4px; height: 9px;
  border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(43deg);
}
.prep-check:disabled { opacity: .45; cursor: default; }
/* 內容包一層：項目文字與導購連結並排（維持改版前的版面），結論／補結論鈕
   靠 `flex-basis: 100%` 自己換到下一行。 */
.prep-item-main { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; }
.prep-item.is-done .prep-item-text { color: var(--muted); }
.prep-item-text { flex: 1; min-width: 0; font-size: var(--text-sub); line-height: var(--leading-body); }
/* 一個項目可能同時篩出兩個候選（見 affiliates.ts 的 PARTNER_CANDIDATES）——
   直向堆疊在右側，跟只有一個候選時的版面完全相容（單一子元素時看起來一樣）。 */
.prep-item-links { flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
/* 連結固定不縮：`flex: 0 0 auto` 直接寫進元件自己的規則，不靠排在誰後面（§5.2 治本做法） */
.prep-item-link {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 3px;
  font-size: var(--text-sub); color: var(--deep); text-decoration: none;
  border: 1px solid var(--line); border-radius: 999px; padding: 4px 10px;
  white-space: nowrap;
}
.prep-item-tag { font-size: var(--text-tag); color: var(--muted); margin-left: 2px; }
/* 兩個以上候選時，「贊助」改成掛在整組堆疊最上面，不用每個連結各標一次
   （見 PrepBox.tsx 的說明）——這裡不需要 margin-left，它不是貼在文字後面。 */
.prep-item-tag-group { margin-left: 0; margin-bottom: 1px; }

/* 結論那一行（§6.3.7b）。🔴 **用襯線字**——那是人打的字，要跟上面系統產生的
   項目文字分得開（跟旅人證的自我介紹同一招）。底色用 --detail-surface，
   不是另外挑一個新的淺灰。 */
.prep-note {
  flex-basis: 100%; margin-top: 3px;
  background: var(--detail-surface); border: 1px solid var(--line);
  border-radius: 9px; padding: 8px 10px;
}
.prep-note-text {
  display: block; font-family: var(--font-serif);
  font-size: var(--text-sub); line-height: var(--leading-body);
}
.prep-note-by { display: block; margin-top: 4px; font-size: var(--text-tag); color: var(--muted); }
/* 就地編輯（2026-09-22 把 window.prompt 換掉，§5.2）。⚠️ textarea 要跟顯示
   態同一個襯線字與行高，不然按下「存起來」的瞬間那段字會跳一下。 */
.prep-note.is-editing { display: block; }
.prep-note-input {
  width: 100%; resize: vertical;
  font-family: var(--font-serif); font-size: var(--text-sub);
  line-height: var(--leading-body);
  border: 1px solid var(--line); border-radius: 7px;
  background: var(--panel); padding: 6px 8px;
}
.prep-note-edit-actions { display: flex; gap: 6px; margin-top: 6px; }
/* ⚠️ 全域 `button { min-height: 44px }`，而 min-height 永遠贏過 height——
   這兩顆是行內的小鈕，不歸零會把整段結論區撐成兩倍高（第八次踩到）。 */
.prep-note-edit-actions button { min-height: 0; padding: 4px 12px; width: auto; }
/* 虛線＝「還可以加點什麼」，跟站上其他可按的虛線外框同一個語彙
   （`.profile-wish-chip`／`.board-add-split-btn`）。⚠️ 全域 `button` 有
   `min-height: 44px`，而 **min-height 永遠贏過 height**——這個坑已經踩到第七次。 */
.prep-note-add {
  flex-basis: 100%; align-self: flex-start; margin-top: 4px;
  min-height: 0; padding: 3px 11px; width: auto;
  background: none; border: 1px dashed var(--muted); color: var(--muted);
  border-radius: 999px; font-size: var(--text-tag);
}
.prep-note-add:hover { color: var(--deep); border-color: var(--deep); }

/* 分組標題那一行：標題 ＋ 這是誰的 ＋ 進度。
   🔴 那顆「只有你看得到」不是裝飾——團體跟個人的清單長得一模一樣，不標的話
   使用者會以為自己的打包進度全團都看得到。 */
.prep-group-head { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; margin-bottom: 7px; }
/* ⚠️ 標題自己的 margin 交給外層那一列，不然會跟 gap 疊起來 */
.prep-group-head .prep-group-title { margin: 0; }
.prep-scope {
  display: inline-flex; align-items: center;
  font-size: var(--text-tag); font-weight: 500; color: var(--muted);
  border: 1px solid var(--line); border-radius: 999px; padding: 1px 7px;
}
/* 「＋ 自己加一項」與它長出來的輸入框（2026-10-08，§6.3.7c）。
   虛線＝還可以加點什麼，跟 .prep-note-add 同一個語彙。⚠️ min-height: 0（第 N 次）。 */
.prep-add-btn {
  margin-top: 8px; min-height: 0; padding: 4px 12px; width: auto;
  background: none; border: 1px dashed var(--muted); color: var(--muted);
  border-radius: 999px; font-size: var(--text-tag);
}
.prep-add-btn:hover { color: var(--deep); border-color: var(--deep); }
.prep-add-form { margin-top: 8px; }
.prep-add-form .prep-add-input { width: 100%; }
/* 自訂項目旁的刪除 ×：小而淡，刪之前會再確認一次 */
.prep-item-remove.prep-item-remove {
  flex: 0 0 auto; min-height: 0; width: 26px; height: 26px; padding: 0;
  border: 0; background: none; color: var(--muted); border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
}
.prep-item-remove.prep-item-remove:hover { color: var(--danger); background: var(--danger-soft); }
.prep-progress {
  margin-left: auto; font-size: var(--text-tag); font-weight: 600;
  color: var(--deep); background: var(--deep-soft);
  border-radius: 999px; padding: 1px 8px;
}
.prep-group.is-self .prep-progress { color: var(--sage); background: var(--sage-soft); }

/* 要送出去的內容原封不動先給使用者看。`white-space: pre-wrap` 是必要的：
   那段文字的換行本身就是版面，用 <br> 拼會讓前後端各有一份格式。 */
.share-preview {
  border: 1px solid var(--line); border-radius: 10px; background: #FBFAF6;
  padding: 13px 14px; font-size: var(--text-sub); line-height: var(--leading-loose); white-space: pre-wrap;
}
.share-excl { margin-top: 12px; border-radius: 10px; background: var(--deep-soft); padding: 11px 13px; }
.share-excl h4 { margin: 0 0 5px; font-size: var(--text-sub); font-weight: 700; color: var(--deep); }
.share-excl p { margin: 0; font-size: var(--text-sub); color: #3C5A4E; line-height: var(--leading-loose); }

/* 「我的行程」頁的區塊小標（我開的／我參加的）。只有兩邊都有東西時才會出現，
   所以樣式要低調——它是分隔線的角色，不是頁面標題。 */
.my-trips-sec {
  font-family: var(--font-serif); font-size: var(--text-body); font-weight: 600; color: var(--deep);
  margin: 18px 0 8px;
}
.my-trips-sec:first-of-type { margin-top: 4px; }

/* ---------- 補救建議／沒成團（RescueBox.tsx） ----------
   ⚠️ 這一區的顏色刻意都不是紅色。「還差 2 人」跟「募集已結束」都是事實不是錯誤，
   用警示色會把使用者本來沒有的焦慮製造出來（使用者 2026-08-27 明確把 mockup 上
   沒成團的灰色狀態標籤改成墨綠）。 */
.rescue {
  border-top: 1px solid var(--line); margin-top: 12px; padding-top: 12px;
}
.rescue-lead { margin: 0 0 10px; font-size: var(--text-sub); color: var(--muted); line-height: var(--leading-loose); }
.rescue-h { margin: 12px 0 4px; font-size: var(--text-sub); font-weight: 700; }
.rescue-acts { display: flex; flex-direction: column; gap: 8px; }
/* 按鈕裡有兩行（動作＋算好的金額），所以不能沿用 .sm 的單行高度 */
.rescue-act { width: 100%; text-align: left; height: auto; padding: 9px 14px; line-height: var(--leading-body); }
.rescue-hint { display: block; font-size: var(--text-sub); font-weight: 400; color: var(--muted); margin-top: 2px; }
.rescue-act.primary .rescue-hint { color: #C6D8CE; }

/* 推薦的其他團 */
.rescue-alt { display: flex; align-items: center; gap: 10px; padding: 11px 0; border-top: 1px solid var(--line); }
.rescue-alt:first-of-type { border-top: none; }
.rescue-alt-main { flex: 1; min-width: 0; }
.rescue-alt-t { font-size: var(--text-body); font-weight: 600; }
.rescue-alt-d { font-size: var(--text-sub); color: var(--muted); }
.rescue-alt button { flex: 0 0 auto; }

/* 「募集已結束」——中性的事實，不是壞消息 */
.tag.closed { color: var(--deep); background: var(--deep-soft); }

/* ---------- 整張可點的行程卡（我參加的行程 → 行程頁） ----------
   列表卡的工作只有「讓我認出這是哪一趟」跟「有沒有事要我處理」，細節在按進去
   之後才出現（使用者 2026-08-28，見 PrepBox.tsx 開頭）。所以整張卡就是那顆
   按鈕，不是卡片裡再放一顆——一張卡只做一件事的時候，不該讓人先找按鈕在哪。
   `<button>` 會繼承瀏覽器預設的置中與字級，這裡要全部歸零回卡片的外觀。 */
.trip-card-btn {
  display: block; width: 100%; text-align: left; font: inherit; color: inherit;
  cursor: pointer; position: relative; padding-right: 34px;
}
.trip-card-btn .card-head { padding-right: 0; }
.trip-card-go {
  position: absolute; right: 14px; top: 50%; transform: translateY(-50%);
  color: var(--deep); display: inline-flex;
}

/* ---------- 加入前的自動審核問答（2026-09-02，見 shared/domain/approvalQuestions.ts） ---------- */
/* 主揪端：題目編輯器（TripDialog 第 3 步，勾了「先讓我審核」才出現） */
.approval-q { margin-top: 6px; }
.approval-q-hint { font-size: var(--text-sub); color: var(--muted); margin: 0 0 10px; line-height: var(--leading-body); }

/* ---- 新增區（釘在最上面，見 ApprovalQuestionsField.tsx 的說明） ---- */
.approval-q-add { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
/* ⚠️ 實心框（對比下面常用題目的虛線框）：這兩顆是這個視窗的主要動作，
   而 `sm ghost` 沒有邊框、看起來比底下的快速帶入還弱，層級剛好相反。
   實線＝自己寫、虛線＝現成的，同一種「新增」用兩種線型分主次。 */
.approval-q-add .sm.ghost {
  flex: 1 1 0; min-width: 140px; justify-content: center;
  border: 1px solid var(--deep); color: var(--deep); background: var(--panel);
  border-radius: 999px; font-weight: 600;
}
.approval-q-add .sm.ghost:hover { background: var(--deep-soft); }
/* 常用題目：一行橫向捲動，不 wrap。做法照 .filter-bar（§5.5 第 9 點）。 */
.approval-q-presets {
  display: flex; flex-wrap: nowrap; overflow-x: auto; gap: 6px;
  scrollbar-width: none; padding-bottom: 2px;
}
.approval-q-presets::-webkit-scrollbar { display: none; }
/* ⚠️ 不准被壓縮：沒有這行 chip 會為了塞進一行而被擠扁（跟 .filter-bar > * 同一個坑）。 */
.approval-q-preset.approval-q-preset {
  flex: 0 0 auto; white-space: nowrap;
  min-height: 0; padding: 6px 11px; border-radius: 999px; font-size: var(--text-sub);
  border: 1px dashed var(--line); background: none; color: var(--muted);
}
.approval-q-preset.approval-q-preset:hover { border-color: var(--deep); color: var(--deep); background: none; }

/* ---- 已加的題目清單（往下長，不會把上面的控制項推走） ---- */
.approval-q-list { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line); }
/* 🔴 一題＝一張卡。之前題目輸入框跟它的答案鈕是兩個各自獨立的元素平鋪，
   簡答題的答案鈕換行之後看起來像是「下一題的東西」，讀不出誰屬於誰。
   把整題包成一張卡、輸入框本身去掉邊框，一題就是一個看得出來的單位。 */
.approval-q-row {
  display: flex; align-items: flex-start; gap: 6px; margin-bottom: 8px;
  border: 1px solid var(--line); border-radius: 12px; padding: 8px 8px 8px 12px;
  background: var(--panel);
}
.approval-q-main { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* ⚠️ 輸入框的邊框拿掉——外層的卡片已經是邊界了，兩層框會變成框中框。 */
.approval-q-text.approval-q-text {
  flex: 1 1 150px; min-width: 0;
  border: 0; background: none; padding: 4px 0; min-height: 0; border-radius: 0;
}
.approval-q-text.approval-q-text:focus { outline: 0; box-shadow: none; }
.approval-q-answer { flex: 0 0 auto; display: flex; gap: 4px; }
/* 「是／否」與「有填就通過／我要看過」。⚠️ 全域 `button` 有 min-height:38px／
   border-radius:9px，這裡要的是小顆的膠囊，兩項都明確覆寫。 */
.approval-q-pick.approval-q-pick {
  min-height: 0; padding: 6px 12px; border-radius: 999px; font-size: var(--text-sub); white-space: nowrap;
  border: 1px solid var(--line); background: var(--bg); color: var(--muted);
}
.approval-q-pick.approval-q-pick.on {
  background: var(--deep); border-color: var(--deep); color: #fff; font-weight: 600;
}
.approval-q-del.approval-q-del {
  flex: 0 0 auto; min-height: 0; width: 30px; height: 30px; padding: 0; margin-top: 2px;
  border-radius: 999px; border: 0; background: none; color: var(--muted);
}
.approval-q-del.approval-q-del:hover { background: var(--danger-soft); color: var(--danger); }

/* 申請人端：作答（JoinDialog）。
   ⚠️ 這裡只顯示題目，**沒有也拿不到「主揪要的答案」**——那個欄位伺服器端
   刻意不送出來（送過來等於把答案卷發下去）。 */
.join-q-row {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 0; border-top: 1px solid var(--line);
}
.join-q-row:first-of-type { border-top: 0; }
.join-q-text { flex: 1; min-width: 0; font-size: var(--text-body); }
/* 簡答題：題目一行、輸入框一行（是非題那種左右排會把輸入框擠到沒有寬度）。 */
.join-q-row-text { display: block; padding: 10px 0; }
.join-q-row-text .join-q-text { display: block; margin-bottom: 6px; }
.join-q-row-text textarea { width: 100%; }
.join-q-pick { flex: 0 0 auto; display: flex; gap: 4px; }

/* ---------- 平板／桌機的寬螢幕版面（2026-09-06） ---------- */
/*
 * 🔴 **這四條刻意都不用 media query，而且在手機上是 no-op。**
 *
 * 起因是 App Store 要求上傳 13 吋 iPad 截圖——`TARGETED_DEVICE_FAMILY` 一直是
 * Capacitor 模板的預設值 "1,2"（iPhone ＋ iPad），等於我們一直宣告支援 iPad，
 * 卻從來沒有人在 iPad 上看過它。實測 1024px 寬：所有東西都是 992px 滿版單欄，
 * 行程卡片橫跨整個螢幕、「加入」按鈕 473px 寬、底部導覽五個項目被拉得極開。
 *
 * ⚠️ 網頁版在桌機瀏覽器上**本來就是同一個畫面**（只是沒有人回報過），所以修在
 * 共用的 style.css、不是 App 專屬的複本——同一件事寫兩份正是 §11.1 那個坑。
 *
 * ⚠️ 每一條的門檻都設計成「手機根本碰不到」，所以不需要斷點也不會影響手機：
 *   - 網格的 minmax 下限 340px > 手機扣掉 padding 的可用寬 → 永遠只排得下一欄
 *   - max-width 720／560px 都比手機螢幕寬 → 等於沒有這條規則
 *   - 底部導覽單項 110px 的上限，在 375px 螢幕上每項只分到 75px → 碰不到上限
 * 🚫 不要「為了保險」把它們包進 @media (min-width: …)：多一個斷點就多一個
 * 「哪些寬度沒被測到」的縫，而這幾條本來就是照內容尺寸自己決定的。
 */

/* 瀏覽頁：寬螢幕多排幾欄。那一頁的任務是「一次看很多筆」，寬度該拿來放內容，
   不是留白。auto-fill 從 320px 到 1180px 都自己算得出欄數，不用斷點。 */
.split-feed-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  gap: 14px;
  align-items: start;
}

/* 我的行程／訊息／個人／管理看板：這幾頁是「一筆一列、每列有動作」的清單，
   拉到 1024px 寬只會讓「管理這個行程」這種按鈕橫跨整個螢幕。收在一個舒適的
   欄寬裡置中。⚠️ 720px 是站上既有的 `.wrap.narrow` 同一個數字，不是新發明的。 */
.view-column { max-width: 720px; margin-inline: auto; }

/* 底部導覽：條本身仍然滿版（背景要鋪滿），但五個項目收攏置中——不然在 iPad 上
   會變成五顆散落在螢幕兩端的圖示，看起來像壞掉。 */
.bottom-nav { justify-content: center; }
.bottom-nav-item { flex: 1 1 0; max-width: 110px; }

/* 「選擇出遊區間」在寬螢幕會變成一條 992px 的白帶，收窄成跟其他篩選控制項
   相稱的寬度。 */
.date-range { max-width: 560px; }
/* ═══ 「我的資料」的旅人證（2026-09-21，使用者：「獨立成一區」「更多元有層次」）═══

   身分那批欄位（姓名／性別／出生年／手機／LINE ID／頭貼／IG·Facebook／自我介紹）
   從一排 .field 變成一張卡。**層次是刻意堆出來的**，mockup 第一版整張白底加框線，
   使用者的評語是「很死板」——問題不是元素不夠多，是一張卡裡從頭到尾只有一種質地。

   現在由上而下換四次：深綠斜紋帶 → 咬進去的照片框 → 票根撕邊 → 白紙底線欄位
   → 淺灰聯絡方塊 → 襯線自我介紹。

   🚫 **底下那六組特質（.pick-field）一行都沒有改。**「獨立成一區」是比較出來的：
   上面這張是一張有深色帶、圓角與陰影的**卡**，底下是用細線分隔的**平鋪區塊**
   （裡面的特質卡本來就有自己的插畫，那是 §6.3.29b 的事，跟這裡無關）。
   兩種結構擺在一起，才看得出上面那張自成一區；把底下也包成卡片就白做了。
   🚫 **沒有用 --brand（陶土橘）。** 那個只給金額與截止日（§5.3）。這裡的層次全部
   靠明度與質地，不靠多加一個顏色。 */
.idcard {
  background: var(--panel); border-radius: 16px; overflow: hidden;
  margin-bottom: 14px;
  box-shadow: 0 1px 2px rgba(33, 48, 42, .05), 0 8px 22px -14px rgba(33, 48, 42, .45);
}

/* 深色帶的底下留 52px，是給照片框咬上來的空間（見 .idcard-plate 的負 margin）。
   ⚠️ 斜紋與明暗都是疊在 var(--deep) 上的黑白透明度，不是新色票——一塊純平的
   深綠在手機上看起來像沒載入完的區塊。 */
.idcard-band {
  position: relative; padding: 16px 16px 52px; color: #fff;
  background-color: var(--deep);
  background-image:
    repeating-linear-gradient(135deg, rgba(255, 255, 255, .05) 0 1px, transparent 1px 9px),
    linear-gradient(160deg, rgba(255, 255, 255, .07) 0%, rgba(0, 0, 0, .14) 100%);
}
.idcard-kicker {
  display: block; font-family: var(--font-serif); font-size: var(--text-sub);
  font-weight: 600; letter-spacing: .2em;
}
.idcard-sub { display: block; font-size: var(--text-tag); opacity: .66; margin-top: 3px; }

/* 戳章：整張卡只有它是歪的。一個刻意的不規則，畫面才不會像表格。 */
.idcard-stamp {
  position: absolute; top: 14px; right: 14px; width: 70px; height: 70px;
  border-radius: 50%; border: 1.5px dashed rgba(255, 255, 255, .5);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  transform: rotate(-9deg); text-align: center;
}
.idcard-stamp em { font-style: normal; font-size: 9px; letter-spacing: .14em; opacity: .72; }
.idcard-stamp b { font-family: var(--font-serif); font-size: 14px; font-weight: 700; line-height: 1.25; }

/* 🔴 這是整張卡唯一「兩層真的重疊」的地方，也是它的深度來源：照片框往上咬進
   深色帶 40px。⚠️ 外圈那 4px 白框是用 box-shadow 畫的（不是 border），這樣
   不會改變盒子尺寸、也不用另外算 photo 的寬高。 */
.idcard-plate {
  display: flex; align-items: flex-end; gap: 14px;
  padding: 0 16px; margin-top: -40px; position: relative;
}
.idcard-photo {
  position: relative; width: 84px; height: 100px; flex: 0 0 auto;
  border-radius: 8px; background: var(--deep-soft); overflow: visible;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-serif); font-size: var(--text-hero); color: var(--deep);
  box-shadow: 0 0 0 4px var(--panel), 0 6px 14px -8px rgba(33, 48, 42, .6);
}
.idcard-photo img { width: 100%; height: 100%; object-fit: cover; border-radius: 8px; }
/* 例圖的裁切角標。⚠️ 有頭貼時要蓋在照片上面，所以 z-index 不能省。 */
.idcard-photo::before, .idcard-photo::after {
  content: ""; position: absolute; width: 13px; height: 13px; z-index: 1;
  border: 1.5px solid var(--deep); opacity: .55;
}
.idcard-photo:has(img)::before, .idcard-photo:has(img)::after { border-color: #fff; opacity: .8; }
.idcard-photo::before { top: 6px; left: 6px; border-right: 0; border-bottom: 0; }
.idcard-photo::after { bottom: 6px; right: 6px; border-left: 0; border-top: 0; }

/* 🔴 min-height: 0 不能省——全域有一條 button { min-height: 44px }，而
   **min-height 永遠贏過 height**，這顆 22px 高的膠囊會被撐成 44px 蓋住半張照片。
   這個坑是第五次踩到（.profile-settings-btn／.avatar-btn／.trip-cover-owner .avatar／
   .filter-btn），任何自訂尺寸的小按鈕都要記得歸零。 */
.idcard-photo-btn {
  position: absolute; left: 50%; bottom: -11px; transform: translateX(-50%); z-index: 2;
  min-height: 0; padding: 2px 10px; white-space: nowrap;
  font-size: var(--text-tag); color: var(--deep);
  background: var(--panel); border: 1px solid var(--line); border-radius: 999px;
  box-shadow: 0 2px 6px -3px rgba(33, 48, 42, .4);
}
.idcard-who { min-width: 0; flex: 1 1 auto; padding-bottom: 2px; }
/* 🔴 證件上那個大大的名字**就是**姓名輸入框（見 ProfileDialog.tsx 那段註解）。
   底線在沒 focus 時很淡——它要先讀起來像一張證件上印好的名字，點下去才變成
   一個明顯的輸入框。 */
.idcard-name {
  font-family: var(--font-serif); font-size: var(--text-title); font-weight: 700;
  border: 0; border-bottom: 1px solid var(--line); border-radius: 0;
  background-color: transparent; padding: 0 0 4px;
}
/* 票根撕邊：站上既有的視覺語言（.ticket 那套鋸齒的親戚）。放在這裡是講
   「上面是證件、下面是內容」，順便讓卡片中段不會是一整片白。
   🔴 **圓點刻意用 --detail-surface，不是「卡片外面的底色」。** 撕邊本來的做法是讓
   圓點跟外面同色、看起來像真的被咬掉一口——但這張卡是包在 .card（純白）裡面的，
   同色等於整排圓點直接消失，只剩一條虛線。差一階的灰讓它讀成扇貝狀的撕口。
   ⚠️ 所以搬到別的底色上時這個顏色要重看一次，不是搬過去就對。 */
.idcard-perf {
  height: 14px; margin-top: 18px; position: relative;
  background: radial-gradient(circle at 7px 7px, var(--detail-surface) 5px, transparent 5.5px) repeat-x;
  background-size: 16px 14px;
}
.idcard-perf::before {
  content: ""; position: absolute; left: 16px; right: 16px; top: 6px; height: 1px;
  background: repeating-linear-gradient(90deg, var(--line) 0 5px, transparent 5px 10px);
}

.idcard-sheet { padding: 10px 16px 18px; }
/* 卡片裡的欄位改成底線式（例圖那種「昵稱：____」），跟外面一格一格的方框拉開差別。
   🔴 覆寫 select 的背景一定要用 background-color，不能用 background 簡寫——
   簡寫會把箭頭那張 background-image 一起重設掉，結果是那一格完全沒有箭頭而且
   沒有任何錯誤（§5.3a，2026-09-21 當場踩過一次）。 */
.idcard-sheet .field > input,
.idcard-sheet .field > select,
.idcard-sheet .field > textarea {
  border: 0; border-bottom: 1px solid var(--line); border-radius: 0;
  background-color: transparent; padding-left: 0; padding-right: 0;
}
.idcard-sheet .field > select { padding-right: 28px; background-position: right 2px center; }
.idcard-sheet .field { margin-bottom: 0; }
.idcard-sheet > .row { margin-bottom: 16px; }

/* 聯絡方式自成一塊淺底：它跟上面那兩格不是同一種東西（上面是「你是誰」，
   這裡是「怎麼找到你」），而且這三格是唯一會流出去給別人看的。 */
.idcard-contact {
  background: var(--detail-surface); border-radius: 12px; padding: 12px 14px;
  display: flex; flex-direction: column; gap: 12px;
}
.idcard-contact .field > input { border-bottom-color: #E9E7E0; }

/* 自我介紹是這張卡上唯一一段「人講的話」，用襯線跟寬行高，質地跟上面一格一格
   的資料分開。⚠️ textarea 這裡要留框（有框才看得出可以打很多字），只是底色
   換成淺一階。 */
.idcard-bio { margin-top: 16px; }
.idcard-sheet .idcard-bio textarea {
  font-family: var(--font-serif); line-height: var(--leading-loose);
  border: 1px solid var(--line); border-radius: 10px;
  background-color: var(--detail-surface); padding: 10px 12px;
}

/* 320px：只縮尺寸、不改結構。三格式的東西這裡沒有，唯一會擠的是照片與名字那排。 */
@media (max-width: 340px) {
  .idcard-plate { gap: 10px; }
  .idcard-photo { width: 72px; height: 88px; font-size: var(--text-title); }
  .idcard-stamp { width: 62px; height: 62px; }
}

/* 後台的真人驗證待審佇列（2026-09-22，只有 /admin 用得到）。
   ⚠️ 照片刻意用 `object-fit: contain` 不是 `cover`——要判斷的正是「手有沒有
   入鏡」，裁掉邊緣等於把要看的東西裁掉（跟 §6.4.11c 那條「拍照不能用會裁成
   正方形的那支縮圖函式」同一個理由）。底色給深一點，白襯衫才不會跟背景糊在一起。 */
.verify-queue { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 12px; }
.verify-item {
  display: flex; gap: 12px; align-items: flex-start;
  border: 1px solid var(--line); border-radius: 12px; padding: 12px;
  background: var(--panel);
}
.verify-shot {
  width: 132px; height: 176px; flex: 0 0 auto;
  object-fit: contain; background: #E4E6E3;
  border-radius: 8px; display: block;
}
.verify-meta { min-width: 160px; }

/* 「你可能是用 Google／Apple 註冊的」面板（2026-09-22，§7.13a）。
   🔴 **不是第二個 `<dialog>`**，是 `.auth-card` 自己的絕對定位子元素——兩個
   同時開著的 modal dialog 在這個環境會互相牽連（§5.2，跟個人卡片的「更多」
   選單同一招）。所以 `.auth-card` 要有 position。
   ⚠️ 蓋住整張卡（不是浮在角落）：它出現的時機是使用者已經撞牆了，這時候
   半遮半掩只會讓他繼續在原本那張表單上瞎試。 */
.auth-card { position: relative; }
.auth-help {
  position: absolute; inset: 0; z-index: 2;
  background: var(--panel); border-radius: inherit;
  display: flex; flex-direction: column; justify-content: center;
  gap: 10px; padding: 24px 22px; text-align: center;
}
.auth-help-title { margin: 0; font-size: var(--text-head); font-weight: 700; }
.auth-help-body {
  margin: 0; font-size: var(--text-sub); color: var(--muted);
  line-height: var(--leading-loose);
}
/* ⚠️ 直向堆疊不是並排：手機上三顆並排會各自被擠成兩行字。 */
.auth-help-actions { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
/* 網頁版的 Google 是 <a> 不是 <button>（整頁導轉，見 AuthDialogs.tsx），
   要自己補齊按鈕的外觀——`.btn` 已經有一份，這裡只補它缺的置中與行高。 */
.auth-help-actions .btn { display: flex; align-items: center; justify-content: center; text-decoration: none; }

/* 頭貼裁切視窗（2026-09-23，AvatarCropDialog.tsx）。
   🔴 **取景框是圓的**：站上每一個顯示頭貼的地方都是圓的（`.avatar` 的
   border-radius），給使用者看方框等於讓他對著一個永遠不會出現的畫面調整
   ——對齊到四個角的東西最後全看不到。存下來的仍然是正方形。
   ⚠️ 260px 這個數字跟 AvatarCropDialog 的 `VIEWPORT` **必須一致**，那支的
   幾何是拿它算的；只改一邊會讓「畫面上看到的」跟「裁出來的」對不起來，
   而且畫面上完全正常（預覽是 CSS、輸出是 canvas，兩套計算）。 */
.avatar-crop-body { align-items: center; gap: 14px; display: flex; flex-direction: column; }
.avatar-crop-view {
  width: 260px; height: 260px; border-radius: 50%; overflow: hidden;
  position: relative; background: var(--detail-surface);
  /* 🔴 不關掉的話手機上拖曳會被瀏覽器判成捲動頁面，圖片完全拖不動 */
  touch-action: none; cursor: grab; user-select: none;
  /* 圓形之外壓一圈淺色，讓「圓框外面的部分不會顯示」這件事看得出來 */
  box-shadow: 0 0 0 1px var(--line);
}
.avatar-crop-view:active { cursor: grabbing; }
/* ⚠️ 尺寸與位移由 JSX 的 inline style 給（那是算出來的），這裡只管不要被
   別的規則影響：`max-width:100%` 之類的通用圖片規則會把它壓回去。 */
.avatar-crop-view img {
  position: absolute; top: 0; left: 0; max-width: none; display: block;
  transform-origin: 0 0; pointer-events: none;
}
.avatar-crop-zoom { width: 260px; display: block; }
.avatar-crop-zoom input { width: 100%; }


/* 名額設定對話框底部的危險區（2026-09-23）——全站刪除這個名額／移除成員的
   唯一入口。使用者要求：「刪除的行為都不要直接放在明面上，要統一收在設定裡」。

   ⚠️ 用一條上框線跟上面的表單欄位斷開，並整塊退到危險色的淡底上——這一區
   做的事跟上面「改這個名額的內容」完全不同層級，看起來就不該像是再多幾個欄位。
   🚫 不要把它做成收合區：收起來就等於又藏了一層，使用者會找不到（他本來就是
   點「設定」進來找它的）。 */
.split-danger {
  margin-top: 18px; padding: 12px; border-radius: 10px;
  background: var(--danger-soft); border: 1px solid rgba(168, 73, 61, .18);
  display: flex; flex-direction: column; gap: 10px;
}
.split-danger-title { font-size: var(--text-sub); font-weight: 700; color: var(--danger); }
.split-danger-members { display: flex; flex-direction: column; gap: 6px; }
.split-danger-member {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  background: var(--panel); border-radius: 8px; padding: 6px 6px 6px 10px;
}
/* 刪整個名額會連同底下所有報名一起消失，所以它是這一區唯一的實心危險按鈕
   ——範圍差多少用份量講，不是靠使用者讀完文案才發現。 */
/* 🔴 **選擇器一定要寫成重複類別**（`.split-danger-del.split-danger-del`，
   權重 0,2,0）。全域有一條 `button.danger { color: var(--danger) }`，權重
   0,1,1——單一類別的 `.split-danger-del`（0,1,0）**贏不過它**，結果是
   深紅底配深紅字，按鈕上的字整個看不見，而且測試全綠（jsdom 不算樣式）。
   實機截圖才抓到。跟 §5.2 那條 `.row > *` 的陷阱是同一種東西。 */
.split-danger-del.split-danger-del {
  align-self: flex-start;
  background: var(--danger); border-color: var(--danger); color: #fff;
}
.split-danger-del.split-danger-del:hover { filter: brightness(1.06); }

/* 跟團看板的「＋ 加更多（選填）」（2026-09-24）：跟分頁同一排，但它不是一個分頁，
   是打開其他分頁的開關——所以不畫底線、字色退一階。
   ⚠️ 外層多包一層 `.kind-more-wrap`（2026-09-24 稍晚）：按下去不再直接把剩下
   的分頁全部攤開，是先彈出一個小選單問「還想找什麼」，勾完按「加入」才真的
   加進來（使用者要求）。`.kind-tabs-row` 的子元素本來就靠 `flex: 1 1 0` 平分
   寬度，這層 wrap 要接手同一份 flex 設定，選單本身才有 `position: relative`
   的祖先可以錨定，不會跑到頁面左上角去。 */
.kind-more-wrap { position: relative; flex: 1 1 0; min-width: 0; }
.kind-more-wrap .kind-tab-more { width: 100%; }
.kind-tab.kind-tab-more, .kind-tab.kind-tab-more:hover { color: var(--muted); font-weight: 500; border-bottom-color: transparent; }

/* 「還想找什麼？」選單——跟 `.profile-more-menu` 同一招：不是第二個
   `<dialog>`，是觸發它的按鈕自己的絕對定位子元素（見 BoardCards.tsx 的
   說明）。`right: 0` 貼齊分頁列右緣，不是貼齊這顆窄窄的分頁本身寬度——
   「加更多」通常是最右邊那個分頁，選單往左展開才不會被卡片邊緣切掉。 */
.kind-more-menu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 4;
  min-width: 176px; padding: 10px 12px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: var(--shadow);
}
/* 🔴 `<label>` 上一定要疊一份 `.checkbox`（全站既有的 label+checkbox 排版，
   `.checkbox input { width: auto }`）——單獨一個 `.kind-more-item` 沒有覆寫
   `input` 的寬度，checkbox 會吃到全域 `input, select, textarea { width: 100% }`，
   被拉成一條看不見的長條，把圖示跟文字擠到自己換行（實機截圖抓到過一次）。
   這裡只補 `.checkbox` 沒有的：字級跟滑鼠游標。 */
.kind-more-item { font-size: var(--text-sub); cursor: pointer; }
.kind-more-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 8px; }

/* ---------- 評價的快速標籤（ReviewDialog.tsx，2026-09-30） ----------
   三欄等寬、六個剛好兩排：一眼掃得完。選中用墨綠（§5.3）。
   ⚠️ min-height: 0——全域 button 有 min-height: 44px（§6.4.0a 那個坑）。 */
.review-tags { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.review-tag.review-tag {
  min-height: 0; padding: 9px 4px;
  border: 1px solid var(--line); border-radius: 9px; background: var(--panel);
  color: var(--muted); font-size: var(--text-sub); font-weight: 600; line-height: var(--leading-tight);
  justify-content: center; text-align: center;
}
.review-tag.review-tag.on { border-color: var(--deep); background: var(--deep-soft); color: var(--deep); }
/* 個人卡片上的正面評價標籤次數（ReviewTagChips.tsx） */
.review-tag-count b { margin-left: 4px; font-weight: 700; color: var(--deep); }

/* 自動轉成的連結（Linkify.tsx）：顏色跟著所在的文字走（自己的聊天泡泡是白字），
   用底線表示可以點，不用另一種顏色——深綠泡泡上的連結色會看不見。
   break-all 是因為長網址沒有空白可以斷行，不斷會撐破泡泡。 */
.auto-link { color: inherit; text-decoration: underline; text-underline-offset: 2px; word-break: break-all; }

/* 真人驗證已通過（2026-10-08）：只有標章，沒有任何說明或按鈕。底下是白卡，所以用
   淺墨綠底＋墨綠字（主視覺上那顆是深色玻璃，因為底下是照片）。 */
.verify-approved {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 12px; border-radius: 999px;
  background: var(--deep-soft); color: var(--deep);
  font-size: var(--text-sub); font-weight: 600;
}
