/* ═══════════════════════════════════════════════════════════════
   不動產管理系統 — Premium Design System v3
   Warm Editorial — Cream + Gold + Serif Brand Aesthetic
   ═══════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════
   CASCADE LAYERS — 「誰贏」的唯一權威（2026-08-11，issue #15）
   ═══════════════════════════════════════════════════════════════

   ## 為什麼有這三層

   本檔 3300+ 行、117 個 selector 出現 ≥2 次，主要構成方式一直是「檔尾後寫贏」。
   但那句話是**錯的**：CSS cascade 先比 specificity、同分才比原始碼順序。兩次
   事故都出在這個誤解上（docs/DIAGNOSIS.md 踩坑記錄）：

     2026-07-14  手機 16px 防縮放 reset 寫在檔尾 media query 裡，仍被前段的
                 `.form-group input`（0,1,1 > 0,0,1）壓過，實測還是 14px。
     2026-07-17  誤刪「看起來已被檔尾覆蓋」的 `.home-row-dots`，結果檔尾區塊
                 根本沒有同名規則，清單卡片跑出三顆狀態圓點爆版。

   cascade layer 的規則是：**先比層，再比 specificity**。後面的層贏前面的層，
   跟 specificity 無關、跟行號無關。所以「手機覆寫寫在最後就一定贏」這句話，
   在導入分層之後才第一次成為真的。

   ## 三層各是什麼（順序即優先序，後面贏）

     @layer base        設計 token（:root）、reset、字體排版、圖示基礎。
                        只放「全站無條件成立」的東西，不放任何元件樣式。
     @layer components  所有元件與畫面樣式（桌機為主）。日常改樣式九成在這層。
     @layer mobile      MOBILE APP SHELL：手機 App 質感改造專用區塊。

   ## 動筆前必讀的三條紀律

   1. **手機覆寫一律寫進 `@layer mobile`**，不准插在 components 層裡新開
      `@media (max-width: …)`。components 層裡現存的 6 個手機 media query 是
      分層之前的歷史遺留，已被守護 #23 鎖住數量，只准減不准增。
   2. **要覆寫誰，就在 mobile 層寫同一個 selector**——不用再抄對方的組合寫法
      來湊 specificity（那是分層前的權宜之計）。層級本身就保證你贏。
   3. **刪 CSS 之前**：先確認「覆蓋它的規則」真的存在同名 selector，不是靠
      「反正後面那段會蓋掉」的直覺（07-17 事故）。`npm run guards` 的 #22 會把
      每個 selector 的全部行號印出來，刪之前先看那份清單。

   ## 兩個必須知道的陷阱

   - **`!important` 會把層序整個反過來**：important 宣告是早的層贏晚的層。
     本檔目前只有一個 `!important`（.copy-card .card-body 的 margin-top），
     mobile 層沒有任何規則碰 .card-body/.copy-card，所以沒有反轉風險。
     要新增 `!important` 之前，先確認沒有跨層的同名同屬性宣告。
   - **層外的規則永遠贏過所有層**：沒被包進 @layer 的規則屬於「無名層」，
     優先序最高。所以本檔不允許任何規則落在三個 @layer 區塊之外，
     守護 #23 會擋。

   ## 瀏覽器支援（唯一的殘留風險，要知道）

   `@layer` 需要 Chrome 99+／Safari 15.4+（iOS 15.4，2022 年 3 月）／Firefox 97+。
   不支援的瀏覽器**會把整個 @layer 區塊當成不認得的 at-rule 整段丟掉**，也就是
   全站零樣式，不是「部分失效」。可以跑 iOS 15.4 的最舊機型是 iPhone 6s。
   業主與同事的機型都遠新於此，但如果哪天有人回報「畫面完全沒有樣式、像純 HTML」，
   第一個要問的是「你的手機系統版本多舊」，不要去查 CSS 內容。

   ## 這次分層沒有改變任何一個像素

   轉換方式刻意是「只插入包裝行、不重排、不縮排」，並用兩道驗證確認等價：
   (a) 12 個畫面 × 手機/桌機兩種視窗，逐一比對每個元素、每個 CSS 屬性的
       getComputedStyle 快照（含 ::before/::after），零差異；
   (b) 同樣 12 個畫面 × 兩種視窗的截圖逐張像素比對，零差異。
   ═══════════════════════════════════════════════════════════════ */
@layer base, components, mobile;

/* ═══ @layer base ═══ 設計 token／reset／字體排版（全站無條件成立） ═══ */
@layer base {

/* ── Design Tokens ── */
:root {
  /* Color System — Warm Neutral Foundation + Gold Accent */
  --primary:     #2b241b;
  --primary-l:   #4a4032;
  --primary-ll:  #6b5f4d;
  --accent:      #b5904a;
  --accent-l:    #d2af6e;
  --accent-d:    #8f6f33;
  --accent-glow: rgba(181, 144, 74, .18);

  /* Surfaces */
  --bg:          #fbf8f2;
  --bg-elevated: #f5ead8;
  --card:        #fffdf8;
  --card-hover:  #ffffff;

  /* Text */
  --text:        #2b241b;
  --text-secondary: #5b5142;
  --text-muted:  #8a7d68;
  --text-inverse:#fffdf9;

  /* Borders & Shadows */
  --border:      #ecdfc4;
  --border-light:#f1e6cf;
  --shadow-sm:   0 1px 3px rgba(60,45,10,.05);
  --shadow:      0 2px 10px rgba(60,45,10,.07);
  --shadow-md:   0 8px 20px rgba(60,45,10,.09);
  --shadow-lg:   0 16px 32px rgba(60,45,10,.1);
  --shadow-xl:   0 24px 40px rgba(60,45,10,.12);

  /* Semantic Colors */
  --success:     #2f9e63;
  --success-bg:  #eaf6ef;
  --success-border: #bfe6cf;
  --warning:     #b54708;
  --warning-bg:  #fef3e8;
  --warning-border: #f5d2ab;
  --danger:      #b3433a;
  --danger-bg:   #fbeeec;
  --danger-border: #ecc6c1;
  --info:        #6b5f4d;
  --info-bg:     #f1ead9;
  --info-border: #e3d7bd;

  /* Layout */
  --radius:      12px;
  --radius-sm:   10px;
  --radius-lg:   14px;
  --sidebar-w:   250px;

  /* Transitions */
  --ease:        cubic-bezier(.4, 0, .2, 1);
  --ease-spring: cubic-bezier(.175, .885, .32, 1.275);
  --duration:    .2s;
  --duration-lg: .35s;
}

/* ── Reset ── */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* ── Lucide SVG icons ── */
.lucide {
  display: inline-flex;
  flex-shrink: 0;
  vertical-align: middle;
  stroke-width: 1.5;
}
button .lucide, a .lucide { pointer-events: none }

/* ── Typography ── */
body {
  /* 內文用系統內建字型（2026-08-11 業主拍板「用主流做法」）。iOS 是蘋方
     PingFang TC、Android 是思源黑體、Windows 是微軟正黑——都是各平台為中文
     調校過的高品質字型,而且**零下載**。實測蝦皮與樂屋網都是這個做法(信義
     房屋才下載 Noto Sans TC)。標題保留 Noto Serif TC,那是品牌識別。
     省下約 250 KB 首次載入;廣告頁的買家沒有快取,那邊的收益更大(另一張單子)。
     'Noto Sans TC' 留在最後當萬一沒有系統中文字型時的後備。 */
  font-family: -apple-system, BlinkMacSystemFont, 'PingFang TC', 'Noto Sans TC', system-ui, 'Segoe UI', sans-serif;
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
  font-size: 15px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, .page-title, .prop-name, .prop-price, .sidebar-title {
  font-family: 'Noto Serif TC', 'Inter', system-ui, sans-serif;
}

}

/* ═══ @layer components ═══ 所有元件與畫面樣式（桌機為主）═══ */
@layer components {
/* ═══════════════════════════════════════════════════════════════
   MOBILE TOP BAR
   ═══════════════════════════════════════════════════════════════ */
.mobile-header {
  display: none;
  background: var(--card);
  color: var(--text);
  padding: 0 20px;
  height: 56px;
  align-items: center;
  gap: 12px;
  position: sticky;
  top: 0;
  z-index: 200;
  border-bottom: 1px solid var(--border);
  box-shadow: 0 1px 8px rgba(43,36,26,.04);
}
.mobile-header .nav-logo {
  font-size: 16px;
  line-height: 1;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: 'Noto Serif TC', sans-serif;
}
.mobile-header .nav-title {
  font-family: 'Noto Serif TC', sans-serif;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: .02em;
}

/* ═══════════════════════════════════════════════════════════════
   LAYOUT
   ═══════════════════════════════════════════════════════════════ */
.layout {
  display: flex;
  min-height: 100vh;
}

/* 登入頁：隱藏導航 */
body.auth-page .sidebar,
body.auth-page .bottom-nav,
body.auth-page .mobile-header {
  display: none;
}

/* 開機防閃現：index.html 的 body 預設帶 booting class，JS 第一次 Router.resolve()
   判斷完登入狀態後才移除。沒有這層 gate，殼層（側欄/頂欄/底部導覽）寫死在靜態
   HTML 裡，未登入開站也會先閃出後台框架再跳登入頁（2026-07-14 使用者回報）。
   用 visibility 不用 display，避免亮相瞬間版面跳動。 */
body.booting .layout,
body.booting .sidebar,
body.booting .bottom-nav,
body.booting .mobile-header {
  visibility: hidden;
}

/* ═══════════════════════════════════════════════════════════════
   SIDEBAR — Warm Editorial
   ═══════════════════════════════════════════════════════════════ */
.sidebar {
  width: var(--sidebar-w);
  background: var(--card);
  color: var(--text);
  display: flex;
  flex-direction: column;
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  z-index: 100;
  overflow: hidden;
  border-right: 1px solid var(--border);
}

/* Brand Area */
.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 14px 18px 20px;
  cursor: pointer;
  user-select: none;
  transition: background var(--duration) var(--ease);
  position: relative;
}
.sidebar-brand:hover { background: var(--bg-elevated) }
.sidebar-logo {
  font-size: 15px;
  line-height: 1;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: 'Noto Serif TC', sans-serif;
  font-weight: 700;
  flex-shrink: 0;
  box-shadow: 0 2px 6px rgba(60,45,10,.18);
}
.sidebar-brand-text { flex: 1; min-width: 0 }
.sidebar-title {
  font-size: 16px;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: .03em;
  color: var(--text);
}
.sidebar-sub {
  font-size: 10.5px;
  color: var(--text-muted);
  margin-top: 3px;
  letter-spacing: .12em;
}

/* Search + 新增物件（同一排，新增是清單的工具列動作，放這裡比放最上面的收合列更貼近情境） */
.sidebar-search-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 16px 14px;
  border-bottom: 1px solid var(--border);
}
.sidebar-search {
  flex: 1;
  min-width: 0;
}
.sidebar-search input {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: #fff;
  color: var(--text);
  font-size: 13px;
  font-family: inherit;
  transition: all var(--duration) var(--ease);
}
.sidebar-search input::placeholder { color: var(--text-muted) }
.sidebar-search input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}

/* Status filter nav */
.sidebar-filters {
  padding: 14px 12px 4px;
}
.sidebar-filter-label {
  font-size: 10.5px;
  color: var(--text-muted);
  letter-spacing: .12em;
  text-transform: uppercase;
  margin: 0 8px 6px;
}
.sidebar-filter-item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
  transition: background var(--duration) var(--ease);
  margin-bottom: 1px;
}
.sidebar-filter-item:hover { background: var(--bg-elevated) }
.sidebar-filter-item.active {
  background: var(--bg-elevated);
  color: var(--accent-d);
  font-weight: 600;
}
.sidebar-filter-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--text-muted);
  flex-shrink: 0;
}
.sidebar-filter-dot.dot-all   { background: var(--accent) }
.sidebar-filter-dot.dot-live  { background: var(--success) }
.sidebar-filter-dot.dot-draft { background: #c9b896 }
.sidebar-filter-dot.dot-others{ background: #a39a8a }
.sidebar-filter-dot.dot-warn  { background: var(--warning) }
.sidebar-filter-label-text { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.sidebar-filter-count {
  font-size: 10.5px;
  color: var(--text-muted);
  background: var(--bg-elevated);
  padding: 2px 8px;
  border-radius: 999px;
  font-family: 'Inter', sans-serif;
}
.sidebar-filter-item.active .sidebar-filter-count {
  background: var(--accent);
  color: #fff;
}
.sidebar-filter-item.warn-item { color: var(--warning) }
.sidebar-filter-item.warn-item .sidebar-filter-count {
  background: var(--warning-bg);
  color: var(--warning);
}

/* Nav List */
.sidebar-nav {
  flex: 1;
  overflow-y: auto;
  padding: 6px 8px 10px;
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}
.sidebar-nav-label {
  font-size: 10.5px;
  color: var(--text-muted);
  letter-spacing: .12em;
  text-transform: uppercase;
  margin: 10px 12px 6px;
}
.sidebar-item {
  padding: 10px 12px;
  cursor: pointer;
  border-radius: var(--radius-sm);
  border-left: 2px solid transparent;
  margin-bottom: 2px;
  transition: all var(--duration) var(--ease);
}
.sidebar-item:hover {
  background: var(--bg-elevated);
}
.sidebar-item.active {
  background: var(--bg-elevated);
  border-left-color: var(--accent);
}
.sidebar-item-name {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.35;
}
.sidebar-item-sub {
  font-size: 11.5px;
  color: var(--text-muted);
  margin-top: 2px;
}
.sidebar-empty, .sidebar-loading {
  padding: 24px 16px;
  font-size: 13px;
  color: var(--text-muted);
  text-align: center;
}

/* Footer */
.sidebar-footer {
  padding: 14px 16px;
  border-top: 1px solid var(--border);
}
.sidebar-agent-btn {
  display: block;
  width: 100%;
  padding: 8px 12px;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: 12.5px;
  font-family: inherit;
  cursor: pointer;
  text-align: center;
  transition: all var(--duration) var(--ease);
}
.sidebar-agent-btn:hover {
  background: var(--bg-elevated);
  border-color: var(--accent);
  color: var(--accent-d);
}

/* 「更多」選單：代理人設定／帳號管理／登出收進這個下拉，避免側邊欄塞滿次要按鈕。
   觸發鈕做成使用者資訊列（頭像＋姓名＋角色），參考一般 SaaS 工具左下角帳號選單的慣例。 */
.sidebar-more {
  position: relative;
}
.sidebar-profile-trigger {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: inherit;
  cursor: pointer;
  text-align: left;
  transition: all var(--duration) var(--ease);
}
.sidebar-profile-trigger:hover,
.sidebar-more.open .sidebar-profile-trigger {
  background: var(--bg-elevated);
  border-color: var(--accent);
}
.sidebar-profile-avatar {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-size: 13px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 6px rgba(60,45,10,.18);
}
.sidebar-profile-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  line-height: 1.3;
}
/* 「我的資料」頁頭像（2026-08-08 LINE ID／頭像欄位）：跟 .sidebar-profile-avatar
   同一套視覺語言（圓形、accent 底色、姓氏縮寫佔位），放大給編輯頁用；
   有頭像時裡面塞 <img>，沒有時裡面直接放姓氏文字，共用同一顆圓框。 */
.profile-avatar-lg {
  flex-shrink: 0;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--accent);
  color: #fff;
  font-size: 24px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 6px rgba(60,45,10,.18);
}
.profile-avatar-lg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.sidebar-profile-name {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sidebar-profile-role {
  font-size: 10.5px;
  color: var(--text-muted);
}
.sidebar-profile-caret {
  flex-shrink: 0;
  font-size: 10px;
  color: var(--text-muted);
  transition: transform var(--duration) var(--ease);
}
.sidebar-more.open .sidebar-profile-caret {
  transform: rotate(180deg);
}
.sidebar-more-menu {
  display: none;
  position: fixed;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
  overflow: hidden;
  z-index: 200;
  padding: 4px;
}
.sidebar-more.open .sidebar-more-menu {
  display: block;
}
.sidebar-more-menu button {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 10px;
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: 12.5px;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition: all var(--duration) var(--ease);
}
.sidebar-more-menu button .mi {
  width: 16px;
  text-align: center;
  flex-shrink: 0;
}
.sidebar-more-menu button:hover {
  background: var(--bg-elevated);
  color: var(--accent-d);
}

/* Collapse toggle — lives inside .sidebar-brand's own row now (right-aligned
   via .sidebar-brand-text's flex:1), so it never needs to bleed past the
   sidebar's border (sidebar has overflow:hidden). */
.sidebar-collapse-btn {
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  border-radius: 50%;
  font-size: 14px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all var(--duration) var(--ease);
  background: var(--card);
  border: 1px solid var(--border);
  color: var(--text-muted);
}
.sidebar-collapse-btn:hover { background: var(--bg-elevated); color: var(--accent-d) }

body.sidebar-collapsed .sidebar { width: 64px }
body.sidebar-collapsed .sidebar-collapse-btn { transform: rotate(180deg) }
body.sidebar-collapsed .sidebar-brand {
  justify-content: center;
  padding: 14px 0 10px;
}
body.sidebar-collapsed .sidebar-logo,
body.sidebar-collapsed .sidebar-brand-text,
body.sidebar-collapsed .sidebar-search,
body.sidebar-collapsed .sidebar-filters,
body.sidebar-collapsed .sidebar-nav,
body.sidebar-collapsed .sidebar-footer .lbl {
  display: none;
}
body.sidebar-collapsed .sidebar-search-row { display: none }
body.sidebar-collapsed .sidebar-footer { padding: 14px 10px; margin-top: auto }
body.sidebar-collapsed .sidebar-agent-btn {
  padding: 8px 0;
}
body.sidebar-collapsed .sidebar-profile-trigger {
  justify-content: center;
  padding: 6px;
  gap: 0;
}
body.sidebar-collapsed .main-wrap { margin-left: 64px }

/* ═══════════════════════════════════════════════════════════════
   MAIN CONTENT
   ═══════════════════════════════════════════════════════════════ */
.main-wrap {
  margin-left: var(--sidebar-w);
  flex: 1;
  min-width: 0;
}
.app-body {
  max-width: 900px;
  margin: 0 auto;
  padding: 36px 28px;
}
@media (min-width: 1200px) {
  .app-body { max-width: 1100px }
}
@media (min-width: 1600px) {
  .app-body { max-width: 1320px }
}
@media (min-width: 1920px) {
  .app-body { max-width: 1480px }
}

/* ═══════════════════════════════════════════════════════════════
   BOTTOM NAV (Mobile)
   ═══════════════════════════════════════════════════════════════ */
.bottom-nav {
  display: none;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 62px;
  background: rgba(255,253,249,.94);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-top: 1px solid var(--border);
  z-index: 200;
  justify-content: space-around;
  align-items: center;
  box-shadow: 0 -2px 12px rgba(43,36,26,.06);
}
.bottom-nav-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  /* flex:1 而非固定 padding：2026-07-11 加了「更多」鍵，五顆按鈕在 375px 窄
     螢幕下用原本 6px 28px 的固定 padding 會超出可視寬度（390px 內容 vs 369px
     可用寬度），改用等分寬度讓 3～5 顆按鈕都能剛好撐滿、不溢出 */
  flex: 1 1 0;
  min-width: 0;
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px 4px;
  color: var(--text-muted);
  font-family: inherit;
  transition: color var(--duration) var(--ease);
}
.bottom-nav-item.active { color: var(--accent-d) }
.bnav-icon { display: flex; align-items: center; justify-content: center; height: 24px }
.bnav-label { font-size: 10px; font-weight: 700; letter-spacing: .02em }

/* 手機版「更多」抽屜（客戶管理／學區查詢／封存物件／我的資料／帳號管理／組織
   管理，桌機版側邊欄有但手機版原本沒有入口，2026-07-11 補上）。base 一律
   display:none，只有下面 max-width:768px 的 media query 裡才會被打開，跟
   .bottom-nav 本身的隱藏/顯示模式一致，桌機版永遠碰不到。 */
.bnav-more-overlay {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  /* 停在 .bottom-nav（62px 高）上緣，不要蓋住整條底部導覽列——蓋住的話
     遮罩會攔截點擊，導致「再點一次更多鍵」關閉抽屜、或點其他底部導覽按鈕
     都點不到（實測發現的問題，2026-07-11） */
  bottom: calc(62px + env(safe-area-inset-bottom, 0px));
  background: rgba(20, 16, 8, .32);
  z-index: 210;
  opacity: 0;
  transition: opacity var(--duration-lg) var(--ease);
  /* 疊層本身不需要回應任何原生手勢（捲動/縮放），沒有這行的話 iOS Safari
     的觸控拖曳還是會透到背後把清單捲動掉，光靠 body 的 scroll lock 不夠
     （2026-07-11 使用者實機回報） */
  touch-action: none;
}
.bnav-more-sheet {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  /* 同上：停在底部導覽列上緣，底部導覽（含「更多」鍵本身）全程保持可見可點 */
  bottom: calc(62px + env(safe-area-inset-bottom, 0px));
  z-index: 220;
  background: var(--card);
  border-top: 1px solid var(--border);
  border-radius: 16px 16px 0 0;
  box-shadow: 0 -8px 24px rgba(0,0,0,.16);
  padding: 10px 10px 14px;  /* 錨點已含安全區，內距不再補償（2026-07-17） */
  transform: translateY(100%);
  transition: transform var(--duration-lg) var(--ease);
  touch-action: none;
}
.bnav-more-sheet-handle {
  width: 36px;
  height: 4px;
  border-radius: 2px;
  background: var(--border);
  margin: 6px auto 10px;
}
.bnav-more-sheet button {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 13px 12px;
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition: background var(--duration) var(--ease), color var(--duration) var(--ease);
}
.bnav-more-sheet button .mi {
  width: 20px;
  text-align: center;
  flex-shrink: 0;
  color: var(--accent-d);
}
.bnav-more-sheet button:active {
  background: var(--bg-elevated);
}
.bnav-more-version {
  text-align: center;
  font-size: 10.5px;
  letter-spacing: .12em;
  color: var(--text-muted);
  opacity: .7;
  padding: 10px 0 2px;
  user-select: none;
}

/* 搜尋 sheet（P2）：完整樣式在 Mobile App Shell 區塊（@media max-width:768px），
   但元素常駐 DOM，桌機也會渲染——基礎 display:none 必須寫在 media query 外，
   否則 >768px 時零樣式、裸內容會露出在頁尾（2026-07-15 staging 驗收抓到）。
   開合由 body.bnav-search-open 控制，該組規則只在手機 media query 內，
   桌機即使誤加 body class 也維持隱藏。 */
.bnav-search-overlay,
.bnav-search-sheet {
  display: none;
}

/* ═══════════════════════════════════════════════════════════════
   RESPONSIVE
   ═══════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {
  .sidebar { display: none }
  .main-wrap { margin-left: 0 }
  .mobile-header { display: flex }
  .bottom-nav { display: flex }
  .app-body { padding: 20px 16px 80px }
  .toast { bottom: calc(74px + env(safe-area-inset-bottom, 0px)) }
  .tabs { overflow-x: auto; scrollbar-width: none }
  .tabs::-webkit-scrollbar { display: none }
  .edit-grid { grid-template-columns: 1fr }

  /* 「更多」抽屜開合狀態：body class 由 Sidebar.toggleMobileMoreMenu() 控制
     （frontend/static/js/core/sidebar.js），只在手機寬度生效 */
  body.bnav-more-open .bnav-more-overlay,
  body.bnav-more-open .bnav-more-sheet {
    display: block;
  }
  body.bnav-more-open .bnav-more-overlay {
    opacity: 1;
  }
  body.bnav-more-open .bnav-more-sheet {
    transform: translateY(0);
  }
}

/* ═══════════════════════════════════════════════════════════════
   CARDS
   ═══════════════════════════════════════════════════════════════ */
.card {
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 26px 28px;
  border: 1px solid var(--border);
  transition: box-shadow var(--duration-lg) var(--ease),
              transform var(--duration-lg) var(--ease);
}
.card + .card { margin-top: 16px }

/* ═══════════════════════════════════════════════════════════════
   PROPERTY GRID — Landing Cards
   ═══════════════════════════════════════════════════════════════ */
/* 2026-08 桶二死碼清理：.prop-grid/.prop-card/.prop-cover/.prop-body 舊物件卡
   整套（P5「取代原本的卡片牆」改用下面 .home-row 之前的版本）在 JS／HTML
   全 repo 零引用，逐一 grep 確認後刪除（2026-07-19 體檢 2.2-M1）。 */

/* ── 物件總覽（首頁）：統計列 + 緊湊清單，取代原本的卡片牆 ── */
.home-head { display:flex; justify-content:space-between; align-items:flex-end; gap:12px; flex-wrap:wrap; margin-bottom:22px }
.home-sub { font-size:13px; color:var(--text-muted); margin-top:4px }
.home-stats { display:grid; grid-template-columns:repeat(6,1fr); gap:14px; margin-bottom:24px }
.home-stat {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 16px 18px;
  cursor: pointer;
  transition: all var(--duration) var(--ease);
}
.home-stat:hover { border-color: var(--accent-l); box-shadow: var(--shadow) }
.home-stat.active { border-color: var(--accent); background: var(--accent-glow) }
.home-stat-n { font-family:'Noto Serif TC',serif; font-size:26px; font-weight:700; color:var(--text) }
.home-stat.active .home-stat-n { color: var(--accent-d) }
.home-stat-l { font-size:12px; color:var(--text-muted); margin-top:2px }
/* 手機首頁重排「方案 A」（docs/DESIGN-LANGUAGE.md，2026-07-16）：.m-home-*
   桌機預設隱藏，比照 .mobile-header/.bottom-nav 的既有慣例（style.css:102/567）
   ——基礎規則 display:none，手機版顯示規則寫在檔尾 Mobile App Shell 區塊，
   那裡同時把 .home-head/.home-stats 隱藏成手機不顯示。桌機這裡零改動。 */
.m-home-head, .m-home-chips { display: none; }
.home-rows {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.home-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 18px;
  border-bottom: 1px solid var(--border-light);
  cursor: pointer;
  transition: background var(--duration) var(--ease);
}
.home-row:last-child { border-bottom: none }
.home-row:hover { background: var(--bg-elevated) }
.home-row-thumb {
  width: 46px; height: 46px; border-radius: 10px; background: var(--bg-elevated);
  flex-shrink: 0; overflow: hidden;
}
.home-row-thumb img { width:100%; height:100%; object-fit:cover; display:block }
.home-row-info { flex:1; min-width:0 }
.home-row-name { font-size:14px; font-weight:700; color:var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
.home-row-sub { font-size:12px; color:var(--text-muted); margin-top:2px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
.home-row-price { font-family:'Noto Serif TC',serif; font-size:14.5px; font-weight:700; color:var(--accent-d); width:90px; text-align:right; flex-shrink:0 }
.home-row-status { width:80px; text-align:center; flex-shrink:0 }
.home-row-dots { display:flex; gap:5px; width:50px; justify-content:flex-end; flex-shrink:0 }
.home-row-dot { width:7px; height:7px; border-radius:50%; background:var(--border) }
.home-row-dot.ok { background: var(--success) }
.home-row-dot.bad { background: var(--danger) }
/* 批次4清理（design-audit.md dashboard 節）：這裡原本有一段舊「清單縮小版」
   @media(max-width:768px) 規則（.home-stats/.home-row-sub/.home-row-dots/
   .home-row-price/.home-row-status/.home-row-name），對應的元素現在全部被
   檔尾 P1／P3 區塊（Mobile App Shell）用同 specificity、後寫贏的規則整批
   覆蓋或直接 display:none（.home-stats 手機恆為 none），是死碼，故移除；
   桌機規則（808-838 行，2026-08-05 體檢校正行號）不受影響。 */

.prop-name {
  font-weight: 700;
  font-size: 16px;
  color: var(--text);
  margin-bottom: 3px;
  line-height: 1.35;
  letter-spacing: .01em;
}
.prop-meta {
  font-size: 12.5px;
  color: var(--text-muted);
  margin-bottom: 2px;
}
.prop-price {
  font-family: 'Noto Serif TC', sans-serif;
  font-size: 21px;
  font-weight: 700;
  color: var(--accent-d);
  margin: 10px 0 2px;
  letter-spacing: 0;
}

/* ═══════════════════════════════════════════════════════════════
   BADGES
   ═══════════════════════════════════════════════════════════════ */
.badges { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px }
.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11.5px;
  font-weight: 600;
  padding: 3px 10px;
  border-radius: 99px;
  letter-spacing: .01em;
  border: 1px solid transparent;
}
.badge-ok {
  background: var(--success-bg);
  color: var(--success);
  border-color: var(--success-border);
}
.badge-miss {
  background: var(--danger-bg);
  color: var(--danger);
  border-color: var(--danger-border);
}
.badge-info {
  background: var(--info-bg);
  color: var(--info);
  border-color: var(--info-border);
}
/* .badge-warn 刪除（2026-08 桶二死碼清理，2026-07-19 體檢 2.2-M1）：
   format.js 的狀態映射不產生 warn，JS/HTML 全 repo 零引用。 */
.badge-sold {
  background: #efebe3;
  color: var(--text-secondary);
  border-color: var(--border);
}

/* Status ribbon badges (property lifecycle) */
.badge-draft  { background: #fff; color: var(--text-muted); border-color: var(--border) }
.badge-live   { background: var(--accent); color: #fff; border-color: var(--accent) }
.badge-archived { background: #efebe3; color: var(--text-muted); border-color: var(--border) }

/* ═══════════════════════════════════════════════════════════════
   BACK BUTTON
   ═══════════════════════════════════════════════════════════════ */
.back-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--text-muted);
  cursor: pointer;
  margin-bottom: 20px;
  padding: 6px 2px;
  background: none;
  border: none;
  font-family: inherit;
  transition: color var(--duration) var(--ease);
}
.back-btn:hover { color: var(--accent-d) }

/* ═══════════════════════════════════════════════════════════════
   DETAIL HEAD
   ═══════════════════════════════════════════════════════════════ */
.detail-head {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 24px;
}
.detail-head-thumb {
  width: 64px; height: 64px; border-radius: var(--radius-sm);
  background: var(--bg-elevated); flex-shrink: 0; overflow: hidden;
}
.detail-head-thumb img { width: 100%; height: 100%; object-fit: cover; display: block }
.detail-head-info { flex: 1; min-width: 0 }
.detail-head-info .page-title { margin: 0; line-height: 1.3 }
.detail-head-meta { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 4px }
.detail-head-id { font-size: 12.5px; color: var(--text-muted) }
.detail-head-addr { font-size: 12.5px; color: var(--text-secondary); text-decoration: none }
a.detail-head-addr:hover { color: var(--accent-d); text-decoration: underline }
.detail-head-actions { display: flex; gap: 8px; align-items: center; flex-shrink: 0 }
.status-wrap { position: relative }
.status-pill {
  display: flex; align-items: center; gap: 6px;
  border-radius: 999px; border: 1.5px solid var(--border);
  background: var(--bg-elevated);
  font-size: 12.5px; font-weight: 600; padding: 6px 12px;
  cursor: pointer; white-space: nowrap; color: var(--text-secondary);
  transition: border-color var(--duration) var(--ease), color var(--duration) var(--ease);
}
.status-pill:hover { border-color: var(--accent); color: var(--accent) }
.status-pill--上架中 { border-color: var(--success-border); color: var(--success); background: var(--success-bg) }
.status-pill--已售出 { border-color: var(--border); color: var(--text-muted) }
.status-pill--封存  { border-color: var(--danger-border); color: var(--danger); background: var(--danger-bg) }
.status-dropdown {
  display: none; position: absolute; left: 0; top: calc(100% + 4px); z-index: 200;
  background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: 0 4px 16px rgba(0,0,0,.12); min-width: 110px; padding: 4px 0;
}
.status-wrap.open .status-dropdown { display: block }
.status-opt {
  display: block; width: 100%; padding: 9px 14px; text-align: left;
  font-size: 13px; font-weight: 500; background: none; border: none;
  cursor: pointer; color: var(--text);
}
.status-opt:hover { background: var(--accent-glow) }
.status-opt.active { color: var(--accent); font-weight: 600 }
@media (max-width: 640px) {
  .detail-head { flex-wrap: wrap }
  .detail-head-actions { width: 100%; justify-content: space-between }
}

/* ═══════════════════════════════════════════════════════════════
   SECTION & PAGE TITLES
   ═══════════════════════════════════════════════════════════════ */
.section-title {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .06em;
  color: var(--text-secondary);
  margin-bottom: 16px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.section-title::before {
  content: '';
  width: 18px;
  height: 2px;
  background: var(--accent);
  flex-shrink: 0;
}
.page-title {
  font-size: 26px;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 4px;
  letter-spacing: .02em;
}
/* 我的資料頁首身分行（方案 A，2026-07-18）：帳號／角色從兩個 disabled
   input 降級成一行純文字——這頁的主角是「可以改的東西」，唯讀身分不需要
   佔用表單元件的視覺重量 */
.id-line { font-size: 13px; color: var(--text-muted); }
.id-line b { color: var(--text-secondary); font-weight: 600; }
/* 廣告文案標題——原本是 property-detail-core.js 裡的 inline style，抽成
   class 才能在手機 media query 覆寫字級（inline style 蓋不掉，見 audit #6）；
   這裡的值跟原本 inline 完全相同，桌機零影響。 */
.ad-headline {
  font-size: 18px;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 12px;
  font-family: 'Noto Serif TC', serif;
}

/* ═══════════════════════════════════════════════════════════════
   TABS
   ═══════════════════════════════════════════════════════════════ */
.tabs {
  display: flex;
  gap: 28px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 24px;
}
.tab {
  padding: 12px 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
  transition: all var(--duration) var(--ease);
  font-family: inherit;
}
.tab:hover { color: var(--text-secondary) }
.tab.active {
  color: var(--accent-d);
  border-bottom-color: var(--accent);
}
.tab-panel { display: none }
.tab-panel.active { display: block }

/* 「內部」分頁（P6 兩頁重排，2026-07-18）：機密區獨立視覺語言，跟公開分頁的
   底線式 tab 做色階區隔——未選中沿用 --bg-elevated／--text-secondary（跟手機
   版一般 tab 底色相同，靠圖示＋文字辨識即可，不用額外刺眼），選中時改
   --primary（#2b241b）深墨底反白，全頁最深的一塊色，一眼認出「這裡是機密
   資料」。桌機／手機共用同一組規則（見下方 768px 內的 .active 覆寫）。 */
.tab.lock {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--bg-elevated);
  color: var(--text-secondary);
  border-bottom-color: transparent;
}
.tab.lock:hover { color: var(--text-secondary) }
.tab.lock.active {
  background: var(--primary);
  color: #f5ead8;
  border-bottom-color: transparent;
}
/* 桌機底線式 tab 沒有背景色可承載 chip 感，這裡單獨補 padding/圓角；
   手機版 .tab 本身已是 999px 圓角膠囊（768px 內），.tab.lock 沿用即可，
   不額外設定圓角以免跟同排其他 tab 的膠囊形狀不一致。 */
@media (min-width: 769px) {
  .tab.lock { padding: 6px 14px; border-radius: 8px; margin-bottom: 0; }
}

/* 小型分段切換鈕（例如謄本來源：PDF上傳／貼上連結），之後加新來源只加一顆按鈕，
   不用再疊一整組輸入框＋分隔線 */
.deed-source-toggle {
  display: flex;
  gap: 2px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 2px;
}
.deed-source-toggle button {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 5px 11px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text-muted);
  background: transparent;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-family: inherit;
  transition: all var(--duration) var(--ease);
}
.deed-source-toggle button:hover { color: var(--text-secondary) }
.deed-source-toggle button.active {
  background: var(--card-hover);
  color: var(--accent-d);
  box-shadow: var(--shadow-sm);
}

/* ═══════════════════════════════════════════════════════════════
   PROPERTY DETAIL — Gallery + Sticky Spec Aside
   ═══════════════════════════════════════════════════════════════ */
.detail-gallery-wrap {
  position: relative;
  margin-bottom: 20px;
}
.detail-gallery {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  scroll-behavior: smooth;
  scrollbar-width: none;
}
.detail-gallery::-webkit-scrollbar { display: none }
.detail-gallery > div {
  flex: 0 0 200px;
  height: 150px;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--bg-elevated);
  cursor: pointer;
  position: relative;
}
.detail-gallery img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.gallery-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--text);
  font-size: 16px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-md);
  z-index: 2;
}
.gallery-nav.prev { left: -14px }
.gallery-nav.next { right: -14px }
.gallery-nav[disabled] { opacity: 0; pointer-events: none }
/* 「＋」上傳磚——2026-07-18 兩頁重排（詳情 C 方案）：照片橫捲廊尾端常駐
   一顆上傳入口，取代原本「照片管理只能在編輯表單裡加」的動線。永遠顯示
   （沒照片時也顯示，取代原本的 .detail-gallery-empty 空狀態），flex-shrink:0
   跟其他縮圖同規則。 */
.detail-gallery-add {
  flex: 0 0 200px;
  height: 150px;
  border-radius: var(--radius);
  border: 1.5px dashed var(--accent);
  color: var(--accent-d);
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all var(--duration) var(--ease);
}
.detail-gallery-add:hover { background: var(--accent-glow) }

/* 規格卡（總覽瘦身，2026-07-18）：原本「快速資訊」桌機 aside＋「完整規格」
   主欄兩張卡合併成一張——不再有 aside 需要 sticky/雙欄，改回單欄卡片順序
   排列（見下方 .detail-main，桌機/手機共用同一套 flow）。.aside-price／
   .aside-quick 類名沿用（樣式沒變，只是卡片外層從「aside」改成「card」，
   保留類名省一次全面改名的風險）。 */
.aside-price {
  font-family: 'Noto Serif TC', sans-serif;
  font-size: 28px;
  font-weight: 700;
  color: var(--accent-d);
  margin-bottom: 14px;
}
.aside-price small {
  font-size: 13px;
  color: var(--text-muted);
  font-weight: 400;
  font-family: 'Noto Sans TC', sans-serif;
  margin-left: 4px;
}
.aside-quick {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px 10px;
}
.aside-quick .k {
  display: block;
  font-size: 11px;
  color: var(--text-muted);
  margin-bottom: 2px;
}
.aside-quick .v {
  font-size: 13px;
  font-weight: 600;
}
/* 「更多規格 ▾」收合——低頻的面積明細/建材/公設比，展開前只留一行入口 */
.spec-more { border-top: 1px dashed var(--border); margin-top: 14px; padding-top: 10px; }
.spec-more-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  min-height: 40px;
  font-size: 13.5px;
  color: var(--accent-d);
  background: none;
  border: none;
  cursor: pointer;
  font-family: inherit;
}
.spec-more-body { margin-top: 10px }
.spec-more-body[style*="display:none"] { margin-top: 0 }

@media (max-width: 768px) {
  .detail-gallery > div,
  .detail-gallery-add { flex: 0 0 140px; height: 105px }
  .gallery-nav { display: none }
}

.g-more {
  position: absolute;
  inset: 0;
  background: rgba(20,16,10,.55);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  font-weight: 700;
  font-family: 'Noto Serif TC', sans-serif;
}

/* ═══════════════════════════════════════════════════════════════
   LIGHTBOX
   ═══════════════════════════════════════════════════════════════ */
.lightbox {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(20,16,10,.55);
  z-index: 1000;
  align-items: center;
  justify-content: center;
  padding: 32px;
}
.lightbox.show { display: flex }
.lightbox-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  background: rgba(20,16,10,.92);
  border-radius: 16px;
  padding: 20px 24px;
  box-shadow: var(--shadow-xl);
  width: min(900px, 94vw);
  height: min(680px, 88vh);
}
.lightbox-row {
  flex: 1 1 auto;
  width: 100%;
  min-height: 0;
  display: flex;
  align-items: center;
  gap: 12px;
}
.lightbox-stage {
  flex: 1 1 auto;
  height: 100%;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.lightbox img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: 4px;
}
.lightbox img.loading { opacity: .6; filter: blur(1px) }
.lightbox-close {
  position: absolute;
  top: -16px;
  right: -16px;
  background: var(--card);
  color: var(--text);
  border: 1px solid var(--border);
  width: 34px;
  height: 34px;
  border-radius: 50%;
  cursor: pointer;
  z-index: 1;
  box-shadow: var(--shadow-md);
  display: flex;
  align-items: center;
  justify-content: center;
}
.lightbox-close:hover { background: var(--bg-elevated) }
/* 手機版返回箭頭 icon（property-photos.js 注入，僅 ≤768px 顯示，見手機
   media query）。基底一律隱藏：桌機與 modal 借用的關閉鈕維持原本的 X。 */
.lightbox-close-back { display: none; }
.lightbox-download {
  position: absolute; top: -16px; right: 26px;
  background: var(--card); color: var(--text);
  border: 1px solid var(--border);
  width: 34px; height: 34px; border-radius: 50%;
  cursor: pointer; z-index: 1;
  box-shadow: var(--shadow-md);
  display: flex; align-items: center; justify-content: center;
}
.lightbox-download:hover { background: var(--bg-elevated) }
/* .lightbox-copy-btn 刪除（2026-08 桶二死碼清理，2026-07-19 體檢 2.2-M1）：
   JS/HTML 全 repo 零引用，複製原圖走 strip 雙擊（property-copy.js），這顆
   浮動按鈕沒有對應的產生端。 */
.lightbox-nav {
  flex: 0 0 auto;
  background: var(--card);
  color: var(--text);
  border: 1px solid var(--border);
  width: 42px;
  height: 42px;
  border-radius: 50%;
  cursor: pointer;
  box-shadow: var(--shadow-md);
  display: flex;
  align-items: center;
  justify-content: center;
}
.lightbox-nav:hover { background: var(--bg-elevated) }
.lightbox-counter {
  flex: 0 0 auto;
  align-self: flex-start;
  background: rgba(255,255,255,.1);
  color: rgba(255,255,255,.85);
  font-size: 12.5px;
  letter-spacing: .04em;
  padding: 5px 12px;
  border-radius: 20px;
  margin-bottom: 12px;
}
.lightbox-strip {
  flex: 0 0 auto;
  display: flex;
  gap: 6px;
  margin-top: 16px;
  max-width: 100%;
  overflow-x: auto;
  padding: 2px;
}
.lightbox-strip img {
  width: 64px;
  height: 48px;
  object-fit: cover;
  border-radius: 3px;
  cursor: pointer;
  opacity: .5;
  border: 2px solid transparent;
  flex-shrink: 0;
  box-shadow: none;
}
.lightbox-strip img.active {
  opacity: 1;
  border-color: var(--accent-l);
}
@media (max-width: 768px) {
  /* 手機版改全螢幕沉浸式看圖，不再是桌機那種「浮動卡片、四周看得到變暗
     背景」的樣子——原本的版型直接把桌機版等比縮小，手機上反而顯得四不像
     （2026-07-19 使用者實機截圖回報：「感覺很鳥、上不了檯面」）。 */
  /* 畫布補高（2026-07-19 真機診斷儀實測定案）：iOS standalone＋black-translucent
     下 layout viewport 高度=螢幕高−瀏海高（852−59=793,上端延伸進瀏海但總高
     沒補回）,inset:0 的燈箱因此下端短少 59px——那條「怎麼拖都蓋不掉的底部
     黑帶」就是畫布外死區。高度改 100dvh＋safe-area-inset-top 把下端補到
     螢幕底;無瀏海環境 env=0 行為不變。 */
  /* 高度由 JS 錨定實體螢幕（_openLightbox 設 max(innerHeight, screen.height)）：
     真機診斷發現 iOS standalone 的視窗高會在 793↔852 間漂移（時而短掉瀏海高），
     任何 CSS 視窗單位都跟著漂——改錨定不會變的 screen.height。CSS 只留 fallback。 */
  .lightbox { padding: 0; background: rgb(10, 8, 5); bottom: auto; height: 100dvh }
  /* .lightbox-panel { height: 100% } 刪除（2026-08 桶二死碼清理，2026-07-19
     體檢 2.2-H3「同一 media 區塊內自我矛盾」）：七層補丁殘跡，5 行後同一個
     media 區塊內 .lightbox-panel 完整重定義為 height:100dvh（見下），這行
     從未生效過，純粹誤導下一次改動的人。 */
  /* iOS standalone 底部 home 橫條區的邊緣色是從頁面底色取樣的——開燈箱時
     連 body 一起切黑，賭 iOS 重新取樣（2026-07-19；頂部已用 black-translucent
     物理解決，底部這條若 iOS 不重採屬 WebKit 已知限制，保留無害）。 */
  body.lightbox-open { background: rgb(10, 8, 5); }
  .lightbox-panel {
    width: 100vw;
    height: 100dvh;
    /* 全視窗置中模型〔拍板 2026-07-19，取代同日稍早的 safe-area letterbox〕：
       照片版位永遠固定——1x contain 於「整個視窗」（含安全區），以螢幕正
       中心置中；直式滿版照片頂端鋪到狀態列文字底下（iOS 相簿標準行為，
       黑底白字時鐘浮在照片上，使用者真機驗收接受）。
       【鐵則】照片版位不因 chrome 顯隱改變：單擊只淡出/淡入 chrome，照片
       一個像素都不動。letterbox 版本的三個真機回報（單擊照片上跳、放大
       蓋過瀏海但底部留黑帶、59/34 不對稱中心偏移）都是「padding 隨
       chrome 切換」造成的，禁止走回頭路——詳 docs/LIGHTBOX-CONTRACT.md。 */
    padding: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    touch-action: none;
  }
  .lightbox-row { gap: 0 }
  /* P5.2（2026-07-19）：手機改用左右滑動切換＋雙指縮放，箭頭讓位給更大
     可視區，桌機不受影響（這條規則本來就只在這個 media query 裡）。 */
  .lightbox-nav { display: none }
  .lightbox img { max-height: 100%; touch-action: none; will-change: transform; border-radius: 0 }
  .lightbox-strip { display: none }
  /* 手機版燈箱 chrome 佈局〔拍板 2026-07-19〕：關閉鍵改「左上返回箭頭」，
     同全站層級導覽（.mobile-chrome-back-btn）的視覺語言——44×44、無底無框、
     白色 icon（使用者回報：右上 X 跟全站左上返回箭頭風格不一致，「主框架
     要規範，風格太多會雜」）。右上僅留下載（維持圓形 chip），張數計數移到
     頂部置中。桌機維持右上 X 不動（箭頭 markup 由 property-photos.js 注入，
     CSS 依斷點切換同一顆按鈕內的兩個圖示，基底規則見 .lightbox-close-back）。
     【selector 一律加 .lightbox 前綴】（2026-07-19 體檢④）：school/share
     modal 的關閉鈕借用了 .lightbox-close class（index.html #school-modal／
     #share-modal），裸 class selector 會外洩過去，把淺色 bottom sheet 上的
     關閉鈕染成「近白底上的白色 ×」＝隱形。這區只准描述燈箱裡的東西。 */
  .lightbox .lightbox-close {
    top: calc(env(safe-area-inset-top, 0px) + 8px);
    left: 8px;
    right: auto;
    width: 44px;
    height: 44px;
    background: none;
    border: none;
    box-shadow: none;
    color: #fff;
  }
  .lightbox .lightbox-close > :not(.lightbox-close-back) { display: none; } /* 手機藏 X，只留箭頭 */
  .lightbox .lightbox-close .lightbox-close-back { display: flex; align-items: center; justify-content: center; }
  .lightbox .lightbox-download {
    top: calc(env(safe-area-inset-top, 0px) + 8px);
    right: 8px;
    background: rgba(255,255,255,.12); border-color: rgba(255,255,255,.2); color: #fff;
  }
  .lightbox .lightbox-counter {
    position: absolute;
    top: calc(env(safe-area-inset-top, 0px) + 16px);
    left: 50%;
    transform: translateX(-50%);
    align-self: auto;
    margin-bottom: 0;
    /* 【chrome 永遠在照片之上】（issue #40，2026-08-11）：計數器原本沒有
       z-index，靠「定位元素畫在非定位元素之上」的預設順序浮在照片上——但
       手機版 .lightbox img 帶 will-change: transform、.lightbox-stage 在檔尾
       手機區也帶 will-change: transform，兩者都因此各自建立堆疊環境；沒有
       z-index 的堆疊環境跟 z-index:auto 的定位元素同層，同層就比 DOM 順序，
       而 stage 排在計數器後面 → 照片贏。結果是直式（滿版）照片把「3 / 7」
       整個蓋掉，只有橫式照片上下留黑帶時、那個座標剛好落在**透明的** stage
       上才看得見。左上返回鍵與右上下載鍵沒中獎純粹是因為它們本來就寫了
       z-index:1（見基底 .lightbox-close／.lightbox-download）。
       這裡把三顆 chrome 一起提到明寫的 z-index:2，讓「chrome 在照片之上」
       變成規則而不是透明度巧合。
       pointer-events: none 是配套、不可省：燈箱手勢（單擊切 chrome／左右滑
       換張／下滑關閉／雙擊放大）全部綁在 #lightbox-stage 上，而計數器是
       .lightbox-panel 的子節點、不在 stage 裡面。只提 z-index 不擋滑鼠事件的
       話，落在計數器那一小塊的觸控會被它接走、傳不到 stage，等於在照片正上方
       開了一個手勢死角。計數器本身純顯示、沒有任何互動，擋掉不損失功能，
       命中測試維持跟修改前一模一樣（穿透到底下的照片）。
       【契約】只改繪製層級與命中測試，版面零位移——照片版位不因 chrome 顯隱
       改變的鐵則不受影響（見 docs/LIGHTBOX-CONTRACT.md 六）。 */
    z-index: 2;
    pointer-events: none;
  }
  .lightbox .lightbox-close, .lightbox .lightbox-download { z-index: 2 }
  /* P5.2：單擊照片/黑底切換這三個 chrome 元素的顯隱，取代原本規劃給手機
     的「點黑底關閉」（D4 #4）——手機面板全螢幕貼滿，那個判斷從未真的
     觸發過，改用單擊切 chrome 更貼近 IG/相簿的原生慣例；class 由
     property-photos.js 的 initLightboxSwipe() 單擊判定切換。 */
  .lightbox .lightbox-close, .lightbox .lightbox-download, .lightbox .lightbox-counter {
    transition: opacity var(--duration) var(--ease);
  }
  .lightbox.chrome-hidden .lightbox-close,
  .lightbox.chrome-hidden .lightbox-download,
  .lightbox.chrome-hidden .lightbox-counter {
    opacity: 0;
    pointer-events: none;
  }
}

/* ═══════════════════════════════════════════════════════════════
   MODAL（表單內快速查詢用，不離開當前頁面/不丟未存檔內容）
   ═══════════════════════════════════════════════════════════════ */
.modal {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(20,16,10,.45);
  z-index: 1100;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.modal.show { display: flex }
.modal-panel {
  background: var(--card);
  border-radius: 12px;
  padding: 24px;
  box-shadow: var(--shadow-xl);
  width: min(640px, 94vw);
  max-height: 86vh;
  overflow-y: auto;
}
/* 批次4：school-modal／share-modal 標題列原本是 inline style（抽 class，
   零行為改動——數值原樣照抄），方便手機 media query 統一調整 */
.modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
.modal-header .section-title { margin: 0; }
.modal-header .lightbox-close { position: static; }
.modal-result { margin-top: 16px; }

/* ═══════════════════════════════════════════════════════════════
   DEED PDF SPLIT VIEW
   ═══════════════════════════════════════════════════════════════ */
.deed-split {
  display: flex;
  align-items: flex-start;
  gap: 0;
}
.deed-main { flex: 1; min-width: 0 }
.deed-pdf-pane { display: none }
.deed-split.open .deed-pdf-pane {
  display: flex;
  flex-direction: column;
  flex: 0 0 44%;
  margin-left: 20px;
  position: sticky;
  top: 20px;
  height: calc(100vh - 100px);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.deed-pdf-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
  color: var(--text-secondary);
  flex: 0 0 auto;
}
.deed-pdf-frame {
  flex: 1 1 auto;
  width: 100%;
  border: none;
}
@media (max-width: 1199px) {
  .deed-split.open .deed-pdf-pane { display: none }
}

/* ═══════════════════════════════════════════════════════════════
   DATA ROWS
   ═══════════════════════════════════════════════════════════════ */
.data-grid {
  display: grid;
  grid-template-columns: max-content 1fr max-content 1fr;
  gap: 11px 18px;
  font-size: 13.5px;
}
.data-grid dt {
  color: var(--text-muted);
  font-weight: 500;
  padding-top: 1px;
  white-space: nowrap;
}
.data-grid dd {
  color: var(--text);
  font-weight: 500;
}
@media (max-width: 768px) {
  /* audit #10：手機 1fr 1fr 仍是「一行塞 label+value」，長標籤（屋主身分證號）
     擠壓 value；改單欄，dt 在上 dd 在下（見 design-audit.md property-detail-core 節） */
  .data-grid { grid-template-columns: 1fr; gap: 2px 0 }
  .data-grid dt { white-space: normal; margin-top: 8px }
  .data-grid dt:first-child { margin-top: 0 }
}

/* 屋主電話／客戶電話可點擊撥號（tel:），視覺上維持跟一般文字一樣、
   不做成明顯的連結樣式，只在互動時給提示 */
.tel-link { color: inherit; text-decoration: none }
.tel-link:hover, .tel-link:active { text-decoration: underline; color: var(--accent-d) }

/* 開價異動歷史刪除鈕——原本是 inline style="padding:2px 6px"，抽成 class
   才能在手機 media query 蓋掉（audit #8）；這裡的值跟原本 inline 完全相同，
   桌機零影響。 */
.price-history-del { padding: 2px 6px; }

/* ═══════════════════════════════════════════════════════════════
   MORTGAGE TABLE
   ═══════════════════════════════════════════════════════════════ */
.mortgage-item {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  font-size: 14px;
  margin-bottom: 10px;
  transition: border-color var(--duration) var(--ease);
}
.mortgage-item:hover { border-color: var(--accent) }
.mortgage-item dt { color: var(--text-secondary); font-size: 12px; margin-top: 6px }
.mortgage-item dd { font-weight: 600 }

/* ═══════════════════════════════════════════════════════════════
   FEATURES LIST
   ═══════════════════════════════════════════════════════════════ */
.features { list-style: none; margin: 0; padding: 0 }
.features li {
  padding: 9px 0;
  border-top: 1px solid var(--border-light);
  font-size: 13.5px;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  color: var(--text-secondary);
}
.features li:first-child { border-top: none }
.features li::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  margin-top: 6px;
  flex-shrink: 0;
}

/* ═══════════════════════════════════════════════════════════════
   SCHOOL TAGS
   ═══════════════════════════════════════════════════════════════ */
.school-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px }
.school-tag {
  background: var(--bg-elevated);
  color: var(--accent-d);
  font-size: 12px;
  font-weight: 600;
  padding: 3px 11px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
}

.edit-form-wrap {
  max-width: 720px;
}

.edit-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 16px;
  padding: 10px 14px;
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}

/* 車位／機車位清單列（property-edit-form.js _carSpotsHtml/_motoSpotsHtml）：
   桌機版型（一列＝選類型/樓層/編號/刪除鈕，橫排）用 class 承載而非 inline
   style，是為了手機 media query 能整組覆寫成 grid 換行——inline style 沒辦法
   被外部 CSS 覆寫。桌機這裡維持跟改版前完全一樣的橫排效果。 */
.spot-row { display: flex; gap: 8px; margin-bottom: 8px; }
.spot-row .spot-type { flex: 1.2; }
.spot-row .spot-input { flex: 1; }

/* ═══════════════════════════════════════════════════════════════
   FORMS
   ═══════════════════════════════════════════════════════════════ */
.form-group { margin-bottom: 18px }
.form-group label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
  margin-bottom: 6px;
}
.form-group input,
.form-group select,
.form-group textarea {
  width: 100%;
  padding: 11px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 14px;
  color: var(--text);
  background: #fff;
  transition: all var(--duration) var(--ease);
  font-family: inherit;
}
.form-group textarea {
  resize: vertical;
  line-height: 1.65;
}
/* .form-group input 那組 width:100%/padding/border 是給文字類輸入框用的，
   checkbox/radio 也是 <input>，沒排除的話會被撐成一整條滿版的空心方框
   （手機上尤其明顯——附近設施勾選 chip、客戶表單「同電話」勾選框都中招，
   2026-07-11 使用者實機截圖回報），用 all:revert 整組還原成瀏覽器原生
   checkbox 外觀，不繼承這裡任何一條規則 */
.form-group input[type=checkbox],
.form-group input[type=radio] {
  all: revert;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}
.form-hint {
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 5px;
}

/* ── 規格卡狀態 chip（總覽瘦身，2026-07-18）：開價旁的狀態標記，色彩沿用
   .status-pill 既有的狀態語意（上架中＝綠、封存＝紅…），不是另外發明一組
   顏色——同一個狀態在同一頁面用同一種顏色，不然使用者要重新學一次。 ── */
.spec-status-chip {
  display: inline-flex;
  align-items: center;
  font-size: 12px;
  font-weight: 700;
  padding: 3px 10px;
  border-radius: 8px;
  margin-left: 8px;
  vertical-align: 4px;
  border: 1px solid transparent;
}
.spec-status-chip--上架中 { background: var(--success-bg); color: var(--success); border-color: var(--success-border); }
.spec-status-chip--已售出 { background: var(--bg-elevated); color: var(--text-muted); }
.spec-status-chip--封存 { background: var(--danger-bg); color: var(--danger); border-color: var(--danger-border); }
.spec-status-chip--草稿 { background: var(--bg-elevated); color: var(--text-secondary); }

/* ── 謄本預設 / 手動覆寫 badge ── */
.ov-badge {
  display: inline-block;
  font-size: 10.5px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 999px;
  margin-left: 6px;
  letter-spacing: .01em;
}
.ov-badge.deed {
  background: var(--bg-elevated);
  color: var(--text-muted);
}
.ov-badge.manual {
  background: var(--accent-glow);
  color: var(--accent-d);
}

/* ── 客戶追蹤狀態 badge（2026-07-17 P5）──
   跟上面的 .ov-badge 分開：.ov-badge 代表謄本欄位「預設/手動覆寫」語義，
   客戶狀態（追蹤中/已成交/已流失）本來借用它的 class 名純粹因為視覺相近，
   語義是錯的，日後改 .ov-badge 會誤傷這裡，所以獨立一組。色彩對應維持跟
   借用時期一樣（追蹤中＝中性灰、已成交＝強調色、已流失＝無底色純文字），
   沒有新增顏色，仍符合全頁彩色元素 ≤3 處的原則。 */
.cust-badge {
  display: inline-block;
  font-size: 10.5px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 999px;
  letter-spacing: .01em;
}
.cust-badge-tracking {
  background: var(--bg-elevated);
  color: var(--text-muted);
}
.cust-badge-closed {
  background: var(--accent-glow);
  color: var(--accent-d);
}

/* ── Edit grid (2-col form layout) ── */
.edit-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 24px;
}
/* 客戶表單「同電話」checkbox 的外層 label（原本 inline style，2026-07-17
   P5 抽成 class 方便在手機 media query 加大觸控範圍，桌機視覺值不變）*/
.cust-line-same-label {
  font-weight: 400;
  font-size: 12px;
  margin-left: 8px;
  cursor: pointer;
  color: var(--text-muted);
}

/* ═══════════════════════════════════════════════════════════════
   FILE DROP ZONE
   ═══════════════════════════════════════════════════════════════ */
.dropzone {
  border: 2px dashed var(--border);
  border-radius: var(--radius);
  padding: 40px 24px;
  text-align: center;
  cursor: pointer;
  transition: all var(--duration-lg) var(--ease);
  color: var(--text-muted);
  background: var(--bg);
  position: relative;
}
.dropzone:hover, .dropzone.drag-over {
  border-color: var(--accent);
  background: var(--accent-glow);
  color: var(--accent-d);
}
.dropzone.drag-over {
  box-shadow: inset 0 0 0 2px var(--accent-glow);
}
.dropzone input { display: none }
.dropzone-icon { margin-bottom: 10px; display: flex; justify-content: center; color: var(--text-muted) }
.dropzone-label { font-size: 15px; font-weight: 600; color: var(--text-secondary) }
.dropzone-hint { font-size: 12.5px; margin-top: 6px; color: var(--text-muted) }

/* ═══════════════════════════════════════════════════════════════
   BUTTONS
   ═══════════════════════════════════════════════════════════════ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 10px 20px;
  border-radius: var(--radius-sm);
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: .03em;
  cursor: pointer;
  border: none;
  font-family: inherit;
  transition: all var(--duration) var(--ease);
  position: relative;
  overflow: hidden;
}
.btn:active { transform: scale(.97) }
.btn:disabled { opacity: .45; cursor: not-allowed }

/* Primary */
.btn-primary {
  background: var(--accent);
  color: #fff;
  box-shadow: 0 1px 3px rgba(176,141,79,.3);
}
.btn-primary:hover:not(:disabled) {
  background: var(--accent-d);
  box-shadow: 0 4px 12px rgba(176,141,79,.35);
}

/* Accent */
.btn-accent {
  background: var(--accent);
  color: #fff;
  box-shadow: 0 1px 3px rgba(176,141,79,.3);
}
.btn-accent:hover:not(:disabled) {
  background: var(--accent-d);
  box-shadow: 0 4px 12px rgba(176,141,79,.35);
}

/* Ghost */
.btn-ghost {
  background: transparent;
  color: var(--text-secondary);
  border: 1px solid var(--border);
}
.btn-ghost:hover:not(:disabled) {
  border-color: var(--accent);
  color: var(--accent-d);
  background: var(--accent-glow);
}

/* .btn-danger／:hover 刪除（2026-08 桶二死碼清理，2026-07-19 體檢 2.2-M1）：
   JS/HTML 全 repo 零引用，危險動作走 .dropdown-item-danger 或 inline
   style="color:var(--danger)"。 */

.btn-sm { padding: 7px 14px; font-size: 12.5px }
.btn-row { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 20px }
/* 客戶表單「封存客戶」鈕（2026-07-17 P5，原本 inline style 抽成 class）：
   桌機沿用原本效果——靠 margin-left:auto 推到 .btn-row 最右邊、紅字表示
   破壞性操作。手機版覆寫見檔尾 mobile media query，跟儲存/取消分開一行，
   不動 .btn-row 本身（其他表單頁也共用這個 class，不能波及）。 */
.cust-archive-btn { margin-left: auto; color: var(--danger); }

/* ═══════════════════════════════════════════════════════════════
   ALERTS
   ═══════════════════════════════════════════════════════════════ */
.alert {
  padding: 13px 16px;
  border-radius: var(--radius-sm);
  font-size: 13.5px;
  margin-bottom: 12px;
  border-left: 3px solid;
  font-weight: 500;
}
.alert-danger {
  background: var(--danger-bg);
  color: var(--danger);
  border-left-color: var(--danger);
}
/* .alert-warn 刪除（2026-08 桶二死碼清理，2026-07-19 體檢 2.2-M1）：
   JS/HTML 全 repo 零引用，警示提示全站走 .alert-danger。 */
.alert-ok {
  background: var(--success-bg);
  color: var(--success);
  border-left-color: var(--success);
}

/* ═══════════════════════════════════════════════════════════════
   TOAST
   ═══════════════════════════════════════════════════════════════ */
.toast {
  position: fixed;
  bottom: 28px;
  left: 50%;
  transform: translateX(-50%) translateY(80px);
  background: var(--primary);
  color: #fffdf9;
  padding: 12px 24px;
  border-radius: var(--radius);
  font-size: 13.5px;
  font-weight: 500;
  box-shadow: var(--shadow-xl);
  transition: transform var(--duration-lg) var(--ease-spring);
  z-index: 1100;
  white-space: nowrap;
  pointer-events: none;
}
.toast.show { transform: translateX(-50%) translateY(0) }

/* error/info 變體（2026-07-19 體檢：showToast(msg,'error') 契約修復，
   見 core/toast.js 開頭註解）——只換底色，版型/位置/動畫沿用 .toast 本體。 */
.toast-error { background: var(--danger) }
.toast-info  { background: var(--info) }

/* 帶「復原」按鈕的 toast（例如刪除照片）——一般 toast 是 pointer-events:none
   純顯示用，這種要能被點擊，所以另外開一個修飾類別蓋掉 */
.toast-with-action {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 14px;
}
.toast-undo-btn {
  background: none;
  border: 1px solid rgba(255,255,255,.4);
  color: #fffdf9;
  padding: 4px 12px;
  border-radius: 999px;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  font-family: inherit;
}
.toast-undo-btn:hover { background: rgba(255,255,255,.15) }

/* ═══════════════════════════════════════════════════════════════
   LOADING — Skeleton + Spinner
   ═══════════════════════════════════════════════════════════════ */
.loading {
  text-align: center;
  padding: 60px 20px;
  color: var(--text-secondary);
  font-size: 14px;
}
.spinner {
  width: 34px;
  height: 34px;
  border: 3px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin .7s linear infinite;
  margin: 0 auto 14px;
}
@keyframes spin { to { transform: rotate(360deg) } }

/* Skeleton Loader */
.skeleton {
  background: var(--bg-elevated);
  border-radius: var(--radius-sm);
  position: relative;
  overflow: hidden;
}
.skeleton::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(255,255,255,.7) 50%,
    transparent 100%
  );
  animation: skeleton-shimmer 1.5s infinite;
}
@keyframes skeleton-shimmer {
  0%   { transform: translateX(-100%) }
  100% { transform: translateX(100%) }
}
.skeleton-line {
  height: 14px;
  margin-bottom: 10px;
  border-radius: 6px;
}
.skeleton-line:last-child { margin-bottom: 0 }
/* .w-60／.w-80 刪除（2026-08 桶二死碼清理，2026-07-19 體檢 2.2-M1）：
   JS/HTML 全 repo 零引用；.w-40 仍在用（core/dom.js:19），保留。 */
.skeleton-line.w-40 { width: 40% }
.skeleton-card {
  height: 160px;
  border-radius: var(--radius);
}

/* 骨架屏圓角修飾（甲「先畫後補」批次，2026-07-18）：DESIGN-LANGUAGE 規範
   容器卡片 12px、內部小元件 8px。既有 .skeleton（--radius-sm=10）與
   .skeleton-card（--radius=12）沿用不動，新增的骨架元件依規範各自標 8/12。 */
.skeleton.sk-r8  { border-radius: 8px }
.skeleton.sk-r12 { border-radius: 12px }

/* 先畫後補用的灰條：詳情即時版未知欄位／清單與客戶頁首載骨架 */
.sk-v, .sk-line, .sk-title, .sk-para { display: block }
.sk-v     { height: 13px; width: 70%; margin-top: 2px }          /* 對齊 .aside-quick .v */
.sk-title { height: 16px; width: 42%; margin-bottom: 14px }      /* 對齊 .section-title */
.sk-para  { height: 13px; width: 100%; margin-bottom: 10px }
.sk-para:last-child { margin-bottom: 0 }

/* 尊重「減少動態」偏好：關掉 shimmer 掃光、維持靜態灰塊（DESIGN-LANGUAGE 規範） */
@media (prefers-reduced-motion: reduce) {
  .skeleton::after { animation: none }
}

/* 授權縮圖載入中的底色（2026-08-11「縮圖改回傳網址」，見 core/auth-img.js）。
   縮圖改成一張一個請求之後，<img> 會有一小段「還沒有 src」的時間；不給底色
   的話那是一個透明的空洞，一排照片看起來像破圖。給了就是「灰塊逐一被照片
   填滿」——業主原本看到的是整頁灰骨架卡住不動，這個是同一種灰、但會一格一格
   消失，那正是這批要換到的體驗。
   用 background-color 而不是 .skeleton 的 ::after shimmer：<img> 是替換元素，
   偽元素在它身上不會渲染，寫了也是白寫。 */
img.auth-img:not(.auth-img-ready) {
  background-color: var(--bg-elevated);
}

/* ═══════════════════════════════════════════════════════════════
   EMPTY STATE
   ═══════════════════════════════════════════════════════════════ */
.empty {
  text-align: center;
  padding: 80px 20px;
  color: var(--text-secondary);
}
.empty-icon { font-size: 52px; margin-bottom: 16px; opacity: .6 }
.empty-text {
  font-size: 17px;
  font-weight: 700;
  margin-bottom: 6px;
  color: var(--text);
  font-family: 'Noto Serif TC', sans-serif;
}
.empty-sub { font-size: 14px; color: var(--text-muted) }

/* ═══════════════════════════════════════════════════════════════
   DIVIDER
   ═══════════════════════════════════════════════════════════════ */
.divider {
  border: none;
  border-top: 1px solid var(--border-light);
  margin: 24px 0;
}

/* ═══════════════════════════════════════════════════════════════
   PHOTO GRID
   ═══════════════════════════════════════════════════════════════ */
.photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px;
}
.photo-item {
  position: relative;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg);
  border: 1px solid var(--border-light);
  cursor: grab;
  transition: all var(--duration-lg) var(--ease);
}
.photo-item:hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}
.photo-item.dragging { opacity: .4 }
.photo-item.drag-over { box-shadow: 0 0 0 2px var(--accent) inset }
.photo-order {
  position: absolute;
  top: 6px;
  left: 6px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: rgba(43,36,26,.65);
  backdrop-filter: blur(4px);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
}
.photo-item img {
  width: 100%;
  aspect-ratio: 4/3;
  object-fit: cover;
  display: block;
  transition: transform var(--duration-lg) var(--ease);
}
.photo-item:hover img {
  transform: scale(1.03);
}
.photo-name {
  font-size: 10px;
  color: var(--text-muted);
  padding: 6px 8px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.photo-del {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: rgba(43,36,26,.65);
  backdrop-filter: blur(4px);
  color: #fff;
  border: none;
  cursor: pointer;
  font-size: 11px;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: all var(--duration) var(--ease);
}
.photo-del:hover { background: var(--danger) }
.photo-item:hover .photo-del { opacity: 1 }
.photo-cover {
  position: absolute;
  top: 6px;
  right: 36px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: rgba(43,36,26,.65);
  backdrop-filter: blur(4px);
  color: #fff;
  border: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: all var(--duration) var(--ease);
}
.photo-cover:hover { background: var(--accent) }
.photo-item:hover .photo-cover { opacity: 1 }

/* 觸控裝置沒有 hover 這個手勢，操作鈕不能靠 hover 才現身，否則手機上根本點不到 */
@media (hover: none) {
  .photo-del, .photo-cover { opacity: 1 }
}

/* ═══════════════════════════════════════════════════════════════
   COMPLIANCE PANEL
   ═══════════════════════════════════════════════════════════════ */
.compliance-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 0;
  border-bottom: 1px solid var(--border-light);
  font-size: 13.5px;
}
.compliance-row:last-child { border-bottom: none }
.compliance-key { color: var(--text-secondary); font-weight: 500 }
.compliance-val { font-weight: 600; color: var(--text) }

/* ═══════════════════════════════════════════════════════════════
   LOGIN
   ═══════════════════════════════════════════════════════════════ */
body.auth-page {
  background: var(--bg);
  background-image:
    radial-gradient(circle at 12% 18%, rgba(176,141,79,.10), transparent 38%),
    radial-gradient(circle at 88% 82%, rgba(176,141,79,.08), transparent 42%);
}
.login-panel {
  max-width: 380px;
  margin: 100px auto;
  padding: 40px 36px;
}
.login-brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  margin-bottom: 26px;
}
.login-logo {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 20px;
  font-family: 'Noto Serif TC', sans-serif;
  margin-bottom: 14px;
}
.login-brand-name {
  font-family: 'Noto Serif TC', sans-serif;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--text);
}
.login-brand-sub {
  font-size: 10px;
  color: var(--text-muted);
  letter-spacing: .16em;
  margin-top: 4px;
}
.login-divider {
  width: 30px;
  height: 2px;
  background: var(--accent);
  margin: 0 auto 26px;
}
.login-foot {
  text-align: center;
  margin-top: 20px;
  font-size: 11px;
  color: var(--text-muted);
  letter-spacing: .03em;
}

/* ═══════════════════════════════════════════════════════════════
   UTILITIES & ENHANCEMENTS
   ═══════════════════════════════════════════════════════════════ */

/* Smooth scrollbar for the whole page */
::-webkit-scrollbar { width: 6px }
::-webkit-scrollbar-track { background: transparent }
::-webkit-scrollbar-thumb {
  background: var(--border);
  border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover { background: var(--text-muted) }

/* Focus-visible for keyboard navigation */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Selection color */
::selection {
  background: rgba(176,141,79,.3);
  color: #2b241b;
}

/* ════════════════════════════════════
   側邊欄微調（搜尋框 icon、篩選徽章、清單項目選中狀態）
   ════════════════════════════════════ */
.sidebar-search { position: relative; }
.sidebar-search::before {
  content: '\1F50D'; position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  font-size: 12px; opacity: .45; pointer-events: none;
}
.sidebar-search input { padding-left: 32px; border-radius: 999px; background: var(--bg-elevated); border-color: transparent; }

.sidebar-filters { padding: 16px 12px 8px; }
.sidebar-filter-count {
  background: transparent; color: var(--text-muted);
  font-weight: 600;
}
.sidebar-filter-item.active .sidebar-filter-count { background: transparent; color: var(--accent-d) }
.sidebar-filter-item.warn-item .sidebar-filter-count { background: transparent; color: var(--warning) }
.sidebar-nav { padding: 8px 10px 10px; }
.sidebar-item { border-left-width: 3px; padding: 11px 13px; }
.sidebar-item:hover { background: var(--accent-glow); }
.sidebar-item.active { background: var(--accent-glow); border-left-color: var(--accent); }


/* ── 物件總覽：內部資料分隔線 ── */
.section-separator {
  display: flex; align-items: center; gap: 12px;
  margin: 28px 0 20px;
  color: var(--text-muted); font-size: 12px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
}
.section-separator::before,
.section-separator::after {
  content: ''; flex: 1; height: 1px; background: var(--border);
}

/* ── 頁首「⋯」更多選單 ── */
.detail-more-wrap { position: relative; }
.detail-more-dropdown {
  display: none; position: absolute; right: 0; top: calc(100% + 4px); z-index: 200;
  background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: 0 4px 16px rgba(0,0,0,.12); min-width: 140px; padding: 4px 0;
}
.detail-more-wrap.open .detail-more-dropdown { display: block; }
.dropdown-item {
  display: block; width: 100%; padding: 9px 16px; text-align: left;
  font-size: 14px; background: none; border: none; cursor: pointer; color: var(--text);
}
.dropdown-item:hover { background: var(--accent-glow); }
.dropdown-item-danger { color: var(--danger); }
.dropdown-item-danger:hover { background: var(--danger-bg); }

/* ── 文案 tab ── */
.copy-panel {
  display: flex; flex-direction: column; gap: 16px; padding: 4px 0 24px;
  max-width: 860px;
}
.copy-card {
  background: var(--card); border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden;
}
.copy-card.card-collapsible { padding-left: 0; }
.copy-card.card-collapsible.collapsed { padding-left: 0; }
.copy-card-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 20px;
}
.copy-card.card-collapsible .copy-card-header { padding-left: 17px; }
.copy-card.card-collapsible.collapsed .copy-card-header { padding-left: 16px; }
.copy-card .card-body { margin-top: 0 !important; }
.copy-card-label {
  font-size: 12px; font-weight: 700; letter-spacing: .06em;
  color: var(--text-secondary);
  display: flex; align-items: center; gap: 8px;
  transition: color 0.22s var(--ease);
}
.copy-card-label::before {
  content: ''; width: 18px; height: 2px;
  background: var(--accent); flex-shrink: 0;
  transition: background 0.22s var(--ease);
}
.copy-card.card-collapsible.collapsed .copy-card-label {
  color: var(--text-muted);
}
.btn-xs {
  padding: 3px 10px; font-size: 12px; border-radius: var(--radius-sm);
}
.copy-textarea {
  display: block; width: 100%; min-height: 240px;
  padding: 12px 14px; border: none; resize: vertical;
  font-size: 14px; line-height: 1.6; color: var(--text);
  background: var(--card); font-family: inherit;
  box-sizing: border-box;
}
.copy-textarea:focus { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ── 備忘多頁 tab ── */
.memo-tabs {
  display: flex; align-items: flex-end; gap: 2px;
  padding: 8px 14px 0; border-bottom: 1px solid var(--border);
  overflow-x: auto; scrollbar-width: none;
}
.memo-tabs::-webkit-scrollbar { display: none; }
.memo-tab {
  display: flex; align-items: center; gap: 5px;
  padding: 5px 10px; border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  font-size: 12px; color: var(--text-muted); cursor: pointer; user-select: none;
  border: 1px solid transparent; border-bottom: none;
  transition: background .15s, color .15s; white-space: nowrap;
}
.memo-tab:hover { background: var(--bg-elevated); color: var(--text); }
.memo-tab.active {
  background: var(--card); color: var(--accent); font-weight: 600;
  border-color: var(--border); position: relative;
}
.memo-tab.active::after {
  content: ''; position: absolute; bottom: -1px; left: 0; right: 0; height: 1px;
  background: var(--card);
}
.memo-tab-close {
  background: none; border: none; cursor: pointer; padding: 0 1px; line-height: 1;
  font-size: 14px; color: var(--text-muted); opacity: .5; transition: opacity .15s;
}
.memo-tab-close:hover { opacity: 1; color: #c0392b; }
.memo-tab-add {
  background: none; border: none; cursor: pointer; padding: 4px 8px; margin-bottom: 1px;
  font-size: 16px; line-height: 1; color: var(--text-muted); border-radius: var(--radius-sm);
  transition: background .15s, color .15s;
}
.memo-tab-add:hover { background: var(--bg-elevated); color: var(--accent); }

/* ── 文案 tab：照片格 ── */
.copy-photo-grid {
  display: flex; flex-wrap: wrap; gap: 8px; padding: 12px 14px;
}
.copy-photo-item {
  position: relative; width: 88px; height: 66px; border-radius: var(--radius-sm);
  overflow: hidden; cursor: pointer; border: 2px solid transparent;
  transition: border-color .15s, box-shadow .15s;
}
.copy-photo-item img { width: 100%; height: 100%; object-fit: cover; display: block; }
.copy-photo-item:hover { box-shadow: 0 0 0 2px var(--accent-light, #d4b483) inset; }
.copy-photo-check {
  position: absolute; top: 5px; right: 5px; width: 20px; height: 20px;
  border-radius: 50%; background: var(--accent); color: #fff;
  font-size: 12px; font-weight: 700; display: flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity .15s; box-shadow: 0 1px 4px rgba(0,0,0,.3);
}
.copy-photo-item.selected { border-color: var(--accent); }
.copy-photo-item.selected .copy-photo-check { opacity: 1; }
.copy-photo-item:hover .copy-photo-check { opacity: .35; }
.copy-photo-item.selected:hover .copy-photo-check { opacity: 1; }

/* ── 封存物件列表 ── */
.archive-list { display: flex; flex-direction: column; gap: 8px; }
.archive-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px 16px;
  flex-shrink: 0; /* .home-row 同型缺口（2026-07-19 體檢 2.4）：今日容器自然高度不咬人，
    一旦放進固定高度容器會重演 07-17 的卡片壓縮溢出事故，補齊不變量 */
}
.archive-row-info { min-width: 0; }
.archive-row-name { font-size: 15px; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.archive-row-sub { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
.archive-row-meta { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.archive-row-date { font-size: 12px; color: var(--text-muted); }

/* 客戶管理搜尋框（2026-07-17 P5，原本 inline style="max-width:320px" 抽成
   class：桌機沿用同樣的 320px 窄版，手機版在檔尾 media query 覆寫成滿版）*/
.cust-search-group { max-width: 320px; margin-top: 16px; }

/* ── 周邊設施提醒 ── */
.nearby-warning {
  font-size: 12px; color: #92600a; background: #fef9ec;
  border: 1px solid #f5d87a; border-radius: 6px;
  padding: 8px 12px; margin-bottom: 10px; line-height: 1.6;
}
.nearby-warning a { color: #92600a; text-decoration: underline; }
.nearby-ok {
  font-size: 12px; color: var(--text-muted);
  margin-bottom: 10px;
}
.nearby-ok a { color: var(--accent-d); text-decoration: underline; }

/* ── 內部資訊備註區塊 ── */
.internal-note-block {
  margin-top: 10px; padding: 10px 12px;
  background: var(--bg); border: 1px solid var(--border); border-radius: 6px;
  font-size: 13px; line-height: 1.8; white-space: pre-wrap;
}
.internal-note-label {
  display: block; font-size: 11px; font-weight: 600; color: var(--text-muted);
  letter-spacing: .05em; margin-bottom: 4px;
}

/* ── 「內部」分頁專用元件（P6 兩頁重排，2026-07-18）── */

/* 頂部安心提示條：三層底色制第 3 層（--bg-elevated），跟卡片白底做出區隔 */
.secure-banner {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12.5px;
  color: var(--text-secondary);
  background: var(--bg-elevated);
  border-radius: var(--radius);
  padding: 10px 14px;
  margin-bottom: 16px;
}

/* 工作摘要卡的一行式欄位（底價/議價、屋主/撥號）*/
.lockline {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--text-secondary);
  padding: 10px 0;
}
.lockline + .lockline { border-top: 1px dashed var(--border); }
.lockline .lk { font-size: 12px; color: var(--text-muted); flex-shrink: 0; }
.lockline b { font-family: 'Noto Serif TC', serif; color: var(--text); font-size: 16px; font-weight: 700; }

/* 撥號鈕——手機帶看/通話中直接點了打電話，不用先複製門號 */
.telbtn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  min-height: 40px;
  padding: 0 14px;
  border-radius: 20px;
  background: var(--accent);
  color: #fff;
  font-size: 13.5px;
  font-weight: 700;
  text-decoration: none;
  flex-shrink: 0;
}
.telbtn:hover { background: var(--accent-d); color: #fff; }

/* 已確認鈕：本頁唯一常駐 primary 滿版按鈕（原則 #7：此刻最該做的一件事）*/
.confirm-btn { width: 100%; margin-top: 12px; }

/* 低頻／空狀態內容收成一行卡片（開價異動歷史空狀態等），比常駐空卡省版面 */
.detail-oneline {
  font-size: 13.5px;
  color: var(--text-muted);
  padding: 10px 14px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

/* ── Collapsible cards ── */
.card-collapsible {
  border-left: 3px solid var(--accent);
  padding-left: 26px;
  transition: border-color 0.22s var(--ease), border-left-width 0.22s var(--ease),
              padding-left 0.22s var(--ease);
}
.card-collapsible.collapsed {
  border-left: 2px solid var(--border);
  padding-left: 27px;
}
.card-collapsible .card-header {
  display: flex;
  align-items: center;
}
.card-toggle-btn {
  flex: 1;
  display: flex;
  align-items: center;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
  text-align: left;
  min-width: 0;
}
.card-toggle-btn .section-title {
  flex: 1;
  margin-bottom: 0;
  user-select: none;
  transition: color 0.22s var(--ease);
}
.card-collapsible.collapsed .card-toggle-btn .section-title {
  color: var(--text-muted);
}
.card-body {
  overflow: hidden;
  transition: max-height 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}
.card-collapsible .card-body { margin-top: 16px; }
.card-collapsible.collapsed .card-body { margin-top: 0; }

/* 收合卡片標頭的展開/收起箭頭（我的資料方案 A 的兩個 accordion 用）——
   _card() 元件本身沒有內建箭頭圖示，這裡用 headerExtra 插入純文字箭頭，
   收合時朝下、展開時轉 180 度朝上 */
.acc-caret {
  color: var(--text-muted);
  margin-left: 8px;
  display: inline-block;
  transition: transform var(--duration) var(--ease);
}
.card-collapsible:not(.collapsed) .acc-caret { transform: rotate(180deg); }

/* ── 批次4（design-audit.md 低頻頁節，2026-07-17）：把幾個原本裸 inline
   style 的重複列排版抽成 class（零行為改動——基準值原樣照抄），純粹是
   為了能在下面的手機 media query 裡精準覆寫，桌機這裡的值跟抽出前完全
   一樣。 ── */
.doc-list-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.doc-list-actions { display: flex; gap: 8px; align-items: center; }
.doc-row { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--border); }
.acct-row { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px; }
.acct-row-actions { display: flex; align-items: center; gap: 8px; }

}

/* ═══ @layer mobile ═══ 手機覆寫的唯一去處，贏過上面兩層 ═══ */
@layer mobile {
/* ═══════════════════════════════════════════════════════════════
   MOBILE APP SHELL（2026-07-14 起，docs/MOBILE-PWA-BRIEF.md）
   手機 App 質感改造專用區塊，之後各階段（P1～P5）新增/修改的手機樣式
   一律追加在這個區塊「內」，不要插到檔案前段——這裡集中在檔尾是唯一保證
   贏過前面桌機規則 cascade 順序的位置（本檔案曾發生手機覆寫寫在前段被
   後段無 media query 的桌機規則蓋掉的 bug，.data-grid 事件）。
   全部規則包在 @media (max-width:768px) 內，桌機零影響。
   ═══════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {
  /* ── B2：捲動容器決策（body 維持唯一捲動容器，不引入內層 overflow-y:auto）──
     2026-07-17 P3 驗收時發現：body 同時宣告 overflow-x:hidden 會依 CSS
     overflow 配對規則把 body 的 overflow-y 從預設 visible 提升為 auto
     （devtools 量測證實：document.scrollingElement 是 html，body.scrollTop
     永遠是 0、body 自己從未真的捲動），等於「意外」造出一個內層捲動容器
     ——跟這條規則自己講的目標正好相反，而且會讓 body 變成 .mobile-header／
     .tabs 這類 position:sticky 元件的「最近捲動祖先」，但這個祖先永遠不動，
     sticky 因此完全失效（往下捲頁面時 header／tabs 整個滑走，不會釘住；
     此 bug 在 P3 之前就已存在，只是沒人捲夠遠去驗證過 .mobile-header）。
     修法：overflow-x:hidden 只留在 html（html 才是真正的捲動元素，
     document.scrollingElement 量測證實由它負責裁切水平溢出／擋橡皮筋），
     body 不重複宣告 overflow-x，避免觸發配對副作用；body 的實際捲動行為
     （唯一捲動容器）不變，只是不再被自己的 CSS 誤判成內層容器。已用
     playwright 在 staging 驗證：拿掉後 header／tabs 恢復正常 sticky。 */
  html { overflow-x: hidden; }
  body { overscroll-behavior-y: contain; } /* 擋掉整頁橡皮筋透出白底（html 的 overflow-x:hidden 已負責裁切水平溢出） */
  /* 基礎規則是固定 height，全站又是 box-sizing:border-box——只加 padding 會把
     內容往內擠、不會把列撐高，必須同時改 height:auto + min-height 把安全區
     「加」在外面（B1 的 viewport-fit=cover 生效後這幾條才真的有作用） */
  /* min-height 必須含瀏海高度：border-box 下 min-height:56px 會被 59px 的
     safe-area padding 整個吃掉，品牌列內容區塌到 ~31px 擠在狀態列正下方
     （black-translucent 上線後使用者實機回報「banner 被壓縮」，2026-07-19）。
     56px＋inset 也讓 P3 tabs sticky 的 top: calc(56px + inset) 假設重新成立。 */
  .mobile-header { height: auto; min-height: calc(56px + env(safe-area-inset-top, 0px)); padding-top: env(safe-area-inset-top, 0px); }
  /* 高度固定為 62px＋底部安全區：sheet/overlay 的錨點（--bnav-offset）要跟
     導覽列實際高度精確對齊。原本 height:auto 在瀏海機被安全區撐到 ~96px，
     而 sheet 錨點寫死 62px，下緣直接壓住整排圖示（2026-07-17 使用者 iPhone
     實機截圖抓到；模擬器無安全區所以歷輪驗收都測不到）。 */
  .bottom-nav { height: calc(62px + env(safe-area-inset-bottom, 0px)); min-height: 0; padding-bottom: max(8px, env(safe-area-inset-bottom)); }
  /* （2026-07-19 撤除）曾在此以 --vp-bottom-gap 補償視窗縮水,實測 iOS 連
     可繪範圍都裁掉,補償內容直接不被渲染——改為根治:捲動鎖不再用
     body position:fixed(見 dom.js),縮水不再被觸發,無需補償。 */


  /* ── B3：消除網頁感 Reset ── */
  /* 點擊高亮與長按選取：只鎖「UI 骨架」，內容區不鎖（業務要長按複製文案/電話） */
  .bottom-nav, .mobile-header, .bnav-more-sheet, .tabs, button {
    -webkit-tap-highlight-color: transparent;
    -webkit-user-select: none;
    user-select: none;
  }
  /* 消除 300ms 延遲與誤縮放 */
  .bottom-nav button, .bnav-more-sheet button, .tabs * { touch-action: manipulation; }
  /* iOS 聚焦輸入框自動放大的觸發條件是 font-size < 16px：全部輸入元件鎖 16px 起跳。
     .form-group 變體必須並列（1695 行的 .form-group input 是 14px、specificity 較高，
     只寫裸 input 會被它蓋掉——cascade 比 specificity 不比檔案順序；行號 2026-08-05 體檢校正）；
     checkbox/radio 依保護清單 A3 #5 排除，不碰 all:revert 修復。 */
  input:not([type=checkbox]):not([type=radio]), select, textarea,
  .form-group input:not([type=checkbox]):not([type=radio]),
  .form-group select, .form-group textarea { font-size: 16px; }
  /* 【分層等價修補，2026-08-11 issue #15】上面那條註解描述的是「分層之前」的世界：
     那時候要贏就得抄對方的組合寫法。分層之後 mobile 層本來就贏，於是裸 textarea
     的 16px 會蓋過 components 層的 .copy-textarea（14px）——文案分頁五個文字框
     字級整組變大。照抄一份維持原樣。
     ⚠ 這代表 .copy-textarea 目前**沒有**吃到 16px 防縮放，iOS 點進去仍會自動放大；
     這是既有偏差（分層前就這樣），不在本批範圍，要修另開單子，不要順手改值。 */
  .copy-textarea { font-size: 14px; line-height: 1.6; }
  /* 觸控回饋基準（各元件可再細化，但不得沒有回饋） */
  button:active, .bnav-item:active { transform: scale(0.97); transition: transform 120ms; }
  /* 【分層等價修補，2026-08-11 issue #15】上一行的 transition 會在「按住」期間蓋掉
     元件自己的 transition。燈箱的關閉／下載鈕（components 層 .lightbox .lightbox-close
     那組，0,2,0）分層前贏得過 button:active（0,1,1），按下去 scale 是瞬間的；分層後
     會變成 120ms 動畫。照抄一份維持原樣。 */
  .lightbox .lightbox-close:active,
  .lightbox .lightbox-download:active { transition: opacity var(--duration) var(--ease); }

  /* ── C2：底部導覽——物件／客戶／搜尋／更多，四鍵全平面 ──
     「新增」原本是凸出導覽列的圓形主鍵（FAB），2026-07-15 先改平面、
     2026-07-16 使用者拍板整顆移除：新增物件屬低頻操作撐不起導覽列位置，
     入口回歸首頁頂部滿版按鈕；四鍵版每鍵觸控範圍更寬，並替未來功能留位。
     四鍵共用 .bottom-nav-item 樣式，無任何專屬規則。 */

  /* ── C3：更多抽屜——分隔線＋登出紅字（順序見 index.html #bnav-more-sheet）── */
  .bnav-more-sheet-divider {
    height: 1px;
    background: var(--border);
    margin: 6px 4px;
  }
  .bnav-more-sheet button.bnav-more-logout,
  .bnav-more-sheet button.bnav-more-logout .mi {
    color: #c0392b;
  }
  .bnav-more-sheet button.bnav-more-logout:active {
    background: rgba(192,57,43,.08);
  }

  /* ── P1：首頁物件卡片化（docs/MOBILE-PWA-BRIEF.md D2，2026-07-14）──
     沿用 .home-row / .home-row-thumb / .home-row-info / .home-row-name /
     .home-row-sub / .home-row-price / .home-row-status 這批桌機也在用的既有
     class，不新增 DOM 節點（除了無封面時的縮圖佔位 div）。純靠這個區塊排在
     檔尾贏過 815 行那個舊版「清單縮小版」@media(max-width:768px) 區塊（同
     specificity、後寫的贏，理由同本區塊開頭註解）：那個舊區塊原本把
     .home-row-sub/.home-row-dots 隱藏、價格狀態欄位縮窄——這裡整批覆蓋成
     D2 規格的卡片版型。桌機因為不吃這條 media query，畫面完全不受影響。 */
  .home-rows {
    background: transparent;
    border: none;
    border-radius: 0;
    overflow: visible;
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
  .home-row {
    display: grid;
    /* flex-shrink:0——.home-rows 是 flex 直排，首頁容器自然高度沒事，但搜尋
       sheet 的結果區（#bnav-search-results）高度固定，flex 預設會把卡片壓到
       min-height，實機字體較高時內容溢出卡框（2026-07-17 使用者 iPhone 抓到；
       模擬器字體剛好塞進 88px 所以測不到）。卡片永遠不准被壓縮。 */
    flex-shrink: 0;
    /* 第 2 欄寬度由開價撐出來、第 3 欄吃剩餘空間：狀態 chip 因此貼著開價
       右側 8px 排（D2 ASCII 圖的「開價 [狀態chip]」），而不是 justify-end
       吊在卡片右緣（2026-07-15 使用者回報切齊邊緣不好看） */
    grid-template-columns: 72px auto 1fr;
    grid-template-rows: auto auto;
    column-gap: 12px;
    row-gap: 4px;
    align-items: center;
    min-height: 88px;
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--card);
  }
  /* :last-child 的 specificity（0,2,0）比單純 .home-row 高，源自 799 行那條
     舊規則會贏過上面 border 覆蓋——單獨列出來才能讓最後一張卡也有完整邊框 */
  .home-row:last-child { border-bottom: 1px solid var(--border); }
  .home-row:active { background: var(--bg-elevated); transition: background 120ms; }
  /* 【分層等價修補，2026-08-11 issue #15】上面這條 .home-row 把 background 設成
     var(--card)。分層之前，components 層的 .home-row:hover（0,2,0）specificity 比它高，
     所以窄視窗接滑鼠時仍會有 hover highlight；分層之後「層」先比，mobile 層的
     .home-row 會贏，hover 就沒了。這裡照抄一份是為了「這批不准改任何像素」，
     不是新設計。真手機沒有 hover 手勢，摸得到的只有上一行的 :active。 */
  .home-row:hover { background: var(--bg-elevated); }
  .home-row-thumb { grid-column: 1; grid-row: 1 / 3; width: 72px; height: 72px; border-radius: 8px; }
  .home-row-info { grid-column: 2 / 4; grid-row: 1; }
  .home-row-name { font-size: 16px; }
  .home-row-sub { display: block; font-size: 14px; }
  /* D2 物件卡沒有狀態圓點。原本靠 830 行舊區塊的 display:none 隱藏，批次 4
     清死碼時誤判「全被本區塊覆蓋」把它連帶刪除，圓點跑進卡片擠爆版面
     （使用者在搜尋 sheet 抓到，2026-07-17）——隱藏規則屬於本區塊，補回這裡。 */
  .home-row-dots { display: none; }
  .home-row-price {
    grid-column: 2; grid-row: 2; justify-self: start;
    width: auto; font-size: 16px; text-align: left;
  }
  .home-row-status { grid-column: 3; grid-row: 2; justify-self: start; width: auto; }
  /* 無封面佔位：狀態色塊＋房型 icon（純 CSS mask 畫 icon，不掛真的 <i data-lucide>
     節點——若直接掛節點，desktop 會在同一批 render() 輸出裡也長出這顆 icon，
     破壞桌機零影響；mask 圖案整塊包在這個 media query 裡，桌機完全看不到）。
     class 刻意不用 badge-*（見 dashboard.js 註解），此處自訂 ph-* 系列。 */
  .home-row-thumb-ph {
    position: relative;
    width: 100%;
    height: 100%;
    background: var(--bg-elevated);
  }
  .home-row-thumb-ph::after {
    content: '';
    position: absolute;
    inset: 0;
    margin: auto;
    width: 28px;
    height: 28px;
    background-color: var(--text-muted);
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z'/%3E%3Cpolyline points='9 22 9 12 15 12 15 22'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z'/%3E%3Cpolyline points='9 22 9 12 15 12 15 22'/%3E%3C/svg%3E");
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: contain;
    mask-size: contain;
  }
  .home-row-thumb-ph.ph-live { background: var(--accent); }
  .home-row-thumb-ph.ph-live::after { background-color: #fff; }
  .home-row-thumb-ph.ph-sold,
  .home-row-thumb-ph.ph-archived { background: #efebe3; }
  .home-row-thumb-ph.ph-sold::after,
  .home-row-thumb-ph.ph-archived::after { background-color: var(--text-secondary); }

  /* ── P2：搜尋/篩選 sheet（docs/MOBILE-PWA-BRIEF.md C4，2026-07-15）──
     結構照抄 BOTTOM NAV (Mobile) 區塊裡的 .bnav-more-overlay/.bnav-more-sheet
     （style.css 約 608～676 行）。差異：1) 高度鎖 85dvh（更多抽屜是內容自然
     高度撐出來的；搜尋結果清單長度不固定，需要固定高度＋內部捲動）。
     2) sheet 內部有可捲動的結果清單（#bnav-search-results，直接借用 P1 的
     .home-rows 排版，只覆寫 overflow 讓它可以捲動）跟可橫向捲動的 chips 列，
     touch-action 依區塊分開設定，不能像 more-sheet 整塊 none（那裡面沒有
     需要捲動的子區域）。這批規則新寫，全部包在本區塊內，符合 A2 #1；
     旧 more-sheet 那組規則是 2026-07-11（本規則生效前）的舊帳，不是先例。 */
  .bnav-search-overlay {
    display: none;
    position: fixed;
    left: 0; right: 0; top: 0; bottom: calc(62px + env(safe-area-inset-bottom, 0px));
    background: rgba(20, 16, 8, .32);
    z-index: 210;
    opacity: 0;
    transition: opacity var(--duration-lg) var(--ease);
    touch-action: none;
  }
  .bnav-search-sheet {
    display: none;
    flex-direction: column;
    position: fixed;
    left: 0; right: 0; bottom: calc(62px + env(safe-area-inset-bottom, 0px));
    /* 70dvh（原 85dvh）：sheet 越高、外部「點一下就關」的 overlay 區域越小，
       85dvh 時只剩頂部一條縫，使用者真機回報點外面關不掉（2026-07-16）。
       底色用頁面的 --bg 而非 --card：結果卡片本身是 --card 白底，白疊白
       只剩框線，跟物件頁「米色底、白卡片」的層次不一致（同日使用者回報）。 */
    height: 70vh; height: 70dvh;
    z-index: 220;
    background: var(--bg);
    border-top: 1px solid var(--border);
    border-radius: 16px 16px 0 0;
    box-shadow: 0 -8px 24px rgba(0,0,0,.16);
    padding: 10px 14px 14px;  /* 同上：錨點已含安全區 */
    transform: translateY(100%);
    transition: transform var(--duration-lg) var(--ease);
    touch-action: none;
  }
  body.bnav-search-open .bnav-search-overlay { display: block; opacity: 1; }
  body.bnav-search-open .bnav-search-sheet { display: flex; transform: translateY(0); }
  .bnav-search-sheet-handle {
    width: 36px; height: 4px; border-radius: 2px;
    background: var(--border);
    margin: 6px auto 10px;
    flex-shrink: 0;
  }
  .bnav-search-top {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-shrink: 0;
  }
  .bnav-search-input-wrap {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 8px;
    height: 44px;
    padding: 0 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--card);
    color: var(--text-muted);
  }
  .bnav-search-cancel {
    flex-shrink: 0;
    min-height: 44px;
    padding: 0 10px;
    border: none;
    background: none;
    font-family: inherit;
    font-size: 16px;
    color: var(--accent-d);
  }
  .bnav-search-cancel:active { opacity: .6; }
  #bnav-search-input {
    flex: 1;
    height: 100%;
    border: none;
    background: transparent;
    font-family: inherit;
    font-size: 16px;
    color: var(--text);
    padding: 0;
  }
  #bnav-search-input:focus { outline: none; }
  /* chips 列：橫向捲動一列，資料來源與桌機 #sidebar-filters 同一份
     （core/sidebar.js 的 _filterOptions() 共用），這裡只是渲染成 chip 而非
     直向列表項。touch-action:pan-x 讓橫滑手勢不被外層 sheet 的 none 卡住。 */
  .bnav-search-chips {
    display: flex;
    gap: 8px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding: 12px 2px 6px;
    flex-shrink: 0;
    touch-action: pan-x;
  }
  .bnav-search-chips::-webkit-scrollbar { display: none; }
  .m-search-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    min-height: 44px;
    padding: 0 14px;
    border-radius: 18px;
    border: 1px solid var(--border);
    background: var(--card);
    color: var(--text-secondary);
    font-family: inherit;
    font-size: 13px;
    font-weight: 700;
    white-space: nowrap;
  }
  .m-search-chip:active { transform: scale(0.96); transition: transform 120ms; }
  .m-search-chip-count { font-size: 11px; font-weight: 700; color: var(--text-muted); }
  .m-search-chip.active { background: var(--accent); border-color: var(--accent); color: #fff; }
  .m-search-chip.active .m-search-chip-count { color: rgba(255,255,255,.85); }
  .m-search-chip.warn { color: var(--warning); border-color: var(--warning-border); }
  .m-search-chip.warn.active { background: var(--warning); border-color: var(--warning); color: #fff; }
  .m-search-chip.warn.active .m-search-chip-count { color: rgba(255,255,255,.85); }
  /* 結果清單：直接借用 P1 的 .home-rows（flex column + gap:10px），只覆寫
     overflow 讓它在固定 85dvh 高度的 sheet 內可以捲動——id 的 specificity
     天然贏過 class，不用擔心跟 .home-rows{overflow:visible} 的順序問題。 */
  #bnav-search-results {
    flex: 1;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    touch-action: pan-y;
    padding: 6px 0 4px;
  }
  .m-search-empty {
    padding: 48px 20px;
    text-align: center;
    color: var(--text-muted);
    font-size: 14px;
  }

  /* ── P3：首頁重排「方案 A：清單即主角」（docs/DESIGN-LANGUAGE.md 首頁重排
     試點，2026-07-16）：六張統計卡＋滿版新增按鈕，改成標題列＋一列可橫向
     捲動的狀態膠囊。桌機零影響——這裡只是切換顯示哪組 DOM，.home-head/
     .home-stats 桌機的樣式（778/780 行）完全沒動，只在這個 media query
     裡把它們在手機隱藏、換成下面的 .m-home-*（views/dashboard.js 同一次
     render() 輸出兩組，見該檔註解）。
     【2026-07-16 回歸修正】.home-head 不是首頁專屬——customers.js 的頁首
     也用它。無條件隱藏會把客戶頁的標題與「＋新增客戶」一起藏掉（盤點抓到、
     正式站已中招一晚）。改用「前面有 .m-home-head 才隱藏」當條件：只有
     首頁的 render() 會輸出 .m-home-head，客戶頁不受影響。 */
  .m-home-head ~ .home-head, .m-home-head ~ .home-stats { display: none; }
  .m-home-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 16px;
  }
  .m-home-title { font-size: 22px; font-weight: 700; color: var(--text); line-height: 1.2; }
  .m-home-subtitle { font-size: 12px; color: var(--text-muted); margin-top: 4px; }
  .m-home-add-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 44px;
    padding: 0 18px;
    border: none;
    border-radius: 22px;
    background: var(--accent);
    color: #fff;
    font-family: inherit;
    font-size: 14px;
    font-weight: 700;
    flex-shrink: 0;
  }
  .m-home-add-btn:active { transform: scale(0.97); transition: transform 120ms; }
  /* 膠囊列：高 40px 依方案 A 定案規格。註：略低於 D1 的 44×44 觸控基準，
     是這次設計判斷的明確取捨（列高本身留白＋左右 gap 8px 撐開誤觸間距），
     沒有額外加高處理——若之後真機回報誤觸，再回頭加高，不在本次範圍內自行更動。 */
  .m-home-chips {
    display: flex;
    gap: 8px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding: 2px 2px 4px;
    margin-bottom: 16px;
    touch-action: pan-x;
  }
  .m-home-chips::-webkit-scrollbar { display: none; }
  .m-home-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    /* mockup 畫 40px，但 D1 觸控鐵則 ≥44px 優先，且與搜尋 sheet 的
       .m-search-chip 一致（同為 44） */
    min-height: 44px;
    padding: 0 14px;
    border: 1px solid var(--border);
    border-radius: 22px;
    background: var(--card);
    color: var(--text-secondary);
    font-family: inherit;
    font-size: 13px;
    font-weight: 500;
    white-space: nowrap;
  }
  .m-home-chip:active { transform: scale(0.96); transition: transform 120ms; }
  .m-home-chip-count { font-family: 'Noto Serif TC', serif; font-weight: 700; color: var(--accent-d); }
  .m-home-chip.active { background: var(--accent-d); border-color: var(--accent-d); color: #fff; }
  .m-home-chip.active .m-home-chip-count { color: #fff; }
  .m-home-chip.warn { color: var(--warning); border-color: var(--warning-border); background: var(--warning-bg); }
  .m-home-chip.warn .m-home-chip-count { color: var(--warning); }
  .m-home-chip.warn.active { background: var(--warning); border-color: var(--warning); color: #fff; }
  .m-home-chip.warn.active .m-home-chip-count { color: #fff; }

  /* ── 全域槓桿修正（2026-07-17，design-audit.md「全域」節，
     docs/DESIGN-LANGUAGE.md 原則 #4/#5＋觸控 ≥44 規範）：一次修、
     全站受益的槓桿點，範圍限手機（≤768px），桌機零影響。 ── */

  /* 觸控目標：.btn-sm/.btn-xs 桌機實高約 35px/26px，詳情頁、文件庫、
     成交紀錄、文案分頁的主要操作幾乎全是這兩顆——撐 padding，字級不動 */
  .btn-sm { min-height: 44px; padding: 10px 14px; }
  .btn-xs { min-height: 44px; padding: 10px 10px; }

  /* .back-btn（純文字「←」箭頭）、.status-pill（詳情頁最常按的元件之一）
     桌機實高約 33px，同樣的觸控問題，同一批修正 */
  .back-btn { min-height: 44px; padding: 10px 6px; display: none; }
  .status-pill { min-height: 44px; padding: 10px 14px; }

  /* 卡片內距：桌機 26px 28px 遠高於 token「12–14px」規範，手機每張卡
     浪費約 30px 寬；統一收窄 */
  .card { padding: 16px 14px; }
  /* 【分層等價修補，2026-08-11 issue #15】.card-collapsible.collapsed（0,2,0）在
     components 層把 padding-left 設成 27px，分層前贏過上面的 .card（0,1,0），分層後
     會反過來變 14px——「我的資料」頁兩張收合卡會往左位移 13px。照抄一份維持原樣。
     附註：展開狀態的 .card-collapsible 也是 0,1,0，分層前就已經被 .card 蓋成 14px，
     所以「收合 27px／展開 14px」這個不一致是既有現象，不是這次造成的，留待另案。 */
  .card-collapsible.collapsed { padding-left: 27px; }

  /* 圓角兩級制（原則 #5：容器 12px／小元件 8px，只此兩級）：--radius-sm
     基準值是 10px，介於兩級之間的第三級圓角，全站按鈕/輸入框/縮圖/
     school-tag/下拉選單都吃這顆變數。桌機重做前不能動 :root 的基準宣告
     （會波及桌機），改成手機這裡逐一校正常用元件本身的 border-radius。 */
  .btn,
  /* checkbox/radio 排除理由同 3122 行（分層等價修補，issue #15）：分層前靠
     components 層 all:revert 的較高 specificity 擋著，分層後擋不住，圓角會套到
     原生勾選框上。 */
  .form-group input:not([type=checkbox]):not([type=radio]),
  .form-group select,
  .form-group textarea,
  .school-tag,
  .detail-head-thumb,
  .status-dropdown,
  .detail-more-dropdown,
  .mortgage-item,
  .alert,
  .photo-item {
    border-radius: 8px;
  }
  .memo-tab { border-radius: 8px 8px 0 0; }
  /* 搜尋 sheet 與首頁膠囊同為 44px 高全圓 pill，圓角卻一個 18px 一個
     22px——同型元件兩種圓角，統一 22px（= 44/2） */
  .m-search-chip { border-radius: 22px; }

  /* 留白 4 倍數階（原則 #4）：form-group 間距、輸入框內距、封存列內距
     原本 18px／11px 14px／14px 16px，都不是 4 的倍數 */
  .form-group { margin-bottom: 16px; }
  /* 【分層等價修補，2026-08-11 issue #15】checkbox/radio 必須排除，理由同 1765 行
     那條 all:revert（2026-07-11 使用者實機回報「勾選框被撐成滿版空心方框」）。
     分層之前這裡不排除也沒事：components 層的
     .form-group input[type=checkbox]{all:revert}（0,2,1）specificity 比這條（0,1,1）高，
     替我們擋掉了。分層之後 mobile 層先贏，勾選框會被填成米色底——擋不住了，
     所以把排除條件寫進 selector 本身，不再寄生在別人的 specificity 上。 */
  .form-group input:not([type=checkbox]):not([type=radio]),
  .form-group select,
  .form-group textarea {
    padding: 12px 14px;
    /* 全域補件（design-audit「全域」節）：輸入框背景 hardcode #fff 疊在
       --card（#fffdf8）白卡上,近似白疊白,只剩框線撐層次。手機改用 --bg
       （頁底米色）,形成「米底 → 白卡 → 米色輸入框」三層制;只動手機,
       桌機仍是 #fff（沿用既有版型,不冒風險）。 */
    background: var(--bg);
  }
  .archive-row { padding: 16px; }

  /* 字級階（22/16/14/12）：page-title 桌機 26px 不在階上，手機標題頁
     （客戶管理、封存物件、新增物件…）一律降到 22 */
  .page-title { font-size: 22px; }

  /* ── P3：物件詳情分頁標籤與操作列（docs/MOBILE-PWA-BRIEF.md D3，
     design-audit.md property-detail-core 節，2026-07-17）。沿用既有
     .tabs/.tab class 與既有 DOM，不新增節點；JS 端 activateTab()
     （core/format.js）另外加 scrollIntoView 置中，這裡只管樣式。
     桌機零影響：本區塊全部包在這個 media query 內，.tabs/.tab 桌機規則
     （997/1023 行附近）完全不動。 ── */
  .tabs {
    position: sticky;
    top: calc(56px + env(safe-area-inset-top, 0px));
    z-index: 190;
    background: var(--bg);
    gap: 8px;
    margin: 0 -16px 16px;
    padding: 8px 16px;
    border-bottom: none;
    scroll-snap-type: x proximity;
    -webkit-mask-image: linear-gradient(to right, transparent, #000 16px, #000 calc(100% - 16px), transparent);
    mask-image: linear-gradient(to right, transparent, #000 16px, #000 calc(100% - 16px), transparent);
  }
  .tab {
    scroll-snap-align: center;
    flex-shrink: 0;
    height: 48px;
    display: flex;
    align-items: center;
    padding: 0 18px;
    border-radius: 999px;
    background: var(--bg-elevated);
    color: var(--text-secondary);
    border-bottom: none;
    margin-bottom: 0;
  }
  .tab:active { transform: scale(0.97); transition: transform 120ms; }
  .tab.active { background: var(--accent-d); color: #fff; }
  .tab.lock.active { background: var(--primary); color: #f5ead8; }

  /* D3 第 4 點：頁尾操作列（狀態 pill／分享／編輯／更多）滿版、≥48px、間距 12px */
  .detail-head-actions { gap: 12px; }
  .detail-head-actions .status-pill,
  .detail-head-actions .btn-sm { min-height: 48px; }
  /* 分享/編輯手機只留圖示（動作用圖示、資訊留文字）：文字 span 隱藏、
     按鈕收成 48px 方鈕。狀態 pill 是資訊，保留文字。 */
  .detail-head-actions .act-label { display: none; }
  .detail-head-actions .btn-sm { min-width: 48px; padding-left: 0; padding-right: 0; justify-content: center; }
  /* 982 行舊規則的 space-between 會把四顆平均打散（行號 2026-08-05 體檢校正）；
     照 mockup 改為狀態 pill 靠左、圖示鈕聚右成一組 */
  .detail-head-actions { justify-content: flex-start; }
  .detail-head-actions > .btn-sm:first-of-type { margin-left: auto; }

  /* ── 總覽密度修正（2026-07-18 使用者實機回報：更多規格/學區/特點還是
     桌機的鬆散排版直落手機）── */
  /* 展開明細改「標籤左、值右同一行」：蓋掉通用 .data-grid 手機單欄規則
     （那條是給短表用的；13 行流水帳兩行一欄位會拉出近三屏） */
  .spec-more-body { display: grid; grid-template-columns: 96px 1fr; row-gap: 0; }
  .spec-more-body dt, .spec-more-body dd { margin: 0; padding: 9px 0; border-top: 1px solid var(--border-light); }
  .spec-more-body dt { font-size: 12.5px; color: var(--text-muted); }
  .spec-more-body dd { font-size: 14.5px; }
  .spec-more-body dt:first-of-type, .spec-more-body dd:first-of-type { border-top: none; }
  /* 學區一行式 */
  .school-line { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
  .school-line-label { font-size: 12px; color: var(--text-muted); font-weight: 600; }
  /* 特點：短句列表不需要整條分隔線，行距收緊 */
  .features li { border-top: none; padding: 4px 0; }

  /* audit #6：廣告文案標題不在字階上，手機降到 16（桌機沿用 .ad-headline 18px 不變） */
  .ad-headline { font-size: 16px; }

  /* audit #7：開價是本頁該有的主角，收在 22 字階上（桌機仍是 28px serif 不變） */
  .aside-price { font-size: 22px; }

  /* audit #8：開價異動歷史刪除鈕原本 padding 2px 6px 太小，手機撐大到 44 見方以上 */
  .price-history-del { min-width: 44px; min-height: 44px; padding: 10px 12px; }

  /* audit #5：整頁彩色元素超過 3 處（原則 #6）——學區標籤、特點圓點降中性灰，
     只留狀態 pill 與逾期紅字這兩處彩色 */
  .school-tag { color: var(--text-secondary); }
  .features li::before { background: var(--text-muted); }

  /* audit #4：周邊設施提醒 hardcode 色（#92600a/#fef9ec/#f5d87a）改走 --warning
     token，圓角收到 8px（兩級制第二級，原本 6px 是第三級） */
  .nearby-warning {
    color: var(--warning); background: var(--warning-bg);
    border-color: var(--warning-border); border-radius: 8px;
  }
  .nearby-warning a { color: var(--warning); }

  /* audit #3：周邊設施裸 table（property-detail-core.js:196 一帶）手機卡片化。
     JS 端 markup 不動（桌機零風險），純 CSS 把 table/tr/td 轉區塊：長店名
     獨自一行、距離＋交通時間另起一行，不再擠壓三欄。.detail-main 底下全站
     只有這一個 table，範圍安全。 */
  .detail-main table { display: block; width: 100%; }
  .detail-main table tr {
    display: flex; flex-wrap: wrap; align-items: baseline;
    column-gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border-light);
  }
  .detail-main table tr:last-child { border-bottom: none; }
  .detail-main table td { display: block; padding: 0; white-space: normal; }
  .detail-main table td:first-child { flex: 1 1 100%; }

  /* ── P5：客戶管理頁卡片化（docs/MOBILE-PWA-BRIEF.md D2 補件，design-audit
     customers 節，2026-07-17）。沿用既有 .archive-list/.archive-row／
     .archive-row-info／.archive-row-name／.archive-row-sub／.archive-row-meta
     class，不新增 DOM 節點。archived.js（封存物件列表）也用同一組 class，
     但它的容器沒有 id——customers.js 的容器是 #customer-rows（customers.js:41），
     這裡全部加上 #customer-rows 前綴限定只在客戶頁生效，不會波及封存物件頁
     的手機版面（發包限制明講不能動 .archive-row 全域規則，這裡改用 ID
     選擇器提高 specificity 來限定範圍，而不是改寫共用規則）。 */
  #customer-rows.archive-list { gap: 10px; }
  #customer-rows .archive-row {
    min-height: 88px;
    border-radius: 12px;
    padding: 14px 16px;
    flex-shrink: 0; /* 同上（2026-07-19 體檢 2.4），客戶頁手機版覆寫區塊也要補齊 */
  }
  #customer-rows .archive-row:active { background: var(--bg-elevated); transition: background 120ms; }

  /* 搜尋框：桌機 320px 窄版在手機上不滿版、跟其他手機輸入框 44px 高不一致
     （design-audit #4），這裡滿版＋固定 44px 高 */
  .cust-search-group { max-width: 100%; }
  .cust-search-group input { min-height: 44px; }

  /* 「同電話」checkbox 觸控範圍加大（design-audit #5）：label 本身撐到
     44px 高、checkbox 本體放大到 20px 見方，維持原生 checkbox 外觀（不用
     width/height 硬拉伸——那樣在部分瀏覽器會把方框拉成長方形） */
  .cust-line-same-label {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 6px 4px;
    gap: 6px;
  }
  #cust-line-same { width: 20px; height: 20px; }

  /* 封存客戶（破壞性操作）獨立一行置底，不跟儲存/取消擠在同一列
     （design-audit #6）。.btn-row 本身是 display:flex + flex-wrap，靠
     flex-basis:100% 讓這顆按鈕自己換行，不改動 .btn-row 共用規則。 */
  .cust-archive-btn {
    flex-basis: 100%;
    margin-left: 0;
    margin-top: 4px;
    order: 3;
    text-align: center;
  }

  /* ── 批次 3：物件編輯表單（docs/MOBILE-PWA-BRIEF.md，design-audit
     property-edit-form 節，2026-07-17）。沿用既有 DOM/class,只加
     .spot-row/.spot-type/.spot-input/.spot-del（property-edit-form.js
     _carSpotsHtml/_motoSpotsHtml 已改成吃這組 class 而非 inline style）。
     桌機零影響：本區塊全部包在這個 media query 內。 ── */

  /* audit #1：edit-toolbar sticky top:0,手機上滑進 .mobile-header
     （z-index 200）底下,儲存/取消鈕被完全遮住（實測 rect：toolbar 與
     header 同樣 top:0,按鈕落在 header 的 0–56px 範圍內）。
     第一版修法只讓出 header 高度（56px）,結果撞上另一個問題：編輯表單
     頁面本身也有 .tabs（P3 已改 sticky top:56px、z-index:190),兩個 sticky
     元件搶同一個 top 位置時,後面的（.edit-toolbar,DOM 順序在 .tabs 之後）
     會被先黏住的 .tabs 蓋住——實測 toolbar 完全消失在 tabs 底下,跟原本
     被 header 遮住的 bug 現象一樣,只是換一層擋。正確做法是再疊加 .tabs
     的實際高度（48px 內容高＋上下 padding 8px×2 = 64px,CSS 是固定 px、
     不受內容影響),讓 toolbar 排在 tabs 下緣。z-index 維持 10（低於
     header 200、也低於 tabs 190),不搶在兩者前面。 */
  .edit-toolbar {
    top: calc(56px + 64px + env(safe-area-inset-top, 0px));
  }

  /* audit #2：車位/機車位列 4 件套（類型 select＋樓層/編號 input＋刪除鈕）
     inline flex 不換行,手機 <400px 每格擠到 ~70px。改 grid 兩欄：
     - 類型 select（.spot-type，只有車位列才有）獨占一行；
     - 樓層／編號 input 並排（機車位列沒有 .spot-type,這兩個 input 就會
       自然佔滿第一行）；
     - 刪除鈕（.spot-del）獨占一行、靠右,避免跟輸入框太近誤觸。 */
  .spot-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
  }
  .spot-row .spot-type { grid-column: 1 / -1; }
  .spot-row .spot-del { grid-column: 1 / -1; justify-self: end; }

  /* ── 批次4：低頻頁視覺掃尾（design-audit.md，2026-07-17）。各小節註明
     對應哪個畫面/audit 條目，全部包在本 media query 內，桌機零影響。 ── */

  /* 殼層 modal（index.html #school-modal/#share-modal）：手機慣用 bottom
     sheet，原本置中 modal 在鍵盤彈起時可用高度吃緊。.modal-panel 貼底、
     圓角改上緣兩角，寬度滿版。 */
  .modal { align-items: flex-end; padding: 0; }
  .modal-panel {
    width: 100%;
    max-height: 85vh;
    border-radius: 16px 16px 0 0;
    padding: 20px 16px calc(env(safe-area-inset-bottom, 0px) + 20px);
  }

  /* property-deed：deed-source-toggle（PDF上傳／貼上連結切換鈕）觸控<44 */
  .deed-source-toggle button { min-height: 44px; padding: 10px 14px; }

  /* property-copy：memo-tab 分頁列觸控偏小（列高~30px、關閉×鈕與新增＋鈕
     熱區都<20px），撐高整列＋加大兩顆小按鈕的熱區 */
  .memo-tab { min-height: 44px; padding: 0 14px; }
  .memo-tab-close { padding: 12px; margin: -12px; font-size: 15px; }
  .memo-tab-add { min-width: 44px; min-height: 44px; display: flex; align-items: center; justify-content: center; padding: 0; }
  /* copy-photo-item 縮圖本身 66px 高可接受，勾勾熱區順手加大一點 */
  .copy-photo-check { width: 24px; height: 24px; font-size: 13px; }

  /* property-deals：金額類欄位完全沒套「數字是明星」，成交紀錄卡片內的
     data-grid 值改用襯線字體加大加粗（只限交易紀錄卡片，不影響謄本等其他
     data-grid 用法）；空 form-group 佔位格手機單欄無意義，隱藏 */
  [id^="card-deal-"] .data-grid dd {
    font-family: 'Noto Serif TC', serif;
    font-weight: 700;
    font-size: 16px;
    color: var(--accent-d);
  }
  .form-group:empty { display: none; }

  /* property-share：Ctrl+V 貼到 LINE Desktop 是桌機話術，手機看不到滑鼠
     剪貼簿貼上情境，直接隱藏（audit #1 建議的兩個修法之一） */
  .share-ctrlv-hint { display: none; }

  /* myprofile／agent-settings：設施勾選 chip（_nearbyChips，app.js）觸控
     <44，兩個畫面共用同一份渲染邏輯、無專屬 class，用容器 id 限定範圍 */
  #ag-nearby-near label, #ag-nearby-far label,
  #my-nearby-near label, #my-nearby-far label {
    min-height: 44px;
    padding: 10px 14px;
  }

  /* agent-settings／myprofile：步行門檻 input（width:110px inline）＋
     說明文字同列，手機說明文字被擠掉行，改允許換行。用 :has() 精準鎖定
     這兩個 input 所在的那一列，不影響其他頁面同樣是 flex 的 form-group
     子層（例如 deed 分頁的貼上連結列） */
  .form-group:has(#ag-nearby-walk) > div:last-child,
  .form-group:has(#my-nearby-walk) > div:last-child {
    flex-wrap: wrap;
    row-gap: 4px;
  }
  #ag-nearby-walk, #my-nearby-walk { flex-shrink: 0; }

  /* property-documents：卡片 header 一列塞「已上傳文件」標題＋類型
     select＋上傳鈕，手機必擠爆，改兩行（標題行＋操作行）；列本身補
     :active 按壓回饋 */
  .doc-list-head { flex-wrap: wrap; }
  .doc-list-actions { width: 100%; flex-wrap: wrap; }
  .doc-row:active { background: var(--bg-elevated); transition: background 120ms; }

  /* accounts：每列使用者卡組織/角色 select＋重設密碼／停用鈕 4 件擠一列，
     手機讓操作列獨立換行、select 統一 44 高 */
  .acct-row-actions { width: 100%; flex-wrap: wrap; }
  .acct-row-actions select { min-height: 44px; }

  /* property-photos：photo-del/photo-cover 24px 圓鈕觸控<44 且相距僅
     6px，刪除是不可逆傾向操作（雖有 undo toast 護底）容易誤觸，手機放大
     並拉開距離 */
  .photo-del, .photo-cover { width: 36px; height: 36px; }
  .photo-del { top: 8px; right: 8px; }
  .photo-cover { top: 8px; right: 60px; }

  /* login/setup：login-panel 上下留白 100px 過大、內距非 4 倍數 */
  .login-panel { margin: 48px auto; padding: 32px 24px; }

  /* ── P4：燈箱蝦皮式下滑關閉（docs/MOBILE-PWA-BRIEF.md D4，2026-07-17）──
     跟手/放手動畫全部由 JS 直接寫 inline style（views/property-photos.js
     的 initLightboxSwipe），這裡只加 will-change 效能提示，不影響版面；
     transform/opacity 的實際數值一律來自 JS，不在這裡定義。 */
  .lightbox-stage { will-change: transform; }

  /* ── 層級式導覽（2026-07-18）：core/chrome.js 用 body.chrome-inner 切換。
     .mobile-header 容器沿用，只切內容（品牌組 vs 返回組），不新增 sticky
     容器——P3 的 .tabs sticky top:calc(56px+safe-area) 與編輯表單工具列
     sticky top:calc(56px+64px+safe-area) 都依賴 header 固定 56px 高度，
     兩組內容高度一致（都在同一個 min-height:56px 容器內），不會跑位。 ── */
  .mobile-header .mobile-chrome-brand {
    display: flex;
    align-items: center;
    gap: 12px;
  }
  .mobile-header .mobile-chrome-back-group {
    display: none;
    align-items: center;
    gap: 4px;
    flex: 1;
    min-width: 0;
  }
  body.chrome-inner .mobile-header .mobile-chrome-brand { display: none; }
  body.chrome-inner .mobile-header .mobile-chrome-back-group { display: flex; }
  .mobile-chrome-back-btn {
    width: 44px;
    height: 44px;
    margin-left: -10px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    color: var(--text);
    padding: 0;
    cursor: pointer;
  }
  .mobile-chrome-title {
    font-size: 16px;
    font-weight: 700;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* 房間模式：底部導覽整條隱藏（含其 safe-area 高度），內容區與 toast 的
     留白對應縮減——原本 80px/74px 是為固定底部導覽預留的空間，房間模式
     沒有底部導覽，不需要這段留白。 */
  body.chrome-inner .bottom-nav { display: none; }
  body.chrome-inner .app-body { padding-bottom: 24px; }
  body.chrome-inner .toast { bottom: calc(20px + env(safe-area-inset-bottom, 0px)); }
}

}