/* ════════════════════════════════════════════════════════════
   全站共用樣式（base.html 的 <style> 搬出來的）

   原本這 561 行直接嵌在 base.html 裡，62 個繼承它的頁面每次載入
   都要重送一次，瀏覽器永遠快取不到。搬成獨立檔案 + 內容 hash 之後，
   第二頁開始是零 bytes、零來回。

   ⚠️ 引用一定要走 asset()，不要寫死路徑：
        <link rel="stylesheet" href="{{ asset('css/base.css') }}">
   sw.js 對 /static/ 是 Cache First 不重新驗證，少了 ?v=<hash>
   使用者會拿到舊 CSS 配新 HTML —— 版面錯位而且完全不報錯。
   詳見 routes/assets.py 開頭的說明。
   ════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════
   設計系統 Design Tokens
═══════════════════════════════════════ */
:root {
  --green:        #06C755;
  --green-dark:   #05a847;
  /* 品牌綠拿來當「文字」時在白底只有 2.26:1，遠低於 AA 的 4.5:1 ——
     按鈕底色照舊用 --green（那是品牌識別），文字/連結一律用這一支（5.04:1）。 */
  --green-ink:    #068040;
  --green-bg:     #f0fdf4;
  --green-border: #bbf7d0;

  --blue:         #1d6fe5;
  --blue-bg:      #eff6ff;
  --purple:       #7c3aed;
  --purple-bg:    #f5f3ff;
  --orange:       #ea580c;
  --orange-bg:    #fff7ed;
  --red:          #dc2626;
  --red-bg:       #fef2f2;

  /* 說明文字用的兩級曾經是 #6b7280 / #9ca3af，對比只有 4.8:1 與 2.5:1。
     #9ca3af 那級（全站說明文字都用它）遠低於 WCAG AA 的 4.5:1，
     實際回報是「一堆人看不清楚」。加深到 6.0:1 與 5.6:1。 */
  --text-1:  #0f172a;
  --text-2:  #2b3444;
  --text-3:  #5b6472;
  --text-4:  #5f6875;

  --bg:      #edf0f5;
  --surface: #ffffff;
  --border:  #e2e6ed;
  --border-2:#f0f2f6;

  --shadow-xs: 0 1px 2px rgba(30,40,80,0.06);
  --shadow-sm: 0 2px 6px rgba(30,40,80,0.07), 0 1px 2px rgba(30,40,80,0.04);
  --shadow:    0 4px 14px rgba(30,40,80,0.08), 0 1px 4px rgba(30,40,80,0.04);
  --shadow-md: 0 8px 24px rgba(30,40,80,0.10), 0 2px 6px rgba(30,40,80,0.05);
  --shadow-lg: 0 20px 48px rgba(30,40,80,0.13), 0 4px 12px rgba(30,40,80,0.06);

  --radius-sm: 8px;
  --radius:    12px;
  --radius-lg: 16px;
  --radius-xl: 24px;
  --radius-full: 9999px;

  --transition: 0.18s ease;
}

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

/* rem 是相對 html 的，不是相對 body —— 寫在 body 上整站字級不會有任何變化，
   而且不會報錯，只會看起來「明明改了卻沒變大」。放大要改這裡。
   16px → 17px：全站 rem 字級一起放大 6%，最小的 0.8rem 從 13.6px 起跳。 */
html { font-size: 17px; }

body {
  font-family: "Noto Serif TC", "PingFang TC", "Microsoft JhengHei", serif;
  background: linear-gradient(135deg, #dbeafe 0%, #ede9fe 35%, #d1fae5 70%, #fce7f3 100%);
  background-attachment: fixed;
  color: var(--text-1);
  /* 字重 400 → 500：教會很多長輩，細體在手機上本來就吃力。 */
  font-size: 1rem;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  font-weight: 500;
  letter-spacing: 0.01em;
}
img { max-width: 100%; }
a { color: inherit; }

/* ─ Navigation ─ */
nav {
  background: var(--green);
  padding: 0 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 56px;
  position: sticky;
  top: 0;
  z-index: 100;
  box-shadow: 0 2px 8px rgba(6,199,85,0.25);
}
.nav-logo {
  display: flex;
  align-items: center;
  gap: 8px;
  color: white;
  text-decoration: none;
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
}
.nav-logo span { opacity: 0.9; }
.nav-right {
  display: flex;
  align-items: center;
  gap: 4px;
}
.nav-link {
  /* 原本 0.9 透明度：白字疊在品牌綠上只剩 2.06:1。拿掉淡化並加粗，
     在不動品牌綠底色的前提下把導覽列拉到最亮。 */
  color: #fff;
  font-weight: 600;
  text-decoration: none;
  font-size: 0.85rem;
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  transition: background var(--transition);
  white-space: nowrap;
}
.nav-link:hover { background: rgba(255,255,255,0.15); }
.nav-avatar {
  width: 30px; height: 30px;
  border-radius: var(--radius-full);
  border: 2px solid rgba(255,255,255,0.5);
  object-fit: cover;
}
.nav-admin-badge {
  background: rgba(0,0,0,0.2);
  color: white;
  font-size: 0.8rem;
  padding: 4px 10px;
  border-radius: var(--radius-full);
  text-decoration: none;
  font-weight: 600;
  transition: background var(--transition);
}
.nav-admin-badge:hover { background: rgba(0,0,0,0.35); }

.line-browser-tip {
  display: none;
  background: #fff7ed;
  border-bottom: 1px solid #fed7aa;
  padding: 10px 16px;
  color: #9a3412;
  font-size: 0.86rem;
}
.line-browser-tip-inner {
  max-width: 920px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.line-browser-tip-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
.line-browser-tip button {
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-weight: 700;
  font-size: 0.8rem;
  padding: 7px 10px;
}
.line-browser-tip .open-external {
  background: #06C755;
  color: white;
}
.line-browser-tip .dismiss {
  background: transparent;
  color: #9a3412;
  padding: 7px 4px;
}

/* ─ Page Layout ─ */
main {
  max-width: 920px;
  margin: 28px auto 80px;
  padding: 0 16px;
}

/* ─ Page Header ─ */
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 28px;
  flex-wrap: wrap;
}
.page-header-left .breadcrumb {
  font-size: 0.83rem;
  color: var(--text-4);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-bottom: 6px;
}
.page-header-left .breadcrumb:hover { color: var(--text-3); }
.page-title {
  font-size: 1.45rem;
  font-weight: 700;
  color: var(--text-1);
  letter-spacing: 0.02em;
}
.page-subtitle {
  font-size: 0.85rem;
  color: var(--text-3);
  margin-top: 4px;
}

/* ─ Cards ─ */
.card {
  background: rgba(255,255,255,0.72);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-radius: var(--radius-lg);
  padding: 20px 24px;
  box-shadow: 0 4px 20px rgba(99,102,241,0.08), 0 1px 4px rgba(0,0,0,0.04);
  border: 1px solid rgba(255,255,255,0.85);
  transition: transform var(--transition), box-shadow var(--transition);
  position: relative;
  overflow: hidden;
}
.card::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 2.5px;
  background: linear-gradient(90deg, var(--green) 0%, #34d399 50%, transparent 100%);
  opacity: 0;
  transition: opacity var(--transition);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  z-index: 1;
}
.card:hover {
  transform: translateY(-3px);
  box-shadow: 0 8px 32px rgba(99,102,241,0.13), 0 2px 8px rgba(0,0,0,0.06);
}
.card:hover::before { opacity: 1; }
.card-sm {
  background: rgba(255,255,255,0.72);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-radius: var(--radius);
  padding: 16px 20px;
  box-shadow: 0 4px 20px rgba(99,102,241,0.08), 0 1px 4px rgba(0,0,0,0.04);
  border: 1px solid rgba(255,255,255,0.85);
  transition: transform var(--transition), box-shadow var(--transition);
}
.card-sm:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 32px rgba(99,102,241,0.13), 0 2px 8px rgba(0,0,0,0.06);
}

/* ─ Buttons ─ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 9px 18px;
  border-radius: var(--radius-sm);
  font-size: 0.88rem;
  font-weight: 600;
  border: none;
  cursor: pointer;
  text-decoration: none;
  transition: all var(--transition);
  white-space: nowrap;
  line-height: 1;
}
.btn:active { transform: scale(0.97); }
.btn-primary {
  background: var(--green);
  color: white;
}
.btn-primary:hover { background: var(--green-dark); box-shadow: 0 4px 12px rgba(6,199,85,0.35); }
.btn-secondary {
  background: var(--border-2);
  color: var(--text-2);
}
.btn-secondary:hover { background: var(--border); }
.btn-outline {
  background: transparent;
  color: var(--text-2);
  border: 1.5px solid var(--border);
}
.btn-outline:hover { border-color: var(--text-3); background: var(--border-2); }
.btn-danger {
  background: var(--red-bg);
  color: var(--red);
}
.btn-danger:hover { background: #fee2e2; }
.btn-blue {
  background: var(--blue-bg);
  color: var(--blue);
}
.btn-blue:hover { background: #dbeafe; }
.btn-sm {
  padding: 6px 12px;
  font-size: 0.8rem;
}
.btn-lg {
  padding: 12px 28px;
  font-size: 0.95rem;
}

/* ─ Badges / Tags ─ */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 3px 10px;
  border-radius: var(--radius-full);
  font-size: 0.8rem;
  font-weight: 600;
  white-space: nowrap;
}
.badge-green  { background: var(--green-bg);  color: #16a34a; }
.badge-red    { background: var(--red-bg);    color: var(--red); }
.badge-blue   { background: var(--blue-bg);   color: var(--blue); }
.badge-purple { background: var(--purple-bg); color: var(--purple); }
.badge-orange { background: var(--orange-bg); color: var(--orange); }
.badge-gray   { background: var(--border-2);  color: var(--text-3); }

/* ─ Table ─ */
.table-wrap {
  background: var(--surface);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}
.table-wrap table {
  width: 100%;
  border-collapse: collapse;
}
.table-wrap th {
  background: #f9fafb;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  text-align: left;
}
.table-wrap td {
  padding: 12px 16px;
  border-bottom: 1px solid var(--border-2);
  font-size: 0.9rem;
  color: var(--text-2);
}
.table-wrap tr:last-child td { border-bottom: none; }
.table-wrap tbody tr:hover { background: #fafafa; }

/* ─ Form Elements ─ */
.form-label {
  display: block;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-2);
  margin-bottom: 5px;
}
.form-input {
  width: 100%;
  padding: 9px 13px;
  border: 1.5px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 0.9rem;
  color: var(--text-1);
  background: var(--surface);
  transition: border-color var(--transition), box-shadow var(--transition);
  outline: none;
}
.form-input:focus {
  border-color: var(--green);
  box-shadow: 0 0 0 3px rgba(6,199,85,0.12);
}
textarea.form-input { resize: vertical; min-height: 80px; }

/* ─ Empty State ─ */
.empty-state {
  background: var(--surface);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  padding: 64px 24px;
  text-align: center;
}
.empty-state .icon { font-size: 3rem; margin-bottom: 16px; }
.empty-state p {
  color: var(--text-3);
  font-size: 0.95rem;
}

/* ─ Section Label ─ */
.section-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.83rem;
  font-weight: 700;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 12px;
}
.section-label::before {
  content: '';
  display: inline-block;
  width: 3px; height: 14px;
  background: var(--green);
  border-radius: 2px;
}

/* ─ Divider ─ */
.divider {
  border: none;
  border-top: 1px solid var(--border);
  margin: 20px 0;
}

/* ─ Info Banner ─ */
.info-banner {
  display: flex;
  gap: 12px;
  padding: 14px 16px;
  border-radius: var(--radius);
  font-size: 0.88rem;
  margin-bottom: 20px;
}
.info-banner.yellow { background: #fffbeb; border-left: 3px solid #f59e0b; color: #92400e; }
.info-banner.blue   { background: var(--blue-bg); border-left: 3px solid var(--blue); color: #1e40af; }
.info-banner.green  { background: var(--green-bg); border-left: 3px solid var(--green); color: #166534; }
.info-banner.red    { background: var(--red-bg); border-left: 3px solid var(--red); color: #991b1b; }

/* ─ Responsive ─ */
@media (max-width: 600px) {
  main { margin-top: 20px; padding: 0 12px; }
  .page-header { margin-bottom: 20px; }
  .page-title { font-size: 1.2rem; }
  .line-browser-tip-inner {
    align-items: flex-start;
    flex-direction: column;
  }
  .line-browser-tip-actions {
    width: 100%;
  }
  .line-browser-tip button.open-external {
    flex: 1;
  }
}

/* ─ 手機導覽列優化 ─ */
/* 桌機：admin-short 預設隱藏 */
.nav-admin-short { display: none; }

@media (max-width: 600px) {
  /* Logo 只保留圖示，隱藏文字 */
  .nav-logo .nav-logo-text { display: none; }
  /* 隱藏非必要的導覽連結 */
  .nav-desktop-only { display: none !important; }
  /* 管理員徽章：手機顯示「⚙️後台」 */
  .nav-admin-badge .nav-admin-full  { display: none; }
  .nav-admin-badge .nav-admin-short { display: inline !important; }
  .nav-admin-badge { background: rgba(255,255,255,0.25); font-size: 0.82rem; padding: 5px 10px; }
  /* 收緊右側間距 */
  .nav-right { gap: 2px; }
  nav { padding: 0 12px; }
  /* 姓名最多顯示 5 個字，防止超長名字撐爆 */
  .nav-name-link {
    max-width: 5em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* ─ Notification Bell ─ */
.notif-bell-btn {
  position: relative; background: none; border: none; cursor: pointer;
  padding: 6px 8px; color: white; display: flex; align-items: center;
  border-radius: var(--radius-sm); transition: background var(--transition);
}
.notif-bell-btn:hover { background: rgba(255,255,255,0.15); }
.notif-badge {
  display: none; position: absolute; top: 0; right: 0;
  background: #ef4444; color: white; font-size: 0.8rem; font-weight: 800;
  min-width: 16px; height: 16px; border-radius: 9999px;
  align-items: center; justify-content: center; padding: 0 3px;
  border: 1.5px solid var(--green); line-height: 1;
}
.notif-panel {
  display: none; position: absolute; right: 0; top: calc(100% + 10px);
  width: 320px; background: white; border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg); z-index: 9999; overflow: hidden;
  border: 1px solid var(--border); animation: fadeIn 0.18s ease;
}
.notif-panel-header {
  padding: 12px 16px; border-bottom: 1px solid var(--border);
  display: flex; justify-content: space-between; align-items: center;
}
.notif-item {
  padding: 12px 16px; border-bottom: 1px solid var(--border-2);
  cursor: pointer; transition: background var(--transition);
  display: flex; align-items: flex-start; gap: 10px;
}
.notif-item:hover { background: #f9fafb; }
.notif-item.unread { background: #f0fdf4; }
.notif-item.unread:hover { background: #dcfce7; }
.notif-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--green); flex-shrink: 0; margin-top: 5px;
}
.notif-dot-empty { width: 8px; flex-shrink: 0; }

/* ─ Animations ─ */
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(6,199,85,0.55); }
  70%  { box-shadow: 0 0 0 14px rgba(6,199,85,0); }
  100% { box-shadow: 0 0 0 0 rgba(6,199,85,0); }
}
@keyframes fadeIn {
  from { opacity:0; transform:translateY(6px); }
  to   { opacity:1; transform:translateY(0); }
}
@keyframes fadeOut { from{opacity:1} to{opacity:0} }
.help-pulse { animation: pulse 1.6s ease-out infinite; }

/* 小安要開口時的一次性小動作。
   ★ 它是「主動性」裡最便宜的一段：動的東西會被眼角看到，而一次就停 ——
     持續動的東西會被當成廣告，人會學著不看那個角落。
   ★ reduced-motion 一律不跑（有人會因為前庭敏感開這個設定）。 */
@keyframes xa-nudge {
  0%, 100% { transform: translateY(0); }
  30%      { transform: translateY(-5px); }
  62%      { transform: translateY(-2px); }
}
/* ══ 小舟的問答面板 ══════════════════════════════
   手機：從底部滑上來的抽屜。使用者：「手機用起來是還可以，不會太佔位」。
   桌機：貼在右下角的一張卡片。使用者：「點擊小舟以後從底部彈出對話窗，
         這行為在電腦版網頁有點怪異」—— 他是對的，底部抽屜是**手機的
         手勢語言**（拇指往上滑），在滑鼠上沒有對應的動作，
         而且整條螢幕寬的東西為了一個問答框太重了。
   ★ 這些規則一定要放在 CSS 不能放 inline —— inline 贏過 media query，
     寫在 HTML 裡的話桌機那一套永遠不會生效。 */
#ask-panel {
  position: fixed; z-index: 9991;
  left: 50%; bottom: 0; transform: translateX(-50%);
  width: min(560px, 100vw); max-height: 82vh; overflow-y: auto;
  background: var(--surface, #fff);
  border-radius: 16px 16px 0 0;
  box-shadow: 0 -8px 40px rgba(0,0,0,.28);
  padding: 20px 20px 26px;
}
#ask-overlay {
  position: fixed; inset: 0; z-index: 9990;
  background: rgba(15,23,42,.45);
}
@media (min-width: 760px) {
  #ask-panel {
    left: auto; right: 24px; bottom: 24px; transform: none;
    width: 420px; max-height: min(640px, 78vh);
    border-radius: 16px;
    box-shadow: 0 12px 44px rgba(0,0,0,.22);
  }
  /* ★ 桌機不把整頁壓暗：這是「問一句」不是「請你先處理這件事」。
     遮罩留著（點旁邊要能關掉），只是看不見。 */
  #ask-overlay { background: transparent; }
}

/* 圓圈底下那行「問我」。
   ★ 白色描邊而不是白色底：底色會變成一顆小膠囊，而使用者明確說過
     不要膠囊。描邊讓它在深色照片（首頁 hero 可以換圖）上也讀得到。 */
.xa-label {
  font-size: .72rem; font-weight: 800; line-height: 1;
  color: var(--text-1, #1f2937); letter-spacing: .04em;
  text-shadow: 0 0 3px #fff, 0 0 3px #fff, 0 0 6px #fff;
  pointer-events: none;
}

/* 呼吸感：慢慢的一縮一放。使用者：「呼吸感可以保留XD」。
   ★ 4.5 秒一次、只放大 3.5% —— 快或大都會變成「在閃」，
     而閃的東西會被當成廣告，人會學著不看那個角落。
   ★ 講話的時候停下來（.xa-speaking 的動作優先），不然兩個動作疊在一起。 */
@keyframes xa-breathe {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.035); }
}
#help-btn .xa-face { animation: xa-breathe 4.5s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  #help-btn .xa-face { animation: none; }
}

/* ★ 動的是**頭像**不是整顆按鈕：講話時按鈕裡還有一個泡泡，
     整個一起跳的話跳的是那塊白色方塊，看起來像畫面在抖。 */
#help-btn.xa-speaking .xa-face { animation: xa-nudge .55s ease-out 1; }
@media (prefers-reduced-motion: reduce) {
  #help-btn.xa-speaking .xa-face { animation: none; }
}

/* ════════════════════════════════════════════════════════════
   Toast 與提醒輪播（base.html 的第二個 <style> 區塊）
   ════════════════════════════════════════════════════════════ */

._toast {
  padding:11px 22px; border-radius:var(--radius-full); font-size: 0.88rem; font-weight:600;
  color:white; box-shadow:var(--shadow-md);
  animation:fadeIn 0.22s ease, fadeOut 0.3s ease 2.7s forwards;
  text-align:center; max-width:100%;
}
._toast.success { background:#16a34a; }
._toast.error   { background:var(--red); }
._toast.info    { background:var(--blue); }
._toast.warn    { background:var(--orange); }

/* ═══ 提醒輪播（前台「接下來」與後台「今天要做的事」共用一份）═══
   提醒多的時候不要往下疊成一長串 —— 首頁被推很長，反而每一則都被略過。
   改成一次只佔一張卡的高度，左右滑看下一則。 */
.rem-track {
  display: flex; gap: 12px;
  overflow-x: auto; scroll-snap-type: x mandatory;
  scrollbar-width: none; -ms-overflow-style: none;
  /* 卡片有陰影，容器要留一點內距不然會被裁掉 */
  padding: 4px 2px 6px;
  scroll-behavior: smooth;
}
.rem-track::-webkit-scrollbar { display: none; }
.rem-track > * {
  scroll-snap-align: start;
  /* 煞車：沒有這行的話，一次快滑會直接飛過好幾則，
     停在哪一則完全看手勁 —— 要的是「一滑就是下一則」。 */
  scroll-snap-stop: always;
  flex: 0 0 100%;              /* 一次剛好一張，滑動邊界很明確 */
  min-width: 0;
}
/* 一律一次一張，桌機也一樣。
   原本桌機放兩張（怕單張拉太寬），但那跟輪播的概念衝突 ——
   看到兩則、點點卻是一則一顆，數量對不起來，看的人會困惑。 */
/* 自動換片的垂直過場。手動是水平捲動（原生 scroll-snap），
   兩者不同軸，所以自動換不能用捲動做 —— 改成淡出上移、
   悄悄把水平位置跳到下一張、再從下方淡入。 */
.rem-track { transition: transform .16s ease, opacity .16s ease; }
.rem-track.rem-out  { transform: translateY(-14px); opacity: 0; }
.rem-track.rem-in   { transform: translateY(14px);  opacity: 0; transition: none; }
@media (prefers-reduced-motion: reduce) {
  .rem-track, .rem-track.rem-out, .rem-track.rem-in {
    transition: none; transform: none; opacity: 1;
  }
}
.rem-dots {
  display: flex; justify-content: center; align-items: center; gap: 7px;
  margin-top: 6px;
}
.rem-dot {
  width: 7px; height: 7px; border-radius: 50%; border: none; padding: 0;
  background: rgba(120,130,150,0.28); cursor: pointer;
  transition: background 0.2s, width 0.2s;
}
.rem-dot.on { background: var(--green); width: 18px; border-radius: 4px; }
/* 只有一則時不需要輪播的任何裝飾 */
.rem-carousel:not(.multi) .rem-dots { display: none; }
@media (prefers-reduced-motion: reduce) {
  .rem-track { scroll-behavior: auto; }
}

/* ══════════════════════════════════════════════════════════
   目的層級（主目的 / 次目的 / 狀態）

   來自一個實際的觀察：禱讀本登記頁「主要功能就是登記數量，
   但進去以後登記的錨點很不明顯」。

   去比對之後發現不是「東西太多」，也不是版面不好 ——
   是每一塊都被畫成同一個量級：

     簡介卡：漸層底 + 陰影 + 圖示 + 彩色標題 0.95rem 粗體
     登記卡：純色底 + 邊框        + 深色標題 0.95rem 粗體

   兩張卡構造幾乎一樣，簡介甚至多了圖示和彩色標題，比主目的更搶眼。
   使用者得自己判斷該先看哪個。

   ★ 關鍵：那些不是雜訊，是「次目的」。

   簡介在服務主目的（不知道這是什麼書就填不出本數），
   別組登記狀況是社會參照，價格截止日是決策依據 —— 都有工作。
   所以不能刪，也不該只是「變小」。

   做法是分「種類」不分「大小」：
     .act-*    主目的 → 長得像操作台，不像卡片。一頁只能有一個。
     .sub-*    次目的 → 保留全部內容，拿掉爭取注意力的裝飾。
                        它需要「被找得到」，不是「被注意」。
     .state-*  狀態   → 一進來就看得到「我做過了沒」，
                        因為那決定接下來要不要做事。
   ══════════════════════════════════════════════════════════ */

/* ── 主目的：操作台 ────────────────────────────── */
.act-panel {
  background: var(--surface);
  border: 2px solid var(--green);
  border-radius: 16px;
  padding: 18px 18px 20px;
  margin-bottom: 16px;
  box-shadow: 0 4px 18px rgba(6,199,85,.14);
}
.act-panel-title {
  margin: 0 0 2px;
  font-size: 1.08rem;
  font-weight: 800;
  color: var(--text-1);
  letter-spacing: -.01em;
}
.act-panel-hint {
  margin: 0 0 14px;
  font-size: 0.85rem;
  color: var(--text-3);
}
.act-field-label {
  display: block;
  margin-bottom: 5px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-2);
}
/* 主要欄位要比次要欄位明顯大 —— 原本本數和備註同寬，
   看起來像兩個同等重要的東西，但只有本數是必填。 */
.act-input {
  padding: 13px 14px;
  border: 2px solid var(--green-border);
  border-radius: 11px;
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--text-1);
  font-family: inherit;
  background: var(--surface);
}
.act-input:focus {
  outline: none;
  border-color: var(--green);
  box-shadow: 0 0 0 3px rgba(6,199,85,.15);
}
.act-input-sub {
  padding: 12px 14px;
  border: 1.5px solid var(--border);
  border-radius: 11px;
  font-size: 0.92rem;
  font-family: inherit;
  color: var(--text-1);
  background: var(--surface);
  box-sizing: border-box;
}
.act-input-sub:focus { outline: none; border-color: var(--green); }
/* 整頁唯一的實心大按鈕 */
.act-submit {
  width: 100%;
  margin-top: 14px;
  padding: 15px;
  background: var(--green);
  color: #fff;
  border: none;
  border-radius: 12px;
  font-size: 1.05rem;
  font-weight: 800;
  font-family: inherit;
  cursor: pointer;
  box-shadow: 0 5px 16px rgba(6,199,85,.32);
}
.act-submit:active { transform: scale(.985); }
.act-submit:disabled { background: #9aa3ad; box-shadow: none; cursor: not-allowed; }

/* 必填標記：寫「必填」兩個字，不用紅色星號
   ───────────────────────────────────────
   星號要**先知道它是什麼意思**才看得懂 —— 而這套系統的使用者有很多長輩，
   表單上還常常同時出現分級收費的價格、民國年月日這些數字和符號。
   一顆小小的紅色 * 混在裡面，最好的情況是被忽略，
   比較糟的情況是被當成「這裡有錯」。

   ★ 顏色刻意不用紅色。紅色在這套系統裡一路都是「有問題／還沒做完」
     （未繳費、還沒到、選項對不上）。還沒開始填就先紅一片的話，
     那個顏色就不再代表任何事情了。 */
   ★ 顏色可以換，形狀不行。代禱麥田是麥田金、福音探索是暖橘 ——
     在那些頁面上放一塊系統綠會像貼錯地方。所以顏色開兩個變數讓頁面
     自己設（設在容器上，例如 `.prayer-form { --req-ink: #8a5a10 }`），
     **但不可以自己重新定義 `.req-tag`** —— 尺寸和形狀只有這裡說了算，
     不然「必填」在每一頁大小不一樣，而每一頁分別看都很正常。 */
.req-tag {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 4px;
  background: var(--req-bg, var(--green-bg, #eaf7ee));
  color: var(--req-ink, var(--green-ink, #0b7a3d));
  font-size: 0.72rem;
  font-weight: 700;
  vertical-align: 1px;
  letter-spacing: .02em;
}

/* ── 次目的：資訊，不是卡片 ────────────────────── */
.sub-card {
  background: var(--surface);
  border: 1px solid rgba(128,128,128,.18);
  border-radius: 12px;
  padding: 14px 16px;
  margin-bottom: 14px;
}
.sub-card-title {
  margin: 0 0 6px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-3);
}

/* ── 狀態：我做過了沒 ──────────────────────────── */
.state-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 15px;
  border-radius: 12px;
  margin-bottom: 14px;
  font-size: 0.92rem;
  font-weight: 600;
}
.state-bar.done    { background: var(--green-bg); color: #14532d; border: 1px solid var(--green-border); }
.state-bar.todo    { background: #fff8e1;         color: #7c4a03; border: 1px solid #ffe082; }
.state-bar.blocked { background: var(--red-bg);   color: #7f1d1d; border: 1px solid #fecaca; }
.state-bar-icon { font-size: 1.15rem; flex-shrink: 0; }

/* ══ 後台掃描頁：主角是資料，不是按鈕 ══════════════════
   前台那套 .act-panel 在這裡不適用。前台的主目的是「完成一個動作」，
   所以要有一個最醒目的操作台；後台名冊 / 簽到狀況這類頁面不是——
   人來這裡是「先看懂現況，再決定要不要動手」。

   所以這裡的失敗模式跟前台相反：不是「沒有明顯的按鈕」，
   而是「工具列比資料還大聲」。報名名單原本頁首 6 個按鈕
   統統是 6px 16px / 0.85rem 粗體，而真正的主角（報了幾人、到了幾人）
   反而更小、還散在三個不同的區塊裡。

   對策：現況數字收成一塊、放大成頁面上最大的東西；
   工具列一律降成同一種樣式，只留一個 primary。 */

.scan-head {
  background: var(--surface);
  border-radius: var(--radius-lg);
  padding: 18px 20px;
  margin-bottom: 16px;
  box-shadow: var(--shadow-sm);
}
.scan-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 28px;
  align-items: baseline;
}
.scan-stat { display: flex; align-items: baseline; gap: 7px; }
/* ★ 這個字級是整頁最大的，刻意的——它是主角 */
.scan-stat-num {
  font-size: 2rem;
  font-weight: 800;
  line-height: 1.1;
  color: var(--text-1);
  font-variant-numeric: tabular-nums;
}
.scan-stat-num.good { color: var(--green-ink); }
.scan-stat-num.warn { color: #b45309; }
.scan-stat-label { font-size: 0.9rem; font-weight: 600; color: var(--text-3); }
.scan-stat-sub   { font-size: 0.85rem; color: var(--text-4); }

.scan-bar {
  margin-top: 14px;
  background: var(--border);
  border-radius: 999px;
  height: 10px;
  overflow: hidden;
}
.scan-bar > i {
  display: block;
  height: 100%;
  background: var(--green);
  border-radius: 999px;
  transition: width .5s ease;
}

/* 次目的：這一批資料的背景資訊（時間、地點、費用、開放與否） */
.scan-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  margin-top: 14px;
  padding-top: 13px;
  border-top: 1px solid var(--border-2);
  font-size: 0.88rem;
  color: var(--text-3);
}
.scan-meta strong { color: var(--text-2); font-weight: 700; }

/* ── 工具列：一個 primary，其餘一律同一種 ghost ──────── */
/* 名單頁的搜尋／篩選／排序（static/js/roster-filter.js）
   ───────────────────────────────────────────────
   一頁按報名順序排下來的人名，要找某一組的人得從頭掃到尾。
   這一列的目的只有一個：**把要找的人挑出來**，所以它不該比名單大聲 ——
   淺色、不搶眼，但一定看得到。 */
.roster-tools {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
  margin-bottom: 12px;
}
.roster-tools input[type="search"],
.roster-tools select {
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 0.92rem;
  font-family: inherit;
  background: var(--surface);
  color: var(--text-1);
  max-width: 100%;
}
.roster-tools input[type="search"] { flex: 1 1 200px; min-width: 0; }
.roster-tools input[type="search"]:focus,
.roster-tools select:focus { outline: none; border-color: var(--green); }
.roster-count {
  font-size: 0.85rem;
  color: var(--text-3);
  white-space: nowrap;
  margin-left: auto;
}
/* 篩選中要看得出來 —— 不然「篩掉一半」跟「只有這些人報名」長得一樣 */
.roster-count.filtered { color: var(--green-ink); font-weight: 700; }
.roster-reset {
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text-3);
  font-size: 0.85rem;
  font-family: inherit;
  cursor: pointer;
}
.roster-reset:hover { border-color: #cfd6e2; color: var(--text-1); }
.roster-empty {
  padding: 28px 16px;
  text-align: center;
  color: var(--text-3);
  font-size: 0.92rem;
}

/* ★ 名單表要看得出來「可以往右滑」
   ────────────────────────────────
   手機的捲軸預設是**捲動時才出現**的 —— 靜止的畫面上完全沒有線索，
   於是右邊那幾欄（付款、已領、完訓）等於不存在。
   使用者的話：「名單 橫向給個卷軸才能知道往右滑」。

   三個線索：
   1. 一條**常駐**的細捲軸（知道有多長、現在在哪）
   2. 右邊一道漸層陰影（捲到底就消失，靠 JS 加 .at-end）
   3. 表格**上面**一句話 —— ★ 捲軸長在表格底部，長名單在手機上
      要先捲到最下面才看得到它，等於沒有。那一句由 roster-filter.js 產生。

   ★ 只給漸層不夠 —— 那看起來像陰影，不像「可以拖」。 */
.roster-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;                 /* Firefox */
  scrollbar-color: #b6bfcc transparent;
  position: relative;
}
.roster-scroll::-webkit-scrollbar { height: 9px; -webkit-appearance: none; }
.roster-scroll::-webkit-scrollbar-track {
  background: var(--border-2, #f0f2f6);
  border-radius: 99px;
}
.roster-scroll::-webkit-scrollbar-thumb {
  background: #b6bfcc;
  border-radius: 99px;
  border: 2px solid var(--border-2, #f0f2f6);
}
.roster-scroll::-webkit-scrollbar-thumb:active { background: #8f9bad; }
/* 右邊的漸層：告訴人「還有東西在右邊」，捲到底就淡出 */
.roster-scroll-wrap { position: relative; overflow: hidden; }
.roster-scroll-wrap::after {
  content: '';
  position: absolute;
  top: 0; right: 0; bottom: 9px;         /* 讓開捲軸 */
  width: 26px;
  pointer-events: none;
  background: linear-gradient(to right, rgba(255,255,255,0), rgba(255,255,255,.92));
  opacity: 1;
  transition: opacity .2s;
}
.roster-scroll-wrap.at-end::after,
.roster-scroll-wrap.no-scroll::after { opacity: 0; }
/* 表格上方那一句：捲軸在底部，長名單在手機上根本看不到它 */
.roster-scroll-hint {
  font-size: 0.8rem;
  color: var(--text-3);
  margin: 0 0 6px;
  line-height: 1.6;
}

/* ★ 姓名欄釘住：手機上往右捲時不會跟著捲掉
   ────────────────────────────────────────
   名單表在手機上一定要橫捲（付款、已領、完訓那幾欄在右邊）。
   捲過去之後畫面上是**一整排可以點的鈕，但看不到任何名字** ——
   同工是在對一個他認不出來的人按「已付款」「已領書」，
   旁邊還有一顆移除學員的 ✕。

   而它不像壞掉：每一顆按鈕都正常回應，只是可能按在別人身上。
   要等對帳或那個人說「我明明繳了」才會發現。

   釘住之後，序號和小組會滑到名字底下（本來就是次要資訊），
   名字永遠在最左邊。 */
.roster-name {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--surface, #fff);
  /* ★ 一定要給寬度。不給的話瀏覽器會把這一欄壓到最窄，
     「王大明」被折成三行，每一列變得又高又難掃 ——
     而釘住本身是成功的，所以看起來只是「表格怪怪的」。 */
  min-width: 132px;
  /* 捲動時右邊給一道陰影，讓人看得出「這一欄是釘住的」，
     不是版面壞掉 */
  box-shadow: 6px 0 6px -6px rgba(15, 23, 42, .18);
}
/* 名字本身不折行，太長就切掉 —— 折成兩行的話整列變高，
   而名單頁的重點是「一眼掃過去」。暱稱是次要的，先被切掉是對的。 */
/* ★ 寬度上限要放在 td **裡面**的元素上。
   table-layout:auto 會忽略 td 的 max-width —— 只寫在 td 上的話，
   名字後面的「回鍋」標籤會滿出這一欄、蓋在下一欄上面，
   而那看起來只是「版面有點亂」，不像設定沒生效。 */
.roster-name > div { flex-wrap: nowrap; min-width: 0; }
/* ★ 寬度上限只有窄螢幕需要（釘住的那一欄不能吃掉半個畫面）。
   桌面也套的話，明明放得下的暱稱會被無謂地截成「（大…」。 */
@media (max-width: 700px) {
  .roster-name > div { max-width: 176px; }
}
.roster-name > div > div { min-width: 0; }
.roster-name > div > div > div,
.roster-name > div > div > span {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
thead .roster-name { z-index: 3; background: #f5f5f5; }
/* 已報到的列有自己的底色，釘住的那一格要跟著換，
   不然那一格會是白的，看起來像沒被標記到 */
tr[data-checkin="已報到"] .roster-name { background: #f9fbe7; }

.scan-tools { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.scan-tool {
  padding: 8px 15px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-2);
  font-size: 0.88rem;
  font-weight: 600;
  font-family: inherit;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition);
}
.scan-tool:hover { background: var(--bg); border-color: #cfd6e2; }
.scan-tool.primary {
  background: var(--green);
  border-color: var(--green);
  color: #fff;
  box-shadow: 0 3px 10px rgba(6,199,85,.28);
}
.scan-tool.primary:hover { background: var(--green-dark); border-color: var(--green-dark); }

/* ── 兩欄表單：窄螢幕自動收成一欄 ────────────────────
 *
 * ★ 寫死 `1fr 1fr` 的代價是看不見的：手機上每格只剩約 160px，而
 *   `<input type="datetime-local">` 的原生顯示是「2026/10/09 下午6:00」——
 *   塞不下時瀏覽器**把「下午」切掉**，於是上午 6:00 和下午 6:00
 *   在畫面上長得一模一樣。
 *
 *   值一直是對的（點開選單看得到 18:00），只有顯示在說謊 ——
 *   而管理員正是照著那一行在核對活動時間。
 *
 * auto-fit + minmax：放得下兩欄就兩欄，放不下自動變一欄。
 * 不用 media query 是因為真正的限制是「格子有多寬」，不是「螢幕有多寬」
 * （同一支手機，這個表單在對話框裡和在整頁裡可用寬度就不一樣）。
 */
.form-grid-2 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 16px;
}
/* grid 子項預設 min-width:auto，內容撐不下時會溢出而不是收縮 */
.form-grid-2 > * { min-width: 0; }

/* ── 長文字自動收合（活動說明、注意事項）────────────────────
   使用者：「一進頁面看到一堆字卻不知道在哪裡報名」。

   這一頁的目的是報名；備註是給已經決定要報的人看的細節。
   三十行注意事項擺在最上面，等於把唯一的主要動作推出螢幕外。
   實測 390×780 手機：收合前報名鈕在 y=1314px（要往下捲 534px），
   收合後 y=551px。

   ★ 收合是純視覺的（max-height），文字一直在 DOM 裡 ——
     Ctrl+F、螢幕閱讀器、列印都還找得到。用 display:none 的話
     那三件事會同時壞掉，而畫面上看不出來。 */
.longtext { position: relative; }
.longtext-body {
    max-height: 13em;
    overflow: hidden;
    transition: max-height .25s ease;
}
.longtext.open .longtext-body { max-height: none; }
/* 漸層告訴人「下面還有」—— 硬切一刀的話看起來像文章就到這裡 */
.longtext-clamped:not(.open)::after {
    content: '';
    position: absolute;
    left: 0; right: 0; bottom: 2.2em;
    height: 3em;
    background: linear-gradient(to bottom, transparent, var(--longtext-fade, #fff));
    pointer-events: none;
}
.longtext-more {
    display: block;
    width: 100%;
    margin-top: 6px;
    padding: 7px 10px;
    border: 1px solid var(--border, #e0e0e0);
    border-radius: 8px;
    background: transparent;
    color: var(--green-ink, #2e7d32);
    font-size: 0.86rem;
    font-weight: bold;
    cursor: pointer;
}

/* ── 代報：每一筆的填答收起來 ──────────────────────────
   使用者：「如果有人代表幫一堆人報名 那會不會她的頁面會被拉得很長阿」

   會。每一筆都把全部填答攤開，10 個人 × 7 個欄位就是 70 行，
   而她真正要做的事（再報一個、改某一筆、看總共多少錢）全被推到最下面。

   ★ 只有兩筆以上才收（模板判斷）—— 一個人自己報名時攤開才是對的。
   ★ 用原生 <details>：鍵盤、螢幕閱讀器都通，不需要 JS。 */
.reg-answers { margin-bottom: 12px; }
.reg-answers > summary {
    cursor: pointer;
    font-size: 0.85rem;
    color: var(--text-3, #5f6875);
    padding: 6px 0;
    list-style: none;
}
.reg-answers > summary::-webkit-details-marker { display: none; }
.reg-answers > summary::before { content: '▸ '; }
.reg-answers[open] > summary::before { content: '▾ '; }
.reg-answers-body {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 0.875rem;
    color: var(--text-2, #43505f);
}
.reg-answers-body > div > span { font-weight: 600; color: var(--text-1, #222); }
/* 列印時一律展開 —— 印出來少一半的報名資料沒有意義 */
@media print {
    .reg-answers > summary { display: none; }
    .reg-answers > .reg-answers-body { display: flex !important; }
}
