# Countdown
Đồng hồ đếm ngược dạng pill đổi màu theo trạng thái: thuế 72h "Còn 23:59:12", hồi chiêu "Hồi chiêu 4:59", ca đêm.
## Khi nào dùng
- Có hạn chót hoặc thời gian hồi chiêu cần người chơi nhìn thấy liên tục.
- Chỉ cần mốc giờ cố định ("Hạn chót 11:10"): viết chữ thường hoặc dùng Badge.
## Biến thể
| Biến thể | Lớp | Dùng khi |
|---|---|---|
| Còn dư giờ | `tt-countdown tt-countdown--ok` | Nền `timer-ok`, icon ⏳ / ic_hourglass |
| Sắp hết | `tt-countdown tt-countdown--soon` | Nền `timer-soon`, icon ⚠ / ic_warn, chữ "Sắp hết · mm:ss" (mặc định khi còn ≤ 1 giờ) |
| Quá hạn | `tt-countdown tt-countdown--overdue` | Nền `timer-overdue`, icon ✖ / ic_angry, nhấp nhô `tt-pulse`, chữ "Quá hạn mm:ss" đếm lên |
## Cấu trúc
```html
Còn 23:59:12
```
Thường bạn chỉ cần một `` rồi gọi `TTMU.countdown`, JS dựng phần còn lại.
## Trạng thái
| Trạng thái | Hiệu ứng |
|---|---|
| `--ok` | Nền `timer-ok` |
| `--soon` | Nền `timer-soon`, icon ⚠, chữ "Sắp hết · mm:ss"; đổi khi còn ≤ `soonSeconds` |
| `--overdue` | Nền `timer-overdue`, nhấp nhô; chữ "Quá hạn mm:ss" đếm lên; `onEnd` gọi một lần |
## Hành vi JS
```js
const c = TTMU.countdown(el, { until?, seconds?, soonSeconds?, label?, soonLabel?, overdueLabel?, icons?, onEnd? }); // { destroy, remaining }
TTMU.countdown(el, { seconds: 72 * 3600, label: 'Còn', icons: { ok: 'assets/Icons/ic_hourglass.png', soon: 'assets/Icons/ic_hourglass.png', overdue: 'assets/Icons/ic_warn.png' } });
c.destroy(); // dừng timer
```
- `until` nhận `Date`, ms epoch hoặc chuỗi ISO; hoặc `seconds` tính từ lúc gọi. Đếm bằng `Date.now()` nên không trôi khi tab bị treo hoặc thiết bị ngủ.
- Định dạng: `23:59:12` khi từ 1 giờ trở lên, `4:59` khi dưới 1 giờ. Chữ số đều cột (`tabular-nums`).
- Mỗi phần tử chỉ gắn một lần (`data-tt-bound="1"`); gọi lại trả về cùng API và bỏ qua `opts` mới. Muốn đổi hạn (`until` / `seconds`) hay nhãn thì `destroy()` rồi gọi lại. Hãy `destroy()` khi gỡ phần tử.
- `until` không hợp lệ (không phải ngày giờ) được coi như đã hết giờ: hiện "Quá hạn 0:00" và gọi `onEnd`, không bao giờ hiện "NaN".
- Chữ gán bằng `textContent`; `icons` là đường dẫn ảnh, bỏ trống thì dùng ký tự ⏳ (ok), ⚠ (soon), ✖ (overdue).
## Token sử dụng
`timer-ok`, `timer-soon`, `timer-overdue`, `on-fill`, `radius-round`, `radius-20`, `space-6`, `space-12`.
## Khả năng tiếp cận
- `role="timer"` với `aria-live="off"`: screen reader không đọc mỗi giây. `aria-label` được cập nhật thưa (mỗi phút, dưới 1 phút thì mỗi 10 giây, và khi đổi trạng thái): "Còn 23 giờ 59 phút".
- Trạng thái không chỉ khác màu: mỗi trạng thái có icon riêng (⏳ ok, ⚠ soon, ✖ overdue) và chữ riêng ("Còn", "Sắp hết ·", "Quá hạn").
- Chữ `on-fill` trên `timer-ok` 5.0:1, `timer-overdue` 4.8:1 (chữ đậm); `timer-soon` chỉ 3.2:1 ở bốn theme gốc (giữ đúng màu game) nên đã có icon ⚠ và chữ "Sắp hết" đi kèm, không dựa vào màu; Tương phản cao: cả ba từ 7.9:1.
- Hiệu ứng nhấp nhô tắt khi `prefers-reduced-motion`.
## Nên / Không nên
| Nên | Không nên |
|---|---|
| Đặt cạnh nhãn nói rõ đếm cho việc gì ("Thuế 72h") | Đặt đồng hồ trơ trọi không rõ nghĩa |
| Gọi `destroy()` khi gỡ phần tử | Để timer chạy ngầm sau khi phần tử đã rời DOM |
## Nguồn
`.tax-timer-badge` và biến thể `.active|.grace|.overdue`, `@keyframes pulseOverdue` — css/style.css dòng 2052–2082; markup `#taxCountdownLive` — js/game.js. `.active` thành `--ok`, `.grace` thành `--soon`. Màu hex (`#15803d`, `#d97706`, `#dc2626`) đổi sang token `timer-*`. Bổ sung có chủ đích: icon, `role="timer"` và hành vi JS đếm theo giờ thật.