/* 開課快手 商戶後台 — 共用樣式。
 * 品牌色：teal #13B0B6／blue #2563B5（見 claude-config/ACpay_company_data.md）。
 * 手機優先：預設即窄版單欄，桌面用 min-width media query 放寬，避免 375px 視窗出現橫向捲動。
 */

:root {
  /* 2026-08-24 商戶後台改苔綠（Tesla 指定，跟 LINE 選單／學員頁同一個苔綠 #5C7050）：
   * --teal／--blue 這兩個 token 名沿用（後台大量引用，改名要動幾十處），值改成苔綠系——
   * --teal＝苔綠主色（頂欄、強調、chip），--blue＝深一階苔綠（主要按鈕、連結，深一點是為了
   * 白底上的文字對比）。語意色（danger／warn／ok／badge）刻意不動，它們傳達狀態不是品牌色。 */
  --teal: #5C7050;
  --blue: #4A5A42;
  --bg: #f4f7f8;
  --card-bg: #ffffff;
  --text: #1f2933;
  --text-muted: #62717b;
  --border: #dde3e6;
  --danger: #d93025;
  --warn: #b6740a;
  --ok: #1a8f5a;
  --radius: 10px;
  --nav-h: 56px;
}

* { box-sizing: border-box; }

/* 防呆：本檔許多規則（button/.btn 的 display:inline-block、.form-grid 的 display:grid 等）
 * 跟瀏覽器內建的 [hidden]{display:none} 屬於同一特異性（class 與屬性選擇器同權重），
 * 依 CSS cascade 規則 author stylesheet 會贏過 UA 預設值，導致單靠 hidden 屬性
 * 「有時候」壓不住這些規則、元素還是顯示出來（曾在 products.html 的「取消編輯」按鈕、
 * pass 專屬欄位 .form-grid 上實測到）。用 !important 保證 hidden 屬性永遠優先生效。 */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  max-width: 100%;
  overflow-x: hidden; /* 保底：任何子元素不小心溢出也不讓整頁橫向捲動 */
}

body {
  font-family: -apple-system, BlinkMacSystemFont, "PingFang TC", "Noto Sans TC", "Microsoft JhengHei", sans-serif;
  background: var(--bg);
  color: var(--text);
  line-height: 1.5;
}

a { color: var(--blue); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ---------------------------------------------------------------------------
 * 導覽列（由 admin.js 注入到 #app-topbar；此處只定義樣式）
 * ------------------------------------------------------------------------- */

#app-topbar {
  position: sticky;
  top: 0;
  z-index: 50;
  min-height: var(--nav-h);
  background: var(--teal); /* 2026-08-24 純苔綠色塊（Tesla：不要漸層），見 :root 註解 */
  color: #fff;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
}

.topbar-brand {
  font-size: 1.1rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  margin-right: 8px;
  white-space: nowrap;
  color: #fff;
}
.topbar-brand small {
  display: block;
  font-size: 0.62rem;
  font-weight: 400;
  opacity: 0.85;
}

.topbar-nav {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 0;
  padding: 0;
  flex: 1 1 auto;
}
.topbar-nav li { margin: 0; }
.topbar-nav a, .topbar-nav .topbar-link {
  display: inline-block;
  padding: 6px 10px;
  border-radius: 999px;
  color: #fff;
  font-size: 0.88rem;
  white-space: nowrap;
}
.topbar-nav a:hover { background: rgba(255,255,255,0.15); text-decoration: none; }
.topbar-nav a.active { background: rgba(255,255,255,0.28); font-weight: 700; }

/* 導覽分組下拉（2026-08-05，spec: 2026-08-05-admin-nav-onboarding-design.md）：
 * 群組按鈕外觀比照既有 nav 連結（同一排視覺一致）；下拉浮層掛在群組 li 底下，
 * 點擊開合（觸控友善）。手機（≤720px）改漢堡＋直向攤平，見下方 media query。 */
.topbar-nav .nav-group { position: relative; }
.nav-group-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 6px 10px;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: #fff;
  font-size: 0.88rem;
  font-family: inherit;
  white-space: nowrap;
  cursor: pointer;
}
.nav-group-btn:hover { background: rgba(255,255,255,0.15); }
.nav-group-btn.active { background: rgba(255,255,255,0.28); font-weight: 700; }
.nav-caret { font-size: 0.7em; opacity: 0.85; }
.nav-dropdown {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  min-width: 150px;
  list-style: none;
  margin: 0;
  padding: 6px;
  background: #fff;
  border-radius: 10px;
  box-shadow: 0 6px 24px rgba(0,0,0,0.18);
  z-index: 60;
}
.nav-dropdown li { margin: 0; }
.nav-dropdown a {
  display: block;
  padding: 8px 12px;
  border-radius: 6px;
  color: var(--ink, #1f2937);
  font-size: 0.9rem;
  white-space: nowrap;
}
.nav-dropdown a:hover { background: rgba(19,176,182,0.1); text-decoration: none; }
.nav-dropdown a.active { background: rgba(19,176,182,0.16); color: var(--teal); font-weight: 700; }

/* 商品卡片的狀態提示（2026-08-05）：
 * .product-hint     — 中性說明（草稿／已關閉時取代報名連結區，說明為什麼這裡沒有連結）
 * .product-hint-warn — 需要商家動作（單堂／系列課還沒有班次，發佈會被擋） */
.product-hint {
  margin-top: 8px;
  padding: 8px 10px;
  background: #f3f4f6;
  border-radius: 8px;
  color: #667;
  font-size: 0.85rem;
}
.product-hint-warn {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  padding: 8px 10px;
  background: #fff7ed;
  border: 1px solid #fdba74;
  border-radius: 8px;
  color: #9a3412;
  font-size: 0.85rem;
}

/* 新手上路卡（2026-08-05）：儀表板頂部進度卡，步驟依 dashboard onboarding counts 自動勾。 */
.onboarding-card { margin-bottom: 20px; border-left: 4px solid var(--teal); }
.onboarding-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}
.onboarding-head strong { font-size: 1.05rem; }
.onboarding-progress {
  margin-left: 8px;
  font-size: 0.85rem;
  color: var(--teal);
  font-weight: 700;
}
.onboarding-lead { margin: 6px 0 10px; color: #556; font-size: 0.9rem; }
.onboarding-step {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 8px 0;
  border-top: 1px solid rgba(0,0,0,0.06);
}
.onboarding-step.is-done { opacity: 0.62; }
.onboarding-step-body { flex: 1 1 auto; min-width: 0; }
.onboarding-step-title { font-weight: 600; font-size: 0.95rem; }
.onboarding-step.is-done .onboarding-step-title { text-decoration: line-through; text-decoration-thickness: 1px; }
.onboarding-step-hint { font-size: 0.82rem; color: #667; margin-top: 2px; overflow-wrap: anywhere; }
.onboarding-url { font-size: 0.82rem; background: rgba(19,176,182,0.08); padding: 1px 5px; border-radius: 4px; }
.onboarding-check {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  margin-top: 1px;
  border-radius: 50%;
  border: 2px solid #cbd5e1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  color: #fff;
}
.onboarding-check.done { background: var(--teal); border-color: var(--teal); }
.onboarding-done {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  margin-bottom: 20px;
  padding: 10px 14px;
  background: rgba(19,176,182,0.08);
  border: 1px solid rgba(19,176,182,0.25);
  border-radius: 10px;
  font-size: 0.92rem;
}

/* 漢堡鈕：桌機隱藏，≤720px 現身 */
#nav-hamburger {
  display: none;
  border: 1px solid rgba(255,255,255,0.4);
  border-radius: 6px;
  background: rgba(255,255,255,0.12);
  color: #fff;
  font-size: 1.05rem;
  line-height: 1;
  padding: 6px 10px;
  cursor: pointer;
}
@media (max-width: 720px) {
  #nav-hamburger { display: inline-block; }
  /* 手機：導覽預設收起，漢堡展開後整組直向攤平（群組標題＋項目一次看全，不用逐組點） */
  .topbar-nav { display: none; }
  .topbar-nav.nav-open {
    display: flex;
    flex-direction: column;
    flex-basis: 100%;
    gap: 2px;
    padding: 6px 0 4px;
  }
  .topbar-nav.nav-open > li > a { display: block; }
  .nav-group-btn { pointer-events: none; opacity: 0.75; font-size: 0.78rem; } /* 展開態當小節標題用 */
  .nav-dropdown {
    position: static;
    box-shadow: none;
    background: transparent;
    padding: 0 0 4px 14px;
    min-width: 0;
  }
  .nav-dropdown a { color: #fff; }
  .nav-dropdown a:hover { background: rgba(255,255,255,0.15); }
  .nav-dropdown a.active { background: rgba(255,255,255,0.28); color: #fff; }
}

.topbar-user {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
  white-space: nowrap;
}
.topbar-user .merchant-name { opacity: 0.9; }
/* 帳號設定入口（2026-08-03）：這裡原本只是把使用者名字包成一個**沒有任何樣式**的 <a>——
 * 在青藍漸層的深色頁首上，它看起來跟旁邊的商戶名稱一樣是純文字，沒有底線、沒有 hover、
 * 連游標都不會變，實際結果是「使用者根本不知道可以點進去改密碼」（Tesla 2026-08-03 回報
 * 「沒有改密碼的按鈕」，程式上明明有這個連結）。改成與登出按鈕同款的可點外觀，並在名字後面
 * 直接寫出「帳號設定」四個字——入口要靠字面說清楚，不能靠使用者猜名字可以點。 */
.topbar-user .topbar-account {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  background: rgba(255,255,255,0.18);
  color: #fff;
  border: 1px solid rgba(255,255,255,0.4);
  border-radius: 6px;
  padding: 5px 10px;
  font-size: 0.85rem;
  text-decoration: none;
  cursor: pointer;
}
.topbar-user .topbar-account:hover { background: rgba(255,255,255,0.3); }
.topbar-user .topbar-account-label {
  font-size: 0.78rem;
  opacity: 0.85;
  border-left: 1px solid rgba(255,255,255,0.35);
  padding-left: 6px;
}
/* 窄螢幕（手機）頁首本來就擠：只留「帳號設定」四個字，名字省略——入口的可辨識度優先於
 * 顯示是誰登入（商戶名還在左邊，且手機上不會有人搞不清自己是誰）。 */
@media (max-width: 560px) {
  .topbar-user .topbar-account-name { display: none; }
  .topbar-user .topbar-account-label { border-left: 0; padding-left: 0; }
}
/* 2026-07-29（Phase 2 Task 3）：這兩條規則原本是裸的 `#btn-logout`（全站唯一 id 選擇器），但
 * 它的顏色是為了**商戶後台深色頁首**設計的（白字＋半透明白底，只在青藍漸層 topbar 上才看得
 * 清楚）。學員端會員中心（public/s/me.html）也需要一顆登出按鈕、放在白底頁面上，若沿用同一個
 * id 就會拿到白字白底、幾乎看不見（實際踩到過，靠瀏覽器驗證才發現）。改成 `.topbar-user`
 * 底下才生效（那正是這顆按鈕唯一存在的位置，見 public/assets/admin.js 注入的 topbar 結構），
 * 學員端則用自己的 id（#btn-student-logout）搭配通用的 .btn-ghost。 */
.topbar-user #btn-logout {
  background: rgba(255,255,255,0.18);
  color: #fff;
  border: 1px solid rgba(255,255,255,0.4);
  border-radius: 6px;
  padding: 5px 10px;
  cursor: pointer;
  font-size: 0.85rem;
}
.topbar-user #btn-logout:hover { background: rgba(255,255,255,0.3); }

/* ---------------------------------------------------------------------------
 * 版面容器
 * ------------------------------------------------------------------------- */

.page {
  max-width: 960px;
  margin: 0 auto;
  padding: 16px 12px 48px;
}

.page h1 {
  font-size: 1.3rem;
  margin: 4px 0 4px;
}
.page h2 {
  font-size: 1.05rem;
  margin: 24px 0 10px;
}
.page-lead {
  color: var(--text-muted);
  font-size: 0.88rem;
  margin: 0 0 16px;
}

.card {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px;
  margin-bottom: 16px;
}

/* ---------------------------------------------------------------------------
 * 表單
 * ------------------------------------------------------------------------- */

.form-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}
@media (min-width: 640px) {
  .form-grid.two-col { grid-template-columns: 1fr 1fr; }
}

label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  margin-bottom: 4px;
  color: var(--text);
}
.field-hint {
  font-size: 0.76rem;
  color: var(--text-muted);
  margin-top: 2px;
}

/* 2026-07-29：補上 input[type="tel"]——它一直不在這份清單裡，所以四個用得到電話欄位的頁面
 * （學員端 enroll.html／book.html／course.html 的候補表單、後台 roster.html 的手動加入學員）
 * 的電話輸入框都是瀏覽器預設樣式：明顯比同一列的其他欄位窄、邊框/圓角也不一樣。 */
input[type="text"], input[type="number"], input[type="email"], input[type="password"],
input[type="tel"],
input[type="date"], input[type="datetime-local"], select, textarea {
  width: 100%;
  padding: 9px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 0.95rem;
  background: #fff;
  color: var(--text);
  max-width: 100%;
}
textarea { min-height: 70px; resize: vertical; }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--teal);
  box-shadow: 0 0 0 2px rgba(19,176,182,0.2);
}

.field { margin-bottom: 10px; }

/* ---------------------------------------------------------------------------
 * 按鈕
 * ------------------------------------------------------------------------- */

button, .btn {
  display: inline-block;
  border: none;
  border-radius: 8px;
  padding: 9px 16px;
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  background: var(--border);
  color: var(--text);
}
.btn-primary { background: var(--blue); color: #fff; }
.btn-primary:hover { background: #1f5497; }
.btn-teal { background: var(--teal); color: #fff; }
.btn-teal:hover { background: #0f9297; }
.btn-danger { background: #fbe3e1; color: var(--danger); }
.btn-danger:hover { background: #f8cfcb; }
.btn-ghost { background: transparent; border: 1px solid var(--border); }
.btn-sm { padding: 5px 10px; font-size: 0.8rem; }
/* 學員端目錄頁篩選 chip（前端預約介面優化，2026-08-08）：比照 .btn-ghost/.btn-sm 的視覺風格，
   選中狀態用品牌 teal 實心，跟 .badge-published 用同一組語意色（青＝已選中/生效）。 */
.chip-group { display: flex; gap: 6px; flex-wrap: wrap; }
.chip {
  padding: 5px 12px;
  font-size: 0.8rem;
  font-weight: 600;
  border-radius: 999px;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-muted);
}
.chip-active { background: var(--teal); border-color: var(--teal); color: #fff; }
/* 月曆格：7 欄一定要塞得進手機畫面（這頁是 LINE 圖文選單第一格，學員一定在手機上看）。
   兩個地方非寫不可，少一個就會溢出：
   1) minmax(0, 1fr)——`1fr` 的展開是 `minmax(auto, 1fr)`，軌道下限是格子的 min-content
      寬度，不是 0；格子塞不下時軌道會被撐大，7 欄加總超過容器就把整頁推寬。minmax(0, …)
      把下限壓回 0，軌道才真的平分容器寬度。
   2) .month-cell 的 padding——不寫就會吃到上面 `button` 的 9px 16px，左右各 16px 直接讓
      min-content 大到撐破軌道。實測（375px viewport）：修前 body.scrollWidth=380（週日欄
      被裁掉）、320px 時 =380（近兩欄跑到畫面外）；補上這兩行後兩種寬度都等於 viewport。 */
.month-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; }
.month-head { text-align: center; font-size: 0.8rem; color: #64748b; padding: 4px 0; }
.month-cell { min-height: 48px; border: 1px solid #e2e8f0; border-radius: 6px; background: #fff;
  padding: 4px 2px;
  font-size: 0.85rem; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; cursor: pointer; }
.month-cell-empty { border: none; background: transparent; cursor: default; }
.month-cell-has { border-color: var(--teal); font-weight: 600; }
.month-cell-selected { background: var(--teal); color: #fff; }
.month-dot { font-size: 0.7rem; }
button:disabled, .btn:disabled { opacity: 0.55; cursor: not-allowed; }
.btn-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }

/* ---------------------------------------------------------------------------
 * 表格（寬表格在自己的容器內捲動，不撐開整頁）
 * ------------------------------------------------------------------------- */

.table-scroll {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: #fff;
}
table {
  border-collapse: collapse;
  width: 100%;
  min-width: 560px;
  font-size: 0.88rem;
}
th, td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  text-align: left;
  white-space: nowrap;
}
th { background: #eef3f4; color: var(--text-muted); font-weight: 700; font-size: 0.8rem; }
tr:last-child td { border-bottom: none; }
td.wrap, th.wrap { white-space: normal; }

/* ---------------------------------------------------------------------------
 * 徽章 / 提示 / 訊息
 * ------------------------------------------------------------------------- */

.badge {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 0.76rem;
  font-weight: 700;
  white-space: nowrap;
}
.badge-draft { background: #eceff1; color: #52606d; }
.badge-published { background: #d9f5ee; color: var(--ok); }
.badge-closed { background: #f1e7fb; color: #6a3fb5; }
.badge-pending { background: #fff2d6; color: var(--warn); }
.badge-paid { background: #d9f5ee; color: var(--ok); }
.badge-failed { background: #fbe3e1; color: var(--danger); }
.badge-refunded { background: #eceff1; color: #52606d; }
.badge-partial_refunded { background: #fff2d6; color: var(--warn); }
/* 班次狀態（Phase 1.1 Task 5 新增，見 admin.js SESSION_STATUS_LABELS） */
.badge-scheduled { background: #d9f5ee; color: var(--ok); }
.badge-archived { background: #eceff1; color: #52606d; }
.badge-cancelled { background: #fbe3e1; color: var(--danger); }
/* 訂閱狀態（Task 10 月費會籍，2026-08-04，見 admin.js SUBSCRIPTION_STATUS_LABELS）。
   pending／failed 沿用上面訂單付款狀態的既有 badge-pending／badge-failed 顏色（語意一致：
   橘＝待處理、紅＝失敗），這裡只新增訂閱特有的三種狀態。 */
.badge-active { background: #d9f5ee; color: var(--ok); }
.badge-suspended { background: #fff2d6; color: var(--warn); }
.badge-cancel_scheduled { background: #f1e7fb; color: #6a3fb5; }
.badge-ended { background: #eceff1; color: #52606d; }
/* R6 Task 4（加購項目，2026-08-16）：orders.html 標記「這筆訂單本身就是加購單」（meta.kind
   === 'addon'，見該頁 addonOrderBadge()）——沿用跟 badge-closed／badge-cancel_scheduled 同一組
   紫色（語意：獨立於一般報名收款流程之外的另一種訂單），不是新配色。 */
.badge-addon { background: #f1e7fb; color: #6a3fb5; }
/* Task 2（加購單自動連動，2026-08-16，見 orders.html voidedBadge()／linkedCancelledBadge()）：
   已作廢＝灰（沿用 badge-draft／badge-refunded 同一組「不再有效」的灰）；關聯報名已取消＝橘
   （沿用 badge-pending 同一組「需要商家注意/處理」的橘，語意跟「已付款但要判斷退款」一致）。 */
.badge-voided { background: #eceff1; color: #52606d; }
.badge-linked-cancelled { background: #fff2d6; color: var(--warn); }

/* 已封存的班次／已取消的報名：整列視覺上淡化，跟正常項目一眼區分開來，
 * 不靠使用者去讀「狀態」欄那個字才知道。 */
tr.row-archived td { opacity: 0.6; }
tr.row-archived td:first-child { font-style: italic; }

.msg {
  padding: 10px 12px;
  border-radius: 8px;
  font-size: 0.88rem;
  margin: 10px 0;
  /* Admin.showMsg() 用 textContent 寫入（不注入 HTML），所以多行訊息只能靠 CSS 保留換行。
     2026-07-29 為「LINE 設定 → 測試連線」加的：那個回應要把「推播憑證」與「登入設定」兩半
     分行顯示（見 public/admin/line-settings.html），擠成一行會看不出是兩件事。
     pre-line 只保留換行、仍會摺疊多餘空白，既有單行訊息的外觀完全不變。 */
  white-space: pre-line;
}
.msg-error { background: #fbe3e1; color: var(--danger); }
.msg-ok { background: #d9f5ee; color: var(--ok); }
.msg-info { background: #e6f0fb; color: var(--blue); }
/* 警示：比 info 更需要被看見，但不是錯誤（例如「商品還沒發佈，學員看不到這些班次」）。 */
.msg-warn { background: #fdf1dc; color: var(--warn); }
.msg[hidden] { display: none; }

.empty-hint {
  color: var(--text-muted);
  font-size: 0.88rem;
  padding: 16px 4px;
  text-align: center;
}

.stat-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
}
@media (min-width: 640px) {
  .stat-grid { grid-template-columns: repeat(3, 1fr); }
}
.stat-card {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
}
.stat-card .stat-value {
  font-size: 1.7rem;
  font-weight: 800;
  color: var(--blue);
}
.stat-card .stat-label {
  font-size: 0.85rem;
  font-weight: 700;
  margin-top: 2px;
}
.stat-card .stat-note {
  font-size: 0.76rem;
  color: var(--text-muted);
  margin-top: 6px;
}

.share-box {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
}
.share-box code {
  background: #eef3f4;
  padding: 4px 8px;
  border-radius: 6px;
  font-size: 0.78rem;
  word-break: break-all;
}
.qr-box img {
  width: 140px;
  height: 140px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: #fff;
  padding: 6px;
}

.product-card { border: 1px solid var(--border); border-radius: var(--radius); padding: 14px; margin-bottom: 14px; }
.product-card .product-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; justify-content: space-between; }
.product-card .product-title { font-weight: 700; font-size: 1rem; }
.product-card .product-meta { color: var(--text-muted); font-size: 0.82rem; margin-top: 2px; }

/* ---------------------------------------------------------------------------
 * 通知中心（index.html，Phase 1.1 Task 5 新增）
 * ------------------------------------------------------------------------- */

.notif-item { margin-bottom: 10px; }
.notif-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
}
.notif-event { font-weight: 700; font-size: 0.94rem; }
.notif-time { font-size: 0.78rem; color: var(--text-muted); white-space: nowrap; }
.notif-detail { font-size: 0.85rem; color: var(--text-muted); margin-top: 6px; word-break: break-word; }

/* ---------------------------------------------------------------------------
 * 登入頁
 * ------------------------------------------------------------------------- */

.login-wrap {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: var(--teal); /* 2026-08-24 純苔綠 */
}
.login-card {
  background: #fff;
  border-radius: 14px;
  padding: 28px 24px;
  width: 100%;
  max-width: 360px;
  box-shadow: 0 12px 30px rgba(0,0,0,0.15);
}
.login-brand {
  text-align: center;
  margin-bottom: 18px;
}
.login-brand .wordmark {
  font-size: 1.5rem;
  font-weight: 800;
  color: var(--teal); /* 2026-08-24 純苔綠字標（原漸層文字改純色） */
}
.login-brand .sub { color: var(--text-muted); font-size: 0.82rem; margin-top: 4px; }
.login-card button[type="submit"] { width: 100%; margin-top: 6px; }
.login-hint { font-size: 0.76rem; color: var(--text-muted); margin-top: 14px; text-align: center; }

@media (max-width: 480px) {
  table { min-width: 480px; }

  /* 小螢幕多半是觸控——.btn-sm（發佈/關閉/退款/刪除/名單…）與登出按鈕在桌機用小尺寸沒問題，
   * 但觸控熱區至少要 40px 高才不容易誤觸／點不到，這裡只加高 padding，不動桌機版樣式。 */
  .btn-sm { padding: 11px 14px; min-height: 40px; }
  .topbar-user #btn-logout { padding: 11px 14px; min-height: 40px; }

  /* Phase 3 Task 5 新增：教練時段設定（.avail-row 內的 select/input）、學員選時段
   * （.slot-time-btn）跟這裡沿用同一條規則，不另開一個 media query。 */
  .avail-row select, .avail-row input[type="time"], .avail-row input[type="date"] { min-height: 40px; }
  .avail-row button, .slot-time-btn, .waitlist-item button { min-height: 40px; }
}

/* ---------------------------------------------------------------------------
 * 學員端（public/s/*，shop.js）— 無登入、手機優先。跟上面 admin 那一大塊共用色票／
 * 字體／card／button／msg 等基礎樣式，這裡只加學員端專屬的元件。
 * ------------------------------------------------------------------------- */

/* signage 風（2026-08-24，Tesla 指定「往指標識別系統的設計風格做」，參考 10/10 品牌 LINE
 * 介面）：學員端跟 LINE 圖文選單是同一段體驗（點選單格子就進到這些頁），選單改成
 * signage 風之後，頁面還留在彩色漸層＋重陰影的舊風格會有明顯落差。
 *
 * 作用域刻意鎖在 `.shop-page`（12 個學員頁的共用容器，商戶後台沒有這個 class）——
 * 這個檔案是後台與學員端共用的，改全域色票會一併改掉後台的視覺，那不在這次範圍內。
 * 底下用「同名 token 的區域覆寫」達成整頁換膚，不需要改任何一頁的 HTML 結構。
 *
 * 設計語彙（跟 lib/line/richmenu-image.js 的選單版型同一套）：暖米白底、深炭文字、
 * 細線框、近方角、字距加寬、去掉漸層與重陰影。 */
.shop-page {
  --bg: #F4F1EA;
  --card-bg: #FBFAF6;
  --text: #2B2A28;
  --text-muted: #6B675F;
  --border: rgba(43, 42, 40, 0.22);
  --radius: 6px;
  /* 強調色改用深炭而非品牌漸層色：signage 的重點是排印與線條，不是彩度。品牌色仍保留
   * 在 --teal/--blue（badge、連結等既有元件還在用），只是不再當版面的主色。 */
  --accent: #2B2A28;

  max-width: 640px;
  margin: 0 auto;
  padding: 16px 12px 24px;
  background: var(--bg);
  color: var(--text);
  letter-spacing: 0.02em;
}
/* body 底色也要跟著換，否則 .shop-page 以外的區域（安全區、捲動回彈）會露出舊的藍灰。 */
body:has(.shop-page) { background: #F4F1EA; }

/* 排印：signage 的份量來自字距與留白，不是粗體。既有 HTML 大量用 700/800 字重堆重點，
 * 這裡統一降到 500 並補字距——標題仍然是標題（靠字級與間距分層），但不再「黑成一塊」。 */
.shop-page h1,
.shop-page h2,
.shop-page h3,
.shop-page h4,
.shop-page .course-title,
.shop-page .me-item-head,
.shop-page strong,
.shop-page b {
  font-weight: 500;
  letter-spacing: 0.06em;
}
.shop-page h1, .shop-page h2, .shop-page h3 { line-height: 1.45; }
.shop-page .field-hint,
.shop-page .muted { letter-spacing: 0.03em; }

.shop-page .card,
.shop-page .course-card,
.shop-page .me-item {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: none;
}

/* 主要按鈕：實心深炭＋細字距，取代原本的品牌漸層／藍。 */
.shop-page .btn-primary,
.shop-page button.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #F4F1EA;
  border-radius: var(--radius);
  letter-spacing: 0.06em;
  box-shadow: none;
}
.shop-page .btn-primary:hover { background: #45423D; border-color: #45423D; }
.shop-page .btn-ghost {
  border-radius: var(--radius);
  letter-spacing: 0.06em;
  box-shadow: none;
}
.shop-page input,
.shop-page select,
.shop-page textarea {
  background: #fff;
  border-radius: var(--radius);
  border-color: var(--border);
}

/* 品牌漸層／teal／blue 的收尾（2026-08-24 實機檢視後補）：上面那批只換了框架元件，
 * 頁面裡還有一整批用品牌色的小元件——藍色價格、teal 篩選 chip 與分頁、漸層圓鈕——
 * 在米白 signage 底上顯得零散。統一改成深炭（--accent）：signage 用「實心 vs 細框」
 * 分辨狀態，不用彩度。
 *
 * 刻意**不動**的兩類顏色：
 *   1. LINE 綠（#06C755）——那是 LINE 的品牌識別，學員靠它認得「用 LINE 登入」這顆鈕。
 *   2. 語意色（成功／警告／危險／badge-*）——它們傳達的是狀態不是裝飾，砍掉會讓
 *      「已額滿」「未付款」這類訊息失去辨識度。 */
.shop-page .course-card .course-price,
.shop-page .me-balance { color: var(--accent); font-weight: 500; letter-spacing: 0.02em; }

.shop-page .chip-active,
.shop-page .slot-time-btn.selected,
.shop-page .month-cell-selected {
  background: var(--accent);
  border-color: var(--accent);
  color: #F4F1EA;
}
.shop-page .month-cell-has { border-color: var(--accent); }
.shop-page .session-item.selected,
.shop-page .slot-time-btn:hover { border-color: var(--accent); box-shadow: none; }

.shop-page .me-tab[aria-selected="true"] {
  color: var(--accent);
  border-bottom-color: var(--accent);
  font-weight: 500;
}
.shop-page .result-box.state-waiting .result-icon { color: var(--accent); }
.shop-page .me-subscription-charges summary:hover,
.shop-page .coach-roster summary:hover { color: var(--accent); }
/* 連結顏色統一成深炭。底線**只給內文（段落／列表項）裡的連結**——學員端的 <a> 大多不是
 * 文字連結而是整塊可點區域（課程卡整張是 a.course-card、LINE 登入鈕是 a.account-login、
 * 頁尾兩顆是按鈕外形），對它們加底線只會變髒。用 `p a`／`li a` 圈定範圍，比用 :not() 逐一
 * 排除按鈕 class 穩健得多：新增一種按鈕時不需要回來補排除，天然就不會被選中。
 * （FAQ 頁 /s/help.html 的內文連結正好落在這個範圍內，仍然看得出是連結。） */
.shop-page a { color: var(--accent); }
.shop-page p a,
.shop-page li a { text-decoration: underline; text-underline-offset: 3px; }
.shop-page .msg-info { background: rgba(43, 42, 40, 0.06); color: var(--text); }

/* 課程卡上的種類徽章（單次課程／系列課程／堂票…）沿用了 .badge-published 這個**狀態**
 * class，於是頂著一身「已發佈綠」。但它對學員傳達的是分類、不是狀態——學員本來就只看得到
 * 已發佈的課程，那個綠色不帶任何資訊，只是在 signage 版面上多一塊彩色。改成中性細框標籤。
 * 只縮在 .course-card 底下：真正的狀態徽章（已額滿、待付款…）在別處仍保有語意色。 */
.shop-page .course-card .badge-published {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-muted);
  font-weight: 400;
  letter-spacing: 0.04em;
}

/* 頁首色塊：吃商戶在後台挑的那個選單配色（2026-08-24，見 shop.js applyMerchantBranding()
 * 與 routes/public.js GET /branding）——商戶選苔綠，這塊就是苔綠，跟他的 LINE 選單同色。
 *
 * `var(--brand-*, 預設)` 的預設值是炭黑主題的三個顏色：配色還沒拿到、拿不到、或商戶根本
 * 沒設定過 LINE 時，畫面就是原本的深炭，一切正常——配色是加分項，不是渲染的前提。
 *
 * 淺色配色（米白／淺綠／暖沙／霧粉）會讓這塊跟頁面底色很接近，所以邊界一律靠 `--brand-line`
 * 那條細線交代，而不是靠明暗對比——這正是 signage 的做法（選單本身也是用線分隔格子，不是
 * 用色塊）。深色配色下這條線同樣成立，兩種情況共用一套規則，不需要判斷深淺。 */
.merchant-header {
  text-align: center;
  padding: 22px 12px 18px;
  margin: -16px -12px 16px;
  /* 後備色 2026-08-24 從深炭改苔綠：商戶沒設定配色時 /branding 本來就回 moss，這個後備只在
   * 連不到 API 時才會露出——一併改成苔綠，讓「預設就是苔綠」在任何情況下都成立。 */
  background: var(--brand-bg, #5C7050);
  color: var(--brand-text, #F1F3EA);
  border-bottom: 1px solid var(--brand-line, rgba(255, 255, 255, 0.65));
}
.merchant-header .merchant-name {
  font-size: 1.25rem;
  font-weight: 500;      /* signage 用中字重＋字距，不用粗體堆重量 */
  letter-spacing: 0.14em;
  margin: 0;
}
.merchant-header .merchant-tagline {
  font-size: 0.8rem;
  opacity: 0.78;
  letter-spacing: 0.08em;
  margin-top: 6px;
}

/* 頁首花色（page_pattern，2026-09-08）：商戶在「外觀設定」開了「圓點花色」時，shop.js 會在
 * <html> 掛上 data-brand-pattern="polka_dot"，這一段才生效——沒開的商戶（＝到今天為止的每一家）
 * 上面的 `.merchant-header` 規則一字不動，畫面逐位元＝現況。
 *
 * 做法：只覆寫 background-image（一組錯位的 radial-gradient＝經典的圓點鋪法），底色
 * background-color 仍來自 `.merchant-header` 的 `background:` 簡寫（＝品牌色／主題色），圓點疊在
 * 上面。圓點顏色用 color-mix 從頁首文字色（--brand-text，深底白、淺底近黑，跟 border 同一套語彙）
 * 取 18% 透明度——所以深底自動白點、淺底自動黑點，不必分深淺、也不必上傳任何圖檔。 */
[data-brand-pattern="polka_dot"] .merchant-header {
  background-image:
    radial-gradient(circle at center,
      color-mix(in srgb, var(--brand-text, #F1F3EA) 18%, transparent) 20%, transparent 22%),
    radial-gradient(circle at center,
      color-mix(in srgb, var(--brand-text, #F1F3EA) 18%, transparent) 20%, transparent 22%);
  background-size: 22px 22px;
  background-position: 0 0, 11px 11px;
}

/* 流程進度條：選課→填資料→付款→完成。獨立元件，不塞進導覽列（見 shop.js 檔頭說明）。 */
.flow-progress {
  display: flex;
  list-style: none;
  margin: 0 0 18px;
  padding: 0;
}
.flow-step {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  position: relative;
  font-size: 0.72rem;
  color: var(--text-muted);
}
.flow-step::before {
  content: '';
  position: absolute;
  top: 13px;
  left: -50%;
  width: 100%;
  height: 2px;
  background: var(--border);
  z-index: 0;
}
.flow-step:first-child::before { display: none; }
.flow-dot {
  position: relative;
  z-index: 1;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.78rem;
  font-weight: 700;
  background: var(--border);
  color: var(--text-muted);
  margin-bottom: 4px;
}
.flow-label { white-space: nowrap; }
.flow-step.flow-done .flow-dot { background: var(--teal); color: #fff; }
.flow-step.flow-done .flow-label { color: var(--teal); font-weight: 700; }
.flow-step.flow-done::before { background: var(--teal); }
.flow-step.flow-current .flow-dot { background: var(--blue); color: #fff; }
.flow-step.flow-current .flow-label { color: var(--blue); font-weight: 700; }

/* signage 覆寫（2026-08-24，見 .shop-page 檔頭）：進度條的「已完成／目前」不用品牌雙色，
 * 改用深炭實心 vs 細框空心——signage 靠形狀與線條分辨狀態，不靠彩度。 */
.shop-page .flow-step.flow-done .flow-dot { background: var(--accent); color: #F4F1EA; }
.shop-page .flow-step.flow-done .flow-label { color: var(--accent); font-weight: 500; }
.shop-page .flow-step.flow-done::before { background: var(--accent); }
.shop-page .flow-step.flow-current .flow-dot {
  background: transparent;
  color: var(--accent);
  border: 2px solid var(--accent);
}
.shop-page .flow-step.flow-current .flow-label { color: var(--accent); font-weight: 500; }
.shop-page .flow-dot { font-weight: 500; }

/* 課程卡片（catalog／course 詳情共用） */
.course-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px;
  margin-bottom: 12px;
  background: #fff;
}
.course-card .course-title { font-weight: 700; font-size: 1.02rem; }
.course-card .course-meta { color: var(--text-muted); font-size: 0.82rem; margin-top: 4px; }
.course-card .course-price { font-weight: 800; color: var(--blue); font-size: 1.05rem; margin-top: 6px; }
.course-card .course-desc { font-size: 0.85rem; margin-top: 8px; color: var(--text); white-space: pre-wrap; }

/* 班次挑選（radio 清單） */
.session-list { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 8px; }
.session-item {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  cursor: pointer;
}
.session-item input[type="radio"] { width: 18px; height: 18px; flex: 0 0 auto; }
.session-item .session-time { font-weight: 600; font-size: 0.92rem; }
.session-item .session-remaining { font-size: 0.78rem; color: var(--text-muted); margin-top: 2px; }
.session-item.full { cursor: default; }
/* 額滿的班次本身不能被點選報名，但「加入候補」按鈕（Phase 3 Task 5 新增）必須維持清楚可辨識——
 * 不能沿用舊版整列 opacity 0.55（父層 opacity 會連同子元素一起被畫成半透明，子元素自己設
 * opacity:1 也無法抵銷），改成只淡化左側的核選圓點與時間/名額文字，按鈕保持原色。 */
.session-item.full input[type="radio"] { opacity: 0.4; }
.session-item.full > div { opacity: 0.55; }
.session-item.full .session-remaining { color: var(--danger); font-weight: 700; }
.session-item.selected { border-color: var(--teal); box-shadow: 0 0 0 2px rgba(19,176,182,0.18); }
.session-item [data-action="join-waitlist"] { margin-left: auto; }

/* 結果頁狀態卡（waiting/success/failed/timeout） */
.result-box { text-align: center; padding: 28px 16px; }
.result-icon { font-size: 2.6rem; line-height: 1; margin-bottom: 10px; }
.result-title { font-size: 1.15rem; font-weight: 800; margin: 0 0 6px; }
.result-note { color: var(--text-muted); font-size: 0.88rem; margin: 0 auto; max-width: 420px; }
.result-order-no {
  display: inline-block;
  margin-top: 10px;
  padding: 6px 12px;
  border-radius: 8px;
  background: #eef3f4;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.92rem;
  letter-spacing: 0.03em;
}
.result-box.state-success .result-icon { color: var(--ok); }
.result-box.state-failed .result-icon { color: var(--danger); }
.result-box.state-waiting .result-icon { color: var(--blue); }
.result-box.state-timeout .result-icon { color: var(--warn); }

/* 頁尾（shop.js 注入） */
#app-footer {
  max-width: 640px;
  margin: 24px auto 0;
  padding: 14px 12px 28px;
  text-align: center;
  border-top: 1px solid var(--border);
}
.shop-footer-inner { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.shop-footer-inner a { font-size: 0.85rem; }
.shop-footer-credit { font-size: 0.72rem; color: var(--text-muted); }

/* 2026-08-24：圖文選單改版後預設不再有「使用說明」那一格，這裡就成了學員找 FAQ 的
 * 主要入口——原本是頁尾一行小字，捲到底才看得到。改成細框按鈕（跟 signage 的線條語彙
 * 一致），份量足以被看見，又不會搶走頁面主體（報名流程）的注意力。
 *
 * ⚠️ 作用域刻意**不加 `.shop-page` 前綴**：頁尾由 shop.js 注入在 `.shop-page` 的**外面**
 * （掛在 body 底下，見 renderFooter 的呼叫端），加了前綴的規則一條都不會生效——這正是
 * 2026-08-24 第一版寫成 `.shop-page .shop-footer-inner` 時完全沒作用的原因。
 * `.shop-footer-inner` 本身只出現在學員端，拿它當作用域就夠，商戶後台沒有這個 class。 */
.shop-footer-inner { gap: 10px; padding-top: 4px; }
.shop-footer-inner a {
  display: inline-block;
  padding: 8px 20px;
  border: 1px solid rgba(43, 42, 40, 0.22);
  border-radius: 6px;
  color: #2B2A28;
  text-decoration: none;
  letter-spacing: 0.06em;
}
.shop-footer-inner a:hover { border-color: #2B2A28; text-decoration: none; }

.empty-hint-lg { text-align: center; color: var(--text-muted); padding: 40px 12px; font-size: 0.92rem; }

/* ---------------------------------------------------------------------------
 * Phase 3 Task 5：教練可授課時段設定（staff.html）
 * ------------------------------------------------------------------------- */

.avail-panel { border-top: 1px dashed var(--border); margin-top: 10px; padding-top: 10px; }
.avail-panel h4, .avail-panel h5 { margin: 0 0 8px; font-size: 0.9rem; }
.avail-panel h5 { margin-top: 16px; font-size: 0.85rem; color: var(--text-muted); }
.avail-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-bottom: 8px;
  padding: 8px;
  background: var(--bg);
  border-radius: 8px;
}
.avail-row select, .avail-row input[type="time"], .avail-row input[type="date"] {
  width: auto;
  flex: 1 1 100px;
  min-width: 90px;
}
.avail-row .avail-sep { color: var(--text-muted); font-size: 0.85rem; }

/* ---------------------------------------------------------------------------
 * Phase 3 Task 5：學員一對一預約（book.html）— 依日期分組的可約時段清單
 * ------------------------------------------------------------------------- */

.slot-day-group { margin-bottom: 14px; }
.slot-day-label { font-weight: 700; font-size: 0.88rem; margin-bottom: 6px; }
.slot-time-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.slot-time-btn {
  border: 1px solid var(--border);
  background: #fff;
  border-radius: 8px;
  padding: 9px 12px;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
}
.slot-time-btn:hover { border-color: var(--teal); }
.slot-time-btn.selected { background: var(--teal); border-color: var(--teal); color: #fff; }

/* ---------------------------------------------------------------------------
 * Phase 3 Task 5：候補名單（roster.html）／認領頁（claim.html）
 * ------------------------------------------------------------------------- */

.waitlist-item {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 12px;
  margin-bottom: 8px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.waitlist-item .waitlist-pos {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--border);
  font-weight: 700;
  font-size: 0.8rem;
  flex: 0 0 auto;
}
.waitlist-item .waitlist-info { font-size: 0.85rem; flex: 1 1 160px; }
/* p3-gaps 任務 Gap 2：通知中心（index.html payloadDetailsHtml()）跟班次名單頁
 * （roster.html waitlistItemHtml()）都會渲染同一種「可複製認領連結」區塊，共用同一組樣式。 */
.waitlist-item .waitlist-claim-link, .notif-detail .waitlist-claim-link {
  display: block;
  font-size: 0.76rem;
  color: var(--text-muted);
  word-break: break-all;
  margin-top: 2px;
}
.notif-detail .waitlist-claim-row {
  margin-top: 6px;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.claim-box { text-align: left; padding: 4px 0; }
.claim-box .claim-row { display: flex; justify-content: space-between; gap: 10px; padding: 6px 0; border-bottom: 1px solid var(--border); font-size: 0.9rem; }
.claim-box .claim-row:last-child { border-bottom: none; }
.claim-box .claim-label { color: var(--text-muted); }
.claim-expiry-note { font-size: 0.82rem; color: var(--warn); margin-top: 10px; }

/* ---------------------------------------------------------------------------
 * Phase 2 Task 3：LIFF 會員中心（me.html）— 分頁切換＋課表／課卡／消耗明細清單
 *
 * 分頁列（.me-tabs）是**內容切換**，不是站內導覽，也不是流程進度條——三者在這個系統裡是不同
 * 的東西（見 shop.js 檔頭與 claude-config/memory/acpay_ui_conventions.md：流程步驟一律用獨立的
 * 進度條元件，不塞導覽列）。會員中心沒有先後順序可言（課表／課卡／明細／續購四者平行），所以
 * 用分頁而不是進度條；被選中的分頁用品牌青色底線標示，跟進度條的青勾／藍高亮視覺語彙一致。
 * ------------------------------------------------------------------------- */

.me-tabs {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 14px;
  overflow-x: auto;
}
.me-tab {
  flex: 1 1 auto;
  min-width: 72px;
  background: transparent;
  border: none;
  border-bottom: 3px solid transparent;
  padding: 10px 6px;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  white-space: nowrap;
}
.me-tab[aria-selected="true"] {
  color: var(--teal);
  border-bottom-color: var(--teal);
}

/* 課表／課卡的單筆項目。沿用 .course-card 的邊框與圓角語彙，但多一列「右側動作按鈕」。 */
.me-item {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px;
  margin-bottom: 10px;
  background: #fff;
}
.me-item.me-item-past { background: var(--bg); }
.me-item-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
.me-item-title { font-weight: 700; font-size: 0.98rem; }
.me-item-time { font-weight: 600; font-size: 0.9rem; margin-top: 4px; }
.me-item-meta { color: var(--text-muted); font-size: 0.82rem; margin-top: 3px; }
.me-item-actions { margin-top: 10px; }

/* 課卡餘額：數字要一眼看到（會員中心最常被問的就是「我還剩幾堂」）。 */
.me-balance { font-size: 1.6rem; font-weight: 800; color: var(--blue); line-height: 1.1; }
.me-balance-unit { font-size: 0.85rem; font-weight: 600; color: var(--text-muted); margin-left: 2px; }

/* 消耗明細：一列一筆流水，右側是 ±堂數與累計餘額（爭議時要能逐行核對，見
 * src/routes/student-me.js GET /me/ledger 的說明）。 */
.me-ledger-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 9px 0;
  border-bottom: 1px solid var(--border);
  font-size: 0.86rem;
}
.me-ledger-row:last-child { border-bottom: none; }
.me-ledger-when { color: var(--text-muted); font-size: 0.76rem; margin-top: 2px; }
.me-ledger-delta { flex: 0 0 auto; text-align: right; font-weight: 700; }
.me-ledger-delta.minus { color: var(--danger); }
.me-ledger-delta.plus { color: var(--ok); }
.me-ledger-after { display: block; font-weight: 600; font-size: 0.74rem; color: var(--text-muted); }

/* 我的訂閱（Task 11，月費會籍）：每筆訂閱底下摺疊式的扣款紀錄，沿用 .me-ledger-row 呈現
 * 逐期明細，這裡只補 <details>/<summary> 本身的間距與可點擊感（瀏覽器預設的三角形箭頭照舊）。 */
.me-subscription-charges { margin-top: 10px; font-size: 0.86rem; }
.me-subscription-charges summary { cursor: pointer; font-weight: 600; color: var(--text-muted); }
.me-subscription-charges summary:hover { color: var(--blue); }

/* 教練 LINE 視角 Task 4（2026-08-16）：會員中心「教練」分頁——日期範圍翻頁列、依日分組標題、
 * 展開式學員名單。卡片本身沿用既有 .me-item／.me-item-head 等元件，這裡只補教練分頁特有的
 * 版面。 */
.coach-range-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: 10px 0 14px;
}
.coach-range-label { font-weight: 700; font-size: 0.92rem; }
.coach-day-group { margin-bottom: 18px; }
.coach-day-heading {
  font-size: 0.86rem;
  font-weight: 700;
  color: var(--text-muted);
  margin: 0 0 8px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--border);
}
.coach-roster { margin-top: 10px; font-size: 0.86rem; }
.coach-roster summary { cursor: pointer; font-weight: 600; color: var(--text-muted); }
.coach-roster summary:hover { color: var(--blue); }
.coach-roster-list { margin-top: 8px; display: grid; gap: 8px; }
.coach-roster-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1px solid var(--border);
}
.coach-roster-item:last-child { border-bottom: none; }
.coach-roster-name { font-weight: 600; }
/* 已點名＝綠（跟 .badge-paid／.badge-published 同一組「完成／生效」語意色）；
   未付款＝橘（跟 .badge-pending 同一組「需要商家/學員注意」語意色）。 */
.badge-attended { background: #d9f5ee; color: var(--ok); }
.badge-unpaid { background: #fff2d6; color: var(--warn); }

/* ---------------------------------------------------------------------------
 * Phase 2 Task 5：學員端帳號列（shop.js renderAccountBar 注入）
 *
 * 刻意做得很輕：它不是導覽列、也不是流程進度條（那兩者在這個系統裡是別的東西，見 shop.js
 * 檔頭），只是一行「你是誰／可以去哪」。商家的品牌才是主角，這一列不搶版面。
 * ------------------------------------------------------------------------- */

.account-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 8px 10px;
  margin-bottom: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: #fff;
  font-size: 0.82rem;
}
.account-bar[hidden] { display: none; }
.account-who { font-weight: 700; }
.account-hint { color: var(--text-muted); flex: 1 1 auto; }
.account-link { margin-left: auto; }
.account-logout {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-muted);
  padding: 4px 10px;
  font-size: 0.78rem;
}
/* LINE 綠（#06C755 是 LINE 官方品牌色，用在「用 LINE 登入」這個動作上，學員一眼認得） */
.account-login {
  margin-left: auto;
  background: #06c755;
  color: #fff;
  border-radius: 8px;
  padding: 7px 14px;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}
.account-login:hover { background: #05b34c; text-decoration: none; }

@media (max-width: 480px) {
  /* 觸控熱區（同本檔既有的 .btn-sm 規則） */
  .account-login, .account-logout { min-height: 40px; display: inline-flex; align-items: center; }
}

/* ---------------------------------------------------------------------------
 * Phase 2 Task 6：guide.html「啟用 LINE」的結構圖與可複製網址
 *
 * 結構圖用 inline SVG（不是外部圖檔）：這份說明要能離線看、要跟著文字一起版本控管，而且
 * 商戶最常做錯的就是「把 Login channel 建到另一個 provider 底下」——那是結構問題，用一張圖
 * 講三秒就懂，寫三段文字還是會有人做錯。用 currentColor 讓它跟著文字顏色，不寫死配色。
 * ------------------------------------------------------------------------- */

.line-setup-diagram {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px;
  margin: 10px 0;
  background: var(--bg);
  color: var(--text);
  max-width: 560px;
}

/* 要逐字貼進 LINE Developers Console 的網址：等寬字、可整段框選、長網址不撐破版面 */
.setup-url {
  display: inline-block;
  margin: 4px 0;
  padding: 6px 8px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 0.82rem;
  word-break: break-all;
  user-select: all;
}

/* ---------------------------------------------------------------------------
 * 問阿福（2026-10-08，照 ACQ 重構專案的叫出方式；acq-merchant-portal docs/63-alf-embed.md）。
 * 取代 2026-08-05 起的右下角浮動圓鈕＋可拖曳縮放小視窗。注入邏輯在 admin.js renderAlfPanel；
 * 這裡只管外觀與版面：
 *   - #admin-top-actions：右上角動作列（❓ 看教學＋問阿福）。手機／平板（<1024px）貼在頂列裡、
 *     漢堡鈕左邊；桌機（≥1024px）固定在頁面內容右上角（桌機 #app-topbar 是左側欄，沒有橫向頂列）。
 *   - .alf-panel：手機從下方升起蓋滿；≥768px 從右側滑出 400px；≥1024px 打開時把整個後台往左推
 *     400px（html[data-alf-open]），頁面完整可見可操作。
 * z-index：動作列 190（> #app-topbar 的 50 才浮得在手機頂列上、> 頁面內容用到的最高 80；沿用舊「看教學」的值）、
 * 面板 210（最上層，蓋得住動作列）。
 * ------------------------------------------------------------------------- */

.admin-top-actions {
  position: fixed;
  top: calc((var(--nav-h, 56px) - 40px) / 2);
  right: 62px; /* 讓出漢堡鈕（40px＋右側 14px 內距＋8px 間距） */
  z-index: 190;
  display: flex;
  align-items: center;
  gap: 8px;
}
@media (min-width: 1024px) {
  .admin-top-actions { top: 14px; right: 20px; transition: right .2s ease-out; }
  html[data-alf-open] .admin-top-actions { right: 420px; }
}

.guide-help-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 40px;
  min-width: 40px;
  padding: 0 14px;
  border-radius: 999px;
  background: var(--card-bg, #fff);
  border: 1px solid var(--border);
  color: var(--text);
  font-weight: 700;
  font-size: .88rem;
  text-decoration: none;
  box-shadow: 0 6px 16px rgba(16, 32, 43, .16);
}
.guide-help-toggle:hover { text-decoration: none; border-color: var(--teal); }

/* 問阿福：品牌漸層實心鈕，要一眼看得到（Tesla 2026-10-08：原本右下角那顆不明顯）。 */
.ask-alf-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 40px;
  min-width: 40px;
  padding: 0 16px;
  border: none;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--teal), var(--blue));
  color: #fff;
  font-weight: 700;
  font-size: .9rem;
  cursor: pointer;
  box-shadow: 0 6px 16px rgba(37, 99, 181, .28);
  transition: transform .15s;
}
.ask-alf-btn:hover { transform: translateY(-1px); }
.ask-alf-btn:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.ask-alf-icon { flex: none; }

/* 窄手機：頂列放不下兩段字，看教學只留 ❓（aria-label 仍是「看教學」）。 */
@media (max-width: 420px) {
  .guide-help-toggle { padding: 0; }
  .guide-help-label { display: none; }
  .ask-alf-btn { padding: 0 12px; }
}

/* 未讀徽章（2026-09-06 阿福主動告知）：阿福有事要說時亮在「問阿福」右上角。
 * [hidden] 那條必須留著——本規則的 display 會蓋掉瀏覽器對 [hidden] 的預設 display:none，
 * 少了它徽章會永遠亮著（連沒訊息時也是一顆空紅點）。 */
.ask-alf-badge {
  position: absolute;
  top: -6px;
  right: -6px;
  min-width: 20px;
  height: 20px;
  padding: 0 5px;
  border-radius: 10px;
  background: var(--danger, #d64545);
  color: #fff;
  font-size: .7rem;
  font-weight: 700;
  line-height: 20px;
  text-align: center;
  box-shadow: 0 0 0 2px #fff;
  pointer-events: none;
}
.ask-alf-badge[hidden] { display: none; }

.alf-panel {
  position: fixed;
  inset: 0;
  z-index: 210;
  display: flex;
  flex-direction: column;
  background: var(--panel, #fff);
  color: var(--text);
  box-shadow: 0 12px 40px rgba(31, 41, 51, .22);
  outline: none;
  /* 關閉：滑出去之後才藏起來（visibility 延後 .2s），不進 Tab 順序、不被螢幕閱讀器讀到。 */
  visibility: hidden;
  transform: translateY(100%);
  transition: transform .2s ease-out, visibility 0s linear .2s;
}
.alf-panel[data-state="open"] {
  visibility: visible;
  transform: none;
  transition: transform .2s ease-out;
}
@media (min-width: 768px) {
  .alf-panel {
    left: auto;
    width: 400px;
    border-left: 1px solid var(--border);
    transform: translateX(100%);
  }
}
@media (min-width: 1024px) {
  body:has(> #app-topbar) { transition: padding-right .2s ease-out; }
  html[data-alf-open] body:has(> #app-topbar) { padding-right: 400px; }
}
@media (prefers-reduced-motion: reduce) {
  .alf-panel, .alf-panel[data-state="open"], .admin-top-actions, body:has(> #app-topbar) { transition: none; }
}

.alf-panel-header {
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 52px;
  padding: 4px 4px 4px 16px;
  border-bottom: 1px solid var(--border);
}
.alf-panel-header .ask-alf-icon { color: var(--blue); }
.alf-panel-title { margin: 0; font-size: .95rem; font-weight: 700; }
.alf-panel-newtab {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 8px;
  font-size: .8rem;
  color: var(--text-muted);
  text-decoration: none;
}
.alf-panel-newtab:hover { color: var(--text); text-decoration: underline; }
.alf-panel-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--text-muted);
  font-size: 1rem;
  cursor: pointer;
}
.alf-panel-close:hover { color: var(--text); background: var(--surface-2, rgba(0, 0, 0, .05)); }

.alf-panel-body { flex: 1; min-height: 0; position: relative; }
.alf-panel-iframe { width: 100%; height: 100%; border: none; display: block; }

.alf-panel-loading, .alf-panel-error {
  padding: 24px 16px;
  text-align: center;
  color: var(--text-muted);
  font-size: .92rem;
}
.alf-panel-error { color: var(--danger); }

/* 後台統一行事曆（Task 10，2026-08-08）：四種狀態的顏色慣例，沿用既有 .badge 系列的
 * 語意配色（青/藍＝正常、綠＝可用、紅＝額滿/警示），但這裡是格內文字色而非徽章底色——
 * 一個日期格常常要疊多筆（<br> 分行），做成徽章底色會太搶眼、擠爆窄欄。 */
.cal-cell { display:inline-block; font-size:0.85rem; }
.cal-cell.cal-gap { color: #94a3b8; }
.cal-cell.cal-booked-1on1 { color: #2563eb; }
.cal-cell.cal-open { color: #16a34a; }
.cal-cell.cal-full { color: #dc2626; }

/* ---------------------------------------------------------------------------
 * 學員端語言切換器（i18n.js 注入，R8 範圍 A，2026-08-16）：右上角固定小元件，
 * 「EN｜中」，跟 #app-topbar（商戶後台導覽列，z-index:50）互不影響——學員端頁面
 * 本身沒有導覽列，用更高的 z-index 保證浮在 flow-progress／account-bar 之上。
 * ------------------------------------------------------------------------- */
.lang-switcher {
  position: fixed;
  top: 8px;
  right: 8px;
  z-index: 80;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px 8px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 0.78rem;
  box-shadow: 0 1px 4px rgba(0,0,0,0.08);
}
.lang-switch-btn {
  background: transparent;
  border: none;
  padding: 2px 4px;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
}
.lang-switch-btn.lang-switch-active { color: var(--teal); }
.lang-switch-sep { color: var(--border); }
/* i18n.js 注入切換器後在 <body> 掛這個 class：.shop-page 原本 padding-top 只有 16px，
   窄螢幕（進度條／頁面內容整個頂到滿版寬）下固定右上角的切換器會蓋住 flow-progress
   最後一步的圓點／標籤，補這段讓內容整體往下讓出空間。 */
body.has-lang-switcher .shop-page { padding-top: 44px; }
@media (max-width: 480px) {
  .lang-switcher { top: 4px; right: 4px; padding: 3px 6px; }
  body.has-lang-switcher .shop-page { padding-top: 38px; }
}

/* ── 儀表板收入趨勢長條圖（2026-09-01）─────────────────────────────────────────
 * 刻意用純 CSS 畫，不引圖表函式庫：這張圖只有 12 根長條，為它多載一個 library
 * 會讓每個開後台的人都多下載一份用不到的程式碼；而且後台頁面一律自給自足、不吃外部 CDN。
 * 高度用 flex + 百分比，容器變窄時長條自動變細，不需要 JS 重算。 */
.trend-chart { display: flex; align-items: flex-end; gap: 6px; height: 180px; overflow-x: auto; }
.trend-col { flex: 1 1 0; min-width: 34px; display: flex; flex-direction: column;
             justify-content: flex-end; align-items: center; height: 100%; }
/* ⚠️ 長條一定要放在這個「只裝長條」的彈性區塊裡，百分比才有意義。
 * 曾經直接把 height:N% 掛在 .trend-col 底下的長條上——那個百分比算的是整欄（含上下兩行
 * 文字）的高度，於是最高的長條被文字擠掉、矮的沒被擠，畫出來的比例是錯的
 * （18,000 只有 68,000 的 26%，圖上卻看起來超過一半）。收入圖表把比例畫錯會誤導經營判斷，
 * 比沒有這張圖更糟。 */
.trend-bar-area { flex: 1 1 auto; width: 100%; display: flex; align-items: flex-end; }
/* min-height 讓「有收入但金額很小」的月份仍看得見一條，不會跟 0 元長得一樣。 */
.trend-bar { width: 100%; background: var(--teal); border-radius: 4px 4px 0 0;
             transition: opacity .15s; }
.trend-bar:hover { opacity: .75; }
.trend-bar.is-zero { background: #d9dcd6; }
.trend-col-label { margin-top: 6px; font-size: .7rem; color: #666; white-space: nowrap; }
.trend-col-value { font-size: .68rem; color: #444; margin-bottom: 4px; white-space: nowrap; }
.trend-empty { color: #666; font-size: .9rem; padding: 16px 0; }

/* ---------------------------------------------------------------------------
 * 課程群組（2026-10-13，學員端目錄分層，public/s/index.html）：參考國外選課 App 的卡片式——
 * 大圓角、細邊框、膠囊標籤、留白。圖示底與分段線吃店家品牌色（--brand-bg，見 .merchant-header），
 * 沒設定時走同一個苔綠預設。只有建了群組的店才會出現這些 class，其他店畫面不受影響。
 * ------------------------------------------------------------------------- */
.shop-page .cg-section {
  font-size: 0.8rem; font-weight: 500; letter-spacing: 0.08em; color: var(--text-muted);
  margin: 18px 4px 8px;
}
.shop-page .cg-section:first-child { margin-top: 4px; }
.shop-page .cg-card {
  display: flex; align-items: center; gap: 14px; padding: 14px; margin-bottom: 10px;
  background: var(--card-bg); border: 1px solid var(--border); border-radius: 16px;
  color: inherit; text-decoration: none; transition: border-color 0.15s, transform 0.15s;
}
.shop-page .cg-card:active { transform: scale(0.99); }
.shop-page .cg-card:hover { border-color: var(--brand-bg, #5C7050); }
.shop-page .cg-icon {
  flex: 0 0 48px; height: 48px; border-radius: 14px; display: flex; align-items: center; justify-content: center;
  font-size: 24px; background: color-mix(in srgb, var(--brand-bg, #5C7050) 16%, var(--card-bg));
}
.shop-page .cg-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.shop-page .cg-name { font-size: 1rem; font-weight: 500; color: var(--text); }
.shop-page .cg-desc { font-size: 0.8rem; color: var(--text-muted); }
.shop-page .cg-pills { display: flex; flex-wrap: wrap; gap: 6px; }
.shop-page .cg-pill {
  font-size: 0.72rem; padding: 2px 9px; border-radius: 999px;
  background: color-mix(in srgb, var(--text) 6%, transparent); color: var(--text-muted);
}
.shop-page .cg-foot { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; font-size: 0.82rem; }
.shop-page .cg-price { font-weight: 500; color: var(--accent); }
.shop-page .cg-chev { flex: 0 0 auto; font-size: 1.4rem; line-height: 1; color: var(--text-muted); }
.shop-page .cg-seat { font-size: 0.72rem; padding: 2px 9px; border-radius: 999px; white-space: nowrap; }
.shop-page .cg-seat-open { background: #E1F5EE; color: #085041; }
.shop-page .cg-seat-few { background: #FAEEDA; color: #854F0B; }
.shop-page .cg-seat-full { background: color-mix(in srgb, var(--text) 7%, transparent); color: var(--text-muted); }

.shop-page .cg-head { display: flex; align-items: flex-start; gap: 10px; margin: 2px 0 14px; }
.shop-page .cg-back {
  flex: 0 0 36px; height: 36px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--card-bg); color: var(--text); font-size: 1.4rem; line-height: 1; cursor: pointer; padding: 0 0 2px;
}
.shop-page .cg-title { font-size: 1.15rem; font-weight: 500; margin: 4px 0 2px; }
.shop-page .cg-sub { font-size: 0.82rem; color: var(--text-muted); margin: 0; }
/* 篩選列：手機上一排橫向滑動，不折行（國外 App 常見做法，chip 多也不會把清單擠到下面）。 */
.shop-page .cg-filters { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }
.shop-page .cg-chiprow {
  display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch;
  margin: 0 -12px; padding: 0 12px;
}
.shop-page .cg-chiprow::-webkit-scrollbar { display: none; }
.shop-page .cg-chiprow .chip { flex: 0 0 auto; }
.shop-page .cg-count { font-size: 0.8rem; color: var(--text-muted); margin: 4px 2px 6px; }
.shop-page .cg-list { background: var(--card-bg); border: 1px solid var(--border); border-radius: 16px; overflow: hidden; }
.shop-page .cg-row {
  display: flex; align-items: center; gap: 12px; padding: 13px 14px; min-height: 44px;
  color: inherit; text-decoration: none; border-top: 1px solid var(--border);
}
.shop-page .cg-row:first-child { border-top: 0; }
.shop-page .cg-row-full { opacity: 0.55; }
.shop-page .cg-time { flex: 0 0 54px; display: flex; flex-direction: column; line-height: 1.2; }
.shop-page .cg-time b { font-size: 1rem; font-weight: 500; font-variant-numeric: tabular-nums; }
.shop-page .cg-time small { font-size: 0.72rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.shop-page .cg-time-none { font-size: 1.3rem; }
.shop-page .cg-row-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.shop-page .cg-row-title { font-size: 0.92rem; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.shop-page .cg-row-sub { font-size: 0.78rem; color: var(--text-muted); }
/* 列說明的每一段（星期、堂數、價格）各自不斷行，只在段與段之間換行——「NT$ 2,250」不會被拆成兩行。 */
.shop-page .cg-nb { white-space: nowrap; }
.shop-page .cg-empty { text-align: center; padding: 28px 12px; color: var(--text-muted); }
.shop-page .cg-empty p { margin: 0 0 10px; }
