/* =============================================================================
   品牌配色 —— 鼠尾草綠 + 奶油白（docs/06 §8.1）

   整站不使用 Bootstrap 預設的藍色系。所有顏色集中在這裡，
   禁止在 HTML 內寫死色碼（鐵則 18）。
   本檔必須在 Bootstrap CDN「之後」載入。
   ============================================================================= */

:root {
  /* 主色 — 鼠尾草綠 */
  --sage-900: #2F3A2C;   /* 主要文字、深色外框 */
  --sage-700: #4F6249;   /* 按鈕 hover、標題 */
  --sage-600: #5E7355;   /* 主要按鈕底色（白字對比 5.2:1，通過 AA） */
  --sage-400: #8A9A7B;   /* 品牌色、次要元素 */
  --sage-200: #C3CEB6;   /* 淺底、標籤 */
  --sage-100: #E3EADC;   /* 選取狀態、hover 背景 */

  /*
    底色 — 奶油白

    原規格的 #FDFBF6 與純白只差 2–9 個色階，實際看起來就是白的，
    等於整個「奶油白」的品牌設定沒有生效。這裡整組加深到看得見，
    並維持 docs/06 要求的層次：頁面 < 卡片 < 邊框，由淺到深。
  */
  --cream-50:  #fff;  /* 頁面背景 */
  --cream-100: #fff;  /* 卡片、區塊背景 */
  --cream-200: #E3EADC;  /* 分隔線、邊框 */

  /* 輔助色 */
  --clay-500:  #C08552;  /* 提醒、額滿、候補 */
  --dusty-500: #6E8CA0;  /* 資訊、團課 */
  --brick-500: #B0574C;  /* 錯誤、取消、點數不足 */
  --warm-gray: #A8A093;  /* 停用、關閉時段 */

  --text-body: var(--sage-900);
  --text-muted: #6B7266;
}

body {
  background: var(--cream-50);
  color: var(--text-body);
}

/* --- Bootstrap 覆寫 ------------------------------------------------------- */

.btn-primary {
  padding: 6px 14px;
  --bs-btn-bg: var(--sage-600);
  --bs-btn-border-color: var(--sage-600);
  --bs-btn-hover-bg: var(--sage-700);
  --bs-btn-hover-border-color: var(--sage-700);
  --bs-btn-active-bg: var(--sage-900);
  --bs-btn-active-border-color: var(--sage-900);
  --bs-btn-disabled-bg: var(--sage-200);
  --bs-btn-disabled-border-color: var(--sage-200);
}

.btn-outline-primary {
  padding: 6px 14px;
  --bs-btn-color: var(--sage-700);
  --bs-btn-border-color: var(--sage-400);
  --bs-btn-hover-bg: var(--sage-600);
  --bs-btn-hover-border-color: var(--sage-600);
}

.card {
  background: var(--cream-100);
  border-color: var(--cream-200);
}

.form-control:focus,
.form-select:focus {
  border-color: var(--sage-400);
  box-shadow: 0 0 0 .2rem rgba(138, 154, 123, .25);
}

.navbar {
  background: var(--cream-100) !important;
  border-bottom: 1px solid var(--cream-200);
}

a {
  color: var(--sage-700);
}

a:hover {
  color: var(--sage-900);
}

/* --- 共用元件 ------------------------------------------------------------- */

.brand-mark {
  color: var(--sage-700);
  font-weight: 600;
  letter-spacing: .02em;
}

.alert-error {
  background: #F6E9E7;
  border: 1px solid var(--brick-500);
  color: var(--brick-500);
}

.alert-info-soft {
  background: var(--sage-100);
  border: 1px solid var(--sage-200);
  color: var(--sage-900);
}

.text-muted-soft {
  color: var(--text-muted);
}

/*
  頁首標題旁的操作按鈕靠近內容排。原本不少頁用 justify-content-between，
  桌機會把「新增」之類的按鈕推到容器最右側，和標題距離太遠。
*/
.page-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  gap: .5rem;
}

.page-actions > :first-child {
  margin-right: .5rem;
}

/* 主要操作按鈕最小點擊區 44x44px（docs/06 §8.3） */
.btn-touch {
   min-height: auto;
    height: auto;
    padding: 6px 12px;
    line-height: 1.2;
}

@media (max-width: 575.98px) {
  .page-actions {
    gap: .35rem;
  }

  .page-actions > :first-child {
    flex-basis: 100%;
    margin-right: 0;
  }
}

/*
  大面積留白，卡片用 cream-100 與頁面 cream-50 做層次。
  邊框比陰影優先——陰影在奶油底上容易顯髒。
*/
.auth-card {
  max-width: 26rem;
  margin-inline: auto;
  border: 1px solid var(--cream-200);
}

/* 頁尾邊框走變數，不在 HTML 內寫死色碼（鐵則 18） */
.page-footer {
  border-color: var(--cream-200) !important;
}

.login-content {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 50px;
}

.login-logo {
    width: 320px;
    max-width: 90%;
    height: auto;
    display: block;

    margin: 0;
    padding: 0;
}

.login-card {
    width: 100%;
    margin: 0 !important;
}

/* --- 錯誤頁（templates/error.html） --------------------------------------- */

.error-card {
  max-width: 32rem;
  margin-inline: auto;
  border: 1px solid var(--cream-200);
}

/*
  狀態碼是裝飾性的大字，用 sage-400。
  sage-400 對比不足，不可拿來當內文顏色——真正要讀的字在下面的 h1。
*/
.error-code {
  font-size: 3.5rem;
  line-height: 1;
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--sage-400);
}

.error-hint {
  background: var(--sage-100);
  border: 1px solid var(--sage-200);
  border-radius: .5rem;
}

.error-meta {
  color: var(--text-muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  word-break: break-all;
}

.error-meta dt {
  flex: 0 0 4.5rem;
}

/* 所有頁面在 375px 寬度下必須可正常操作 */
@media (max-width: 400px) {
  .auth-card,
  .error-card {
    border-left: 0;
    border-right: 0;
    border-radius: 0;
  }

  .error-code {
    font-size: 2.75rem;
  }
}

/* --- 後台：導覽、統計卡、報表表格（Phase 8） --------------------------------- */

/*
  nav-pills 的作用中項目預設是 Bootstrap 藍（#0d6efd），必須覆寫（鐵則 18）。
  非作用中的項目用 sage-700 當文字色，hover 給 sage-100 淺底。
*/
.admin-nav .nav-link {
  color: var(--sage-700);
  min-height: 44px;              /* 主要操作最小點擊區（docs/06 §8.3） */
  display: flex;
  align-items: center;
}

.admin-nav .nav-link:hover {
  background: var(--sage-100);
}

.admin-nav .nav-link.active {
  background: var(--sage-600);
  color: #fff;
}

/*
  手機版後台導覽改成收合清單。後台項目多，橫向 pills 在 375px 會把按鈕擠到變形；
  details 保留瀏覽器原生展開/收合語意，不需要額外 JS。
*/
.admin-nav-mobile {
  border: 1px solid var(--cream-200);
  border-radius: .5rem;
  background: var(--cream-100);
  padding: .35rem;
}

.admin-nav-mobile summary {
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  padding: 0 .65rem;
  color: var(--sage-900);
  cursor: pointer;
  list-style-position: outside;
}

.admin-nav-current {
  color: var(--text-muted);
  font-size: .9rem;
  white-space: nowrap;
}

.admin-nav-mobile .nav-link {
  width: 100%;
  border-radius: .4rem;
  padding-inline: .75rem;
}

/* 橫向捲動時不要出現捲軸佔掉版面高度 */
.admin-nav .nav {
  scrollbar-width: none;
}

.admin-nav .nav::-webkit-scrollbar {
  display: none;
}

.stat-card {
  border: 1px solid var(--cream-200);
}

.stat-label {
  color: var(--text-muted);
  font-size: .85rem;
}

.stat-value {
  font-size: 1.75rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--sage-900);
}

.stat-value-sm {
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--sage-900);
}

.stat-unit {
  font-size: 1rem;
  font-weight: 400;
  color: var(--text-muted);
}

.stat-hint {
  color: var(--text-muted);
  font-size: .8rem;
  margin-top: .25rem;
}

/* 與上月同期的增減。往下是提醒不是錯誤，因此用陶土色而不是 .stat-alert 的磚紅 */
.stat-delta {
  margin-top: .15rem;
  font-size: .8rem;
  font-weight: 600;
  color: var(--sage-600);
}

.stat-delta-down {
  color: var(--clay-500);
}

/*
  圖表容器（docs/09）。Chart.js 的 responsive 是跟著「父元素的高度」走的，
  高度沒有固定值時 canvas 會在每次 resize 一路長高，直到把頁面撐爆——
  這是它最有名的坑。因此高度寫死在容器上，並用 maintainAspectRatio: false。
*/
.chart-box {
  position: relative;
  height: 260px;
}

@media (max-width: 575.98px) {
  /* 375px 下橫躺的圖表看不出趨勢，給它高一點的空間（docs/06 §8.3） */
  .chart-box {
    height: 220px;
  }
}

/* 需要有人處理的數字：失敗的通知、欠點、連續沒來達門檻 */
.stat-alert,
.value-alert {
  color: var(--brick-500);
}

.badge-alert {
  display: inline-block;
  margin-left: .25rem;
  padding: .1rem .4rem;
  border: 1px solid var(--clay-500);
  border-radius: .35rem;
  color: var(--clay-500);
  font-size: .7rem;
  white-space: nowrap;
}

.alert-warn-soft {
  background: #F7EFE4;
  border: 1px solid var(--clay-500);
  color: #7A4E23;               /* clay-500 直接當文字色對比不足，壓深一階 */
}

/* 表單欄位錯誤顯示於欄位下方（docs/06 §8.4） */
.field-error {
  color: var(--brick-500);
  font-size: .85rem;
}

.field-error:empty {
  display: none;
}

/* 數字欄位用等寬字，同一欄的位數才對得齊 */
.report-table .num {
  font-variant-numeric: tabular-nums;
}

.report-table > thead th {
  color: var(--text-muted);
  font-weight: 600;
  font-size: .85rem;
  border-bottom-color: var(--cream-200);
}

.report-table .report-total > td {
  border-top: 2px solid var(--cream-200);
  font-weight: 600;
}

/*
  手機版把表格改成卡片式，避免橫向捲動（docs/06 §8.3）。

  每個 td 的 data-label 就是原本的表頭文字，用 ::before 顯示在左側。
  這種寫法的前提是「每個 td 都要有 data-label」——漏掉的欄位會變成
  一行沒有標題的數字，因此新增報表欄位時要一併補上。
*/
@media (max-width: 767.98px) {
  .report-table > thead {
    display: none;
  }

  .report-table,
  .report-table > tbody,
  .report-table > tfoot,
  .report-table > tbody > tr,
  .report-table > tfoot > tr,
  .report-table > tbody > tr > td,
  .report-table > tfoot > tr > td {
    display: block;
    width: 100%;
  }

  .report-table > tbody > tr,
  .report-table > tfoot > tr {
    border: 1px solid var(--cream-200);
    border-radius: .5rem;
    padding: .25rem .5rem;
    margin-bottom: .75rem;
  }

  .report-table > tbody > tr > td,
  .report-table > tfoot > tr > td {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    text-align: right !important;
    border-bottom: 0;
    padding: .35rem .25rem;
  }

  .report-table > tbody > tr > td::before,
  .report-table > tfoot > tr > td::before {
    content: attr(data-label);
    color: var(--text-muted);
    font-size: .85rem;
    text-align: left;
  }
}

/* 桌機：欄位多的報表仍可能超出寬度，讓表格自己捲而不是整頁捲 */
@media (min-width: 768px) {
  .table-wrap {
    overflow-x: auto;
  }
}

/* --- 稽核紀錄的異動明細 ---------------------------------------------------- */

/* 展開的是翻譯過的「欄位：原本 → 現在」，不是 detail_json 原文
   （見 AuditDetailService）。一筆規則異動可能有十幾行，因此收在 <details> 裡，
   而且自己捲——不設高度上限的話，那一列會把旁邊每一欄一起拉長。 */
.audit-detail > summary {
  color: var(--sage-700);
  cursor: pointer;
}

.audit-changes {
  margin: .35rem 0 0;
  padding: .5rem;
  background: var(--sage-100);
  border-radius: .35rem;
  max-height: 14rem;
  overflow-y: auto;
  text-align: left;
}

/* 欄位名固定一欄寬，值靠左對齊成一直線 —— 十幾行之間要看得出哪幾行動過。
   窄螢幕（報表表格在 767px 以下會轉成卡片式）放不下就換行，
   不硬擠成兩欄：擠出來的結果是每個欄位名都斷成三行。 */
.audit-change {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem .75rem;
  padding: .15rem 0;
}

.audit-change > dt {
  flex: 0 0 auto;
  min-width: 8rem;
  color: var(--text-muted);
  font-weight: 400;
}

.audit-change > dd {
  flex: 1 1 auto;
  margin: 0;
  word-break: break-word;
}

/* 舊值不畫刪除線：中間已經有一個箭頭，再加一條線只是雜訊。
   淡一階就足以看出哪一個是現在的值 */
.audit-from,
.audit-arrow {
  color: var(--text-muted);
}

/* 手機版：報表的卡片式版面把每個 td 變成一列 flex（左邊標題、右邊值），
   但「內容」這一欄有三塊（摘要、原因、明細），並排就是三條擠成一行的窄字。
   只有這一欄改成直排——選擇器要跟上面那條一樣長，否則蓋不過去。 */
@media (max-width: 767.98px) {
  .report-table > tbody > tr > td.audit-content {
    display: block;
    text-align: left !important;
  }
}

/* =============================================================================
   學生 / 教練前台（Phase 9）
   ============================================================================= */

/* --- 頁首的登入者姓名 ------------------------------------------------------ */

/* 手機也要看得到現在登入的是誰（同一台裝置常在教練與學生之間換手）。
   375px 下品牌名 + 姓名 + 登出剛好放得下，長姓名截尾——
   讓它換行的話登出按鈕會被推到第二行，而那是頁首最常按的東西。 */
.nav-username {
  max-width: 6.5rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 姓名是「基本資料」的連結。平時不畫底線 —— 頁首同一排已經有品牌名與登出按鈕，
   再多一條底線只是雜訊；滑過與鍵盤聚焦時才顯示，讓它看得出可以按 */
a.nav-username {
  color: inherit;
  text-decoration: none;
}

a.nav-username:hover,
a.nav-username:focus-visible {
  text-decoration: underline;
}

@media (min-width: 576px) {
  .nav-username {
    max-width: 12rem;
  }
}

/* --- 頁首餘額（docs/06 §8.3）---------------------------------------------- */

/*
  餘額自己<b>不畫分隔線</b>。它是 .navbar 裡的最後一列，而 .navbar 已經有
  border-bottom —— 再加一條 border-top 就是兩條只隔著一行字高的同色細線，
  看起來像畫壞的雙線（管理員沒有這一列，所以只有教練與學生會看到）。
  分隔靠留白，橫線整個頁首只留最下面那一條。
*/
.wallet-strip {
  width: 100%;
  flex-wrap: wrap;
  font-size: .85rem;
}

.wallet-label {
  color: var(--text-muted);
  margin-right: .25rem;
}

.wallet-value {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--sage-900);
}

/* 低於門檻（含負餘額）轉 brick-500 —— 負餘額期間所有課型都約不了 */
.wallet-low {
  color: var(--brick-500);
}

.wallet-held {
  color: var(--text-muted);
  font-size: .78rem;
  margin-left: .15rem;
}

/* --- 主導覽（手機底部固定 / 桌機頁首橫排）---------------------------------- */

.app-nav-link {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;              /* 最小點擊區 44x44px（docs/06 §8.3） */
  padding: 0 .75rem;
  border-radius: .5rem;
  color: var(--sage-700);
  text-decoration: none;
  font-size: .95rem;
  white-space: nowrap;
}

.app-nav-link:hover {
  background: var(--sage-100);
  color: var(--sage-900);
}

.app-nav-link.active {
  background: var(--sage-600);
  color: #fff;
}

.app-nav-link.active:hover {
  background: var(--sage-700);
  color: #fff;
}

.app-nav-top {
  align-items: center;
}

/*
  底部固定列。fixed 而不是 sticky：課表捲到一半仍要按得到「我的預約」，
  sticky 在內容比視窗短的時候會浮在半空中。
*/
.app-nav-bottom {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 1030;                 /* 高於 Bootstrap 的 sticky-top，低於 modal */
  display: flex;
  justify-content: space-around;
  gap: .25rem;
  padding: .25rem .5rem calc(.25rem + env(safe-area-inset-bottom));
  background: var(--cream-100);
  border-top: 1px solid var(--cream-200);
}

.app-nav-bottom .app-nav-link {
  flex: 1 1 0;
  font-size: .85rem;
  padding: 0 .25rem;
}

/* 底部列會蓋住頁尾，body 要留出等高的空間 */
@media (max-width: 767.98px) {
  body.has-bottom-nav {
    padding-bottom: calc(52px + env(safe-area-inset-bottom));
  }
}

/* --- 時段卡片 -------------------------------------------------------------- */

.slot-card {
  border: 1px solid var(--cream-200);
}

/* 已額滿 / 待候補的卡片，左緣加一條色帶——不可只靠文字顏色區分 */
.slot-card.is-full {
  border-left: 4px solid var(--clay-500);
}

.slot-card.is-cancelled {
  border-left: 4px solid var(--warm-gray);
}

.slot-card.is-mine {
  border-left: 4px solid var(--sage-600);
}

.slot-time {
  font-size: 1.05rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--sage-900);
}

.slot-meta {
  color: var(--text-muted);
  font-size: .875rem;
}

/*
  卡片上的「本次備註」摘要。收合狀態就是一行可按的文字，因此 summary 本身
  要有 44px 的點擊高度（docs/06 §8.3）——只有幾個字的一行，手指按不準。
*/
.note-edit > summary {
  min-height: 44px;
  display: flex;
  align-items: center;
  color: var(--sage-700);
  font-size: .875rem;
  cursor: pointer;
}

.note-edit[open] > summary {
  color: var(--sage-900);
}

/*
  型態標籤。顏色與 docs/06 §8.2 的行事曆事件一致，並一律帶文字前綴——
  §8.2 明文要求「不可只靠顏色區分」，色弱使用者與小螢幕都需要這道保險。
*/
.type-tag {
  display: inline-block;
  padding: .1rem .45rem;
  border-radius: .35rem;
  font-size: .78rem;
  font-weight: 600;
  color: #fff;
  white-space: nowrap;
}

.type-rental      { background: var(--sage-600); }
.type-group       { background: var(--dusty-500); }
.type-private     { background: var(--sage-900); }
.type-full        { background: var(--clay-500); }
.type-closed      { background: var(--warm-gray); }

.status-tag {
  display: inline-block;
  padding: .1rem .45rem;
  border: 1px solid var(--cream-200);
  border-radius: .35rem;
  font-size: .78rem;
  color: var(--text-muted);
  white-space: nowrap;
}

/*
  Bootstrap 的 .card 是 display:flex，會蓋過瀏覽器預設的 [hidden] { display: none }。
  建立時段的表單靠 hidden 切換型態相關欄位，少了這一條，切成場租之後
  指定學生的整張卡片仍會留在畫面上。
*/
[hidden] {
  display: none !important;
}

/* --- 時段選擇：大按鈕格狀排列，不用原生 select（docs/06 §8.3）--------------- */

.slot-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(5.5rem, 1fr));
  gap: .5rem;
}

/*
  「每週重複」的星期幾（一二三四五六日）—— 七格一定要排成一列。
  沿用 auto-fill 的話 375px 下會斷成兩列，而「一三五」是要一眼看出來的形狀，
  拆成兩列就得先在腦中把它拼回去。七等分後每格仍有 44px 左右（docs/06 §8.4）。
*/
.slot-grid.day-grid {
  grid-template-columns: repeat(7, 1fr);
}

/*
  用 label + 隱藏的 radio 而不是 <button>：表單送出時要帶值，
  而 radio 本來就處理好了鍵盤操作與群組語意，不必自己補 JS。
*/
.slot-option {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: .25rem;
  border: 1px solid var(--sage-200);
  border-radius: .5rem;
  background: var(--sage-100);
  color: var(--sage-900);
  font-variant-numeric: tabular-nums;
  line-height: 1.25;
  cursor: pointer;
  user-select: none;
}

/*
  第二行寫「核心床 2、其他器械 1」或「休」。這一行才是行事曆的核心資訊——
  只顯示時間的格子，教練得一格一格點進去才知道租不租得到（docs/06 §8.2）。
*/
.slot-option .slot-hint {
  font-size: .7rem;
  opacity: .85;
}

.slot-option input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.slot-option:has(input:checked) {
  background: var(--sage-600);
  border-color: var(--sage-600);
  color: #fff;
}

.slot-option:has(input:focus-visible) {
  outline: 2px solid var(--sage-400);
  outline-offset: 2px;
}

.slot-option.is-disabled {
  background: transparent;
  border-color: var(--cream-200);
  color: var(--warm-gray);
  cursor: not-allowed;
  text-decoration: line-through;
}

/*
  已經在建立清單裡、但還沒送出的那一格。

  刻意與「別人排了課」（is-disabled）長得不一樣：兩者的下一步完全相反——
  別人的課他只能改時間，自己清單裡的那一筆他可以移除。用同一個灰色的話，
  他會去找是誰搶先排了他自己剛加進去的那一筆。虛線代表「還沒定案」。

  這只是<b>標記，不代表不能選</b>：清單裡的那一筆只佔走一台核心床時，
  這一格仍然租得到第二台。真的不能選時 is-disabled 會一起套上去，
  由它負責畫成停用的樣子。
*/
.slot-option.is-queued {
  border-style: dashed;
  border-color: var(--sage-400);
}

/* --- 建立清單 -------------------------------------------------------------- */

.draft-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

/*
  一列一筆。375px 下時間與型態各佔一行，右邊留一顆「移除」——
  移除鍵必須一直看得見：清單存在的意義是送出前改得動，
  藏在展開選單裡的話等於要他清空重排（docs/06 §8.4 行動優先）。
*/
.draft-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: .75rem;
  padding: .625rem 0;
  border-bottom: 1px solid var(--cream-200);
}

.draft-row:last-child {
  border-bottom: none;
}

.draft-row-main {
  min-width: 0;   /* 讓長的學生姓名列縮得下去，不要把移除鍵擠出畫面 */
}

.draft-row-when {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

.draft-time {
  color: var(--text-muted);
}

.draft-row-what,
.draft-row-who,
.draft-row-points {
  font-size: .8125rem;
  line-height: 1.5;
}

.draft-row-what {
  display: flex;
  flex-wrap: wrap;
  gap: .375rem;
}

/*
  型態要有自己的顏色塊：清單混排四種型態時，「這三筆是場租、那兩筆是團課」
  必須掃一眼就分得出來，逐列讀文字等於沒有清單。
*/
.draft-type {
  padding: 0 .375rem;
  border-radius: .25rem;
  background: var(--sage-100);
  color: var(--sage-700);
  font-weight: 600;
}

.draft-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .5rem;
  padding-top: .5rem;
  border-top: 1px solid var(--cream-200);
  font-size: .875rem;
}

.draft-warning {
  margin: .75rem 0 0;
  padding: .5rem .625rem;
  border-radius: .375rem;
  background: var(--sage-100);
  color: var(--brick-500);
  font-size: .8125rem;
  line-height: 1.5;
}

/* --- 點數頁 ---------------------------------------------------------------- */

.wallet-card {
  border: 1px solid var(--cream-200);
}

.wallet-card .amount {
  font-size: 1.6rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--sage-900);
}

.wallet-card .amount.is-low {
  color: var(--brick-500);
}

/* --- 學生首頁的大卡片 -------------------------------------------------------
   與 .wallet-card 分開：那一組要塞下各批到期日、已使用、已失效，一張卡十幾個數字；
   首頁只回答「還剩幾點」，因此把數字放到最大、其餘縮成一行。
   卡片整張是連結（進「我的點數」看明細），因此需要自己的 hover 與 focus 樣式。
--------------------------------------------------------------------------- */

.wallet-hero {
  border: 1px solid var(--cream-200);
  color: inherit;
  transition: border-color .15s ease, box-shadow .15s ease;
}

.wallet-hero:hover,
.wallet-hero:focus-visible {
  border-color: var(--sage-400);
  box-shadow: 0 2px 8px rgba(47, 58, 44, .08);
}

.wallet-hero-label {
  font-size: .95rem;
  color: var(--text-muted);
}

.wallet-hero-value {
  /* 手機 375px 下仍要是第一眼看到的東西，因此用 clamp 而不是固定級數 */
  font-size: clamp(2.5rem, 12vw, 3.5rem);
  font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  color: var(--sage-900);
}

.wallet-hero-unit {
  font-size: .875rem;
  color: var(--text-muted);
}

.wallet-hero-sub {
  margin-top: .5rem;
  font-size: .8125rem;
  color: var(--text-muted);
}

/* 低餘額轉 brick-500（docs/06 §8.3）；負餘額再加底色，因為它鎖住全部課型 */
.wallet-hero.is-low .wallet-hero-value,
.wallet-hero.is-negative .wallet-hero-value {
  color: var(--brick-500);
}

.wallet-hero.is-negative {
  border-color: var(--brick-500);
}

.wallet-hero-alert {
  margin-top: .5rem;
  font-size: .8125rem;
  font-weight: 600;
  color: var(--brick-500);
}

.batch-row {
  border-top: 1px solid var(--cream-200);
  padding: .5rem 0;
  font-size: .875rem;
}

.batch-row:first-of-type {
  border-top: 0;
}

/* 到期日快到的批次 —— 過了寬限期整批失效，這個提示不能只是灰字 */
.batch-expiring {
  color: var(--clay-500);
  font-weight: 600;
}

/* --- 行事曆（FullCalendar，docs/06 §8.2）----------------------------------- */

/*
  事件顏色由後端隨事件一起回傳（見 CalendarApiController），
  這裡只處理 FullCalendar 自己的外框與字級，避免與 §8.2 的對照表兩地維護。
*/
#calendar {
  --fc-border-color: var(--cream-200);
  --fc-today-bg-color: var(--sage-100);
  --fc-page-bg-color: var(--cream-50);
  font-size: .875rem;
}

.fc .fc-button-primary {
  background: var(--sage-600);
  border-color: var(--sage-600);
}

.fc .fc-button-primary:hover,
.fc .fc-button-primary:not(:disabled):active,
.fc .fc-button-primary:not(:disabled).fc-button-active {
  background: var(--sage-700);
  border-color: var(--sage-700);
}

.fc .fc-button-primary:disabled {
  background: var(--sage-200);
  border-color: var(--sage-200);
}

.fc .fc-toolbar-title {
  font-size: 1.05rem;
  color: var(--sage-900);
}

/*
  格子裡的字一律白色。

  月檢視預設是「圓點 + 深色字 + 白底」，但事件底色由下面那組規則填成深綠／陶土，
  於是深字疊在深底上完全看不清楚——前端已改成實心色塊（eventDisplay: 'block'），
  這裡把文字顏色一併釘死，避免哪一天檢視設定改了又回到看不清的狀態。
  清單檢視不套用：那邊是白底的表格列，白字等於整列消失。
*/
.fc-daygrid-event,
.fc-daygrid-event .fc-event-title,
.fc-daygrid-event .fc-event-time,
.fc-timegrid-event .fc-event-title,
.fc-timegrid-event .fc-event-time,
.fc-event .cal-event-coach {
  color: var(--cream-50);
  font-weight: 500;
}

/*
  清單檢視（手機預設 listWeek，§8.2）—— 一整列就是事件本體。

  下面那組 `.fc-event.event-*` 的底色也會套到清單的 <tr> 上（FullCalendar 把
  eventClassNames 一併掛在列上），但上面那組文字色只寫了月／週兩種檢視，
  於是深墨綠底疊上 FullCalendar 預設的深色字，整列等於看不到——
  而手機開起來預設就是這個檢視，是最多人會遇到的一個。

  白字與型態標籤（`.type-tag`）同一套：同一種課走到哪一頁都同一個顏色配白字。
*/
.fc .fc-list-event th,
.fc .fc-list-event td,
.fc .fc-list-event .fc-list-event-title a {
  color: var(--cream-50);
  font-weight: 500;
}

/* 圓點沿用文字色 —— 不指定的話會留在 FullCalendar 預設的藍（鐵則 18） */
.fc .fc-list-event .fc-list-event-dot {
  border-color: currentColor;
}

/*
  hover 用半透明白疊在型態色上。FullCalendar 預設是把淺灰實色畫在 <td> 上，
  那會整片蓋掉列的底色，白字就跟著消失——滑過去反而看不見。
*/
.fc .fc-list-event:hover td {
  background-color: rgba(255, 255, 255, .12);
}

/*
  關閉時段例外 —— 暖灰底配白字只有 2.5:1，是全站對比最低的組合。
  這一格本來就不是要人點的，深字反而更符合它「這裡不能用」的語意。
*/
.fc-daygrid-event.event-closed,
.fc-daygrid-event.event-closed .fc-event-title,
.fc-daygrid-event.event-closed .fc-event-time,
.fc-timegrid-event.event-closed .fc-event-title,
.fc-timegrid-event.event-closed .fc-event-time,
.fc .fc-list-event.event-closed th,
.fc .fc-list-event.event-closed td,
.fc .fc-list-event.event-closed .fc-list-event-title a {
  color: var(--sage-900);
}

/*
  格子裡的內容留左右內距。

  事件本身沒有內距，而「自己的時段」那 2px 外框是往內畫的，第一個字會被壓掉——
  月檢視的時間只有「9a」兩個字，吃掉一個就看不出是幾點了。

  內距下在事件元素本身而不是 .fc-event-main：月檢視的實心色塊有那一層，
  dot 樣式（eventDisplay 沒生效時的退路）沒有，只寫 .fc-event-main 的話
  規則會整條落空而且不會有任何錯誤訊息——這正是這個內距曾經失效的原因。
  **前面必須帶 #calendar**：FullCalendar 自己也有一條
  .fc-daygrid-dot-event { padding: 2px 0 }，而它的樣式是 JS 動態插進 <head> 的，
  排在這份樣式表之後——同權重的話會被它蓋掉。
*/
#calendar .fc-daygrid-event,
#calendar .fc-timegrid-event .fc-event-main {
  padding-inline: .35rem;
}

/* 週檢視一格就是一個時段，標題不換行擠成兩三行 */
.fc-timegrid-event .fc-event-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/*
  格子裡分兩行：第一行是型態（月檢視再加時間、週檢視再加器械代碼），
  第二行是教練姓名。

  接在同一行的話，月檢視一格約 150px、手機更窄，中文姓名會被截成
  「私 1V1 楊…」——而「這個時段是誰在場」正是行事曆最常被問的一題（§8.2）。
  class 名稱一律自己取，不沿用 fc-* ——FullCalendar 的樣式是 JS 動態插進
  <head> 的，排在這份樣式表之後，同名同權重的規則會被它蓋掉。
*/
.fc-event .cal-event-stack {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  overflow: hidden;
}

.fc-event .cal-event-line {
  display: flex;
  gap: .25rem;
  max-width: 100%;
}

.fc-event .cal-event-coach {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/*
  事件配色（docs/06 §8.2 的對照表）。後端只回傳 class 名稱，色碼一律在這裡，
  否則調色時 CSS 改了、行事曆沒改，而那是全站唯一看不出來的不一致（鐵則 18）。
*/
.fc-event.event-rental {
  background-color: var(--sage-600);
  border-color: var(--sage-600);
}

.fc-event.event-group {
  background-color: var(--dusty-500);
  border-color: var(--dusty-500);
}

/* 已額滿 / 候補中 —— 顏色本身就要說得出「這一堂只剩候補」 */
.fc-event.event-group-full {
  background-color: var(--clay-500);
  border-color: var(--clay-500);
}

.fc-event.event-private {
  background-color: var(--sage-900);
  border-color: var(--sage-900);
}

.fc-event.event-closed {
  background-color: var(--warm-gray);
  border-color: var(--warm-gray);
}

/*
  自己的預約 / 自己的時段：外框 2px 加粗（§8.2 最後一列）。

  外框用淺綠而不是深墨綠：這個框一律疊在型態色（深綠、霧藍、陶土）上，
  深色框在深色底上看起來就是一圈黑邊，整格像是被描了黑框。
  淺綠在五種型態色上都看得出來，也才像同一套配色裡的東西。

  用內縮陰影畫而不是 outline + outline-offset: -2px：**outline 一律畫在
  所有子孫之上**，往內縮 2px 就是直接蓋在文字上——月檢視的時間只有「11a」
  三個字，被蓋掉第一個就看不出是幾點。內縮陰影畫在背景層、文字在它上面，
  日後就算內距被誰改掉也只是貼邊，不會再把字吃掉。

  教練與學生的行事曆上「每一筆都是自己的」（見 CalendarApiController 的
  mineOnly 與 StudentViewService），因此這一條套的是整張表；管理員那張
  一筆都不會套——那正是同一個版面只有他們兩邊壞掉的原因。
*/
.fc-event.event-mine {
  box-shadow: inset 0 0 0 2px var(--sage-200);
}

/*
  未成團（等待判定）：陶土 + 虛線外框（§8.2）。

  底色一併蓋掉基礎型態的顏色是刻意的——這一堂在成團判定時會被取消並全額退點，
  那件事比「它是團課還是 1V2」重要得多，教練需要一眼看到它與其他課不一樣。
*/
.fc-event.event-pending-open {
  background-color: var(--clay-500);
  border-color: var(--clay-500);
  border-style: dashed !important;
  border-width: 2px !important;
}

/* 關閉時段 / 公休：暖灰 + 斜線底紋 */
.fc-event.event-closed {
  background-image: repeating-linear-gradient(45deg,
      rgba(255, 255, 255, .35) 0, rgba(255, 255, 255, .35) 4px,
      transparent 4px, transparent 8px);
}

/*
  手機版行事曆改用清單（§8.2：手機預設 listWeek）。
  月檢視在 375px 下一格只剩 50px 寬，寫不下「核心床 2、其他器械 1」這種
  行事曆真正要傳達的資訊，等於整個功能失效。
*/
@media (max-width: 575.98px) {
  .fc .fc-toolbar {
    flex-wrap: wrap;
    gap: .5rem;
  }
}

/*
  手機版：內容在視窗中垂直置中（docs/06 §8.3 行動優先）。

  用 margin-block: auto 而不是 justify-content: center——
  後者在內容比容器高的時候會把頂端切掉而且捲不上去，
  前者則會自動退回正常的由上而下排列，長頁面也安全。

  桌機不套用：桌機頁面通常比較長（行事曆、報表），
  垂直置中反而會讓每頁的起始位置跳來跳去。
*/
@media (max-width: 767.98px) {
  .page-main {
    display: flex;
    flex-direction: column;
  }

  /*
    但學生與教練的頁面除外（body.has-bottom-nav）。那些是清單型畫面，
    項目只有兩三筆時整塊浮在螢幕中央，看起來像是上面的內容沒載出來；
    而且每次切換頁面起始位置都在跳。垂直置中要的是登入頁那種
    「一張卡片、內容固定」的情境。
  */
  body:not(.has-bottom-nav) .page-main > .container {
    margin-block: auto;
  }
}

/*
  通知樣板的預覽區塊（/admin/notifications/templates/{type}）。

  信是純文字，因此預覽必須保留換行與空白——用 <pre> 而不是 <div>，
  語意本來就是這個。但瀏覽器預設的 pre 不折行，而「時間：2026/08/20（四）10:00」
  這種行在手機上會撐出橫向捲軸，把整頁一起帶歪；pre-wrap 保留換行又允許折行。

  背景壓一階，讓人一眼分得出「這一塊是信的樣子」而不是又一個輸入框。
*/
.template-preview {
  padding: .75rem;
  border: 1px solid var(--cream-200);
  border-radius: .5rem;
  background: var(--sage-100);
  color: var(--text-body);
  white-space: pre-wrap;
  word-break: break-word;
}
