/* 打卡頁（index.html）的樣式。2026-10-02 改版上線。
 *
 * ================================================================
 * 為什麼是另一個檔
 * ================================================================
 * 改版期間新舊兩版並存：沙盒跟正式站**共用同一份程式碼**（只有資料目錄與埠不同），
 * 沒有「只改沙盒」這回事，所以新版先放在 index2.html，驗收通過才換成 index.html。
 * 舊版的備份在專案根目錄的 _上線前備份/2026-10-02-打卡頁改版/。
 *
 * index.css 是舊版的樣式，其他頁（以及這一頁還沒覆寫到的地方）仍然在用，
 * 這個檔只放這一頁的新外觀，載入順序在它後面。
 *
 * ================================================================
 * 這一版在做什麼
 * ================================================================
 * 邏輯一行都沒動：定位、重試、班制、門禁、外出宣告、跨夜班全部沿用
 * index.js。這裡只換外觀，而且刻意保留原本的 id 與 class
 * （.bigbtn / .statline / .statbar / .locbar / .timeline / .hero …），
 * 這樣 index.js 塞進來的 HTML 不必改就長得對。
 *
 * ================================================================
 * 視覺語言（照設計稿）
 * ================================================================
 *   ・上半是一張有景深的城市（天空、雲或星、101、樓群、樹），底下是一道地平線
 *   ・主按鈕是漸層實心的圓，外面一圈白邊、一圈光暈
 *   ・卡片是白的（夜裡是深藍的），大圓角、柔和的影子，圖示放在有底色的小方塊裡
 *   ・整頁偏藍——跟其他頁的中性灰不同，所以色票只在 .ph-body 底下覆寫
 */

/* ---------------------------------------------------------------- 色票
   ⚠ 這個專案是**深色優先**：:root 放深色，淺色由下面兩個入口覆寫
     （系統淺色且沒手動選深色 / 手動選淺色）。寫反的話，
     淺色主題會拿到深色的值，而且不會報錯。 */
:root {
  --ph-stat-h: 66px;                     /* 三格統計（以及還沒上班時的班別卡）的高度，見「班制那一張」 */
  --ph-tabs-h: 72px;                     /* 底部那一列佔掉的高度：62px 的列＋下面 10px 的空隙（不含瀏海機型底部的安全區） */
  --ph-tab: 59, 142, 240;                /* 底部每一顆的顏色（R, G, B），各顆自己蓋掉 */
  --ph-ring: 70, 140, 245;               /* 主按鈕光暈的顏色（R, G, B），各卡別自己蓋掉 */
  /* 夜景 */
  --ph-sky-a: #10306a;
  --ph-sky-b: #0b1d44;
  --ph-sc-far: #14284f;
  --ph-sc-mid: #1b3668;
  --ph-sc-near: #21427e;
  --ph-sc-win: rgba(255, 216, 130, .9);
  --ph-sc-tower: #1d7a9f;
  --ph-sc-tower-hi: #86ebff;
  --ph-sc-glow: rgba(90, 210, 255, .5);
  --ph-sc-tree1: #14424a;
  --ph-sc-tree2: #10343d;
  --ph-sc-cloud: rgba(255, 255, 255, .035);
  --ph-sc-star: rgba(220, 234, 255, .85);
  --ph-clock: #ffffff;
  --ph-hero-ink: #f2f6ff;
  --ph-hero-sub: #b9c7e2;
  --ph-orb: #ffeeb8;
  --ph-orb-glow: rgba(255, 238, 184, .18);
  --ph-ava: #223a66;
  --ph-ava-tx: #d6e4ff;
  --ph-ava-ring: rgba(255, 255, 255, .9);
  --ph-card: #111f3d;
  --ph-card-line: rgba(120, 160, 255, .16);
  --ph-card-shadow: 0 10px 26px rgba(0, 0, 0, .28);
  --ph-tile: rgba(74, 140, 255, .18);
  --ph-tile2: rgba(150, 120, 255, .2);
  --ph-accent: #5aa8ff;
  --ph-chip: #17294f;
  --ph-chip-line: rgba(130, 170, 255, .24);
  --ph-done-ring: rgba(47, 212, 143, .6);
  --ph-done-halo: rgba(47, 212, 143, .1);
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    /* 日景 */
    --ph-sky-a: #bfe2ff;
    --ph-sky-b: #dcefff;
    --ph-sc-far: #cfe4f8;
    --ph-sc-mid: #b3d3f1;
    --ph-sc-near: #9cc5ec;
    --ph-sc-win: rgba(255, 255, 255, .8);
    --ph-sc-tower: #62b6dc;
    --ph-sc-tower-hi: rgba(255, 255, 255, .8);
    --ph-sc-glow: rgba(255, 255, 255, 0);
    --ph-sc-tree1: #a8dcae;
    --ph-sc-tree2: #86cb97;
    --ph-sc-cloud: rgba(255, 255, 255, .92);
    --ph-sc-star: rgba(255, 255, 255, 0);
    --ph-clock: #0f1f40;
    --ph-hero-ink: #0f1f40;
    --ph-hero-sub: #4f5f78;
    --ph-orb: #ffd45e;
    --ph-orb-glow: rgba(255, 212, 94, .32);
    --ph-ava: #dbe8f7;
    --ph-ava-tx: #2b4a78;
    --ph-ava-ring: #ffffff;
    --ph-card: #ffffff;
    --ph-card-line: rgba(30, 80, 160, .07);
    --ph-card-shadow: 0 10px 26px rgba(30, 70, 140, .09);
    --ph-tile: #e7f1ff;
    --ph-tile2: #efe9ff;
    --ph-accent: #1f7ae0;
    --ph-chip: #ffffff;
    --ph-chip-line: rgba(30, 80, 160, .08);
    --ph-done-ring: rgba(22, 160, 90, .55);
    --ph-done-halo: rgba(22, 160, 90, .1);
  }
}
:root[data-theme="light"] {
  --ph-sky-a: #bfe2ff;
  --ph-sky-b: #dcefff;
  --ph-sc-far: #cfe4f8;
  --ph-sc-mid: #b3d3f1;
  --ph-sc-near: #9cc5ec;
  --ph-sc-win: rgba(255, 255, 255, .8);
  --ph-sc-tower: #62b6dc;
  --ph-sc-tower-hi: rgba(255, 255, 255, .8);
  --ph-sc-glow: rgba(255, 255, 255, 0);
  --ph-sc-tree1: #a8dcae;
  --ph-sc-tree2: #86cb97;
  --ph-sc-cloud: rgba(255, 255, 255, .92);
  --ph-sc-star: rgba(255, 255, 255, 0);
  --ph-clock: #0f1f40;
  --ph-hero-ink: #0f1f40;
  --ph-hero-sub: #4f5f78;
  --ph-orb: #ffd45e;
  --ph-orb-glow: rgba(255, 212, 94, .32);
  --ph-ava: #dbe8f7;
  --ph-ava-tx: #2b4a78;
  --ph-ava-ring: #ffffff;
  --ph-card: #ffffff;
  --ph-card-line: rgba(30, 80, 160, .07);
  --ph-card-shadow: 0 10px 26px rgba(30, 70, 140, .09);
  --ph-tile: #e7f1ff;
  --ph-tile2: #efe9ff;
  --ph-accent: #1f7ae0;
  --ph-chip: #ffffff;
  --ph-chip-line: rgba(30, 80, 160, .08);
  --ph-done-ring: rgba(22, 160, 90, .55);
  --ph-done-halo: rgba(22, 160, 90, .1);
}

/* ================================================================
   這一頁的樣子跟著**時間**走，不是跟著系統的深淺色
   ================================================================
   清晨（05–08）、白天（08–17）、傍晚（17–19）是亮的，夜晚（19–05）是暗的。
   時段由 index.html <head> 裡的 window.__phSky() 決定，它同時寫兩個屬性在 <html> 上：

     data-sky    = dawn | day | dusk | night   → 下面這四組背景的顏色
     data-theme  = light | dark                → 全站既有的深淺色（卡片、文字、地圖…）

   為什麼不是「深／淺 × 四個時段」八種：亮色介面配夜景、暗色介面配白天都很彆扭——
   上半是一張風景，下半的卡片跟它不是同一個時間，看起來像兩頁拼在一起。
   讓介面的深淺跟著背景走，整頁永遠是同一個時間。

   手動選過深淺色的人照他的意思（有人是眼睛怕亮，那不是美觀問題）：
     ・選深色 → 整天都是夜晚這一套
     ・選淺色 → 夜裡不變暗，停在傍晚這一套
   沒選過的（跟隨系統）才完全跟著時間走。

   ⚠ window.__phSky() 寫在 index.html 的 <head> 裡（行內）：要在第一次繪製之前定下來。
     等 punch2.js（在 body 尾端）才設的話，系統是深色的手機在白天會先閃一下黑的。
     punch2.js 之後每次重畫都會再呼叫它，開著頁面跨過時段（例如 17:00）也換得過去。
     那段行內腳本只放程式、不放說明——它每次開頁都會被下載，註解也算在裡面
     （test-asset-cache 有預算）。
   ⚠ 只改這一頁的屬性，不寫回 localStorage——其他頁照舊跟著使用者自己的設定。

   ⚠ 上面三個區塊（:root 與兩個淺色入口）裡的背景色是**墊底**：
     腳本還沒跑、或被擋掉的時候，深色配夜景、淺色配白天。 */
:root[data-sky="dawn"] {
  --ph-sky-a: #cdd9f8;
  --ph-sky-b: #ffe1cf;
  --ph-sc-far: #ead3df;
  --ph-sc-mid: #d5bcd8;
  --ph-sc-near: #bfa4cf;
  --ph-sc-win: rgba(255, 244, 214, .85);
  --ph-sc-tower: #a493cf;
  --ph-sc-tower-hi: rgba(255, 255, 255, .75);
  --ph-sc-glow: rgba(255, 255, 255, 0);
  --ph-sc-tree1: #b7d7b0;
  --ph-sc-tree2: #9bc69d;
  --ph-sc-cloud: rgba(255, 255, 255, .85);
  --ph-sc-star: rgba(255, 255, 255, 0);
  --ph-clock: #0f1f40;
  --ph-hero-ink: #0f1f40;
  --ph-hero-sub: #4f5f78;
  --ph-orb: #ffb469;
  --ph-orb-glow: rgba(255, 180, 105, .3);
}
:root[data-sky="day"] {
  --ph-sky-a: #bfe2ff;
  --ph-sky-b: #dcefff;
  --ph-sc-far: #cfe4f8;
  --ph-sc-mid: #b3d3f1;
  --ph-sc-near: #9cc5ec;
  --ph-sc-win: rgba(255, 255, 255, .8);
  --ph-sc-tower: #62b6dc;
  --ph-sc-tower-hi: rgba(255, 255, 255, .8);
  --ph-sc-glow: rgba(255, 255, 255, 0);
  --ph-sc-tree1: #a8dcae;
  --ph-sc-tree2: #86cb97;
  --ph-sc-cloud: rgba(255, 255, 255, .92);
  --ph-sc-star: rgba(255, 255, 255, 0);
  --ph-clock: #0f1f40;
  --ph-hero-ink: #0f1f40;
  --ph-hero-sub: #4f5f78;
  --ph-orb: #ffd45e;
  --ph-orb-glow: rgba(255, 212, 94, .32);
}
:root[data-sky="dusk"] {
  --ph-sky-a: #aab6f2;
  --ph-sky-b: #ffc7a1;
  --ph-sc-far: #eab5a6;
  --ph-sc-mid: #cf93a3;
  --ph-sc-near: #ad789d;
  --ph-sc-win: rgba(255, 228, 160, .95);
  --ph-sc-tower: #8672b8;
  --ph-sc-tower-hi: rgba(255, 232, 196, .85);
  --ph-sc-glow: rgba(255, 255, 255, 0);
  --ph-sc-tree1: #8eb494;
  --ph-sc-tree2: #6f9d83;
  --ph-sc-cloud: rgba(255, 236, 226, .8);
  --ph-sc-star: rgba(255, 255, 255, 0);
  --ph-clock: #0f1f40;
  --ph-hero-ink: #0f1f40;
  --ph-hero-sub: #4f5f78;
  --ph-orb: #ff8a4d;
  --ph-orb-glow: rgba(255, 138, 77, .32);
}
:root[data-sky="night"] {
  --ph-sky-a: #10306a;
  --ph-sky-b: #0b1d44;
  --ph-sc-far: #14284f;
  --ph-sc-mid: #1b3668;
  --ph-sc-near: #21427e;
  --ph-sc-win: rgba(255, 216, 130, .9);
  --ph-sc-tower: #1d7a9f;
  --ph-sc-tower-hi: #86ebff;
  --ph-sc-glow: rgba(90, 210, 255, .5);
  --ph-sc-tree1: #14424a;
  --ph-sc-tree2: #10343d;
  --ph-sc-cloud: rgba(255, 255, 255, .035);
  --ph-sc-star: rgba(220, 234, 255, .85);
  --ph-clock: #ffffff;
  --ph-hero-ink: #f2f6ff;
  --ph-hero-sub: #b9c7e2;
  --ph-orb: #ffeeb8;
  --ph-orb-glow: rgba(255, 238, 184, .18);
}

/* 這一頁的底色、文字色比全站偏藍（設計稿的調子）。只在 .ph-body 底下覆寫全站的色票，
   其他頁不受影響；index.js 畫出來的東西（今日歷程、確認視窗…）也會跟著用這一組。
   ⚠ 一樣是三個入口：深色（預設）、系統淺色、手動淺色。 */
.ph-body {
  --bg: #0a1428; --panel: #111f3d; --panel2: #182a50; --line: #22355c; --line-strong: #3a5288;
  --head: #f2f6ff; --sub: #9db0cf; --muted: #6b7fa3;
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .ph-body {
    --bg: #f1f6fc; --panel: #ffffff; --panel2: #edf3fa; --line: #e2eaf4; --line-strong: #a9b9cd;
    --head: #0f1f40; --sub: #5f6f86; --muted: #93a1b5;
  }
}
:root[data-theme="light"] .ph-body {
  --bg: #f1f6fc; --panel: #ffffff; --panel2: #edf3fa; --line: #e2eaf4; --line-strong: #a9b9cd;
  --head: #0f1f40; --sub: #5f6f86; --muted: #93a1b5;
}

/* ---------------------------------------------------------------- 底
   ================================================================
   一般狀態要剛好一頁，不捲動；多出來的高度給**主按鈕那一區**
   ================================================================
   #main 是一個直向的 flex，最小高度＝螢幕高度扣掉底部分頁列。
   下面三塊（地點、今日統計、今日歷程）都是固定高度，
   會伸縮的只有最上面的問候區：螢幕越高，主按鈕周圍的留白越多。

   為什麼不是讓地圖伸縮（上上版是）：這一頁是來打卡的，不是來看地圖的。
   地圖只回答「我在不在範圍內」——而那句話地點卡的第二行已經寫了。
   讓它吃掉所有剩下的高度，高一點的手機上它會比主按鈕還大，主從就反了。

   ⚠ 是 min-height 不是 height。展開今日歷程、展開全部卡別、
     跳出補登提示這些時候內容會變多，那時候要能往下捲；
     寫成 height 的話多出來的部分會被切掉，而且切掉的正是訊息。

   ⚠ 100dvh 寫在 100vh 後面當覆寫：舊一點的 LINE 內建瀏覽器不認得 dvh，
     那一行會整條被丟掉，退回上面的 vh（差在網址列的高度，頂多多捲一點點）。 */
.ph-body {
  padding: 0 0 calc(var(--ph-tabs-h) + env(safe-area-inset-bottom, 0px));
  background: var(--bg);
}
.ph-body #main {
  max-width: 480px; margin: 0 auto;
  display: flex; flex-direction: column;
  min-height: calc(100vh - var(--ph-tabs-h) - env(safe-area-inset-bottom, 0px));
  min-height: calc(100dvh - var(--ph-tabs-h) - env(safe-area-inset-bottom, 0px));
}
/* 直向 flex 裡的每一塊預設不伸不縮：有彈性的只有問候區 */
.ph-body #main > * { flex: none; }
.ph-body #main > .ph-hero { flex: 1 1 auto; }

/* （這一版沒有頁內的標題列。
   「定位打卡」四個字 LINE 自己的標題列已經顯示了，同一個標題疊兩層；
   右上角的鈴鐺跟底部分頁列的「提醒設定」是同一個入口。） */

/* ---------------------------------------------------------------- 問候區：背景插畫 */
/* ⚠ 不裁切（overflow 沒有設 hidden）：打卡中／失敗的訊息浮在主按鈕正下方，
     矮螢幕上會稍微超出這一區，裁掉的話正好切掉訊息。插畫本身都在這一區的範圍裡。 */
.ph-hero {
  position: relative; padding: 16px 16px 14px; overflow: visible;   /* 蓋掉 index.css 的 .hero { overflow:hidden }——上面註解說的不裁切，原本其實被它裁了（2026-10-13 模擬） */
  display: flex; flex-direction: column;
  background: linear-gradient(180deg, var(--ph-sky-a) 0%, var(--ph-sky-b) 48%, var(--bg) 100%);
}
/* 夜裡：介面是淺色的（2026-10-05 起一律淺色），但天空還是夜空——
   天空一路深到地平線，由下面那道淺色的弧（.sc-wave）收邊，不要漸層成一片灰。 */
:root[data-sky="night"] .ph-hero,
:root[data-sky="night"] .ph-locart { background: linear-gradient(180deg, var(--ph-sky-a) 0%, var(--ph-sky-b) 100%); }
/* 插畫是三張行內 SVG（形狀在 index.html，顏色在這裡）。
   ⚠ 它是裝飾：aria-hidden、不吃觸控（上面有主按鈕）。 */
.ph-hero > svg,
.ph-locart > svg { position: absolute; z-index: 0; pointer-events: none; display: block; }
.ph-skyfx { left: 0; top: 0; width: 100%; height: 150px; }
.ph-scene { left: 0; bottom: 0; width: 100%; height: 130px; }
/* 101 的高度跟著問候區走（最多 232px）。矮螢幕上問候區只有兩百多 px，
   寫死高度的話塔尖會頂到右上角的時鐘。 */
.ph-tower {
  right: 9%; bottom: 20px; width: auto;
  height: min(232px, 60%);
  filter: drop-shadow(0 0 7px var(--ph-sc-glow));
}
/* 太陽／月亮：一個發光的圓。排在樓群的後面（節點順序在它們之前），
   清晨與傍晚壓低到樓的後面，只露出一部分；白天與夜晚掛在右上。 */
.ph-orb {
  position: absolute; z-index: 0; pointer-events: none; display: block;
  width: 36px; height: 36px; border-radius: 50%;
  right: 7%; top: 66px;
  background: var(--ph-orb);
  box-shadow: 0 0 0 7px var(--ph-orb-glow), 0 0 28px 12px var(--ph-orb-glow);
}
:root[data-sky="dawn"] .ph-orb { right: auto; left: 7%; top: auto; bottom: 62px; }
:root[data-sky="dusk"] .ph-orb { right: 15%; top: auto; bottom: 84px; }   /* 落在 101 的後面 */
.sc-cloud { fill: var(--ph-sc-cloud); }
.sc-star { fill: var(--ph-sc-star); }
.sc-far { fill: var(--ph-sc-far); }
.sc-mid { fill: var(--ph-sc-mid); }
.sc-near { fill: var(--ph-sc-near); }
.sc-win { fill: var(--ph-sc-win); }
.sc-tower { fill: var(--ph-sc-tower); }
.sc-tower-hi { stroke: var(--ph-sc-tower-hi); stroke-width: .8; }
.sc-tree1 { fill: var(--ph-sc-tree1); }
.sc-tree2 { fill: var(--ph-sc-tree2); }
/* 地平線那道弧：跟頁面同一個底色，城市像是從頁面後面升起來的 */
.sc-wave { fill: var(--bg); }

/* 問候那一塊在上，主按鈕在剩下的空間裡**上下置中**（.ph-actwrap 的 margin: auto）。
   padding-bottom 把地平線那一段留出來——主按鈕是在「地平線以上」置中，
   不然圓鈕的下緣會壓在樹上。矮螢幕上這段留白跟著收。 */
.ph-heroin {
  position: relative; z-index: 1; flex: 1 1 auto; display: flex; flex-direction: column;
  padding-bottom: 4px;
  padding-bottom: clamp(4px, 8dvh - 40px, 26px);
}

/* ---------------------------------------------------------------- 問候區：頭像、問候、時間
   兩種排法，看螢幕高度：
     ・不夠高：頭像｜問候｜時間（靠右），一列解決
     ・夠高（772px 以上）：頭像｜問候＋一句話；下面一列是日期與大時鐘（設計稿的樣子）
   用 grid 換位置，節點順序不動。 */
.ph-who {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center; column-gap: 11px; margin: 0 0 12px;
}
.ph-ava {
  position: relative; overflow: hidden;
  width: 48px; height: 48px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 21px; font-weight: 700; color: var(--ph-ava-tx); user-select: none;
  background: var(--ph-ava); border: 2.5px solid var(--ph-ava-ring);
  box-shadow: 0 4px 12px rgba(20, 50, 110, .22);
}
/* 底下墊名字的第一個字（data-initial，punch2.js 寫的），上面蓋 LINE 的大頭貼。
   大頭貼還沒到、載入失敗、或不是從 LINE 進來（開發模式）時，看到的是那個字——
   空著的圓圈看起來像載入失敗。 */
.ph-ava::before { content: attr(data-initial); }
/* 要有底色：去背的大頭貼（透明 PNG）底下那個字會透出來 */
.ph-ava img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block; background: var(--ph-ava);
}
.ph-whotx { min-width: 0; }
/* 頭像旁那一行是「名字，問候」（punch2.js 的 syncGreet 寫的）。
   index.js 寫在 #greet 的狀態（「在公司」「今日已完成」）不顯示：
   狀態主按鈕本身就說了（打勾的圓＝今日已完成），同一件事不講兩次。 */
.ph-whotx .greet { display: none; }
.ph-hello {
  display: flex; align-items: center; gap: 6px; min-width: 0;
  font-size: 1.1rem; font-weight: 800; color: var(--ph-hero-ink);   /* 壓在天空上：夜裡要淺 */
  line-height: 1.25; margin: 0; white-space: nowrap;
}
/* 問候後面的小太陽／月亮（punch2.js 依時段掛 data-tod） */
.ph-whotx[data-tod] .ph-hello::after {
  content: ''; flex: none; width: 19px; height: 19px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='4.6' fill='%23ffb62e'/%3E%3Cg stroke='%23ffb62e' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M12 2.2v2.6M12 19.2v2.6M2.2 12h2.6M19.2 12h2.6M5 5l1.9 1.9M17.1 17.1L19 19M19 5l-1.9 1.9M6.9 17.1L5 19'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat;
}
.ph-whotx[data-tod="night"] .ph-hello::after { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ffd978'%3E%3Cpath d='M20.2 14.6A8.4 8.4 0 0 1 9.4 3.8a8.4 8.4 0 1 0 10.8 10.8z'/%3E%3C/svg%3E"); }
/* 問候語下面那一句。只有夠高的螢幕才放得下（見下面的 min-height） */
.ph-sub {
  display: none; margin: 3px 0 0; font-size: .78rem; color: var(--ph-hero-sub);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ph-time { text-align: right; }
.ph-date { font-size: .76rem; line-height: 1.2; color: var(--ph-hero-sub); }
.ph-body .clock {
  margin: 2px 0 0;
  font-family: "SF Pro Rounded", "SF Pro Display", Roboto, "Segoe UI", system-ui, sans-serif;
  font-size: 2.5rem; font-weight: 900; line-height: 1; letter-spacing: -1px;
  color: var(--ph-clock); font-variant-numeric: tabular-nums;
}
/* 收班之後時鐘退到背景（index.js 掛 .hero.done）。
   原本的做法是把它縮小、變細——版面會跳一下，而且細字在天空上幾乎看不見。
   這裡只讓它變淡，大小粗細不動。 */
.ph-body .hero.done .clock { font-size: 2.5rem; font-weight: 900; opacity: .4; }


@media (min-height: 772px) {
  .ph-who { grid-template-columns: auto minmax(0, 1fr); row-gap: 12px; }
  .ph-time { grid-column: 1 / -1; text-align: left; }
  .ph-date { font-size: .88rem; }
  .ph-body .clock,
  .ph-body .hero.done .clock { font-size: 3.2rem; letter-spacing: -1.5px; }
  .ph-sub:not(:empty) { display: block; }
}
@media (min-height: 824px) {
  .ph-body .clock,
  .ph-body .hero.done .clock { font-size: 3.5rem; }
}

/* 定位膠囊在這一版**不顯示**。
   它講的事（在哪、多遠、在不在範圍內）下面的地點卡全部都有，
   而且那張卡還有地圖跟「重新定位」——同一件事在一屏之內講兩次，
   只會讓人去對兩邊的數字對不對得起來。

   ⚠ 只能藏，不能把節點拿掉：index.js 的 locPill() 每次定位都會寫它，
     punch2.js 的 syncSite() 則是讀它的字與狀態去畫地點卡。
     拿掉節點的話前者會丟例外，整頁停在定位畫面。 */
.ph-pillrow { display: none; }

/* ---------------------------------------------------------------- 主按鈕
   ================================================================
   每一種狀態都是同一個語彙：圓的是打卡，小的是次要
   ================================================================
   index.js 照舊把按鈕畫進 #actionArea 與 #moves，這裡不改它畫什麼，
   只看「畫了幾顆」決定怎麼擺。幾顆由 punch2.js 數好、掛 class 在 .ph-actwrap 上：

     .ph-n1     一顆主按鈕              → 一個大圓
     .ph-n2     並排的兩顆              → 兩個中圓（兩種班別、回公司／回公司加班）
     .ph-done   今天收班了              → 一個不能按的大圓（打勾）
     .ph-flank  大圓的左右各一顆小圓    → 下班打卡／直接外出 ｜ 其他類型
     .ph-pills  兩個中圓下面的一排小膠囊
     .ph-open   全部卡別的底部面板

   都沒掛（三顆以上，例如要先補收昨天的班）就是一般的直排方塊鈕。

   ⚠ 不用結構式的選擇器去數有幾顆。數得出來，但 index.js 有時把按鈕包在 .row 裡、
     有時沒有，那種選擇器會漏掉其中一種——而漏掉的那一種不會報錯，
     只是「早上的上班鈕是方的、下午的外出鈕是圓的」。

   ⚠ 這一區的高度在 n1／n2／done 三種狀態下要差不多。
     不一樣高的話，打完卡的那一瞬間下面的卡片會跳一下。 */
/* .ph-actgroup：地點名稱（圈外才有）＋按鈕＋訊息，一起在問候區剩下的空間裡上下置中 */
.ph-actgroup { display: flex; flex-direction: column; margin: auto 0; }
.ph-actwrap { position: relative; display: flex; flex-direction: column; }
.ph-act { display: flex; flex-direction: column; align-items: center; }
.ph-act .row { display: flex; gap: 10px; width: 100%; margin: 0 !important; }   /* index.js 在 .row 上寫了 inline 的 margin-top */
.ph-act .row .bigbtn { flex: 1; }
.ph-body .ph-actwrap .moves { margin: 10px 0 0; }
/* 次要的那幾顆疊在插畫上，要有底色；圖示（punch2.js 塞的）跟字排在一起 */
.ph-actwrap .mv-row button { background: var(--ph-chip); }
.ph-actwrap .mv-row button,
.ph-act .awaybtn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
.ph-fico { flex: none; display: flex; width: 18px; height: 18px; color: var(--ph-accent); }
.ph-fico .ico { width: 100%; height: 100%; display: flex; }
.ph-fico svg { width: 100%; height: 100%; display: block; }

/* 說明文字不顯示（「您已於 16:51 下班。按上面任一顆…」那一類）。
   它講的是系統內部怎麼記帳，不是人要做的決定；真的會改到紀錄的那一顆
   （回公司加班）按下去另外有確認視窗，那裡才是講這件事的地方。 */
.ph-act p.hint { display: none; }
/* 例外：下班後的「回公司」會把下班卡改記成外出，按之前要看得到（那顆按下去沒有確認視窗，2026-10-13 模擬） */
.ph-act p.hint.keep { display: block; text-align: center; color: var(--ph-hero-ink); opacity: .85; font-size: .85rem; }
/* 已經離開上一張卡的地點：主按鈕的小字兩行——地點名稱（放不下就…）／距離（第十五輪 simZ：一行會斷字、截在奇怪的地方） */
.ph-act .bigbtn small.lp-sub { display: flex; flex-direction: column; align-items: center; gap: 1px; max-width: 86%; white-space: normal; }
.ph-act .bigbtn small.lp-sub > span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }


/* ---- 圓鈕共用：漸層實心、白字、內圈白邊、外圈光暈 ----
   三種顏色＝三種意思：綠藍是開始（上班）、橘是結束（下班）、藍是移動（外出、回來）。
   ⚠ 光暈的顏色放在 --ph-ring（R, G, B 三個數字），各卡別自己蓋掉。
     這樣陰影只要寫一條，不必每種顏色各抄一次。 */
.ph-n1 .ph-act .bigbtn,
.ph-n2 .ph-act .bigbtn {
  flex: none; height: auto; aspect-ratio: 1; border-radius: 50%;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 0 10px; margin: 0; line-height: 1.25; text-align: center;
  color: #fff; border: 0; text-shadow: 0 1px 2px rgba(0, 30, 80, .22);
  background: linear-gradient(150deg, #62c8ff 0%, #3d93f2 52%, #4f6ef2 100%);
  box-shadow: inset 0 0 0 3px rgba(255, 255, 255, .5), inset 0 12px 20px rgba(255, 255, 255, .16),
    0 0 0 9px rgba(var(--ph-ring), .16), 0 16px 32px rgba(var(--ph-ring), .4);
}
.ph-n1 .ph-act .bigbtn.in,
.ph-n2 .ph-act .bigbtn.in {
  --ph-ring: 30, 170, 200;
  background: linear-gradient(150deg, #3ddcaa 0%, #20b5c9 50%, #1b82e6 100%);
}
.ph-n1 .ph-act .bigbtn.out,
.ph-n2 .ph-act .bigbtn.out {
  --ph-ring: 240, 110, 50;
  background: linear-gradient(150deg, #ffa648 0%, #f4712f 52%, #e2521f 100%);
}
.ph-n1 .ph-act .bigbtn:active,
.ph-n2 .ph-act .bigbtn:active { transform: scale(.97); }
.ph-n1 .ph-act .bigbtn small,
.ph-n2 .ph-act .bigbtn small { color: #fff; }
/* 圓鈕中央的圖示由 punch2.js 塞（不同卡別不同圖）：白色粗圈，裡面一個白色的圖 */
.ph-bico {
  flex: none; display: flex; align-items: center; justify-content: center;
  border-radius: 50%; border: 3px solid rgba(255, 255, 255, .95); color: #fff;
  background: rgba(255, 255, 255, .12);
}
.ph-bico svg { width: 100%; height: 100%; display: block; }

/* ---- 一顆：大圓 ---- */
.ph-n1 .ph-act .row { justify-content: center; }
.ph-n1 .ph-act .bigbtn,
.ph-done .ph-act .alldone {
  /* 50vw：窄一點的手機（360 寬）要留位置給左右兩顆小鈕。
     25dvh：矮一點的螢幕圓鈕要跟著小，不然下面三張卡會被擠出畫面。
     （不認得 dvh 的瀏覽器會丟掉第二行，用第一行。） */
  width: min(204px, 50vw);
  width: min(204px, 50vw, 25dvh);
}
.ph-n1 .ph-act .bigbtn { gap: 4px; font-size: 1.36rem; font-weight: 800; letter-spacing: .5px; }
.ph-n1 .ph-act .bigbtn small { font-size: .78rem; font-weight: 500; opacity: .95; margin: 0; letter-spacing: 0; }
.ph-n1 .ph-bico { width: 50px; height: 50px; margin-bottom: 5px; }
.ph-n1 .ph-bico .ico { width: 26px; height: 26px; }

/* ---- 兩顆：兩個中圓 ---- */
.ph-n2 .ph-act .row { justify-content: center; gap: 20px; }
.ph-n2 .ph-act .bigbtn {
  width: min(150px, 41vw);
  width: min(150px, 41vw, 19dvh);
  padding: 0 6px; gap: 2px; font-size: 1.06rem; font-weight: 800;
  box-shadow: inset 0 0 0 2.5px rgba(255, 255, 255, .5), inset 0 10px 16px rgba(255, 255, 255, .16),
    0 0 0 7px rgba(var(--ph-ring), .16), 0 12px 24px rgba(var(--ph-ring), .38);
}
/* 小字要一行放得下。矮螢幕上圓只剩一百二十幾 px，「拿個東西、交代一下」九個字
   用大一點的字級會剩一個「下」字掉到第二行。 */
.ph-n2 .ph-act .bigbtn small { font-size: .64rem; font-weight: 500; opacity: .95; margin: 0; white-space: nowrap; }
.ph-n2 .ph-bico { width: 36px; height: 36px; margin-bottom: 3px; border-width: 2.5px; }
.ph-n2 .ph-bico .ico { width: 19px; height: 19px; }

/* ---- 今天收班了：一個不能按的圓 ---- */
.ph-done .ph-act .alldone {
  aspect-ratio: 1; border-radius: 50%; margin: 0; padding: 0 10px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  /* 「今日上下班打卡已完成」十個字要在一行裡 */
  text-align: center; font-size: .86rem; font-weight: 700; line-height: 1.4; white-space: nowrap;
  color: var(--green); background: var(--ph-card);
  border: 2px solid var(--ph-done-ring);
  box-shadow: 0 0 0 9px var(--ph-done-halo), var(--ph-card-shadow);
}
/* 打勾。用邊框畫，不用字元（✓ 在不同手機上粗細、位置都不一樣） */
.ph-done .ph-act .alldone::before {
  content: ''; width: 20px; height: 36px; margin-top: -8px;
  border: solid currentColor; border-width: 0 4px 4px 0; border-radius: 2px;
  transform: rotate(45deg);
}

/* ---- 大圓左右的小圓 ----
   ⚠ 是**疊**上去的（absolute），不是跟大圓排成一列。
     排成一列的話三個東西要搶同一個寬度，窄一點的手機大圓會被擠扁；
     疊上去則是大圓永遠置中，兩顆小圓各自貼著左右邊。 */
/* ⚠ 這一層只蓋住圓鈕那一段高度（從上緣起算一個圓鈕的高度），不是整個按鈕區。
     圓鈕下面有時還有東西——已宣告「直接外出」時的宣告卡——蓋住整個按鈕區的話，
     兩顆小鈕會對齊「圓鈕＋宣告卡」的中間，而不是圓鈕的中間。
     高度跟大圓同一個算式（兩行：不認得 dvh 的瀏覽器用第一行）。 */
.ph-actwrap.ph-flank .moves {
  position: absolute; inset: 0 0 auto 0; margin: 0;
  height: min(204px, 50vw);
  height: min(204px, 50vw, 25dvh);
  display: flex; align-items: center; justify-content: space-between;
  pointer-events: none;            /* 這一層蓋住整個主按鈕，不能吃掉它的點擊 */
}
.ph-actwrap.ph-flank .mv-row { display: contents; }
.ph-body .ph-actwrap.ph-flank .mv-row > button,
.ph-body .ph-actwrap.ph-flank .awaybtn {
  pointer-events: auto;
  flex: none; width: 68px; min-width: 0; height: 68px; margin: 0; padding: 0 2px;
  border-radius: 50%;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  text-align: center; white-space: nowrap;
  font-size: .68rem; font-weight: 600; line-height: 1.2;
  color: var(--head); background: var(--ph-chip); border: 1px solid var(--ph-chip-line);
  box-shadow: 0 6px 16px rgba(20, 50, 110, .16);
}
.ph-flank .ph-fico { width: 21px; height: 21px; }
/* 「直接外出」畫在 #actionArea 裡（不在 #moves），所以另外定位到左邊那一格。
   它只在還沒上班時出現，那時候左邊剛好是空的。 */
.ph-body .ph-actwrap.ph-flank .awaybtn {
  position: absolute; left: 0; transform: translateY(-50%);
  top: calc(min(204px, 50vw) / 2);
  top: calc(min(204px, 50vw, 25dvh) / 2);
}
/* 「其他類型」不是打卡動作，只是展開清單——字用次要色，
   不要跟旁邊會寫紀錄的按鈕看起來同級。
   margin-left: auto：右邊永遠是它。只有它一顆的時候也要靠右
   （左邊那一格留給「直接外出」）。
   ⚠ 這一條要排在上面那條 margin: 0 的後面——兩條權重一樣，寫在前面會被蓋掉，
     而它被蓋掉的樣子是「跑到左邊、把直接外出整顆蓋住」。 */
.ph-body .ph-actwrap.ph-flank .mv-row > button.more {
  margin-left: auto; color: var(--sub);
}
.ph-flank .more .ph-fico { color: var(--sub); }
.ph-body .ph-actwrap.ph-flank .mv-row > button:active { transform: scale(.95); }
.ph-body .ph-actwrap.ph-flank .awaybtn:active { transform: translateY(-50%) scale(.95); }

/* ---- 兩個中圓下面的一排小膠囊 ----
   「直接外出」在 #actionArea、「其他類型」在 #moves，是兩個容器。
   把容器攤平（display: contents），兩顆才排得進同一列。 */
.ph-actwrap.ph-pills { flex-direction: row; flex-wrap: wrap; justify-content: center; align-items: center; gap: 10px 8px; }
.ph-actwrap.ph-pills .ph-act,
.ph-actwrap.ph-pills .moves,
.ph-actwrap.ph-pills .mv-row { display: contents; }
.ph-actwrap.ph-pills .ph-act .row { flex: 0 0 100%; width: 100%; }
.ph-body .ph-actwrap.ph-pills .mv-row > button,
.ph-body .ph-actwrap.ph-pills .awaybtn {
  flex: none; width: auto; min-width: 0; margin: 0; padding: 6px 14px 6px 11px;
  border-radius: 999px; font-size: .78rem; font-weight: 600; line-height: 1.4;
  color: var(--head); background: var(--ph-chip); border: 1px solid var(--ph-chip-line);
  box-shadow: 0 4px 12px rgba(20, 50, 110, .12);
}
.ph-pills .ph-fico { width: 16px; height: 16px; }

/* ---- 圓鈕下面那一行說明（免打卡人員的「此鈕只用來開公司大門」）----
   index.js 寫的是一個 inline style 的 <div class="muted">。疊在插畫上，
   原本的灰字幾乎看不見——給它一個膠囊底色。 */
.ph-act > div.muted {
  margin: 14px auto 0 !important; padding: 6px 13px; max-width: 300px; text-align: center;
  font-size: .78rem !important; line-height: 1.5; color: var(--sub);
  border-radius: 999px; background: var(--ph-card); border: 1px solid var(--ph-card-line);
}
/* 已宣告「直接外出」的宣告卡：跟下面的卡片同一套外觀 */
.ph-body .ph-act .awaycard {
  border-radius: 16px; border: 1px solid var(--ph-card-line); box-shadow: var(--ph-card-shadow);
}

/* ---- 其餘（三顆以上的直排方塊鈕）：次要的那幾顆也收成膠囊，不要整列寬 ---- */
.ph-body .ph-act .awaybtn { width: auto; margin: 10px auto 0; padding: 6px 15px; border-radius: 999px; font-size: .78rem; }
.ph-act .awaycard { width: 100%; box-sizing: border-box; margin-top: 10px; background: var(--ph-card); }
/* 宣告卡兩行（2026-10-15 使用者：原本五行太高，地圖被擠出畫面）：上面一行地點＋撤回，下面一行時間 */
.ph-body .ph-act .awaycard { padding: 9px 12px 10px; }
.ph-act .awaycard .aw-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.ph-act .awaycard .ttl { min-width: 0; font-size: .88rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ph-act .awaycard .ttl b { font-weight: 800; }
.ph-act .awaycard .ln { margin-top: 3px; font-size: .78rem; }
.ph-act .awaycard .rv { flex: none; width: auto; margin: 0; min-height: 32px; padding: 0 12px; font-size: .78rem; border-radius: 999px; }
/* 兩個中圓（工地上班／已抵達）時，「其他類型」排在宣告卡上面，緊跟著按鈕（2026-10-15 使用者） */
.ph-actwrap.ph-pills .awaycard { order: 1; }

/* ---- 主按鈕上方：圈外要填的地點名稱 ----
   疊在插畫上，所以要有自己的底色；橘色的字是因為它是「不填就打不了卡」的必填。 */
.ph-body #adhocBox {
  margin: 0 0 14px; padding: 11px 12px 12px; text-align: left;
  border-radius: 16px; background: var(--ph-card); border: 1px solid var(--ph-card-line);
  box-shadow: var(--ph-card-shadow);
}
.ph-body #adhocBox.hide { display: none; }
.ph-body #adhocBox label { margin-bottom: 6px; font-weight: 700; }
.ph-body #adhocBox input { margin: 0; border-radius: 11px; }

/* ---- 主按鈕下方：打卡中／失敗的訊息 ----
   一行置中的膠囊，**浮在**按鈕正下方，不佔位置。
   ⚠ 2026-10-05 使用者回報「打卡按鈕後頁面會跳動」：原本它排在按鈕下面、佔一行，
     「📍 打卡中…」一出現，整組按鈕（在剩下的空間裡上下置中）就往上跳 27px，
     打完卡訊息消失又跳回來。浮起來之後按鈕永遠不動。 */
.ph-body .ph-actgroup { position: relative; }
.ph-body #msg { position: absolute; left: 0; right: 0; top: 100%; z-index: 5; margin: 0; pointer-events: none; }
.ph-body #msg .status { pointer-events: auto; }
.ph-body #msg:empty { display: none; }
.ph-body #msg .status {
  margin: 14px auto 0; padding: 8px 14px; max-width: 320px;
  border-radius: 14px; text-align: center; font-size: .86rem; line-height: 1.5;
  background: var(--ph-card); border: 1px solid var(--ph-card-line);
  box-shadow: 0 6px 16px rgba(20, 50, 110, .14);
}
.ph-body #msg .status.err { background: var(--ph-card); color: var(--red-ink); border-color: rgba(230, 70, 70, .35); }
.ph-body #msg .status.ok { background: var(--ph-card); color: var(--green-ink); border-color: rgba(47, 212, 143, .4); }

/* ---------------------------------------------------------------- 卡片 */
.ph-body .card,
.ph-body .statbar,
.ph-body .timeline:not(:empty) {
  border-radius: 20px; background: var(--ph-card); border: 1px solid var(--ph-card-line);
  box-shadow: var(--ph-card-shadow);
}
.ph-body .card { margin: 0 16px 10px; padding: 13px; }

/* ---- 地點卡 ---- */
.ph-siterow { display: flex; align-items: center; gap: 11px; }
.ph-sico {
  flex: none; width: 44px; height: 44px; border-radius: 14px;
  display: flex; align-items: center; justify-content: center;
  background: var(--ph-tile); color: var(--ph-accent);
}
.ph-sico.cal { color: #8b6cf0; background: var(--ph-tile2); }
.ph-sico .ico { width: 23px; height: 23px; }
.ph-sico .ico svg { width: 100%; height: 100%; display: block; }
.ph-sitetx { flex: 1; min-width: 0; }
.ph-sitetx b {
  display: block; font-size: 1.02rem; font-weight: 800; color: var(--head);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* ⚠ 這一行不可以換行。換行會讓整張卡多出一行的高度，
   而這一頁每一塊的高度都是算好的——多一行，矮一點的手機就要捲了。 */
.ph-sitetx small {
  display: flex; align-items: center; gap: 4px;
  margin-top: 3px; font-size: .8rem; color: var(--sub);
  white-space: nowrap; overflow: hidden;
}
.ph-sitetx small:empty { display: none; }
/* 前面那個圖釘跟著字的顏色走（遮罩＋currentColor）。
   在不在範圍內：顏色是結論不是裝飾——綠的就是「這裡可以打卡」，橘的就是「不行」。
   狀態 class 由 punch2.js 照定位膠囊的狀態掛上來，這裡不自己判斷。 */
.ph-sitetx small::before {
  content: ''; flex: none; width: 14px; height: 14px; background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000'%3E%3Cpath fill-rule='evenodd' d='M12 2a7 7 0 0 0-7 7c0 5 7 13 7 13s7-8 7-13a7 7 0 0 0-7-7zm0 9.6a2.6 2.6 0 1 1 0-5.2 2.6 2.6 0 0 1 0 5.2z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000'%3E%3Cpath fill-rule='evenodd' d='M12 2a7 7 0 0 0-7 7c0 5 7 13 7 13s7-8 7-13a7 7 0 0 0-7-7zm0 9.6a2.6 2.6 0 1 1 0-5.2 2.6 2.6 0 0 1 0 5.2z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.ph-site.ph-ok .ph-sitetx small { color: var(--map-ok); }
.ph-site.ph-warn .ph-sitetx small { color: var(--map-warn); }
/* 不在任何登記地點時不顯示地圖。
   那時候主按鈕上方會多一格「目前所在地點名稱（必填）」，兩者的高度剛好抵掉，
   一頁還是放得下；而離登記地點那麼遠，地圖本來也只剩一片空白跟一個點。 */
.ph-site.ph-nomap .mapband { display: none; }
.ph-site.ph-checking .ph-sitetx small::before { animation: pipPulse 1.1s ease-in-out infinite; }

.ph-reloc {
  flex: none; display: flex; align-items: center; gap: 5px;
  width: auto; margin: 0; padding: 8px 12px;
  border-radius: 13px; font-size: .84rem; font-weight: 600; cursor: pointer;
  background: var(--ph-chip); color: var(--ph-accent); border: 1.5px solid var(--ph-tile);
}
.ph-reloc .ico { width: 17px; height: 17px; }
.ph-reloc .ico svg { width: 100%; height: 100%; display: block; }
.ph-reloc:active { background: var(--ph-tile); }

/* 地圖是一小條，不伸縮（為什麼：見檔頭「一般狀態要剛好一頁」）。
   ⚠ height 要明寫：index.css 給 .mapband 的是 clamp(200px, 35vh, 320px)，
     不蓋掉的話它就是那個高度。 */
.ph-site .mapband {
  height: 84px;
  height: clamp(72px, 11.5dvh, 104px);
  margin: 11px 0 0; border-radius: 14px; overflow: hidden;
  position: relative; background: var(--panel2);
}
/* 畫布 absolute：它有 150px 的固有高度，留在文件流裡會把這一小條撐開 */
.ph-site .mapband canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

/* ---- 班制那一張（還沒上班時才有）----
   ⚠ 跟「三格統計」一樣高（--ph-stat-h），而且「今日歷程」那一列在還沒打卡時也佔著位置
     （見下面的 .timeline:empty）。打第一張卡時，這張換成三格統計、歷程從 0 筆變 1 筆——
     兩邊不一樣高的話，下面整片會換位置、主按鈕跟著往上跳（2026-10-05 使用者回報的跳動，
     量到的是 20～40px）。 */
.ph-shift { display: flex; align-items: center; gap: 11px; }
.ph-body .ph-shift,
.ph-body .statbar { box-sizing: border-box; min-height: var(--ph-stat-h); }
.ph-body .ph-shift { padding-top: 9px; padding-bottom: 9px; }
@media (min-height: 824px) { :root { --ph-stat-h: 98px; } }   /* 高螢幕的三格統計多了一排小圓圖示 */
/* 還沒有打卡紀錄時，「今日歷程」那一列照樣佔著（同一張卡、同一個高度），寫「今日 0 筆」。 */
.ph-body .timeline:empty {
  display: flex; align-items: center; gap: 9px; box-sizing: border-box; height: 48px; padding: 0 13px;
  border-radius: 20px; background: var(--ph-card); border: 1px solid var(--ph-card-line); box-shadow: var(--ph-card-shadow);
}
.ph-body .timeline:empty::before {
  content: ''; flex: none; width: 28px; height: 28px; border-radius: 9px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%233b8ef0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='5' y='3.5' width='14' height='17' rx='3'/%3E%3Cpath d='M9 8.5h6M9 12h6M9 15.5h3.5'/%3E%3C/svg%3E") center / 16px no-repeat, rgba(59, 142, 240, .16);
}
.ph-body .timeline:empty::after { content: '今日 0 筆'; font-size: .84rem; font-weight: 700; color: var(--muted); }
/* ⚠ 不要用屬性選擇器去比對 .statline 的 inline style 來判斷它收起來了沒：
   inline style 的序列化格式不保證（有沒有空格、會不會被改寫），比對不到也不會報錯。
   由 punch2.js 掛 class，看得見也測得到。 */
.ph-shift.ph-off { display: none; }
.ph-body .statline {
  flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px;
  margin: 0; border: 0; padding: 0;
}
.ph-body .statline > span:first-child { font-size: 1.02rem; color: var(--head); font-weight: 800; }
.ph-body .statline > span:last-child { font-size: .84rem; color: var(--sub); }

/* ---- 三格統計 ---- */
.ph-body .statbar {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0; overflow: visible;   /* minmax(0,…)：長標籤（「已工作・17:00 起算」）不能把格子撐開、320 寬整頁橫捲（2026-10-13 模擬） */
  margin: 0 16px 10px; padding: 12px 6px;
}
/* ⚠ 收起來的時候要真的收起來。
   上面那條 display: grid 的權重跟 index.css 的 .statbar.hide { display: none } 一樣，
   而這個檔載入得比較晚——不補這一條的話，還沒上班時（三格是空的）
   畫面上會多出一張空白的卡片，還把一頁撐破。 */
.ph-body .statbar.hide { display: none; }
/* 原本的樣式每一格有自己的底色（index.css 的 --panel2），
   放進這張卡裡會變成「卡片裡面又一個框」。這裡把格子的底拿掉，只留中間的分隔線。 */
.ph-body .statbar > div {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  text-align: center; padding: 0 4px; background: transparent;
}
.ph-body .statbar > div + div { border-left: 1px solid var(--line); }
.ph-body .statbar span { font-size: .72rem; color: var(--sub); line-height: 1.25; overflow-wrap: anywhere; }
/* ⚠ 這裡**不可以**指定 color。
   index.css 用 b.late（遲到，紅）與 b.lead（已工作，綠）著色，
   那兩條的權重跟這一條一樣——這個檔載入得比較晚，寫了 color 就會把它們蓋掉，
   遲到的人看到的會是一個跟大家一樣顏色的數字。 */
.ph-body .statbar b { font-size: 1.14rem; font-variant-numeric: tabular-nums; }
/* 每一格上面的小圓圖示：班別（月曆）、上班（時鐘）、工時（長條）。
   只有夠高的螢幕才放——它多出二十幾 px 的高度，矮螢幕上會把一頁撐破。
   放在字的上面而不是左邊：第一格的標籤是「班別 · 08:30–17:30」，放左邊會被擠到截斷。 */
@media (min-height: 824px) {
  .ph-body .statbar > div::before {
    content: ''; width: 28px; height: 28px; border-radius: 50%; margin-bottom: 1px;
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%233b8ef0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='5' width='17' height='15.5' rx='3'/%3E%3Cpath d='M3.5 10h17M8 3v4M16 3v4'/%3E%3C/svg%3E") center / 16px no-repeat, rgba(59, 142, 240, .16);
  }
  .ph-body .statbar > div:nth-child(2)::before { background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2322b07d' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='8.5'/%3E%3Cpath d='M12 7.5V12l3 2'/%3E%3C/svg%3E") center / 16px no-repeat, rgba(34, 176, 125, .16); }
  .ph-body .statbar > div:nth-child(3)::before { background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238b6cf0' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6.5 19v-7M12 19V6M17.5 19v-4.5'/%3E%3C/svg%3E") center / 16px no-repeat, rgba(139, 108, 240, .18); }
}

/* ---- 其餘沿用原本的樣式，只調外距讓它們跟卡片對齊 ---- */
.ph-body .timeline,
.ph-body .moves,
.ph-body .otplan,
.ph-body .site-pick { margin-left: 16px; margin-right: 16px; }
.ph-body .timeline { margin-top: 0; margin-bottom: 12px; }

/* ---- 今日歷程：標題列＋展開後的資料列，同一張卡 ----
   原本卡片是畫在標題列（.tl-h）上，展開之後資料列直接排在頁面底色上——
   看起來像卡片外面掉出一串字。現在卡片畫在整個 #today 上（空的時候不畫），
   標題列只是卡片的第一列。 */
.ph-body .timeline:not(:empty) { overflow: hidden; }
.ph-body .timeline .tl-h {
  padding: 9px 13px; gap: 9px;
  background: transparent; border: 0; border-radius: 0; box-shadow: none;
}
/* 資料列內縮到跟標題的字對齊（13px 外距＋28px 圖示＋9px 間距＝50px），
   跟標題之間一條細線。 */
.ph-body .timeline .tl-body { margin: 0 14px 0 50px; padding: 2px 0 6px; border-top: 1px solid var(--line); }
.ph-body .timeline .tl-body > div:not(.tl-more) { padding: 8px 0; gap: 8px; font-size: .84rem; }
.ph-body .timeline .tl-body > div + div { border-top: 1px dashed var(--line); }
.ph-body .timeline .tl-more { padding: 8px 0 4px; font-size: .78rem; }
.ph-body .timeline .tl-more a { color: var(--ph-accent); text-decoration: none; font-weight: 600; }
/* 時間那一欄等寬，上下幾列的時間才會對齊 */
.ph-body .timeline .tl-body b { min-width: 40px; }
/* 撤回鈕：小膠囊，靠右 */
.ph-body .timeline .rv {
  flex: none; width: auto; margin: 0 0 0 auto; padding: 3px 10px;
  border-radius: 999px; font-size: .74rem; font-weight: 600;
  color: var(--red-ink); background: transparent; border: 1px solid rgba(230, 70, 70, .35);
}
.ph-body .timeline .tl-h::before {
  content: ''; flex: none; width: 28px; height: 28px; border-radius: 9px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%233b8ef0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='5' y='3.5' width='14' height='17' rx='3'/%3E%3Cpath d='M9 8.5h6M9 12h6M9 15.5h3.5'/%3E%3C/svg%3E") center / 16px no-repeat, rgba(59, 142, 240, .16);
}
.ph-body .tl-h .tl-n { font-size: .84rem; font-weight: 700; color: var(--head); }
/* 箭頭用邊框畫（原本是「▸」這個字元，每支手機畫出來的大小粗細都不一樣） */
.ph-body .tl-h .tl-chev {
  /* ⚠ flex: none 要明寫。index.css 有一條 .timeline span:last-of-type { flex: 1 }，
     權重比 .tl-h .tl-chev 高，會把這個箭頭拉成整列寬——用字元畫的時候看不出來，
     改用邊框畫之後就是一條橫過整列的斜線。 */
  flex: none; font-size: 0; width: 8px; height: 8px; margin: 0 3px 0 auto;
  border: solid var(--muted); border-width: 2px 2px 0 0;
  transform: rotate(45deg);
}
.ph-body .tl-h.open .tl-chev { transform: rotate(135deg); }

/* ---------------------------------------------------------------- 底部五個入口
   一張浮起來的圓角卡片（跟上面的卡片同一個語彙），裡面五顆：
   有底色的小方塊圖示＋底下一行字，一顆一個顏色。

   為什麼不做成「分頁列」的樣子（一條貼底的橫條、目前這一頁亮起來）：
   這五顆是去**別頁**的捷徑，打卡頁自己不在裡面——沒有哪一顆該亮。
   做成分頁列，人會去找「我現在在哪一顆」，而答案是都不是。
   顏色的用途是認位置：每天按的是同一顆，認顏色比讀字快。

   ⚠ 選擇器要帶 #shortcuts。index.css 有一條 #shortcuts { position: sticky; padding… }，
     id 的權重比 class 高——只寫 .ph-tabs 的話這裡的 position、z-index、padding 全部不生效，
     而它「看起來」還是釘在底部（內容比螢幕長的時候 sticky 跟 fixed 長得一樣），
     要等內容短到一頁放得下才會露餡：這一列跑到內容後面，底下空一截。 */
#shortcuts.ph-tabs {
  position: fixed; z-index: 40;
  left: 12px; right: 12px; bottom: calc(10px + env(safe-area-inset-bottom, 0px));
  max-width: 456px; margin: 0 auto;
  display: grid; grid-template-columns: repeat(5, 1fr); align-items: center;
  /* ⚠ 高度要明寫，而且跟 #main 的最小高度用同一個變數。
     #main 的最小高度是「螢幕高 − 這一列佔掉的高度」，這一列讓內容自己撐的話，
     兩邊的數字對不起來，頁面就會多出幾 px 可以捲。 */
  box-sizing: border-box;
  height: calc(var(--ph-tabs-h) - 10px);
  padding: 0 6px;
  border-radius: 22px; background: var(--ph-card); border: 1px solid var(--ph-card-line);
  box-shadow: 0 10px 28px rgba(20, 50, 110, .16);
}
/* 這一列是浮著的，左右跟下面有空隙。展開今日歷程之後內容會捲到它後面，
   從空隙露出半截字很亂——在它後面墊一層往上淡出的底色。
   ⚠ 墊在 #main 上（定位畫面還在的時候 #main 是藏著的，這一層就不會出現），
     不是這一列自己的偽元素：自己的偽元素就算 z-index 是負的，
     也會畫在自己的底色**上面**，整張卡會被那層漸層蓋掉。 */
.ph-body #main::after {
  content: ''; position: fixed; z-index: 39; left: 0; right: 0; bottom: 0; pointer-events: none;
  height: calc(var(--ph-tabs-h) + env(safe-area-inset-bottom, 0px) + 12px);
  background: linear-gradient(to bottom, transparent 0, var(--bg) 46%);
}
.ph-tabs a,
#locShortcutGrid > a {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 0 2px; background: none; border: 0; text-decoration: none;
  font-size: .66rem; font-weight: 600; line-height: 1.2; letter-spacing: .2px; color: var(--sub);
}
/* 小方塊圖示：底色是那一顆的顏色調淡，圖示是那一顆的顏色 */
.ph-tabs .ico,
#locShortcutGrid .ico {
  width: 32px; height: 32px; border-radius: 11px; margin: 0; opacity: 1;
  display: flex; align-items: center; justify-content: center;
  color: rgb(var(--ph-tab)); background: rgba(var(--ph-tab), .15);
}
.ph-tabs .ico svg,
#locShortcutGrid .ico svg { width: 19px; height: 19px; display: block; }
.ph-tabs a:active .ico,
#locShortcutGrid > a:active .ico { transform: scale(.92); background: rgba(var(--ph-tab), .28); }
/* 一顆一個顏色。用 href 認，不用「第幾顆」：
   「人員動態」是 index.js 載入之後才插到最前面的，用第幾顆去數會在它插進來的前後各錯一次。 */
/* 載入畫面那一排也用同一組顏色。不寫成 :is(…)：較舊的 WebView 不認得，整條會被丟掉 */
.ph-tabs a[href^="staff"], #locShortcutGrid > a[href^="staff"] { --ph-tab: 59, 142, 240; }        /* 人員動態：藍 */
.ph-tabs a[href^="vendor"], #locShortcutGrid > a[href^="vendor"] { --ph-tab: 240, 138, 36; }     /* 廠商出工：橘 */
.ph-tabs a[href^="records"], #locShortcutGrid > a[href^="records"] { --ph-tab: 34, 176, 125; }   /* 我的紀錄：綠 */
.ph-tabs a[href^="makeup"], #locShortcutGrid > a[href^="makeup"] { --ph-tab: 139, 108, 240; }    /* 補打卡：紫 */
.ph-tabs a[href^="reminder"], #locShortcutGrid > a[href^="reminder"] { --ph-tab: 236, 84, 122; } /* 提醒設定：桃紅 */

/* 矮螢幕（680px 以下）：各塊之間的空隙收一點。
   底部那一列改成浮起來的卡片之後多佔了 12px，矮螢幕上剛好差那幾 px 就要捲。 */
@media (max-height: 680px) {
  .ph-hero { padding-top: 12px; padding-bottom: 10px; }
  .ph-body .card,
  .ph-body .statbar { margin-bottom: 7px; }
  .ph-body .timeline { margin-bottom: 8px; }
  /* 圈外時主按鈕上方那一格「目前所在地點名稱」 */
  .ph-body #adhocBox { margin-bottom: 8px; padding: 8px 10px 9px; }
  .ph-body #adhocBox label { margin-bottom: 4px; }
}

/* ---------------------------------------------------------------- 全部卡別：底部面板
   「其他類型」展開後的樣子。index.js 照舊把清單畫進 #moves，
   這裡只是把 #moves 從「排在主按鈕下面」改成「貼在螢幕底部」。

   ⚠ 沒有滑入的動態效果。這是工作用的工具，按了就要到；而且分頁在背景時
     動態效果的時間軸不會走，面板會卡在半路。 */
.ph-actwrap.ph-open::before {
  /* 遮罩。點它等於按「收合」（punch2.js 的 bindSheet） */
  content: ''; position: fixed; inset: 0; z-index: 1;
  background: rgba(6, 14, 34, .5);
}
.ph-body .ph-actwrap.ph-open .moves {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 2;
  max-width: 480px; margin: 0 auto;
  padding: 8px 16px calc(12px + env(safe-area-inset-bottom, 0px));
  max-height: 72vh; overflow-y: auto;
  background: var(--ph-card); border: 1px solid var(--ph-card-line); border-bottom: 0;
  border-radius: 24px 24px 0 0;
  box-shadow: 0 -12px 36px rgba(6, 14, 34, .3);
}
/* 頂端那一小條：告訴人這是一張可以收起來的面板 */
.ph-actwrap.ph-open .moves::before {
  content: ''; display: block; width: 38px; height: 4px; border-radius: 2px;
  margin: 2px auto 12px; background: var(--line-strong);
}
.ph-actwrap.ph-open .mv-h {
  margin: 0 2px 12px; font-size: .84rem; line-height: 1.5; color: var(--sub);
}
.ph-actwrap.ph-open .mv-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.ph-body .ph-actwrap.ph-open .mv-row > button {
  width: auto; min-width: 0; min-height: 52px; margin: 0; padding: 10px 8px; gap: 8px;
  border-radius: 15px; font-size: .96rem; font-weight: 700;
  color: var(--head); background: var(--panel2); border: 1px solid var(--line);
}
/* 「收合」不是打卡動作：整列寬、沒有框，跟上面會寫紀錄的按鈕分開 */
.ph-body .ph-actwrap.ph-open .mv-row > button.more {
  grid-column: 1 / -1; min-height: 44px;
  background: transparent; border-color: transparent;
  color: var(--sub); font-weight: 500;
}
/* 面板開著的時候：底下那一頁不捲動，面板要蓋過底部分頁列（它是 z-index 40） */
body.ph-sheet-open { overflow: hidden; }
body.ph-sheet-open .ph-heroin { z-index: 60; }


/* ================================================================ 載入（定位）畫面
   開打卡頁的第一個畫面：先取身分、再抓 GPS，一兩秒到十幾秒都有可能。
   跟打卡頁同一個場景——同一張插畫（問候區那段腳本複製過來的）、同一套元件——
   定位成功時只有前景（圈圈、下面那一排）放大淡出，插畫留著，接著出現的打卡頁是同一張。

   ⚠ index.js 會改的東西都沒動：#locRing 的 class（locating／ok／fail）、
     #locBtn 的字（載入中…／定位中…／✓／✕／重試）、#locStatus 的內容、
     #locRetryRow 與 #locSkip 的顯示、.loc-wrap 的 exit。這裡只換外觀。

   ⚠ 一頁放得下：body 底下留了底部那一列的高度（打卡頁要用），載入畫面沒有那一列，
     用負的下外距抵掉，不然載入畫面會多出七十幾 px 可以捲。 */
#locScreen {
  position: relative; overflow: hidden;
  min-height: 100vh;
  min-height: 100dvh;
  margin-bottom: calc(-1 * (var(--ph-tabs-h) + env(safe-area-inset-bottom, 0px)));
  display: flex; flex-direction: column;
}
/* 插畫佔上面大半；地平線那道弧落在下面那一排的上方 */
.ph-locart {
  position: absolute; left: 0; right: 0; top: 0; z-index: 0;
  height: calc(100% - 150px);
  background: linear-gradient(180deg, var(--ph-sky-a) 0%, var(--ph-sky-b) 48%, var(--bg) 100%);
}
.ph-locart .ph-tower { height: min(250px, 52%); }

.ph-body .loc-wrap {
  position: relative; z-index: 1; flex: 1 1 auto;
  min-height: 0; gap: 0; justify-content: flex-start;
  display: flex; flex-direction: column; align-items: stretch;
}
/* 圈圈、說明、重試：在插畫那一段裡上下置中 */
.ph-loctop {
  flex: 1 1 auto; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 14px; padding: 24px 20px 70px;
}

/* ---- 圈圈：跟打卡頁的主按鈕同一個語彙（白底圓、外圈光暈），轉的是外面那一圈 ---- */
.ph-body .loc-ring {
  --ph-ring: 70, 140, 245;
  width: 176px; height: 176px;
  box-shadow: 0 0 0 10px rgba(var(--ph-ring), .1), 0 16px 34px rgba(var(--ph-ring), .22);
}
.ph-body .loc-ring::before {
  inset: 0; border: 4px solid rgba(var(--ph-ring), .16);
}
.ph-body .loc-ring.locating::before { border-top-color: rgb(var(--ph-ring)); border-right-color: rgb(var(--ph-ring)); }
.ph-body .loc-ring.ok { --ph-ring: 34, 176, 125; }
.ph-body .loc-ring.ok::before { border-color: rgb(var(--ph-ring)); }
.ph-body .loc-ring.fail { --ph-ring: 230, 90, 70; }
.ph-body .loc-ring.fail::before { border-color: rgba(var(--ph-ring), .7); }
.ph-body .loc-btn {
  width: 148px; height: 148px; gap: 8px; flex-direction: column;
  font-size: 1.05rem; font-weight: 800; letter-spacing: 1px;
  color: rgb(var(--ph-ring)); background: var(--ph-card); border: 0;
  box-shadow: inset 0 0 0 1px var(--ph-card-line), 0 8px 20px rgba(20, 50, 110, .14);
}
/* 中間的圖釘（只在轉圈時；✓／✕／重試 那幾個字自己就夠清楚） */
.ph-body .loc-ring.locating .loc-btn::before {
  content: ''; width: 30px; height: 30px; background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000'%3E%3Cpath fill-rule='evenodd' d='M12 2a7 7 0 0 0-7 7c0 5 7 13 7 13s7-8 7-13a7 7 0 0 0-7-7zm0 9.6a2.6 2.6 0 1 1 0-5.2 2.6 2.6 0 0 1 0 5.2z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000'%3E%3Cpath fill-rule='evenodd' d='M12 2a7 7 0 0 0-7 7c0 5 7 13 7 13s7-8 7-13a7 7 0 0 0-7-7zm0 9.6a2.6 2.6 0 1 1 0-5.2 2.6 2.6 0 0 1 0 5.2z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.ph-body .loc-ring.fail .loc-btn { font-size: 1.5rem; }
.ph-body .loc-ring.ok .loc-btn { font-size: 2.8rem; font-weight: 700; }   /* 只有一個 ✓，要大到一眼看得到 */

/* ---- 說明、重試、略過 ---- */
.ph-body .loc-status { padding: 0; }
.ph-body .loc-status:empty { display: none; }
.ph-body .loc-status:not(:empty) {
  max-width: 320px; padding: 10px 16px; border-radius: 16px;
  font-size: .88rem; line-height: 1.6; color: var(--head);
  background: var(--ph-card); border: 1px solid var(--ph-card-line); box-shadow: var(--ph-card-shadow);
}
.ph-body #locRetryRow .btn-in {
  width: auto; margin: 0; padding: 11px 24px; border: 0; border-radius: 999px;
  font-size: .92rem; font-weight: 700; color: #fff;
  background: linear-gradient(150deg, #62c8ff 0%, #3d93f2 52%, #4f6ef2 100%);
  box-shadow: 0 8px 20px rgba(70, 140, 245, .35);
}
.ph-body .loc-skip {
  padding: 6px 14px; border-radius: 999px; font-size: .8rem; font-weight: 600; text-decoration: none;
  color: var(--sub); background: var(--ph-chip); border: 1px solid var(--ph-chip-line);
}

/* ---- 下面那一排：跟打卡頁底部那一列同一張卡 ---- */
.ph-body #locShortcuts {
  width: auto; max-width: 456px; margin: 0 12px calc(10px + env(safe-area-inset-bottom, 0px));
  align-self: stretch;
}
@media (min-width: 480px) { .ph-body #locShortcuts { margin-left: auto; margin-right: auto; width: 100%; } }
.ph-loclabel { margin: 0 0 8px; text-align: center; font-size: .74rem; color: var(--sub); }
.ph-body #locShortcutGrid {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 0; align-items: center;
  height: 62px; padding: 0 6px; box-sizing: border-box;
  border-radius: 22px; background: var(--ph-card); border: 1px solid var(--ph-card-line);
  box-shadow: 0 10px 28px rgba(20, 50, 110, .16);
}
.ph-body #locShortcutGrid > a { border: 0; border-radius: 0; background: none; letter-spacing: .2px; }

/* 矮螢幕：圈圈小一點 */
@media (max-height: 680px) {
  .ph-body .loc-ring { width: 150px; height: 150px; }
  .ph-body .loc-btn { width: 126px; height: 126px; }
  .ph-loctop { gap: 10px; padding-bottom: 60px; }
}


/* ================================================================ 打卡結果的彈窗
   App.toast() 畫的那一張（打卡成功、收班、取消預報…）。只在打卡頁換樣式，
   申請、簽核那些頁照舊。內容不動（index.js／app.js 決定寫什麼），這裡只換外觀：
     ・成功的那一張（✅）：表情符號換成畫出來的綠色勾勾徽章（punch2.js 掛 .ph-ok）
     ・時間放大成主角；打的是哪一種卡做成膠囊；地點前面一個圖釘 */
.ph-body .ptoast-mask { background: rgba(6, 14, 34, .45); }
.ph-body .ptoast {
  min-width: 264px; max-width: 320px; box-sizing: border-box; padding: 30px 26px 24px;
  border-radius: 26px; background: var(--ph-card); border: 1px solid var(--ph-card-line);
  box-shadow: 0 24px 60px rgba(6, 14, 34, .32);
}
.ph-body .ptoast .big { line-height: 1; }
.ph-body .ptoast.ph-ok .big {
  position: relative; width: 68px; height: 68px; margin: 0 auto; border-radius: 50%; font-size: 0;
  background: linear-gradient(150deg, #3ddcaa 0%, #1fb98a 100%);
  box-shadow: 0 0 0 8px rgba(34, 176, 125, .14), 0 10px 22px rgba(34, 176, 125, .32);
}
/* 勾勾用邊框畫（✓ 在不同手機上粗細、位置都不一樣） */
.ph-body .ptoast.ph-ok .big::after {
  content: ''; position: absolute; left: 25px; top: 15px; width: 15px; height: 28px;
  border: solid #fff; border-width: 0 5px 5px 0; border-radius: 2px; transform: rotate(45deg);
}
.ph-body .ptoast .title { margin: 16px 0 0; font-size: .95rem; font-weight: 800; letter-spacing: 1px; color: var(--green-ink); }
.ph-body .ptoast .time {
  margin-top: 2px; font-size: 2.8rem; font-weight: 900; line-height: 1.05; letter-spacing: -1px; color: var(--head);
  font-family: "SF Pro Rounded", "SF Pro Display", Roboto, "Segoe UI", system-ui, sans-serif;
}
.ph-body .ptoast .act {
  display: inline-block; margin-top: 10px; padding: 4px 13px; border-radius: 999px;
  font-size: .86rem; font-weight: 700; color: var(--ph-accent); background: var(--ph-tile);
}
.ph-body .ptoast .place {
  display: block; text-align: center;
  margin-top: 12px; font-size: .82rem; color: var(--sub);
}
/* 第一行（地點）之後的那幾行：大門已開、上班時間以…認定（2026-10-20：原本跟地點擠在同一塊、圖釘飄在最左邊） */
.ph-body .ptoast .place .pl-more { margin-top: 2px; font-size: .78rem; color: var(--muted); }
/* 圖釘只給打卡成功那一張（有卡別 .act、底下那行是地點）。撤回、收班的彈窗也用 .place 放說明，那不是地點（2026-10-08） */
/* 圖釘貼在地點那一行的開頭（不是整塊的左邊） */
.ph-body .ptoast .act ~ .place .pl::before {
  content: ''; display: inline-block; width: 13px; height: 13px; margin-right: 3px; vertical-align: -1px; background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000'%3E%3Cpath fill-rule='evenodd' d='M12 2a7 7 0 0 0-7 7c0 5 7 13 7 13s7-8 7-13a7 7 0 0 0-7-7zm0 9.6a2.6 2.6 0 1 1 0-5.2 2.6 2.6 0 0 1 0 5.2z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000'%3E%3Cpath fill-rule='evenodd' d='M12 2a7 7 0 0 0-7 7c0 5 7 13 7 13s7-8 7-13a7 7 0 0 0-7-7zm0 9.6a2.6 2.6 0 1 1 0-5.2 2.6 2.6 0 0 1 0 5.2z'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* 主畫面裡還在定位（2026-10-06：不再擋在入場畫面上等 GPS）。定位回來 index.js 會重畫成正確的那一顆。
   ⚠ 不灰掉（2026-10-06 使用者：「定位中沒有呼吸特效，可能會有人以為是卡住了就關掉了」）：
     灰掉的圓看起來像壞了。改成實心的圓，外圈一波一波往外擴散（像雷達在找），
     中間的定位點輕輕上下跳；底下的小字講進度（正在找 GPS 訊號 → 正在確認位置）。
     全站的 button:disabled 會把它調成 .4 的透明，這裡要蓋回 1。
     系統設「減少動態效果」的人不播動畫，靠小字的進度。
   ⚠ 顏色是紫色，跟定位好之後的任何一顆都不一樣（2026-10-06 使用者：「目前藍色切藍色，如果沒仔細看文字內容，
     會沒發現已經定位好了」）。不用橘色：橘色是「下班」那一顆，定位好之後要按下班的人又變成橘切橘。
     綠藍＝上班、橘＝下班、藍＝外出／回來，紫色只在「還在找位置」出現，換顏色就等於定位好了。 */
.ph-body .ph-act .bigbtn.locating {
  position: relative; opacity: 1; cursor: default; --ph-ring: 140, 82, 230;
  background: linear-gradient(150deg, #c79bff 0%, #9b62ef 52%, #7c44d9 100%);
  text-shadow: 0 1px 2px rgba(50, 10, 90, .25);
}
.ph-body .ph-act .bigbtn.locating:active { transform: none; }
.ph-body .ph-act .bigbtn.locating::before,
.ph-body .ph-act .bigbtn.locating::after {
  content: ''; position: absolute; inset: 0; border-radius: 50%; pointer-events: none;
  border: 2px solid rgba(var(--ph-ring), .6);
  animation: phSonar 2.4s cubic-bezier(.2, .6, .35, 1) infinite;
}
.ph-body .ph-act .bigbtn.locating::after { animation-delay: 1.2s; }
@keyframes phSonar {
  0% { transform: scale(1); opacity: .9; }
  100% { transform: scale(1.42); opacity: 0; }
}
.ph-body .ph-act .bigbtn.locating .ph-bico .ico { animation: phBob 1.4s ease-in-out infinite; }
@keyframes phBob {
  0%, 100% { transform: translateY(1px); }
  50% { transform: translateY(-3px); }
}
.ph-body .ph-act .bigbtn.locating small { white-space: nowrap; font-variant-numeric: tabular-nums; }
@media (prefers-reduced-motion: reduce) {
  .ph-body .ph-act .bigbtn.locating::before,
  .ph-body .ph-act .bigbtn.locating::after,
  .ph-body .ph-act .bigbtn.locating .ph-bico .ico { animation: none; }
  .ph-body .ph-act .bigbtn.locating::before { opacity: .5; transform: scale(1.12); }
  .ph-body .ph-act .bigbtn.locating::after { display: none; }
}

/* ---- 定位失敗的原因（2026-10-06）：地點卡裡一塊紅底 ----
   index.js 的 locErrCard／showLocErr。粗字一句講發生什麼事、底下一句講要做什麼、代碼縮在右下角。
   左邊一個「定位點加一條斜線」的圖示：沒讀字也看得出是定位的問題。
   放在地點卡裡（不是浮在主按鈕底下）：見 index.js 的說明。 */
.ph-locerr[hidden] { display: none; }
.ph-locerr { margin: 12px 0 0; }
.ph-locerr .locerr {
  display: flex; align-items: flex-start; gap: 10px; padding: 11px 12px 10px;
  border-radius: 14px; color: #8a1f1f; background: #fff1f1; border: 1px solid #f6c4c4;
}
.locerr .le-ic {
  flex: none; position: relative; width: 30px; height: 30px; margin-top: 1px; border-radius: 50%; background: #e5484d;
}
.locerr .le-ic::after {
  content: ''; position: absolute; inset: 6px; background: #fff;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 21s-6.5-6.4-6.5-11.2a6.5 6.5 0 0 1 11.6-4'/%3E%3Cpath d='M18.3 8.2c.1.5.2 1 .2 1.6C18.5 14.6 12 21 12 21'/%3E%3Cpath d='M3.5 3.5l17 17'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 21s-6.5-6.4-6.5-11.2a6.5 6.5 0 0 1 11.6-4'/%3E%3Cpath d='M18.3 8.2c.1.5.2 1 .2 1.6C18.5 14.6 12 21 12 21'/%3E%3Cpath d='M3.5 3.5l17 17'/%3E%3C/svg%3E") center / contain no-repeat;
}
.locerr .le-tx { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.locerr .le-tx b { font-size: .92rem; font-weight: 800; color: #b42323; }
.locerr .le-tx span { font-size: .8rem; line-height: 1.5; color: #6b2a2a; }
.locerr .errcode { flex: none; align-self: flex-end; margin-left: 0; font-size: .7rem; color: #b42323; }
.locerr .le-alt {
  align-self: flex-start; margin-top: 6px; font-size: .8rem; font-weight: 700; color: #b42323;
  text-decoration: underline; text-underline-offset: 2px;
}


/* ---------------------------------------------------------------- 鈴鐺（2026-10-07，notices.js）
   在頭像正下方（2026-10-07 使用者：原本放右上角不喜歡，改到頭像下方）。
   ・夠高的螢幕：頭像｜問候 一列；下一列 鈴鐺｜日期＋大時鐘（時鐘跟問候語對齊）
   ・不夠高：一列放 頭像｜問候｜時鐘，鈴鐺浮在頭像下面那一塊天空（不多佔高度，打卡頁要一頁放得下）
   紅色數字＝沒讀過的則數；沒有未讀就只有鈴鐺。壓在天空上：半透明白底，夜裡也看得到。 */
.ph-who { position: relative; }
/* 鈴鐺＋問題回報疊成一欄（2026-10-22）：位置放在外層，兩顆按鈕本身只管長相 */
.ph-acts {
  position: absolute; left: 6px; top: calc(100% + 4px); z-index: 2;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
}
.ph-bell {
  position: relative;
  flex: none; width: 36px; height: 36px; padding: 0; margin: 0;
  display: flex; align-items: center; justify-content: center; border-radius: 50%;
  background: rgba(255, 255, 255, .62); border: 1px solid rgba(255, 255, 255, .8); color: #24406e;
  box-shadow: 0 3px 10px rgba(20, 50, 110, .14); cursor: pointer;
}
:root[data-sky="night"] .ph-bell { background: rgba(255, 255, 255, .16); border-color: rgba(255, 255, 255, .3); color: #f2f6ff; }
.ph-bell svg { width: 20px; height: 20px; }
.ph-bell:active { transform: scale(.94); }
.ph-bell-n {
  position: absolute; top: -4px; right: -5px; min-width: 19px; height: 19px; padding: 0 5px;
  border-radius: 10px; background: #e5322d; color: #fff; border: 2px solid #fff;
  font-size: .7rem; font-weight: 800; line-height: 15px; text-align: center; font-variant-numeric: tabular-nums;
}
.ph-bell-n[hidden] { display: none; }
/* 問題回報（2026-10-22）：跟鈴鐺同一個圓，圖示是三角形驚嘆號；紅點＝MIS 回覆了還沒看過（不寫數字） */
.ph-issue { text-decoration: none; }
.ph-issue-dot {
  position: absolute; top: -1px; right: -1px; width: 11px; height: 11px;
  border-radius: 50%; background: #e5322d; border: 2px solid #fff;
}
.ph-issue-dot[hidden] { display: none; }
@media (min-height: 772px) {
  /* 兩顆疊起來 88px，比旁邊的日期＋時鐘（70px）高：不讓它撐高這一列，多出來的 18px 垂到下面那塊天空
     （2026-10-22 實測 812 高：撐高的話整頁多 18px、要捲動；底下左側到「下班打卡」還有 60px 以上的空） */
  .ph-acts { position: relative; left: auto; top: auto; grid-column: 1; grid-row: 2; justify-self: center; align-self: start; margin-bottom: -18px; }
  .ph-bell { width: 40px; height: 40px; }
  .ph-who .ph-time { grid-column: 2; grid-row: 2; }
}
/* 矮螢幕：鈴鐺浮在頭像下面那一塊天空，平常底下是空的；但「不在範圍內」要填地點時，
   那張地點卡正好長在那裡，會把鈴鐺蓋住（2026-10-07 使用者：「自填地點輸入框會與鈴鐺重疊」）。
   地點卡出現時往下讓出鈴鐺的高度——只有這個狀態才多佔這一點，平常不變。 */
@media (max-height: 771px) {
  .ph-body #adhocBox:not(.hide) { margin-top: 36px; }
  /* 矮螢幕鈴鐺正下方就是「下班打卡」那顆次要按鈕（640 高時會疊到），問題回報改排在鈴鐺右邊（2026-10-22 實測 640／720） */
  .ph-acts { flex-direction: row; }
}

/* 通知清單：從鈴鐺彈出來的一張卡（2026-10-07 使用者：「點鈴鐺的跳窗應該改成由鈴鐺為基準點跳出的視窗」）。
   位置、寬度、箭頭的 x（--ax）由 notices.js 依鈴鐺的位置算好寫進來；從箭頭那一點放大出來。
   背後只淡淡一層（點了就關），不是整頁暗掉——這是「看一眼」的東西，不是要人停下來的對話框。 */
.nt-sheet {
  position: fixed; inset: 0; z-index: 300;
  background: rgba(15, 31, 64, 0); transition: background .16s ease;
}
.nt-sheet[hidden] { display: none; }
.nt-sheet.on { background: rgba(15, 31, 64, .16); }
.nt-box {
  --ax: 30px;   /* 箭頭的 x，notices.js 依鈴鐺位置覆寫 */
  position: fixed; display: flex; flex-direction: column;
  background: #fff; border-radius: 16px; box-shadow: 0 14px 40px rgba(15, 40, 90, .26);
  opacity: 0; transform: scale(.88); transition: transform .18s cubic-bezier(.2, .9, .3, 1.12), opacity .14s ease;
}
.nt-sheet.on .nt-box { opacity: 1; transform: none; }
/* 指向鈴鐺的小三角 */
.nt-box::before {
  content: ''; position: absolute; top: -7px; left: calc(var(--ax) - 8px);
  width: 16px; height: 16px; background: #fff; border-radius: 3px; transform: rotate(45deg);
}
.nt-list { position: relative; overflow-y: auto; min-height: 0; flex: 1; border-radius: 0 0 16px 16px; padding-bottom: 4px; }
.nt-h {
  position: relative; z-index: 1; background: #fff; border-radius: 16px 16px 0 0; display: flex; align-items: center; gap: 10px;
  padding: 12px 12px 10px 16px; border-bottom: 1px solid #e8eef6; font-size: 1rem; color: #0f1f40;
}
.nt-h b { flex: 1; }
.nt-all { width: auto; margin: 0; padding: 4px 10px; border: 0; background: none; color: #1f7ae0; font-size: .84rem; }
.nt-x { width: 32px; height: 32px; margin: 0; padding: 0; border: 0; border-radius: 50%; background: #f0f4fa; color: #4f5f78; }
.nt-row {
  display: flex; align-items: flex-start; gap: 10px; width: 100%; margin: 0; text-align: left;
  padding: 13px 18px; border: 0; border-bottom: 1px solid #f0f3f8; border-radius: 0; background: #fff; color: #4f5f78;
}
.nt-row.new { background: #f3f8ff; color: #0f1f40; }
.nt-dot { flex: none; width: 8px; height: 8px; margin-top: 7px; border-radius: 50%; background: transparent; }
.nt-row.new .nt-dot { background: #e5322d; }
.nt-tx { flex: 1; min-width: 0; }
.nt-tx b { display: block; font-size: .95rem; }
.nt-row:not(.new) .nt-tx b { font-weight: 600; }
.nt-tx small { display: block; margin-top: 3px; font-size: .82rem; line-height: 1.5; color: #5d6b82; white-space: pre-line; }
/* 公告（2026-10-16）：標題前一個小標籤；內文只露兩行，全文點開看 */
.nt-tx .nt-ann { display: inline-block; margin-right: 6px; padding: 0 6px; border-radius: 6px; font-style: normal; font-size: .72rem; font-weight: 700; color: #fff; background: #3d7be8; vertical-align: 1px; }
.nt-tx small.clip { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }   /* 內文有換行（日期一行、原因一行；2026-10-14） */
/* 精簡（2026-10-19 使用者：細節點了才展開，docs/規劃-通知精簡.md）：列表一行重點、標籤四色、原地展開 */
.nt-row { cursor: pointer; }
.nt-row:focus-visible { outline: 2px solid #2f6fe0; outline-offset: -2px; }
.nt-tx small.one { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nt-tag { display: inline-block; margin-right: 6px; padding: 0 6px; border-radius: 6px; font-style: normal; font-size: .72rem; font-weight: 700; vertical-align: 1px; }
.nt-tag.ann { color: #fff; background: #3d7be8; }
.nt-tag.todo { background: #e4eefc; color: #1f5fc4; }
.nt-tag.warn { background: #fff1dc; color: #a85d00; }
.nt-tag.ok { background: #e3f5ea; color: #1d7a43; }
.nt-tag.bad { background: #fde6e6; color: #c0302b; }
.nt-more { flex: none; align-self: center; color: #9aa7ba; font-size: .95rem; transition: transform .15s; }
.nt-row.open .nt-more { transform: rotate(90deg); }
.nt-det { display: block; margin: 8px 0 2px; padding: 10px 12px; border-radius: 10px; background: #f5f8fc; font-size: .84rem; line-height: 1.6; color: #3d4b63; white-space: pre-line; }
/* 卡片上的按鈕（2026-10-21：鈴鐺裡也能按） */
.nt-acts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.nt-act { width: auto; padding: 6px 14px; border-radius: 999px; border: 1px solid #c9d8ee; background: #fff; color: #2f6fe0; font-size: .84rem; font-weight: 600; }
.nt-act.pri { background: #2f6fe0; border-color: #2f6fe0; color: #fff; }
.nt-act.bad { background: #c0302b; border-color: #c0302b; color: #fff; }
.nt-go { display: inline-flex; align-items: center; width: auto; margin: 8px 0 0; padding: 6px 14px; border: 0; border-radius: 999px; background: #2f6fe0; color: #fff; font-size: .84rem; font-weight: 600; }
@media (prefers-reduced-motion: reduce) { .nt-more { transition: none; } }
.nt-when { flex: none; font-size: .76rem; color: #8593a8; margin-top: 2px; }
.nt-empty { margin: 0; padding: 34px 18px; text-align: center; color: #8593a8; font-size: .9rem; }
@media (prefers-reduced-motion: reduce) { .nt-sheet, .nt-box { transition: none; } }

/* ---- 全部卡別：從「其他類型」按鈕彈出（2026-10-07，punch2.js 的 openMorePop）----
   跟鈴鐺的通知卡同一個樣子：獨立的一張卡浮在最上層，背景的按鈕留在原位。
   位置、寬度、放大的原點由 punch2.js 依按鈕位置寫進來。 */
.morepop-back { position: fixed; inset: 0; z-index: 300; background: rgba(6, 14, 34, 0); transition: background .16s ease; }
.morepop-back.on { background: rgba(6, 14, 34, .28); }
.morepop {
  position: fixed; z-index: 301; overflow-y: auto; box-sizing: border-box;
  padding: 12px 12px 10px; border-radius: 18px;
  background: var(--ph-card); border: 1px solid var(--ph-card-line);
  box-shadow: 0 14px 40px rgba(6, 14, 34, .35);
  opacity: 0; transform: scale(.85);
  transition: transform .18s cubic-bezier(.2, .9, .3, 1.12), opacity .14s ease;
}
.morepop.on { opacity: 1; transform: none; }
.morepop .mv-h { margin: 0 2px 10px; font-size: .84rem; font-weight: 700; color: var(--head); }
.morepop .mv-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.ph-body .morepop .mv-row > button {
  width: auto; min-width: 0; min-height: 52px; margin: 0; padding: 10px 8px;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  border-radius: 15px; font-size: .96rem; font-weight: 700; line-height: 1.3;
  color: var(--head); background: var(--panel2); border: 1px solid var(--line); box-shadow: none;
}
.ph-body .morepop .mv-row > button:active { transform: scale(.97); }
.ph-body .morepop .mv-row > button.more { display: none; }   /* 點外面就收，不必再一顆「收合」 */
@media (prefers-reduced-motion: reduce) { .morepop, .morepop-back { transition: none; } }

/* ================================================================ 全站統一（2026-10-12 使用者：「檢查整個系統的統一性…直接幫我全部修正」）
   新外觀的頁面（打卡頁 .ph-body、子頁 .sub2、工地管理 .sitebody）共用的元件，以前各頁不一樣的地方收在這裡：
     ・主要按鈕的漸層：一個 token（--btn-primary），不要再各自硬寫色碼
     ・確認／提示對話框（App.confirm/alert/form/choose）：原本沒有新外觀，確定鍵是實心深青、圓角 10，
       跟頁面上的漸層主按鈕兩個樣子
     ・成功彈窗（App.toast）：原本只有打卡頁換了新外觀，申請、簽核那幾頁還是舊的
   ⚠ 不用 :is()：舊手機的瀏覽器不認，整條規則會被丟掉（test-punch2 擋著），所以三種頁面各寫一次 */
:root {
  --btn-primary: linear-gradient(150deg, #3a8ef0 0%, #2f74e3 55%, #3a5fe0 100%);
  --btn-primary-shadow: 0 8px 18px rgba(47, 116, 227, .3), inset 0 1px 0 rgba(255, 255, 255, .22);
  --mask: rgba(6, 14, 34, .45);
}
.ph-body .dlg-back, .sub2 .dlg-back, .sitebody .dlg-back { background: var(--mask); }
.ph-body .dlg, .sub2 .dlg, .sitebody .dlg {
  border-radius: 22px; border-color: var(--ph-card-line); background: var(--ph-card);
  box-shadow: 0 24px 60px rgba(6, 14, 34, .32); padding: 22px 20px 18px;
}
.ph-body .dlg-title, .sub2 .dlg-title, .sitebody .dlg-title { font-size: 1.05rem; font-weight: 800; }
.ph-body .dlg-btn, .sub2 .dlg-btn, .sitebody .dlg-btn {
  border-radius: 14px; padding: 12px; font-size: .95rem; font-weight: 700;
  background: var(--ph-card); border: 1.5px solid var(--ph-tile); color: var(--ph-accent);
}
.ph-body .dlg-btn.ok, .sub2 .dlg-btn.ok, .sitebody .dlg-btn.ok {
  background: var(--btn-primary); color: #fff; border-color: transparent; box-shadow: var(--btn-primary-shadow);
}
.ph-body .dlg-btn.ok.danger, .sub2 .dlg-btn.ok.danger, .sitebody .dlg-btn.ok.danger {
  background: linear-gradient(150deg, #ef5a52 0%, #d0342c 100%); box-shadow: 0 8px 18px rgba(208, 52, 44, .25);
}
.ph-body .dlg-input, .sub2 .dlg-input, .sitebody .dlg-input { border-radius: 14px; background: var(--panel2); }
.ph-body .dlg-input:focus, .sub2 .dlg-input:focus, .sitebody .dlg-input:focus { border-color: var(--ph-accent); box-shadow: 0 0 0 3px rgba(31, 122, 224, .15); }
.ph-body .dlg-choice, .sub2 .dlg-choice, .sitebody .dlg-choice { border-radius: 16px; background: var(--panel2); border: 1.5px solid transparent; }
.ph-body .dlg-choice:hover, .sub2 .dlg-choice:hover, .sitebody .dlg-choice:hover, .ph-body .dlg-choice:focus, .sub2 .dlg-choice:focus, .sitebody .dlg-choice:focus { border-color: var(--ph-accent); }
/* 成功彈窗：跟打卡頁同一款（勾勾徽章由 App.toast 掛 .ph-ok） */
.sub2 .ptoast-mask, .sitebody .ptoast-mask { background: var(--mask); }
.sub2 .ptoast, .sitebody .ptoast {
  min-width: 264px; max-width: 320px; box-sizing: border-box; padding: 30px 26px 24px;
  border-radius: 26px; background: var(--ph-card); border: 1px solid var(--ph-card-line);
  box-shadow: 0 24px 60px rgba(6, 14, 34, .32);
}
.sub2 .ptoast .big, .sitebody .ptoast .big { line-height: 1; }
.sub2 .ptoast.ph-ok .big, .sitebody .ptoast.ph-ok .big {
  position: relative; width: 68px; height: 68px; margin: 0 auto; border-radius: 50%; font-size: 0;
  background: linear-gradient(150deg, #3ddcaa 0%, #1fb98a 100%);
  box-shadow: 0 0 0 8px rgba(34, 176, 125, .14), 0 10px 22px rgba(34, 176, 125, .32);
}
.sub2 .ptoast.ph-ok .big::after, .sitebody .ptoast.ph-ok .big::after {
  content: ''; position: absolute; left: 25px; top: 15px; width: 15px; height: 28px;
  border: solid #fff; border-width: 0 5px 5px 0; border-radius: 2px; transform: rotate(45deg);
}
.sub2 .ptoast .title, .sitebody .ptoast .title { margin: 16px 0 0; font-size: 1rem; font-weight: 800; color: var(--green-ink); }
.sub2 .ptoast .place, .sitebody .ptoast .place { margin-top: 10px; font-size: .84rem; color: var(--sub); line-height: 1.65; }

/* ================================================================ 打卡中（2026-10-13）
   按下去的那一顆自己變成「打卡中…」，外圈一圈光環轉到打卡完成（index.js 的 punchBusy）。
   原本只有底下一小條「打卡中…」，字不明顯，跑久了不確定有沒有按到。
   字的顏色沿用按鈕原本的字色（--pb-fg，由 JS 量好放上來）；其他打卡鈕同時變淡、按不了（重複按會打兩張卡）。
   光環用 @property 讓角度轉起來：不支援的舊 WebView 就是一圈不轉的光環，字照樣寫「打卡中…」。 */
@property --pb-a { syntax: '<angle>'; inherits: false; initial-value: 0deg; }
@keyframes pbSpin { to { --pb-a: 360deg; } }
.pbusy { --pb-fg: #fff; color: transparent !important; -webkit-text-fill-color: transparent; text-shadow: none !important; pointer-events: none; }
.pbusy > * { opacity: 0 !important; }
.pbusy::before {
  content: attr(data-busy); position: absolute; left: 0; right: 0; top: 0; bottom: 0;
  display: flex; align-items: center; justify-content: center; text-align: center; padding: 0 4px;
  color: var(--pb-fg, #fff); -webkit-text-fill-color: var(--pb-fg, #fff); font-size: 1rem; font-weight: 800; letter-spacing: 1px; white-space: nowrap;
}
.pbusy::after {
  content: ''; position: absolute; left: -6px; right: -6px; top: -6px; bottom: -6px; border-radius: inherit;
  padding: 3px; pointer-events: none;
  background: conic-gradient(from var(--pb-a), rgba(31, 122, 224, 0) 0deg, rgba(31, 122, 224, 0) 200deg, #1f7ae0 330deg, #6cc3ff 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  animation: pbSpin .9s linear infinite;
}
.ph-punching #actionArea button:not(.pbusy), .ph-punching #moves button:not(.pbusy) { opacity: .45; pointer-events: none; }
/* 打卡成功、按鈕剛換成下一顆的那 1 秒：不變暗，只是不吃點擊（防連點按到新的那顆） */
.ph-settle #actionArea button, .ph-settle #moves button { pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
  .pbusy::after { animation: none; background: #1f7ae0; }
}
.ph-n1 .bigbtn.pbusy::before, .ph-n2 .bigbtn.pbusy::before { font-size: 1.35rem; }
/* 打卡成功、等下一顆畫出來的那一下：光環停住變綠、寫「已打卡」（不閃回原本的字） */
.pbusy.pdone::after { animation: none; background: #22b07d; }

/* 重要公告延後（2026-10-22 使用者）：還沒打卡時不擋打卡鈕，打卡鈕下面一行，打完卡才跳全文（見 notices.js） */
.imp-later { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; width: auto; max-width: 330px; margin: 14px auto 0;
  padding: 9px 14px; border: 1px solid rgba(240, 138, 36, .45); border-radius: 14px; background: rgba(255, 248, 238, .96);
  color: var(--orange-ink, #8a3d00); font-size: .88rem; font-weight: 600; text-align: left; box-shadow: 0 2px 10px rgba(20, 40, 80, .12); }
.imp-later b { padding: 1px 8px; border-radius: 999px; background: #f08a24; color: #fff; font-size: .74rem; }
.imp-later span { min-width: 0; overflow-wrap: anywhere; }
.imp-later small { flex-basis: 100%; color: #9a6a3a; font-weight: 400; font-size: .76rem; }
