/* 主題：深色科技霓光（預設）＋淺色高對比（跟隨系統，戶外強光可讀） */
/* 深色版：以易讀與質感為主，光暈僅保留極淡點綴 */
:root {
  --bg: #0b0f17;
  --panel: #121826;
  --panel2: #1a2233;
  --line: #253048;
  /* 需要「被看見」的那些線（外框按鈕、狀態條的分隔）比一般分隔線深一階。
     一般的 --line 是用來輕輕分開兩塊，強光下等於消失；而外框按鈕的邊界
     就是它唯一的形狀，那條線不見了，按鈕也就不見了。 */
  --line-strong: #3d4d6b;
  --text: #cfdcec;
  --head: #eef5fc;
  --sub: #93a6bf;
  --muted: #5d7089;
  --cyan: #4db8ff;
  --green: #2fd48f;
  --orange: #ffa94d;
  --red: #ff7a7a;
  --blue: #6aa9ff;
  /* 加班／請假這類「跟出勤時數有關但不是異常」的標記用它——
     綠色會被讀成正常、紅色會被讀成有問題，而加班兩者都不是。 */
  --violet: #a78bfa;
  /* ---- 壓在「淡底色膠囊」上的字 ----
     膠囊的底是同色系的半透明（例如 rgba(255,169,77,.14)），疊完之後仍然很接近
     卡片的顏色；這時字如果直接用 --orange 這種「拿來當強調色」的值，
     對比只有 2.8～3.2，遠看只剩一團橘。所以另外給一組專門當「墨水」的色：
     深色主題要更亮、淺色主題要更深，兩邊都跟自己的淡底拉開距離。 */
  --orange-ink: #ffc078;
  --green-ink: #5ee6ad;
  --violet-ink: #c4b0ff;
  --cyan-ink: #8fd0ff;
  --red-ink: #ff9b9b;
  /* 光暈全部關掉（見上面的按鈕說明）。變數名稱留著——全站二十幾處在用，
     改名要動的檔案比它值得的多。 */
  --glow-cyan: none;
  --glow-green: none;
  --glow-green-box: none;
  --glow-orange-box: none;
  --glow-cyan-box: none;
  /* 按鈕改實心、不打漸層也不打光暈。
     漸層在小尺寸的按鈕上只看得到「上面比較亮」，卻讓同一顆鈕在深淺兩個主題下
     要各調一次；而光暈在戶外強光下等於不存在，在室內又讓按鈕邊界變得糊糊的。
     實心色塊兩邊都成立，也是唯一在淺色地圖上仍然咬得住的畫法。 */
  /* 深色主題的按鈕是「亮底＋深字」，不是「暗底＋白字」。
     白字壓在飽和的橘／綠上，對比只有 2.6～3.1——那是所有「顏色看起來很髒」的來源：
     為了讓白字讀得出來，底色只能一路往深咖啡色調，顏色就死了。
     反過來用深字之後，底色可以保持鮮豔（橘 7.8:1、綠 7.1:1），
     而且在陽光下比白字更耐看（白字會被環境光洗掉）。
     淺色主題相反：底色本來就得夠深才壓得住白色的頁面，那裡維持白字。 */
  --btn-in-bg: #16b878;
  --btn-out-bg: #f7902a;
  --btn-text: #10151f;
  /* 第三種主按鈕：移動類（外出／回公司／抵達工地）。
     它刻意比「上班／下班」輕——一天按好幾次、按錯代價小，不該跟那兩顆搶重量。
     但原本用的是中性灰（--panel2 ＋ --line-strong），在淺色主題下跟旁邊的
     次要按鈕幾乎同一個顏色，第一眼會被讀成「還不能按」。使用者實際回報過。
     改成淡藍染底＋藍框＋藍字：重量仍然比實心色塊輕，但一眼看得出可以按。 */
  --btn-move-bg: rgba(77, 184, 255, .14);
  --btn-move-line: #4db8ff;
  --btn-move-fg: #8fd0ff;
  /* 藍底被選中時的文字色：深色模式的 --cyan 是亮藍（配深字），淺色模式是深藍（配白字） */
  --on-cyan-fg: #06222a;
  /* 打卡頁的現場地圖（map.js 從這裡讀色）。底色比 --bg 再深一點，
     地圖才會像一塊「看出去的窗」而不是另一張卡片 */
  --map-bg: #0a0d14;
  /* 地面分區：深色版把 Google 那套底色壓暗，色相保留，才分辨得出哪裡是綠地哪裡是學校 */
  --map-water: #16303f; --map-wood: #16281a; --map-scrub: #182619; --map-grass: #1a2a1c;
  --map-park: #16291a; --map-sport: #1b2d1e; --map-cemetery: #1c2620;
  --map-school: #2a2519; --map-hospital: #2b1e1e; --map-civic: #232028;
  --map-retail: #2a2318; --map-parking: #171a20;
  --map-resid: #12161e; --map-comm: #171a22; --map-indus: #14171d; --map-aero: #16181c;
  --map-bld: #1a2029;
  --map-bld-line: #242b36;
  /* 深色版的道路不能太亮：問候語與膠囊字會壓在路面上，太亮就撐不到 AA。
     這一組是 scripts/test-map-contrast.mjs 反推出來的上限，別隨手調亮。 */
  --map-r0: #21252c;   /* 巷弄 */
  --map-r1: #262b33;
  --map-r2: #2b3039;
  --map-r3: #313740;   /* 主要道路 */
  --map-r4: #3d3418;   /* 快速道路。深色版不能太亮：字壓上去會不到 AA */
  --map-c0: #0a0d14; --map-c1: #0d1118; --map-c2: #0f131a; --map-c3: #11151c; --map-c4: #2a2412;
  --map-rail: #333a45;
  --map-halo: #0a0d14;
  --map-label: #8a95ab;
  --map-label-2: #626b7e;
  --map-poi: #7f8899;
  --map-poi-dot: #8a7f63;
  --map-place: #a3adbd;
  --map-here: #8ecdff;
  --map-ring: #0a0d14;
  --map-credit: #4c545f;
  /* 地圖遮罩：淡到看得見街道，濃到字讀得清楚。
     擋住「再淡一點」的一直是問候語的字色——淺色模式的 --sub(#666) 在淡遮罩下不到 AA。
     所以另外給地圖上的次要文字一個深一階的 --map-fg-2，遮罩就能降到模擬圖那個濃度。
     實際落在文字位置的約 .33（時鐘），地圖透得出三分之二。 */
  --map-scrim-top: rgba(8, 11, 18, .45);
  --map-scrim-1: rgba(18, 24, 38, .3);
  --map-scrim-2: rgba(18, 24, 38, .5);
  --map-fg-2: #93a6bf;
  --map-ok: #2fd48f;
  --map-warn: #ffa94d;
  --map-text-shadow: 0 1px 6px rgba(0, 0, 0, .65);
  --map-glass: rgba(255, 255, 255, .1);
  --map-glass-line: rgba(255, 255, 255, .2);
}
/* 亮版變數（兩個入口：系統淺色且未手動選深色 / 手動選淺色） */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --bg: #f4f6f8; --panel: #ffffff; --panel2: #e8ecef; --line: #dfe5ea; --line-strong: #97a5b4;
    --text: #222; --head: #111; --sub: #666; --muted: #999;
    --cyan: #0069a8; --green: #16803c; --orange: #e2610a; --red: #b3261e; --blue: #2563a8; --violet: #6841d4;
  --orange-ink: #8a3d00; --green-ink: #0f6b2f; --violet-ink: #4b32a8;
  --cyan-ink: #005f97; --red-ink: #a01c15;
    --glow-cyan: none; --glow-green: none;
    --glow-green-box: none;
    --glow-orange-box: none;
    --glow-cyan-box: none;
    --btn-in-bg: #15803d; --btn-out-bg: #c85600; --btn-text: #fff;
    /* 淺色的 --cyan 是深藍，所以染底要更淡、字用更深的那一階 */
    --btn-move-bg: rgba(0, 105, 168, .10); --btn-move-line: #0069a8; --btn-move-fg: #00507f;
    --on-cyan-fg: #ffffff;
    /* 拉開層次是關鍵：底色→分區→建物 三階必須看得出差別，
       否則 109 塊分區畫下去仍然是一片均勻的米灰（實際踩過這個坑）。 */
    --map-bg: #f2efe9;
    --map-water: #9ec9ec; --map-wood: #b5d9a5; --map-scrub: #cadfb8; --map-grass: #d2ecbe;
    --map-park: #c2e6ae; --map-sport: #b6dfa4; --map-cemetery: #d2ded9;
    --map-school: #f0e3c2; --map-hospital: #f6d9d2; --map-civic: #e6ded1;
    --map-retail: #f7e2c6; --map-parking: #e4ded2;
    --map-resid: #efebe3; --map-comm: #f2e9da; --map-indus: #e9e3d7; --map-aero: #e2ded6;
    --map-bld: #ddd5c7; --map-bld-line: #cbc1ae;
    --map-r0: #ffffff; --map-r1: #ffffff; --map-r2: #ffffff; --map-r3: #ffffff; --map-r4: #fbd989;
    --map-c0: #ddd6c8; --map-c1: #d5ccbb; --map-c2: #ccc2ad; --map-c3: #c3b8a1; --map-c4: #eec96b;
    --map-rail: #cfcac2;
    --map-halo: #f2efe9;
    --map-label: #5f5d57; --map-label-2: #8b8880;
    --map-poi: #77746d; --map-poi-dot: #b09a76; --map-place: #4f4d48;
    --map-here: #0f5f9b; --map-ring: #ffffff;
    --map-credit: #a19d95;
    --map-scrim-top: rgba(255, 255, 255, .45);
    --map-scrim-1: rgba(255, 255, 255, .34);
    --map-scrim-2: rgba(255, 255, 255, .56);
    --map-fg-2: #4a4a4a;   /* 不能用 --sub(#666)：淡遮罩下只有 4.3:1，不到 AA */
    /* 膠囊的狀態字同理：全站的 --green 在淡遮罩下 4.4:1、--orange 更只有 3.1:1。
       疊在地圖上的文字一律用自己的深色版，不要反過來把遮罩加深、把地圖蓋掉。 */
    --map-ok: #14682e;
    --map-warn: #a8420a;
    --map-text-shadow: 0 1px 5px rgba(255, 255, 255, .8);
    --map-glass: rgba(255, 255, 255, .72);
    --map-glass-line: rgba(0, 0, 0, .12);
  }
}
:root[data-theme="light"] {
  --bg: #f4f6f8; --panel: #ffffff; --panel2: #e8ecef; --line: #dfe5ea; --line-strong: #97a5b4;
  --text: #222; --head: #111; --sub: #666; --muted: #999;
  --cyan: #0069a8; --green: #16803c; --orange: #e2610a; --red: #b3261e; --blue: #2563a8; --violet: #6841d4;
  --orange-ink: #8a3d00; --green-ink: #0f6b2f; --violet-ink: #4b32a8;
  --cyan-ink: #005f97; --red-ink: #a01c15;
  --glow-cyan: none; --glow-green: none;
  --glow-green-box: none;
  --glow-orange-box: none;
  --glow-cyan-box: none;
  --btn-in-bg: #15803d; --btn-out-bg: #c85600; --btn-text: #fff;
  --btn-move-bg: rgba(0, 105, 168, .10); --btn-move-line: #0069a8; --btn-move-fg: #00507f;
  --on-cyan-fg: #ffffff;
    /* 拉開層次是關鍵：底色→分區→建物 三階必須看得出差別，
       否則 109 塊分區畫下去仍然是一片均勻的米灰（實際踩過這個坑）。 */
  --map-bg: #f2efe9;
  --map-water: #9ec9ec; --map-wood: #b5d9a5; --map-scrub: #cadfb8; --map-grass: #d2ecbe;
  --map-park: #c2e6ae; --map-sport: #b6dfa4; --map-cemetery: #d2ded9;
  --map-school: #f0e3c2; --map-hospital: #f6d9d2; --map-civic: #e6ded1;
  --map-retail: #f7e2c6; --map-parking: #e4ded2;
  --map-resid: #efebe3; --map-comm: #f2e9da; --map-indus: #e9e3d7; --map-aero: #e2ded6;
  --map-bld: #ddd5c7; --map-bld-line: #cbc1ae;
  --map-r0: #ffffff; --map-r1: #ffffff; --map-r2: #ffffff; --map-r3: #ffffff; --map-r4: #fbd989;
  --map-c0: #ddd6c8; --map-c1: #d5ccbb; --map-c2: #ccc2ad; --map-c3: #c3b8a1; --map-c4: #eec96b;
  --map-rail: #cfcac2;
  --map-halo: #f2efe9;
  --map-label: #5f5d57; --map-label-2: #8b8880;
  --map-poi: #77746d; --map-poi-dot: #b09a76; --map-place: #4f4d48;
  --map-here: #0f5f9b; --map-ring: #ffffff;
  --map-credit: #a19d95;
  --map-scrim-top: rgba(255, 255, 255, .45);
  --map-scrim-1: rgba(255, 255, 255, .34);
  --map-scrim-2: rgba(255, 255, 255, .56);
  --map-fg-2: #4a4a4a;   /* 不能用 --sub(#666)：淡遮罩下只有 4.3:1，不到 AA */
  /* 膠囊的狀態字同理：全站的 --green 在淡遮罩下 4.4:1、--orange 更只有 3.1:1。
     疊在地圖上的文字一律用自己的深色版，不要反過來把遮罩加深、把地圖蓋掉。 */
  --map-ok: #14682e;
  --map-warn: #a8420a;
  --map-text-shadow: 0 1px 5px rgba(255, 255, 255, .8);
  --map-glass: rgba(255, 255, 255, .72);
  --map-glass-line: rgba(0, 0, 0, .12);
}

* { box-sizing: border-box; margin: 0; }
body {
  font-family: "Microsoft JhengHei", system-ui, sans-serif;
  background: var(--bg); color: var(--text);
  max-width: 480px; margin: 0 auto; padding: 16px;
}
h1 { font-size: 1.2rem; margin-bottom: 12px; color: var(--head); }
.card {
  background: var(--panel); border-radius: 14px; padding: 16px;
  margin-bottom: 12px; border: 1px solid var(--line);
}
.muted { color: var(--muted); font-size: .85rem; }
button {
  font-size: 1rem; padding: 12px; border: 0; border-radius: 10px;
  cursor: pointer; width: 100%;
  font-family: inherit;
}
button:disabled { opacity: .4; cursor: not-allowed; }
.btn-in { background: var(--btn-in-bg); color: var(--btn-text); font-weight: 700; box-shadow: var(--glow-green-box); }
.btn-out { background: var(--btn-out-bg); color: var(--btn-text); font-weight: 700; box-shadow: var(--glow-orange-box); }
.btn-plain { background: var(--panel2); color: var(--text); border: 1px solid var(--line); }
.row { display: flex; gap: 10px; }
.row > * { flex: 1; }
.status { padding: 10px; border-radius: 10px; margin: 10px 0; font-size: .95rem; border: 1px solid var(--line); background: var(--panel2); color: var(--text); }
.status.ok { background: rgba(47, 212, 143, .1); color: var(--green-ink); border-color: rgba(47, 212, 143, .3); }
.status.err { background: rgba(255, 122, 122, .1); color: var(--red-ink); border-color: rgba(255, 122, 122, .3); }

/* 送出時被擋下來的那一格：捲到畫面中間之後閃紅框（見 app.js 的 App.formError）。
   只靠捲動的話，畫面上同時有五六格，人還是要自己找是哪一格。 */
.field-bad, .field-bad input, .field-bad select, .field-bad textarea {
  border-color: var(--red) !important;
  animation: fieldBad 1.1s ease-out 2;
}
@keyframes fieldBad {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 122, 122, 0); }
  30% { box-shadow: 0 0 0 4px rgba(255, 122, 122, .35); }
}
@media (prefers-reduced-motion: reduce) {
  .field-bad, .field-bad input, .field-bad select, .field-bad textarea { animation: none; }
}
table { width: 100%; border-collapse: collapse; font-size: .9rem; }
th, td { padding: 8px 6px; border-bottom: 1px solid var(--line); text-align: left; }
th { color: var(--sub); font-weight: 500; }
input, select, textarea {
  width: 100%; padding: 10px; font-size: 1rem;
  border: 1px solid var(--line); border-radius: 10px; margin: 4px 0 12px;
  background: var(--panel2); color: var(--head);
}
/* 對焦用「多一圈邊框」而不是光暈：光暈在淺色底下幾乎看不見，
   而正在打字的那一格是最需要一眼認出來的。 */
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--cyan); box-shadow: inset 0 0 0 1px var(--cyan);
}
/* 多行輸入：拿掉右下角的拖曳把手（手機上拖不動，看起來像破圖），
   高度改由 App.autoGrow() 隨內容長。固定 rows="2" 在手機上又矮又擠，
   打第三行就開始在小框裡捲動，看不到自己前面寫了什麼。 */
textarea {
  resize: none; min-height: 88px; line-height: 1.65; overflow-y: hidden;
}
label { font-size: .9rem; color: var(--sub); }
.tag { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: .75rem; }
.tag.pending { background: rgba(255, 200, 96, .12); color: var(--orange-ink); border: 1px solid rgba(255, 200, 96, .3); }
.tag.approved { background: rgba(47, 212, 143, .12); color: var(--green-ink); border: 1px solid rgba(47, 212, 143, .3); }
.tag.rejected { background: rgba(255, 122, 122, .12); color: var(--red-ink); border: 1px solid rgba(255, 122, 122, .3); }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .tag.pending { background: #fff3cd; color: var(--orange-ink); border: 0; }
  :root:not([data-theme="dark"]) .tag.approved { background: #e6f4ea; color: var(--green-ink); border: 0; }
  :root:not([data-theme="dark"]) .tag.rejected { background: #fdecea; color: var(--red-ink); border: 0; }
}
:root[data-theme="light"] .tag.pending { background: #fff3cd; color: #7a5c00; border: 0; }
:root[data-theme="light"] .tag.approved { background: #e6f4ea; color: #1a7f37; border: 0; }
:root[data-theme="light"] .tag.rejected { background: #fdecea; color: #b3261e; border: 0; }
nav { display: flex; gap: 8px; margin-bottom: 14px; }
/* 輕量的返回連結。給版面已經很滿的頁面用（人員動態、廠商出工）——
   那兩頁的內容才是主角，返回不該長成一顆跟內容搶注意力的按鈕。
   放在共用檔而不是各頁自己寫：vendor.html 就是漏了沒寫，於是那個連結退回
   瀏覽器預設的亮藍色＋底線，在一整頁深色卡片裡看起來像貼錯的東西。 */
.back {
  display: inline-block; margin-bottom: 10px;
  color: var(--sub); font-size: .85rem; text-decoration: none;
}
.back:hover { color: var(--cyan); }
nav a {
  flex: 1; text-align: center; padding: 10px; border-radius: 10px;
  background: var(--panel2); color: var(--sub); text-decoration: none; font-size: .9rem;
  border: 1px solid var(--line);
}
nav a.active { background: rgba(77, 184, 255, .12); color: var(--cyan); border-color: rgba(77, 184, 255, .35); }

/* ── 深淺色滑動開關（共用元件：App.mountThemeToggle 掛載）── */
.theme-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.theme-row h1 { flex: 1; }
.tswitch {
  width: 54px; height: 28px; border-radius: 14px; position: relative;
  cursor: pointer; flex-shrink: 0; user-select: none;
  background: #ff8a1e; transition: background .25s;
}
.tswitch .knob {
  position: absolute; top: 3px; left: 29px; width: 22px; height: 22px;
  border-radius: 50%; background: #fff; transition: left .25s;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
}
.tswitch .ic { position: absolute; top: 50%; transform: translateY(-50%); font-size: 14px; line-height: 1; }
.tswitch .sun { left: 7px; color: #fff; }
.tswitch .moon { right: 7px; color: #cfe6ff; display: none; }
.tswitch.dark { background: #0d1526; border: 1px solid #223049; }
.tswitch.dark .knob { left: 3px; top: 2px; }
.tswitch.dark .sun { display: none; }
.tswitch.dark .moon { display: inline; }

/* ── 統一載入樣式：<div class="loading-box">載入中…</div>（旋轉圈＋文字）── */
.loading-box { display: flex; align-items: center; justify-content: center; gap: 10px; color: var(--muted); font-size: .9rem; padding: 20px 0; }
.loading-box::before {
  content: ''; width: 18px; height: 18px; border-radius: 50%; flex-shrink: 0;
  border: 2.5px solid var(--line); border-top-color: var(--cyan);
  animation: ldspin .8s linear infinite;
}
@keyframes ldspin { to { transform: rotate(360deg); } }

/* 定位入場畫面失敗時，狀態文字同步轉紅（與紅環一致） */
.loc-ring.fail ~ .loc-status { color: var(--red); }

/* 打卡頁雙大鈕並排（顯示全部/圈外）時縮小字級避免擠壓 */
.row .bigbtn { font-size: 1.02rem; padding: 16px 4px; letter-spacing: 0; }

/* ── 入口頁全屏載入：畫面正中大轉圈（無卡片），選單四格點入時使用 ── */
.page-loading { min-height: 72vh; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; color: var(--muted); font-size: .92rem; }
.page-loading::before {
  content: ''; width: 44px; height: 44px; border-radius: 50%;
  border: 3px solid var(--line); border-top-color: var(--cyan);
  animation: ldspin .8s linear infinite;
}

/* ── 測試（Beta）期間的提示帶：釘在最上方，讓人一眼知道現在的資料還不算數 ── */
.beta-bar {
  position: sticky; top: 0; z-index: 90;
  background: #f0a020; color: #241500;
  font-size: .78rem; font-weight: 700; letter-spacing: .02em;
  text-align: center; padding: 6px 10px;
  /* 抵銷 body 的 16px 內距，讓它真的貼在最上緣。
     不抵銷的話上面會露出一條 16px 的頁面底色，而捲動時它會「跳」上去黏住——
     看起來像畫面破圖，而不是一條刻意釘住的提示帶。
     底下補 10px：緊接在下面的返回連結原本跟橘色帶貼在一起，字的上緣幾乎黏著它。 */
  margin: -16px -16px 10px;
}

/* ── 時間輸入：時 : 分 ＋ 上午/下午
   取代原生 <input type="time">。原生選擇器在各家手機長相不一，
   而且會依系統設定顯示 12 或 24 小時制，先前只好在旁邊補一行「24小時制：」，很醜。
   這裡自己畫，格式固定、看得懂，也不必再補註解。
   真正的值仍存在原本那個 input（隱藏）裡，永遠是 24 小時制的 HH:MM，
   所以送出表單與既有讀取程式完全不用改。 */
/* ---- 起訖時間分組 ----
   日期與時間各自佔一整列。
   之前把「日期｜時間」並排成兩欄，375px 的手機每欄只剩約 165px：
   時間選擇器光是「時 : 分 ＋ 上午/下午」就要 224px，日期更是被壓到
   「8/月/20/日」直排。並排省下的高度，遠不如換來的可讀性。 */
.dtgrp {
  border: 1px solid var(--line); border-radius: 12px;
  padding: 10px 12px 12px; margin: 4px 0 10px; background: var(--panel2, transparent);
}
.dtgrp > .h {
  font-size: .78rem; font-weight: 600; color: var(--sub);
  margin: 0 0 6px; letter-spacing: .5px;
}
.dtgrp label { margin-top: 6px; }
.dtgrp > :first-child { margin-top: 0; }

/* ---- 表單分組 ----
   長表單一路排下來十幾個欄位時，人只看得到「還有很多要填」。
   分成三四組（時間／內容／佐證）之後，每一組都是一個看得到底的小段落，
   而且組標題右邊放得下那一組的規則（「最早可從 18:00 起算」「現場照片必填」）——
   那些話原本散在各個欄位下面，或者根本只在送出被擋時才出現。 */
.fgrp {
  border: 1px solid var(--line); border-radius: 12px;
  padding: 10px 12px 12px; margin: 10px 0; background: var(--panel2, transparent);
}
.fgrp > .gh {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  font-size: .78rem; font-weight: 600; color: var(--sub);
  margin: 0 0 8px; letter-spacing: .5px;
}
.fgrp > .gh .rule { font-weight: 400; font-size: .76rem; color: var(--cyan); text-align: right; }
.fgrp > .gh .rule.must { color: var(--red); }
.fgrp label { margin-top: 8px; }
.fgrp > :first-child { margin-top: 0; }
.fgrp .dtgrp { margin: 0; border: 0; padding: 0; background: none; }

/* 送出鈕吸在底部。
   手機上表單一長，送出鈕就在螢幕外——人填完最後一格，看到的是一片空白，
   得自己想到要往下捲。錯誤訊息也一樣（見 app.js 的 App.formError）。 */
.sticky-submit {
  position: sticky; bottom: 0; z-index: 5;
  margin: 12px -16px -16px; padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
  background: linear-gradient(180deg, transparent, var(--panel) 22%);
}
.sticky-submit button { margin: 0; }

/* ---- 日期選擇 ----
   原生的 input[type=date] 疊在按鈕上層並設成透明：iOS 的 webview 不支援
   showPicker()，隱藏起來的欄位就永遠點不開，只能讓使用者真的點到原生元素。
   所以視覺是我們畫的，接觸到的是原生的。 */
.dpick-wrap { position: relative; margin: 2px 0 4px; }
.dpick-wrap .dpick-native {
  position: absolute; inset: 0; width: 100%; height: 100%;
  margin: 0; padding: 0; opacity: 0; border: 0; background: transparent;
  -webkit-appearance: none; appearance: none;
}
.dpick {
  /* 日期、星期、「今天」三個字級不同，靠 baseline 對齊才不會看起來高高低低。
     但 baseline 只管「彼此對齊」，不管這一整組在框裡的高度——多出來的空間
     全部落在下面，於是字看起來偏上（實際發生過，廠商出工頁上很明顯）。
     開 wrap 之後這一列成為一個「行盒」，align-content 才管得到它的垂直位置。
     height 改 min-height：萬一真的擠到換行，是把框撐高，而不是把字切掉。 */
  width: 100%; margin: 0; min-height: 52px; padding: 0 14px;
  display: flex; align-items: baseline; align-content: center; flex-wrap: wrap; gap: 8px;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel2, var(--panel)); color: var(--fg);
  text-align: left; font-size: 1rem;
}
.dpick .ymd { font-size: 1.25rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.dpick .wk { font-size: .9rem; color: var(--sub); }
/* 週末用橘色點出來：請假、加班挑到假日是常見的填錯，
   讓它在按下送出之前就看得見 */
.dpick .wk.rest { color: var(--orange); font-weight: 600; }
.dpick .rel {
  font-size: .72rem; padding: 1px 7px; border-radius: 7px;
  background: rgba(45, 197, 214, .14); color: var(--cyan); font-weight: 600;
}
.dpick .yr { font-size: .78rem; color: var(--muted); margin-left: auto; }
/* 還沒選的時候沒有年份那一格，要讓 placeholder 把箭頭推到右邊，
   否則箭頭會黏在「選擇日期」旁邊，跟其他列對不齊 */
.dpick .ph { color: var(--muted); font-size: .95rem; flex: 1; }
.dpick .ic { color: var(--muted); font-size: .8rem; margin-left: 6px; align-self: center; }
.dpick-wrap:focus-within .dpick { border-color: var(--cyan); }
@media (max-width: 360px) {
  .dpick { min-height: 48px; padding: 0 11px; gap: 6px; }
  .dpick .ymd { font-size: 1.1rem; }
}

.tpick { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 2px 0 4px; }
.tpick .num {
  width: 62px; height: 48px; margin: 0; padding: 0; text-align: center;
  font-size: 1.5rem; font-weight: 600; font-variant-numeric: tabular-nums;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel2, var(--panel)); color: var(--fg);
}
.tpick .num:focus { outline: none; border-color: var(--cyan); background: rgba(45, 197, 214, .09); }
.tpick .colon { font-size: 1.4rem; font-weight: 600; color: var(--muted); margin: 0 -2px; }
/* 24 小時制版（廠商出工用）：沒有上午／下午那兩顆鈕，欄位也窄一些——
   它跟工項、姓名擠在同一張卡上，佔太多寬度會把別的欄位壓扁。 */
.tpick.h24 { gap: 4px; margin: 0; }
.tpick.h24 .num { width: 2.6em; padding: 8px 4px; font-size: .95rem; }
.tpick.h24 .colon { font-size: 1.1rem; margin: 0; }
/* 上午/下午：比數字框小一號就夠了，不需要跟它一樣高。
   選取態的文字色走 --on-cyan-fg，不能寫死深色——
   淺色模式的 --cyan 是深藍（#0077b6），配深字對比只有 3.4，看不清楚。 */
.tpick .ap { display: flex; gap: 4px; margin-left: 4px; }
.tpick .ap button {
  width: auto; margin: 0; padding: 0 11px; height: 34px; font-size: .82rem; font-weight: 500;
  border: 1px solid var(--line); border-radius: 8px;
  background: transparent; color: var(--muted);
}
.tpick .ap button.on {
  background: var(--cyan); border-color: var(--cyan);
  color: var(--on-cyan-fg); font-weight: 700;
}
@media (max-width: 360px) {
  .tpick .num { width: 54px; height: 44px; font-size: 1.3rem; }
  .tpick .ap button { height: 32px; padding: 0 9px; font-size: .78rem; }
}

/* 撤回自己送出的待簽核申請 */
.cancel-btn {
  width: auto; padding: 2px 10px; font-size: .72rem; margin-left: 6px;
  background: rgba(255, 122, 122, .1); color: var(--red);
  border: 1px solid rgba(255, 122, 122, .35); border-radius: 8px;
}

/* ---- 確認／提示對話框（取代 LINE 內建瀏覽器不可靠的 confirm/alert）---- */
.dlg-back {
  position: fixed; inset: 0; z-index: 9999;
  top: 0; right: 0; bottom: 0; left: 0;   /* 舊 iOS 不認 inset 簡寫 */
  background: rgba(0, 0, 0, .55);
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
  animation: dlgFade .12s ease-out;
}
@keyframes dlgFade { from { opacity: 0 } to { opacity: 1 } }
.dlg {
  width: 100%; max-width: 340px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 16px;
  padding: 20px 20px 16px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, .45);
  animation: dlgPop .14s ease-out;
  /* ---- 系統字級開到最大時，按鈕不能被切掉 ----
     實際發生過：同仁把手機字體調到最大，請假的確認視窗比螢幕還高，
     置中之後上下兩頭都超出畫面，而外層是 position: fixed——捲不動，
     「確認送出」就在螢幕外，假單送不出去。
     用真引擎（Chromium／WebKit）把字級放大重現過：360×640 在 ×1.6、320×568 在 ×1.3 就按不到。

     所以視窗最高就是外層扣掉留白（max-height: 100%），超出的部分由**內容區自己捲**，
     標題與按鈕固定在視窗裡。不用 100vh／100dvh：LINE 內建瀏覽器的網址列會讓那兩個值
     跟實際可見高度對不上，而外層本身就是鋪滿畫面的 fixed，跟著它走最準。 */
  max-height: 100%;
  display: flex; flex-direction: column; overflow: hidden;
}
/* 除了內容區，其他（標題、第三條路、按鈕列）一律不縮——縮的應該是可以捲的那一塊 */
.dlg > * { flex-shrink: 0; }
.dlg > .dlg-body, .dlg > .dlg-form, .dlg > .dlg-choices {
  flex-shrink: 1; min-height: 0;
  overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
}
@keyframes dlgPop { from { transform: translateY(8px) scale(.98); opacity: .6 } to { transform: none; opacity: 1 } }
.dlg-title { color: var(--head); font-size: 1.05rem; font-weight: 700; line-height: 1.45; }
.dlg-body { color: var(--sub); font-size: .86rem; line-height: 1.65; margin-top: 8px; white-space: pre-line; }
/* 取代 prompt() 的輸入框 */
.dlg-input {
  width: 100%; margin: 14px 0 0; padding: 11px 12px; font-size: .95rem;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel2, var(--panel)); color: var(--fg);
}
.dlg-input:focus { outline: none; border-color: var(--cyan); }
/* 並排的大選項：手指按得到，而且兩顆一樣大——大小不同會被讀成「建議按大的那顆」，
   而「去工地」跟「去樓下拿東西」沒有哪一個比較對。 */
.dlg-choices { display: flex; gap: 10px; margin-top: 16px; }
.dlg-choice {
  flex: 1; min-height: 64px; padding: 12px 10px; border-radius: 12px; cursor: pointer;
  background: var(--panel2); border: 1px solid var(--line-strong); color: var(--text);
  font: inherit; display: flex; flex-direction: column; gap: 3px; align-items: center;
  justify-content: center; text-align: center;
}
.dlg-choice b { font-size: .95rem; color: var(--head); }
.dlg-choice small { font-size: .74rem; color: var(--sub); line-height: 1.35; }
.dlg-choice:hover, .dlg-choice:focus { border-color: var(--cyan); }
/* 只剩「取消」時不要讓它佔滿一整行看起來像主要動作 */
.dlg-choices + .dlg-btns { margin-top: 12px; }
.dlg-choices + .dlg-btns .dlg-btn.cancel { flex: 1; }

/* 字大到兩顆放不下一行時，上下疊起來，而不是每顆各自折成兩行字。
   wrap-reverse：疊起來時「確認」在上、「取消」在下；並排時照舊取消在左、確認在右。
   基準寬度用 em，才會跟著字級一起放大——用 px 的話字變大了卻不換行，字就被擠成兩行。 */
.dlg-btns { display: flex; flex-wrap: wrap-reverse; gap: 10px; margin-top: 18px; }
/* 字數不夠時確定鍵按不下去，而且要看得出來是刻意鎖住的，不是壞了 */
.dlg-btn:disabled { opacity: .4; cursor: not-allowed; }
/* 兩顆按鈕等寬：不讓「確定」比「取消」大，危險動作不該被視覺推著按 */
.dlg-btn {
  flex: 1 1 6.5em; padding: 11px 12px; border-radius: 10px; font-size: .92rem; font-weight: 600;
  border: 1px solid var(--line); background: var(--panel2); color: var(--head); cursor: pointer;
}
.dlg-btn.ok { background: var(--cyan); color: var(--on-cyan-fg); border-color: transparent; }
.dlg-btn.ok.danger { background: var(--red); color: #fff; }
.dlg-btn:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .dlg-back, .dlg { animation: none; }
}

/* ---- 送出成功的彈窗 ---- */
/* 原本只在打卡頁；申請與簽核也需要——那些頁面的成功訊息是一行小字，
   使用者捲在畫面下方根本看不到，於是又按一次。彈窗蓋住整個畫面，錯不過去。 */
.ptoast-mask {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(0, 0, 0, .55);
  display: flex; align-items: center; justify-content: center;
  animation: fadeIn .2s;
}
.ptoast {
  background: var(--panel); border: 1px solid var(--line); border-radius: 18px;
  padding: 26px 32px; text-align: center; min-width: 250px; max-width: 85vw;
  animation: popIn .28s cubic-bezier(.2, 1.4, .5, 1);
}
.ptoast .big { font-size: 2.4rem; }
.ptoast .title { color: var(--green); font-weight: 700; font-size: 1.15rem; margin: 6px 0 2px; }
.ptoast .time { font-size: 2.1rem; font-weight: 700; color: var(--head); font-variant-numeric: tabular-nums; }
.ptoast .act { color: var(--head); font-size: .95rem; margin-top: 4px; }
.ptoast .place { color: var(--sub); font-size: .85rem; margin-top: 5px; white-space: pre-line; line-height: 1.6; }
.ptoast-mask.hide { animation: fadeOut .25s forwards; }
@keyframes popIn { from { transform: scale(.75); opacity: 0; } }
@keyframes fadeIn { from { opacity: 0; } }
@keyframes fadeOut { to { opacity: 0; } }

/* 彈窗裡的表單欄位（App.form）。沿用 .dlg 的外觀，只補欄位本身的排版 */
.dlg-form { display: flex; flex-direction: column; gap: 12px; margin-top: 14px; }
.dlg-fld { display: flex; flex-direction: column; gap: 6px; }
.dlg-fld > span { color: var(--sub); font-size: .82rem; }
.dlg-fld > span > i { color: var(--red); font-style: normal; font-size: .74rem; }
.dlg-fld input { width: 100%; }
/* 驗證訊息就地顯示，不關掉彈窗——關掉再叫人重填等於要他把字再打一次 */
.dlg-err { color: var(--red); font-size: .82rem; margin-top: 10px; }

/* 今日直接外出：少數情況的次要動作，排在主要打卡鈕之後，視覺重量明顯低一階 */
.awaybtn {
  width: 100%; margin-top: 10px; padding: 11px 12px;
  border: 1px dashed var(--line); border-radius: 10px;
  background: transparent; color: var(--sub); font-size: .88rem; cursor: pointer;
}
.awaybtn:hover { color: var(--head); border-color: var(--cyan); }
.awaycard {
  margin-top: 12px; padding: 12px 14px;
  border: 1px solid var(--line); border-left: 3px solid var(--cyan); border-radius: 10px;
  background: var(--panel2);
}
.awaycard .ttl { color: var(--head); font-weight: 700; font-size: .92rem; }
.awaycard .ln { color: var(--sub); font-size: .84rem; margin-top: 5px; }
.awaycard .ln b { color: var(--head); }
/* 這一句是整張卡最重要的資訊：沒打卡就不會定案 */
.awaycard .hint { color: var(--orange); font-size: .78rem; margin-top: 8px; line-height: 1.6; }
/* 撤回是次要動作，但仍要按得到：手指的目標不因為視覺層級低就縮水 */
.awaycard .rv {
  margin-top: 10px; padding: 0 14px; min-height: 44px; font-size: .82rem;
  border: 1px solid var(--line); border-radius: 8px; background: transparent; color: var(--sub); cursor: pointer;
}

/* 送出成功的結果卡：表單收起來之後就換成這一塊。
   共用在加班／未來其他申請頁——每一頁各寫一套的話，
   「送出後長什麼樣」就會每一頁都不一樣。 */
.done-card {
  background: rgba(47, 212, 143, .08);
  border: 1px solid rgba(47, 212, 143, .35);
  border-radius: 14px; padding: 16px 16px 14px;
}
.done-card .dc-ttl { color: var(--green); font-weight: 700; font-size: 1.05rem; }
.done-card .dc-when {
  margin-top: 8px; font-size: 1.02rem; color: var(--head); font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.done-card .dc-note { margin-top: 8px; font-size: .84rem; color: var(--sub); line-height: 1.6; }

/* ---- 定位狀態膠囊 ----
   打卡頁與後台的「打卡頁預覽」共用。放在這裡而不是各頁自己寫，
   是因為預覽的意義就在於它跟真的長得一模一樣。 */
/* 定位狀態：毛玻璃膠囊——壓在地圖上就該用這種材質，狀態靠圓點顏色 */
.locbar {
  display: inline-flex; align-items: center; gap: 7px; max-width: 100%;
  margin-top: 10px; padding: 6px 12px; border-radius: 999px; font-size: .8rem;
  background: var(--map-glass); border: 1px solid var(--map-glass-line); color: var(--head);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}
.locbar .pip { width: 6px; height: 6px; border-radius: 50%; background: var(--muted); flex: none; }
/* 背景重新確認位置中：圓點呼吸，不轉圈也不擋操作 */
.locbar.checking .pip { animation: pipPulse 1.1s ease-in-out infinite; }
@keyframes pipPulse { 50% { opacity: .25; } }
.locbar .txt { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.locbar.ok { color: var(--map-ok); border-color: rgba(47, 212, 143, .45); }
.locbar.ok .pip { background: var(--green); }
.locbar.warn { color: var(--map-warn); border-color: rgba(230, 92, 0, .5); }
.locbar.warn .pip { background: var(--orange); }
.locbar .relocate { text-decoration: none; font-size: 1.05rem; line-height: 1; flex: none; opacity: .75; margin-left: 3px; color: inherit; }

/* ---- 收工待辦提示列 ----
   打完下班卡可能同時有好幾件事（填加班單、補廠商收工時間）。人從打卡頁被送到
   其中一頁處理，做完會直接關掉 LIFF——因為畫面上沒有任何東西提到還有另一件。
   這條列是唯一會提醒他的東西，所以它從進頁面就在，而不是做完才出現。 */
.todobar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 9px 14px; font-size: .8rem; line-height: 1.5;
  background: rgba(255, 169, 77, .14);
  border-bottom: 1px solid rgba(255, 169, 77, .35);
  color: var(--orange-ink, #b45309);
}
.todobar span { flex: 1 1 auto; min-width: 0; }
.todobar b { font-weight: 700; }
.todobar button {
  flex: none; padding: 5px 14px; font-size: .78rem; font-weight: 600;
  border: 1px solid currentColor; border-radius: 999px;
  background: transparent; color: inherit; cursor: pointer;
}
.todobar button:active { opacity: .6; }

/* ---- 送出前的填寫總覽 ----
   假單送出去就進入簽核，改要走「編輯」再讓主管重簽一次。所以按下送出之前
   把填了什麼攤開來看一次，比事後補救便宜得多。

   排版的重點是**決定要看的那兩件事**（假別、時長）先講，其餘的用對齊的
   標籤／值排下去——一整片同樣大小的字，人會整段跳過。 */
.lvsum { white-space: normal; }   /* .dlg-body 是 pre-line，會把縮排變成空行 */
.lvsum .hd {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding-bottom: 10px; border-bottom: 1px solid var(--line);
}
.lvsum .hd .ty { font-size: 1.12rem; font-weight: 700; color: var(--head); }
.lvsum .hd .du { font-size: .95rem; font-weight: 600; color: var(--cyan); }
.lvsum dl {
  display: grid; grid-template-columns: auto 1fr; gap: 7px 14px;
  margin: 11px 0 0; align-items: baseline;
}
.lvsum dt { color: var(--sub); font-size: .8rem; white-space: nowrap; }
.lvsum dd { margin: 0; color: var(--head); font-size: .88rem; }
.lvsum dd .wk { color: var(--sub); font-size: .8rem; margin-left: 2px; }
.lvsum dd .none { color: var(--sub); }
/* 日期與時間用等寬數字：兩列的「開始／結束」對不齊的話，
   要比對差幾天幾小時就得一個字一個字看過去。 */
.lvsum dd time { font-variant-numeric: tabular-nums; }
.lvsum .warn {
  margin-top: 11px; padding: 8px 10px; border-radius: 8px; font-size: .82rem;
  background: rgba(230, 162, 60, .16); color: var(--orange-ink, #b06f00);
}
.lvsum .foot { margin-top: 11px; font-size: .8rem; color: var(--sub); }

/* 對話框裡的第三條路：一行低調的文字按鈕，擺在內容與主要按鈕之間。
   做成連結的樣子而不是第三顆按鈕——它是少數人偶爾才需要的東西，
   跟「確認送出」等重的話，主要動作的層級就被稀釋了。 */
.dlg-link {
  display: block; width: 100%; margin-top: 12px; padding: 8px 10px;
  font-size: .82rem; text-align: center; cursor: pointer;
  background: transparent; border: 1px dashed var(--red);
  border-radius: 10px;
  color: var(--red-ink);
  text-decoration: underline;
}
.dlg-link:hover { background: rgba(217, 83, 79, .08); }
.dlg-link:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

/* ---- 換頁的轉場：拿掉了 ----
   曾經在這裡放 `@view-transition { navigation: auto }`，想把換頁時的白閃蓋掉。
   結果 LINE 的 WebView 在同源導頁時整個停住——畫面留在舊的那一頁，
   LINE 顯示「開啟中」不動。打卡頁底下五顆捷徑鈕、圖文選單的電子簽核／點餐／
   其他功能全部進不去，而且不會有任何錯誤訊息。
   那是一個純視覺的優化，換不到讓全公司打不了卡的風險。不要再加回來。 */
