:root {
  --maroon-900: #4a1a22;
  --maroon-700: #7b2c3e;
  --maroon-600: #8f3548;
  --maroon-100: #f3dfe2;
  --cream-100: #f7efe1;
  --cream-50: #fbf7ef;
  --gold: #c9963a;
  --green: #2e7d4f;
  --red: #b0362f;
  --text-dark: #2b1a1f;
  --text-muted: #7a6a63;
  --border: #e7d9c9;
  --shadow: 0 2px 10px rgba(74, 26, 34, 0.08);
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  background: var(--cream-50);
  color: var(--text-dark);
}

a {
  color: inherit;
}

.app-shell {
  display: flex;
  min-height: 100vh;
}

/* Thanh trên cùng + nút mở menu chỉ dùng cho khổ điện thoại (xem media query <=780px) -- trên máy
   tính sidebar luôn hiện cố định bên trái nên 2 phần tử này ẩn hẳn, không chiếm chỗ. */
.mobile-topbar,
.sidebar-backdrop {
  display: none;
}

.sidebar {
  width: 230px;
  background: linear-gradient(180deg, var(--maroon-900), var(--maroon-700));
  color: var(--cream-100);
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  position: sticky;
  top: 0;
  height: 100vh;
}

.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 20px 16px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.15);
}

.sidebar-brand img {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  object-fit: cover;
}

.sidebar-brand .brand-name {
  font-weight: 700;
  font-size: 15px;
  line-height: 1.2;
}

.sidebar-brand .brand-sub {
  font-size: 11px;
  opacity: 0.8;
}

.sidebar-nav {
  flex: 1;
  padding: 12px 8px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.sidebar-nav a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border-radius: 8px;
  text-decoration: none;
  color: var(--cream-100);
  font-size: 14px;
  opacity: 0.85;
}

.sidebar-nav a:hover {
  background: rgba(255, 255, 255, 0.08);
  opacity: 1;
}

.sidebar-nav a.active {
  background: var(--cream-100);
  color: var(--maroon-700);
  font-weight: 600;
  opacity: 1;
}

.sidebar-footer {
  padding: 14px 16px;
  border-top: 1px solid rgba(255, 255, 255, 0.15);
  font-size: 13px;
}

.sidebar-footer .user-name {
  font-weight: 600;
  display: block;
}

.sidebar-footer .role-badge {
  display: inline-block;
  margin-top: 4px;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.15);
  font-size: 11px;
}

.sidebar-footer a {
  display: inline-block;
  margin-top: 10px;
  font-size: 13px;
  text-decoration: underline;
  opacity: 0.85;
}

.push-toggle-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  padding: 4px 10px;
  font-size: 12px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.35);
  background: rgba(255, 255, 255, 0.1);
  color: inherit;
  cursor: pointer;
}

.push-toggle-btn:hover {
  background: rgba(255, 255, 255, 0.2);
}

.push-toggle-btn.is-on {
  background: rgba(255, 255, 255, 0.25);
  font-weight: 600;
}

/* Icon chuông + so chua doc de len goc icon (kieu Facebook), dung chung cho nut chuong o
   mobile-topbar (.notification-bell-btn) va sidebar-footer (.push-toggle-btn mo hop thu). */
.notification-bell-icon-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.notification-bell-count {
  position: absolute;
  top: -5px;
  right: -7px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--red);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
}

.notification-bell-btn .tab-icon,
.push-toggle-btn .tab-icon {
  width: 18px;
  height: 18px;
  vertical-align: middle;
}

.push-ios-hint {
  margin-top: 10px;
  padding: 8px 10px;
  font-size: 12px;
  line-height: 1.4;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.1);
  color: inherit;
  opacity: 0.9;
}

.main-content {
  flex: 1;
  padding: 24px 28px 60px;
  max-width: 1200px;
}

.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 20px;
  flex-wrap: wrap;
  gap: 10px;
}

.page-header h1 {
  font-size: 22px;
  margin: 0;
  color: var(--maroon-700);
}

.flash-list {
  margin-bottom: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.flash {
  padding: 10px 14px;
  border-radius: 8px;
  font-size: 14px;
}

.flash.success {
  background: #e3f3e8;
  color: var(--green);
  border: 1px solid #bfe3cb;
}

.flash.error {
  background: #fbe6e5;
  color: var(--red);
  border: 1px solid #f3c3c0;
}

.card {
  background: #fff;
  border-radius: 12px;
  box-shadow: var(--shadow);
  border: 1px solid var(--border);
  padding: 18px;
}

/* Tay cầm kéo thả sắp xếp thứ tự nhóm món (trang Menu) -- sao y .sched-drag-handle của Lịch làm,
   xem JS initMenuGroupReorder trong page-menu.js (đợt bỏ nút ▲/▼ thay bằng kéo thả 31/08/2026). */
.menu-group-drag-handle {
  display: inline-block;
  cursor: grab;
  color: var(--text-muted);
  margin-right: 4px;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  font-size: 16px;
  line-height: 1;
  vertical-align: middle;
}

.menu-group-card.menu-group-dragging {
  opacity: 0.5;
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
  margin-bottom: 20px;
}

.stat-card {
  background: #fff;
  border-radius: 12px;
  box-shadow: var(--shadow);
  border: 1px solid var(--border);
  padding: 18px 20px;
}

.stat-card .label {
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 6px;
}

.stat-card .value {
  font-size: 22px;
  font-weight: 700;
}

.stat-card.income .value {
  color: var(--green);
}

.stat-card.expense .value {
  color: var(--red);
}

.stat-card.profit .value {
  color: var(--maroon-700);
}

.stat-card.count .value {
  color: var(--gold);
}

.section-title {
  font-size: 16px;
  font-weight: 700;
  color: var(--maroon-700);
  margin: 24px 0 12px;
}

/* Icon bấm để hiện/ẩn ghi chú giải thích dài ngay dưới 1 section-title (xem toggleEdit) — ẩn mặc
   định cho gọn, chỉ hiện khi cần đọc lại. */
.note-info-btn {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  padding: 0;
  margin-left: 2px;
  opacity: 0.65;
  vertical-align: middle;
}

.note-info-btn:hover {
  opacity: 1;
}

.form-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 14px;
  margin-bottom: 14px;
}

.field {
  min-width: 0;
}

.field label {
  display: block;
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 4px;
}

.field input,
.field select,
.field textarea {
  width: 100%;
  min-width: 0;
  max-width: 100%;
  box-sizing: border-box;
  padding: 9px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  background: var(--cream-50);
  color: var(--text-dark);
}

/* Checkbox tự vẽ, không dùng checkbox gốc của trình duyệt -- iOS Safari không tôn trọng
   width/height CSS đặt lên checkbox gốc (vẫn vẽ control theo kích thước hệ thống bất kể CSS),
   khiến checkbox đè lên chữ nhãn ngay cạnh trong danh sách nhiều checkbox liền nhau (đã gặp
   thật ở nhóm checkbox "Ca"/"Gửi cho bộ phận" tại Bàn giao ca -- đổi kích thước cố định vẫn
   không hết, phải bỏ hẳn appearance gốc). Dùng cho mọi checkbox mới thêm vào 1 nhóm nhiều
   checkbox liền nhau kiểu này thay vì tự viết lại style width/height/border/background rời rạc.  */
.gk-checkbox-label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-weight: 400;
  font-size: 14px;
  white-space: nowrap;
}

.gk-checkbox-label input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 17px;
  height: 17px;
  min-width: 17px;
  flex-shrink: 0;
  margin: 0;
  padding: 0;
  border: 1.5px solid var(--border);
  border-radius: 4px;
  background: var(--cream-50);
  position: relative;
  cursor: pointer;
}

.gk-checkbox-label input[type="checkbox"]:checked {
  background: var(--maroon-700);
  border-color: var(--maroon-700);
}

.gk-checkbox-label input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 1px;
  width: 4px;
  height: 8px;
  border: solid #fff;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

.field textarea {
  resize: vertical;
  min-height: 60px;
}

/* Ô ngày/tháng/giờ: bắt buộc tắt kiểu điều khiển gốc của hệ thống.
   Vì sao: trên iPhone (Safari) các ô này là widget gốc của iOS, có bề rộng tối thiểu riêng theo
   nội dung — ngày tiếng Việt kiểu "ngày 8 thg 7, 2026" cần rộng hơn khung chứa. Bề rộng tối
   thiểu đó luôn thắng width/max-width nên ô bị phình ra, tràn khỏi thẻ trắng (đã gặp thật ở
   trang Xuất Excel và form Báo cáo hỏng huỷ). Đặt appearance:none thì Safari coi nó như ô
   thường và tôn trọng đúng width/max-width bên trên. Trên máy tính vẫn giữ nguyên nút chọn lịch
   (::-webkit-calendar-picker-indicator không bị ảnh hưởng). */
input[type="date"],
input[type="month"],
input[type="datetime-local"],
input[type="time"] {
  -webkit-appearance: none;
  appearance: none;
  min-width: 0;
  max-width: 100%;
}

/* Font-size 16px bắt buộc để trình duyệt trên điện thoại (đặc biệt iOS Safari) KHÔNG tự động
   zoom vào ô khi chạm để nhập — zoom dưới 16px làm trang bị lệch, che mất tên nguyên liệu ở cột
   bên cạnh, dễ nhập nhầm dòng khi kiểm tồn cuối ngày. Padding/min-height lớn hơn để dễ bấm trúng
   trên màn hình nhỏ. */
.stock-count-qty,
.food-check-qty {
  width: 100%;
  max-width: 140px;
  box-sizing: border-box;
  padding: 12px 10px;
  min-height: 44px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 16px;
  background: var(--cream-50);
  color: var(--text-dark);
  text-align: right;
}

/* Ô "Ghi chú" trong 2 bảng "Chấm công của tôi": phần đang xem (nội dung + nút bút chì) và phần
   đang sửa (ô nhập + Lưu/Huỷ) thay nhau hiện, không bao giờ chồng cùng lúc — xem
   toggleMyAttendanceNoteEdit() trong hr.html. Nút để 36px cho dễ chạm bằng ngón tay trên điện
   thoại, ô nhập 16px để iOS Safari không tự zoom vào khi chạm (giống .stock-count-qty).
   KHÔNG dùng flex-wrap ở đây (khác .attendance-note-edit bên dưới): ghi chú dài xuống 2-3 dòng thì
   chữ tự wrap BÊN TRONG span của nó (nhờ min-width:0 + overflow-wrap), còn nút bút chì luôn ở
   flex-shrink:0 + ghim mép phải qua justify-content — nhờ vậy nút luôn thẳng hàng theo cột dù ghi
   chú dài ngắn khác nhau giữa các dòng, thay vì bị đẩy xuống dòng riêng lệch vị trí mỗi lần. */
.attendance-note-view {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
}

.attendance-note-view span {
  font-size: 12px;
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: break-word;
}

.attendance-note-view .attendance-note-btn {
  flex-shrink: 0;
  min-width: 36px;
  padding: 6px 8px;
}

.attendance-note-edit {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.attendance-note-actions {
  display: flex;
  gap: 6px;
  flex: 0 0 auto;
}

.btn.attendance-note-btn {
  min-height: 36px;
  padding: 6px 12px;
  flex: 0 0 auto;
}

/* Dòng chi tiết số công dưới tên nhân viên ở bảng lương. Chuỗi này dài hơn hẳn các dòng phụ khác
   ("26 ngày (+2 công lễ) = 28 công x 384.615 đ") mà ô bảng ở khổ điện thoại đang để
   white-space:nowrap, nên nếu không cho xuống dòng thì cột đầu — vốn là cột DÍNH khi bảng cuộn
   ngang — bị kéo rộng gần hết màn hình 375px, che mất các cột còn lại. */
.payroll-cong-note {
  white-space: normal;
}

/* Các dòng tiền trong hàng bảng lương (đã gom từ 4 cột riêng xuống xếp chồng dưới tên nhân viên):
   nhãn được xuống dòng bình thường, nhưng SỐ TIỀN phải nowrap — nếu không "1.000.000 đ" bị ngắt
   thành "1.000.000" và "đ" ở 2 dòng, nhìn như 2 con số khác nhau. */
.payroll-money-lines {
  margin-top: 6px;
}

.payroll-money-lines > div > strong,
.payroll-money-lines > div > span {
  white-space: nowrap;
}

/* Ô nhập % KPI nằm trong hàng bảng lương — nhãn đã là tên cột nên không bọc .field được, phải tự
   khai lại kiểu cho giống ô trong .field (xem quy ước "ô nhập trong table" ở CLAUDE.md). */
.payroll-kpi-input {
  width: 90px;
  box-sizing: border-box;
  padding: 6px 8px;
  min-height: 36px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 16px;
  background: var(--cream-50);
  color: var(--text-dark);
}

.my-attendance-note-input {
  flex: 1 1 160px;
  min-width: 0;
  box-sizing: border-box;
  padding: 8px 10px;
  min-height: 36px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 16px;
  background: var(--cream-50);
  color: var(--text-dark);
}

/* Ô ghi chú trong hàng "Đang chờ duyệt"/"Sửa lại" chấm công (nằm trong ô bảng, không hợp bọc
   .field vì đứng cùng hàng flex với các nút hành động) -- khai lại kiểu cho giống ô trong .field. */
.attendance-note-input {
  box-sizing: border-box;
  padding: 6px 8px;
  min-height: 34px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 13px;
  background: var(--cream-50);
  color: var(--text-dark);
}

/* Các ô input trong 1 hàng <tr><td colspan> ở trang Tồn kho (đúng ngoại lệ 2 trong CLAUDE.md —
   không nhét .field vào giữa hàng bảng hẹp) — form "Chuyển thành BTP", "Sửa nguyên liệu",
   "Sửa công cụ", "Sửa bán thành phẩm", thêm nguyên liệu vào công thức BTP, ô nhập số lượng nguyên
   liệu thô khi Sản xuất/Chế biến (production-raw-qty), và bảng công thức ở form "Ghi bàn giao"
   (handover-recipe-qty, đã có sẵn class nhưng thiếu CSS này).
   Không nằm trong .field nên cần class riêng để có khung/nền đồng bộ các ô khác trong app. */
.production-raw-qty,
.ing-convert-yield-input,
.ing-edit-input,
.tool-edit-input,
.sfp-edit-input,
.sfp-recipe-add-input,
.handover-recipe-qty {
  box-sizing: border-box;
  padding: 9px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  background: var(--cream-50);
  color: var(--text-dark);
}

/* Ô nhập trong bảng "Cấu hình số lượng tối thiểu & đơn vị nhập nhanh" (Quản lý tủ) không nằm
   trong .field nên không tự có khung/nền như các ô nhập khác trong app — thêm riêng để đồng bộ
   giao diện, giữ nguyên max-width canh theo từng ô đã đặt sẵn trong template. */
.cabinet-settings-min-qty,
.cabinet-settings-restock-type,
.cabinet-settings-bulk-label,
.cabinet-settings-bulk-ratio {
  box-sizing: border-box;
  padding: 9px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  background: var(--cream-50);
  color: var(--text-dark);
}

/* Ô sửa công việc trong hàng "Sửa" ở bảng "Tất cả công việc đã giao" (tab Giao việc) — hàng đã
   nằm trong <tr><td colspan="5">, không hợp bọc .field vì các ô xếp cạnh nhau theo hàng flex cùng
   nút Lưu/Huỷ (xem taskEditRowHtml() trong hr.html) -- khai lại kiểu cho giống ô trong .field. */
.task-edit-input {
  box-sizing: border-box;
  padding: 8px 10px;
  min-height: 36px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  background: var(--cream-50);
  color: var(--text-dark);
}

/* Select loại giảm giá + ô số tiền giảm của từng món trong giỏ hàng (Tạo giao dịch Thu +
   modal Sửa giao dịch dùng chung — xem transactions.html hàm cartRowHtml() và base.html hàm
   renderEditCartRows()) — cả 2 nơi đứng trong 1 hàng <tr><td colspan> riêng (đúng ngoại lệ ô
   trong bảng ở CLAUDE.md), chỉ thiếu khung viền/nền để giống các ô trong .field. Không đặt
   width:1% hay ép chiều rộng theo cột (bẫy #5 mobile trong CLAUDE.md) — 2 ô này đã tự khai
   width cố định nhỏ (80px/100px) trực tiếp trong style nội tuyến của thẻ, không phụ thuộc vào
   cột bảng nên không có nguy cơ dồn chữ khi cột khác trống. */
.cart-discount-select,
.cart-discount-input {
  box-sizing: border-box;
  padding: 9px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  background: var(--cream-50);
  color: var(--text-dark);
}

/* Ô "Sửa món" (tên/giá/nhóm) và ô "Công thức" thêm nguyên liệu vào món (trang Menu) đã đúng vị trí
   hàng riêng có colspan (ngoại lệ 2 trong CLAUDE.md — hàng ẩn mặc định, mở bằng nút "Sửa"/"Công
   thức"), không nằm trong .field nên không tự có khung viền/nền như các ô khác — thêm riêng để
   đồng bộ giao diện, theo đúng mẫu .cabinet-settings-min-qty. Xem menuItemRowHtml() trong menu.html
   và _partial_menu_group_list.html dòng 61-105. */
.menu-item-edit-name,
.menu-item-edit-price,
.menu-item-edit-group,
.menu-recipe-ingredient-name,
.menu-recipe-ingredient-qty {
  box-sizing: border-box;
  padding: 9px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  background: var(--cream-50);
  color: var(--text-dark);
}

.menu-item-edit-price {
  max-width: 140px;
}

.menu-recipe-ingredient-name {
  min-width: 180px;
}

.menu-recipe-ingredient-qty {
  max-width: 140px;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 9px 18px;
  border-radius: 8px;
  border: none;
  background: var(--maroon-700);
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}

.btn:hover {
  background: var(--maroon-600);
}

.btn.btn-secondary {
  background: var(--cream-100);
  color: var(--maroon-700);
  border: 1px solid var(--border);
}

.btn.btn-danger {
  background: var(--red);
}

.btn.btn-sm {
  padding: 5px 10px;
  font-size: 12px;
}

.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 18px;
}

.tab-btn {
  padding: 9px 20px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: #fff;
  font-weight: 600;
  font-size: 14px;
  cursor: pointer;
  color: var(--text-muted);
}

.tab-icon {
  width: 16px;
  height: 16px;
  vertical-align: -3px;
  flex-shrink: 0;
}

.tab-btn.active.tab-income {
  background: var(--green);
  color: #fff;
  border-color: var(--green);
}

.tab-btn.active.tab-expense {
  background: var(--red);
  color: #fff;
  border-color: var(--red);
}

.tab-btn.active {
  background: var(--maroon-700);
  color: #fff;
  border-color: var(--maroon-700);
}

.tab-panel {
  display: none;
}

.tab-panel.active {
  display: block;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13.5px;
}

table th,
table td {
  text-align: left;
  padding: 9px 10px;
  border-bottom: 1px solid var(--border);
}

table th {
  color: var(--text-muted);
  font-weight: 600;
  background: var(--cream-100);
}

table tr:hover td {
  background: #fbf6ef;
}

.sticky-first-col {
  table-layout: fixed;
}

/* Cột số tiền của bảng tóm tắt lương: luôn nằm sát lề phải và không bị ngắt dòng giữa chừng
   (vd "9.111.080 đ" bị tách làm 2 dòng trên điện thoại), phần nhãn dài bên trái tự xuống dòng. */
.payslip-summary-table td:last-child {
  width: 1%;
  text-align: right;
  white-space: nowrap;
}

/* Dòng chi tiết thụt vào nằm dưới 1 dòng tổng trong bảng lương ("Lương của tôi") — vd từng khoản
   phụ cấp/khấu trừ cấu thành nên số tổng ở dòng ngay phía trên. Viền dưới nhạt hơn để nhóm các
   dòng con lại với dòng tổng của nó. */
.payslip-subrow td {
  padding: 5px 10px 5px 26px;
  font-size: 12px;
  color: var(--text-muted);
  border-bottom: 1px dashed var(--border);
}

.payslip-subrow td:last-child {
  padding-left: 10px;
}

.txn-compare-table {
  margin-top: 10px;
}

.txn-compare-table th,
.txn-compare-table td {
  border: 1px solid var(--border);
  font-size: 13px;
}

.txn-compare-table th:first-child,
.txn-compare-table td:first-child {
  width: 110px;
  color: var(--text-muted);
  font-weight: 600;
  background: var(--cream-100);
}

.txn-compare-changed {
  background: #fdf1d6;
  color: #96700f;
  font-weight: 600;
}

.badge {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
}

.badge.income {
  background: #e3f3e8;
  color: var(--green);
}

.badge.expense {
  background: #fbe6e5;
  color: var(--red);
}

.badge.warning-yellow {
  background: #fdf1d6;
  color: #96700f;
}

.badge.warning-red {
  background: #fbe6e5;
  color: var(--red);
}

.expiry-card {
  border-left: 4px solid transparent;
  border-radius: 10px;
  padding: 12px 14px;
  margin-bottom: 10px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.expiry-card.level-yellow {
  background: #fdf6e5;
  border-left-color: #d4a017;
}

.expiry-card.level-red {
  background: #fbeceb;
  border-left-color: var(--red);
}

.chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px 6px 14px;
  border-radius: 999px;
  background: var(--cream-100);
  border: 1px solid var(--border);
  font-size: 13px;
}

.chip form {
  margin: 0;
}

.chip button {
  border: none;
  background: rgba(176, 54, 47, 0.12);
  color: var(--red);
  width: 20px;
  height: 20px;
  border-radius: 50%;
  cursor: pointer;
  font-size: 12px;
  line-height: 1;
}

.settings-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 18px;
}

.inline-form {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}

.inline-form input,
.inline-form select {
  flex: 1;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
}

.login-wrap {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at top, var(--maroon-700), var(--maroon-900));
}

.login-card {
  background: #fff;
  border-radius: 16px;
  padding: 36px 34px;
  width: 340px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25);
  text-align: center;
}

.login-card img {
  width: 76px;
  height: 76px;
  border-radius: 50%;
  object-fit: cover;
  margin-bottom: 10px;
}

.login-card h1 {
  font-size: 18px;
  color: var(--maroon-700);
  margin: 0 0 4px;
}

.login-card p.subtitle {
  color: var(--text-muted);
  font-size: 13px;
  margin: 0 0 20px;
}

.login-card .field {
  text-align: left;
  margin-bottom: 14px;
}

.login-card .btn {
  width: 100%;
  justify-content: center;
  padding: 11px;
  margin-top: 6px;
}

.hint {
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 16px;
}

.thumb-link img {
  width: 34px;
  height: 34px;
  border-radius: 6px;
  object-fit: cover;
  border: 1px solid var(--border);
}

.muted {
  color: var(--text-muted);
}

.empty-state {
  padding: 30px;
  text-align: center;
  color: var(--text-muted);
}

/* Cặp nhãn "đầy đủ / rút gọn" cho bảng nhiều cột: in CẢ HAI dạng vào HTML rồi để CSS ẩn/hiện theo
   khổ màn hình (không dùng JS, không cần biết trước máy nào đang xem). Dùng cho:
   - số tiền: {{ x | vnd }} trong .full-only và {{ x | vnd_short }} trong .mini-only ("5,4tr")
   - nhãn ca: {{ s | shift_label }} và {{ s | shift_label_mini }} ("10h-23h" -> "10-23")
   Số tiền/giờ đầy đủ là thứ chiếm chỗ nhiều nhất khiến bảng phải kéo ngang trên điện thoại; máy
   tính vẫn thấy đúng con số đầy đủ. Xem media query <=780px bên dưới để biết chiều ẩn/hiện. */
.mini-only {
  display: none;
}

/* Bề rộng cột + ô ngày của lưới lịch làm tuần. Trước đây khai bằng inline style ngay trên <col> và
   <td>, nhưng inline luôn thắng CSS nên không thể thu gọn theo khổ màn hình — đã chuyển hẳn sang
   class. Giá trị dưới đây giữ nguyên đúng cách hiển thị cũ trên máy tính; media query <=780px bên
   dưới đè lại để bảng vừa màn hình điện thoại. */
.sched-col-name {
  width: 14%;
}

.sched-col-day {
  width: 12.28%;
}

.holiday-badge-del-btn {
  border: none;
  background: none;
  color: var(--red);
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  padding: 0;
  vertical-align: middle;
}

.sched-day-cell {
  min-width: 130px;
}

/* Hàng tiêu đề "Thứ/ngày" phải luôn thẳng hàng trên cùng ở mọi cột. Mặc định trình duyệt căn giữa
   theo chiều dọc (vertical-align: middle) cho <th> -- cột có badge "Lương x2.0" (ngày lễ) cao hơn
   cột thường (thêm badge + nút xoá bên dưới) nên kéo cả hàng cao lên, làm chữ "Thứ/ngày" ở các cột
   không có ngày lễ bị đẩy xuống giữa thay vì nằm trên cùng như cột ngày lễ (chủ quán báo lệch hàng
   31/08/2026). Neo top để mọi cột đồng nhất, badge ngày lễ chỉ kéo dài thêm phần dưới của riêng cột
   đó, không ảnh hưởng vị trí chữ Thứ/ngày ở các cột khác. */
.schedule-week-table thead th {
  vertical-align: top;
}

/* Tay cầm kéo thả sắp xếp nhân viên trong bảng Lịch làm/Bảng nháp lịch làm (chỉ chủ quán thấy) --
   touch-action:none để ngón tay kéo dọc không bị trình duyệt hiểu nhầm thành cuộn trang (xem JS
   initScheduleReorder trong page-hr.js, đợt thêm tính năng kéo thả 30/08/2026). */
.sched-drag-handle {
  display: inline-block;
  cursor: grab;
  color: var(--text-muted);
  margin-right: 4px;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  font-size: 16px;
  line-height: 1;
  vertical-align: middle;
}

.sched-emp-row.sched-row-dragging {
  opacity: 0.5;
}

.sched-emp-row.sched-row-drop-target {
  outline: 2px dashed var(--maroon-700);
  outline-offset: -2px;
}

/* Dòng phụ của bảng Thống kê lương tuần — chỉ dành cho khổ điện thoại (xem media query <=780px).
   Máy tính đã có 3 cột riêng Đơn giá / Tổng giờ / Tổng lương nên phải ẩn hẳn ở đây. */
.stats-sum-row {
  display: none;
}

/* Cỡ chữ mặc định của ô ca trong lưới lịch làm (khổ máy tính). Trước đây khai bằng inline style
   ngay trên chip, nhưng inline luôn thắng CSS class nên 3 mức cỡ chữ theo độ dài nhãn ở khổ điện
   thoại (.shift-chip / -mid / -long) không có tác dụng — đã chuyển hẳn sang class. */
.shift-chip {
  font-size: 12px;
}

/* Dấu cảnh báo rút gọn của badge "Thiếu ca" — chỉ hiện ở khổ điện thoại (xem media query). */
.sched-gap-icon {
  display: none;
}

/* Badge "Thiếu ca". 4 thuộc tính này trước đây khai bằng inline style ngay trên thẻ, nhưng inline
   luôn thắng CSS nên rule ẩn bớt badge ở khổ điện thoại (xem media query) không có tác dụng — phải
   chuyển hẳn sang class. */
.sched-gap-badge {
  display: block;
  margin-bottom: 3px;
  white-space: normal;
  cursor: pointer;
}

/* Mặc định (máy tính): ẩn khối tóm tắt dành riêng cho điện thoại — xem .responsive-desktop-table
   trong media query <=780px bên dưới, nơi khối này được hiện lại thay cho các cột desktop-col. */
.mobile-summary {
  display: none;
}

/* Cột nút hành động cuối bảng .responsive-desktop-table dễ bị table-layout:auto ép hẹp hơn nội
   dung cần (dù cột khác còn dư chỗ trống) khiến chữ trên nút vắt xuống dòng — áp dụng cho cả máy
   tính lẫn điện thoại, không chỉ trong media query bên dưới. */
.responsive-desktop-table .btn,
.responsive-desktop-table button,
.responsive-desktop-table .badge {
  white-space: nowrap;
}

@media (max-width: 780px) {
  .app-shell {
    flex-direction: column;
  }
  /* Danh sách module ("Nhân sự", "Tiền lương"...) trước đây hiện thành 1 hàng nút ngay đầu trang --
     nay ẩn hẳn sau menu trượt (giống Threads: bấm ☰ ở thanh trên cùng mới hiện), nhường chỗ cho nội
     dung trang chính ngay từ đầu. */
  .mobile-topbar {
    display: flex;
    align-items: center;
    gap: 10px;
    position: sticky;
    top: 0;
    z-index: 45;
    background: linear-gradient(180deg, var(--maroon-900), var(--maroon-700));
    color: var(--cream-100);
    padding: calc(12px + env(safe-area-inset-top, 0px)) 14px 12px;
  }
  .mobile-menu-btn {
    background: none;
    border: none;
    color: var(--cream-100);
    font-size: 22px;
    line-height: 1;
    padding: 2px 6px;
    cursor: pointer;
  }
  .mobile-topbar-title {
    font-weight: 700;
    font-size: 15px;
  }
  .notification-bell-btn {
    margin-left: auto;
    background: none;
    border: none;
    color: var(--cream-100);
    line-height: 1;
    padding: 4px 6px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
  }
  .sidebar-backdrop.mobile-open {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    z-index: 55;
  }
  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: 82%;
    max-width: 300px;
    height: 100vh;
    flex-direction: column;
    align-items: stretch;
    overflow-x: visible;
    overflow-y: auto;
    z-index: 60;
    box-shadow: 2px 0 16px rgba(0, 0, 0, 0.25);
    transform: translateX(-100%);
    transition: transform 0.25s ease;
  }
  .sidebar.mobile-open {
    transform: translateX(0);
  }
  .sidebar-brand {
    border-bottom: 1px solid rgba(255, 255, 255, 0.15);
    border-right: none;
    padding-top: calc(20px + env(safe-area-inset-top, 0px));
  }
  .sidebar-nav {
    flex-direction: column;
    gap: 4px;
  }
  .sidebar-nav a {
    padding: 10px 14px;
    font-size: 14px;
  }
  .sidebar-footer {
    border-top: 1px solid rgba(255, 255, 255, 0.15);
    border-left: none;
    white-space: normal;
    padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  }
  body.sidebar-drawer-open {
    overflow: hidden;
  }
  .main-content {
    padding: 16px;
    max-width: 100%;
  }
  .page-header {
    width: 100%;
  }
  .page-header form.inline-form {
    width: 100%;
  }
  .inline-form {
    flex-wrap: wrap;
    width: 100%;
  }
  .inline-form .field {
    min-width: 0;
    flex: 1 1 140px;
  }
  .inline-form input,
  .inline-form select {
    min-width: 0;
    flex: 1 1 140px;
  }
  /* Ô ngày/tháng/giờ và ô chọn ảnh chiếm trọn 1 hàng riêng trên điện thoại.
     Vì sao: Safari trên iPhone KHÔNG cho các ô này co nhỏ lại theo khung (ngày tiếng Việt kiểu
     "ngày 6 thg 8, 2026" cần ~180px), nên khi xếp chung hàng với ô khác chúng tràn khỏi khung
     và đè lên ô bên cạnh — đã gặp thật ở form "Báo cáo hỏng huỷ đồ mới" (ô Ngày đè lên ô Loại
     báo hỏng). Cho mỗi ô 1 hàng đủ rộng là hết chồng lấn, đồng thời dễ bấm trúng hơn.
     Riêng ô chọn ảnh nếu bị bóp hẹp sẽ chỉ còn "chưa...nào", không đọc được trạng thái. */
  /* Cần !important vì các ô này viết sẵn max-width/flex thẳng trong thuộc tính style của thẻ
     (vd style="max-width:160px", style="flex:1"), mà style nội tuyến luôn thắng CSS thường. */
  .inline-form .field:has(input[type="date"]),
  .inline-form .field:has(input[type="month"]),
  .inline-form .field:has(input[type="datetime-local"]),
  .inline-form .field:has(input[type="time"]),
  .inline-form .field:has(input[type="file"]),
  .inline-form .field:has(textarea),
  .inline-form .field:has(input[name="duty_rotation_positions"]) {
    flex: 1 1 100% !important;
    max-width: none !important;
  }
  /* Ô chỉ để hiện số liệu xem trước (vd tạm tính tiền hỏng huỷ): khi chưa có gì thì bỏ hẳn chỗ,
     không để 1 ô rỗng chen giữa hàng làm các ô còn lại lệch sang phải thành lỗ hổng. */
  .inline-form .field-preview:has(> div:empty) {
    display: none;
  }
  .form-row {
    width: 100%;
  }
  /* Hàng lọc chỉ gồm select/ô chữ ngắn (KHÔNG có ô ngày/giờ/file): cho xếp 2 ô mỗi hàng thay vì
     mỗi ô chiếm trọn 1 hàng, đỡ phải cuộn gần hết màn hình mới tới bảng dữ liệu bên dưới.
     CỐ Ý không sửa thẳng .form-row: nhiều form khác trong app có ô ngày nằm trong .form-row, mà ô
     ngày trên Safari/iOS không co xuống dưới ~180px được (xem bẫy ô ngày ở CLAUDE.md) — ép
     minmax nhỏ hơn sẽ làm chúng tràn ra ngoài khung trên máy thật. */
  .form-row-compact {
    grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  }
  table {
    display: block;
    width: 100%;
    overflow-x: auto;
    white-space: nowrap;
  }
  /* Bảng "Kiểm tra thực phẩm" theo ca (Món / SL thực tế / Kết quả) tràn 77px ở khổ 375px. Đây là
     bảng NHẬP LIỆU, cố ý giữ đủ 3 cột thật (xem CLAUDE.md) nên không gom cột — chỉ thu hẹp ô nhập
     và bớt khoảng đệm.
     KHÔNG được hạ font-size xuống dưới 16px: Safari/iOS tự phóng to trang khi focus vào ô nhập có
     chữ nhỏ hơn 16px. Cũng giữ min-height 44px cho đủ vùng chạm. */
  .food-check-qty {
    max-width: 88px;
    padding-left: 6px;
    padding-right: 6px;
  }
  .food-checklist-table th,
  .food-checklist-table td {
    padding: 6px 3px;
  }

  /* 2 bảng thống kê tuần (dưới Lịch làm) có 12 và 9 cột — nhiều hơn hẳn các bảng khác. Riêng khuôn
     .responsive-desktop-table (ẩn 3 cột phụ) + nhãn tiền rút gọn vẫn chưa đủ vừa 375px: 7 cột ngày
     là dữ liệu chính không được ẩn, nên phần còn lại phải lấy từ padding (mặc định 9px 10px = 20px
     mỗi ô × 9 cột = 180px chỉ riêng khoảng đệm) và cỡ chữ. Đo sau khi áp: cả 2 bảng rộng đúng 313px
     = trọn bề rộng trong thẻ .card, không tràn.
     ĐÃ THỬ VÀ LOẠI `overflow-wrap: anywhere`: nó hạ min-content của MỌI ô xuống 1 ký tự nên trình
     duyệt bẻ cả nhãn ngắn — "AM" thành "A/M", "Full" thành "F/ull", header "T2" thành "T/2". */
  .schedule-stats-table th,
  .schedule-stats-table td {
    padding: 6px 1px;
    font-size: 11px;
  }
  /* Cột ngày phải ĐỀU NHAU: mặc định bảng chia bề rộng theo nội dung nên ngày trống co còn 19px
     trong khi ngày có ca dài phình lên 42px, nhìn xộc xệch (chủ quán báo). table-layout:fixed +
     cố định riêng cột tên -> 7 cột ngày tự chia đều phần còn lại. Cột đang display:none không tham
     gia chia, nên không phải khai gì thêm cho chúng. */
  .schedule-stats-table {
    table-layout: fixed;
  }
  /* Hàng tiêu đề ngày (T2..CN) dính lại khi cuộn — bảng dài (29 nhân viên, mỗi người còn 1 dòng
     phụ) nên cuộn tới giữa là mất tiêu đề, không biết cột nào là ngày nào để so sánh. Chép đúng
     khuôn .sticky-first-col mà lưới Lịch làm đang dùng.
     Không cần khai lại background: rule `table th` đã có var(--cream-100), nếu nền trong suốt thì
     dòng dữ liệu sẽ hiện xuyên qua tiêu đề khi cuộn.
     Lưu ý khi sửa sau này: sticky chỉ dính vào MÀN HÌNH khi không tổ tiên nào đặt overflow — 2 thẻ
     .card bọc 2 bảng này từng có overflow-x:auto (đã gỡ ở đợt thu gọn); thêm lại là tiêu đề sẽ dính
     vào thẻ rồi trôi mất khi cuộn qua thẻ. */
  .schedule-stats-table thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    box-shadow: 0 2px 3px -1px rgba(0, 0, 0, 0.12);
  }
  /* BẪY: với table-layout:fixed, cột đang display:none VẪN GIỮ CHỖ theo cách chia mặc định — đo được
     4 cột ẩn ăn mất 100/305px, làm cột tên bị bóp còn 25px. Phải khai width:0 cho chúng.
     Cũng KHÔNG dùng :first-child để nhắm cột tên: cột đầu tiên trong HTML là "Bộ phận" (đang ẩn),
     nên :first-child trỏ nhầm. Dùng class gắn thẳng trong template. */
  .schedule-stats-table .desktop-col {
    width: 0;
  }
  /* Bề rộng cột phải khai TOÀN BỘ bằng %, không được khai px:
     - Khai px cố định (76px + 7x32px = 300px) thì bảng đứng yên ở 300px trong khi thẻ chứa giãn
       theo màn hình -> trên máy rộng hơn 375px bảng chỉ chiếm ~83% thẻ, chừa khoảng trống bên phải
       (chủ quán đã chụp màn hình báo lỗi này).
     - Trộn px với % (name 76px + day calc((100% - 76px)/7)) cũng SAI: đo được trình duyệt lấy cơ sở
       tính % là 221px chứ không phải 305px của bảng, làm cột ngày co còn 21px.
     Khai cả 2 bằng % thì tổng luôn đúng 100% và bảng luôn lấp đầy thẻ ở mọi cỡ máy. */
  .schedule-stats-table .stats-col-name {
    width: 25%;
  }
  .schedule-stats-table .stats-col-day {
    width: calc(75% / 7);
  }
  /* Dòng phụ (đơn giá · tổng giờ · lương) trải hết bề ngang: nhét trong cột tên 76px thì bị vắt
     thành 4 dòng, đó là thứ làm bảng rối nhất. Chỉ hiện ở khổ điện thoại — máy tính đã có sẵn 3 cột
     riêng cho 3 số này. */
  .schedule-stats-table .stats-sum-row {
    display: table-row;
  }
  .schedule-stats-table .stats-sum-row td {
    padding-top: 0;
    padding-bottom: 6px;
    border-top: none;
    line-height: 1.5;
  }
  .schedule-stats-table th:first-child,
  .schedule-stats-table td:first-child {
    padding-left: 0;
  }
  .schedule-stats-table th:last-child,
  .schedule-stats-table td:last-child {
    padding-right: 0;
  }

  /* ===== Lưới lịch làm tuần (Lịch làm + Bảng nháp) thu gọn vừa màn hình =====
     Trước 19/08/2026 hai bảng này cố ý để cuộn ngang + dính cột đầu, vì "bản chất phải rộng"
     (1 cột tên + 7 cột ngày). Đo thật ở 375px: cần 1040px trong khung 305px — chủ quán phải kéo
     ngang liên tục nên yêu cầu đổi hẳn sang kiểu nén vừa màn hình, chấp nhận chữ nhỏ và bấm vào ô
     mới xem được giờ cụ thể.
     display:table (KHÔNG phải block) là bắt buộc — xem bẫy #4 trong CLAUDE.md: Safari/iOS bóp bảng
     display:block hẹp hơn thẻ chứa, Chromium không tái hiện được. */
  .schedule-week-table {
    display: table;
    width: 100%;
    overflow-x: visible;
    white-space: normal;
    /* Huỷ khung cuộn dọc riêng của .sticky-first-col — cho cả trang cuộn tự nhiên, thead vẫn dính
       theo viewport nhờ position:sticky. */
    max-height: none;
    overflow-y: visible;
    font-size: 11px;
  }
  .schedule-week-table .sched-col-name {
    width: 88px;
  }
  /* Cột tên chỉ còn 88px ở điện thoại -- tay cầm kéo phải nhỏ lại để chừa chỗ cho tên, và tên dài
     (2-3 chữ) tự xuống dòng bình thường (không ép nowrap cả ô, xem CLAUDE.md bẫy #5). */
  .schedule-week-table .sched-drag-handle {
    font-size: 13px;
    margin-right: 2px;
  }
  .schedule-week-table .sched-col-day {
    width: calc((100% - 88px) / 7);
  }
  /* Gỡ bề rộng tối thiểu của ô ngày (khai ngoài media query cho máy tính) — chính nó ép bảng rộng
     8 x 130px. */
  .schedule-week-table .sched-day-cell {
    min-width: 0;
    padding: 3px 2px;
  }
  .schedule-week-table th,
  .schedule-week-table td {
    padding: 4px 3px;
  }
  .schedule-week-table thead th {
    font-size: 10px;
    white-space: normal;
  }
  /* Mọi ô ca CÙNG MỘT KÍCH THƯỚC (chủ quán yêu cầu): hộp cố định 24px, chữ căn giữa cả 2 chiều.
     Trước đây chip co giãn theo nội dung nên nhãn dài bẻ 2 dòng làm ô cao gấp đôi ô "AM" bên cạnh. */
  .schedule-week-table .shift-chip {
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    font-size: 9.5px;
    padding: 0 1px !important;
    margin-bottom: 3px !important;
    line-height: 1.15;
    /* Nhãn ngắn (AM/PM/Full) và nhãn 5-6 ký tự đều phải nằm gọn 1 dòng — chỉ .shift-chip-long mới
       được bẻ dòng. Không có nowrap thì "18-23" bị bẻ thành "18-"/"23" trông như 2 ca khác nhau. */
    white-space: nowrap;
  }
  /* Cỡ chữ theo độ dài nhãn — CSS không tự đo được chữ nên class do template gắn theo số ký tự của
     nhãn rút gọn (xem 2 file _partial_*_schedule_*.html). Ô rộng ~31px, chip còn ~25px bên trong:
     5-6 ký tự ("10-23") cần 8.5px mới vừa 1 dòng. */
  .schedule-week-table .shift-chip-mid {
    font-size: 7.5px;
    letter-spacing: -0.2px;
  }
  /* Ca partime giờ lẻ ("14:30-18:30") không thể vừa 1 dòng ở cỡ chữ đọc được — cho bẻ 2 dòng NHƯNG
     vẫn nằm trong hộp 24px, nên ô vẫn bằng các ô khác và không mất thông tin giờ.
     overflow-wrap CHỈ đặt ở đây, KHÔNG đặt cho chip thường hay cho ô/bảng: đặt rộng hơn sẽ hạ
     min-content của mọi cột xuống 1 ký tự và trình duyệt bẻ luôn nhãn ngắn ("AM" thành "A/M",
     tiêu đề "T2" thành "T/2") — đã thử và loại. */
  /* Ca partime giờ lẻ ("13:30-17:30"): template tự cắt 2 mốc giờ thành 2 dòng bằng <br> và BỎ dấu
     gạch nối (dấu gạch giữa 2 dòng vừa vô nghĩa vừa chiếm chỗ trong ô ~31px).
     Nhờ có <br> nên KHÔNG cần overflow-wrap: anywhere nữa — bỏ được đúng cái rule dễ bẻ nhầm nhãn
     ngắn ("AM" thành "A/M", "T2" thành "T/2"). <br> vẫn xuống dòng kể cả khi white-space: nowrap. */
  .schedule-week-table .shift-chip-long {
    font-size: 7px;
    line-height: 1.05;
    letter-spacing: -0.2px;
  }
  /* Ẩn những thứ không thể vừa ô ~31px. Thao tác thay thế: bấm vào ca để xem chi tiết + xoá, bấm ô
     trống để thêm ca (xem openShiftDetail/openAddShiftForm trong hr.html). */
  .schedule-week-table .shift-chip-del,
  .schedule-week-table .add-shift-trigger,
  .schedule-week-table .shift-chip-extra,
  .schedule-week-table .sched-birthday-text,
  .schedule-week-table .sched-pending-text {
    display: none;
  }
  /* Ô CHƯA có ca: hiện khung viền đứt cùng cỡ 24px với dấu "+" ở giữa. Trước đây nút "+ Thêm ca" bị
     ẩn hẳn nên không có gì báo cho chủ quán biết là bấm vào ô trống sẽ xếp được lịch — tính năng coi
     như vô hình. Ô ĐÃ có ca vẫn ẩn nút (tránh rối và đội cao ô). */
  .schedule-week-table .add-shift-trigger-empty {
    display: flex !important;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 24px;
    border: 1px dashed var(--border) !important;
    border-radius: 6px;
    font-size: 13px !important;
    line-height: 1;
    color: var(--maroon-700);
    opacity: 0.55;
    padding: 0 !important;
  }
  /* Badge "Thiếu ca 10h-23h" không vừa ô — rút còn dấu cảnh báo, giữ title đầy đủ. Badge chiếm trọn
     bề ngang ô nên phải căn giữa, nếu không mỗi emoji sẽ dính mép trái. */
  .schedule-week-table .sched-gap-badge {
    font-size: 9px;
    padding: 1px 3px;
    text-align: center;
  }
  /* Ô thiếu nhiều ca cùng lúc: chỉ hiện dấu ⚠️ đầu tiên. Bấm vào nó, openGapDetail vẫn gom đủ MỌI
     cảnh báo của ô (nó dùng querySelectorAll — hàm này vẫn trả về cả phần tử display:none), nên
     không mất thông tin mà hàng "Thiếu ca" không bị cao gấp đôi. */
  .schedule-week-table .sched-gap-badge ~ .sched-gap-badge {
    display: none;
  }
  .schedule-week-table .sched-gap-text {
    display: none;
  }
  .schedule-week-table .sched-gap-icon {
    display: inline;
  }
  /* Nhãn "Lương x2.0" + nút xoá tràn/chồng lên nhau trong ô ngày ~31px (chủ quán báo ảnh chụp iPhone
     31/08/2026). Chữ "Lương" đã rút gọn ở template (chỉ còn "x2.0"), ở đây thu nhỏ cỡ chữ + đưa nút
     xoá xuống hẳn 1 dòng riêng (thay vì chen ngang cùng dòng) để không còn đè lên nhau. */
  .schedule-week-table [id^="holiday-badge-"] .badge {
    font-size: 8px;
    padding: 1px 4px;
    white-space: nowrap;
  }
  .schedule-week-table [id^="holiday-badge-"] .holiday-badge-del-btn {
    display: block;
    font-size: 11px;
    margin: 1px auto 0;
  }
  /* Cột tên: sao đánh giá thu nhỏ theo cho khỏi đẩy cột. */
  .schedule-week-table .star-rating {
    font-size: 9px;
  }

  /* Bảng tóm tắt lương chỉ có 2 cột (tên khoản / số tiền) — không được cuộn ngang kiểu bảng dữ
     liệu rộng ở trên, vì nhãn từng khoản khá dài sẽ đẩy luôn cột số tiền ra ngoài màn hình điện
     thoại (nhân viên xem lương chủ yếu bằng điện thoại). Cho phép xuống dòng bình thường. */
  .payslip-summary-table {
    display: table;
    overflow-x: visible;
    white-space: normal;
  }
  /* Bảng "Chấm công của tôi" (hôm nay + tháng): áp `table { display: block }` chung ở trên khiến
     bảng bị co hẹp lại theo nội dung thay vì rộng bằng thẻ card chứa nó trên Safari/iOS thật (xác
     nhận qua ảnh chụp điện thoại thật của chủ quán — Chromium giả lập không tái hiện được, và
     riêng khai lại `width: 100%` trong rule `table` phía trên KHÔNG đủ để sửa trên Safari thật).
     Cột đều ngắn (giờ/số giờ/ghi chú vài chữ) nên không cần cuộn ngang lẫn dính cột đầu như bảng
     dữ liệu dài ở nơi khác — quay về display:table mặc định (đúng như .payslip-summary-table) để
     trình duyệt tự giãn cột lấp đầy chiều rộng, cho xuống dòng bình thường thay vì cuộn. */
  #my-today-table-wrap table,
  #my-month-table-wrap table {
    display: table;
    overflow-x: visible;
    white-space: normal;
  }
  /* KHÔNG ép các cột ngày/giờ co về tối thiểu (width: 1%) — làm vậy dồn hết phần rộng dư vào cột
     Ghi chú, nên khi ghi chú trống (trường hợp thường gặp nhất) thì chữ các cột bị cụm hết sang
     lề trái và bỏ trống một khoảng lớn bên phải. Để trình duyệt tự chia đều phần dư cho mọi cột
     (hành vi mặc định của table-layout: auto + width: 100%), chỉ đặt chiều rộng TỐI THIỂU cho cột
     Ghi chú để khi ghi chú dài nó không bị bóp còn ~64px làm chữ vắt thành 5-6 dòng dựng đứng. */
  #my-today-table-wrap table thead th:last-child,
  #my-month-table-wrap table thead th:last-child {
    min-width: 110px;
  }
  #my-today-table-wrap table td:not(:last-child),
  #my-month-table-wrap table td:not(:last-child) {
    white-space: nowrap;
  }
  /* Danh sách nguyên liệu (tab Tồn kho): trước đây 5 cột (tên/đơn vị/tồn kho/nút sửa+chuyển BTP/
     nút xoá) + `white-space: nowrap` khiến bảng rộng ~650px, cuộn ngang trên điện thoại thì cột
     tên dính chiếm gần hết màn hình, phải kéo qua lại mới đọc nổi tồn kho (chủ quán báo bằng ảnh
     chụp iPhone). Nay gom còn 3 cột (bỏ cột Đơn vị vì đã hiện ngay cạnh số tồn, dồn nút Chuyển
     BTP/Xoá vào panel "Sửa"), nên cho xuống dòng bình thường như .payslip-summary-table thay vì
     cuộn — tên nguyên liệu dài tự ngắt dòng, không cột nào bị đẩy ra ngoài màn hình.
     Bảng món ăn theo nhóm (trang Menu) cũng đúng y hệt: chỉ 3 cột (tên món/giá/nút) mà nowrap +
     3 nút "Công thức/Sửa/Xoá" nằm 1 hàng đẩy bảng rộng 450-610px, phải kéo ngang liên tục mới
     thấy được giá. Nay nút Xoá chuyển vào panel "Sửa", 2 nút còn lại cho xuống hàng được.
     Bảng bán thành phẩm (5 cột) và 2 bảng giỏ hàng ở Nhập thu chi (6-7 cột) cũng gom về 3 cột
     theo cùng nguyên tắc: thông tin phụ (đơn vị, SL, NSX/HSD, mẻ chuẩn) xuống dòng muted dưới
     tên, ô nhập giảm giá từng món tách sang 1 hàng riêng bật/tắt bằng nút.
     Class dùng chung cho mọi bảng đã gom theo kiểu này là `.mobile-stacked-table`: cột 1 là danh
     tính của dòng (tên + mọi thông tin phụ xếp chồng bên dưới dạng muted), các cột sau đều ngắn
     và giữ nowrap. Khi gom thêm bảng mới chỉ cần gắn class đó, không phải thêm selector ở đây.
     Bảng Kiểm tồn cuối ngày (kiểm số lượng đếm được so với tồn hệ thống) cũng đã gom theo cách
     này, NHƯNG khác 1 điểm quan trọng: số "Tồn hệ thống" (chỉ chủ quán thấy) được giữ lại làm
     dòng tham chiếu ngay trong cột 1, sát cạnh ô nhập ở cột 2 — để chủ quán vẫn so sánh được
     ngay khi gõ số, không phải cuộn lên xuống. KHÔNG áp cho Kiểm kê CCDC — bảng đó có nhiều ô nhập
     cùng lúc trên 1 hàng nên cuộn ngang + dính cột đầu vẫn đúng hơn là gom.
     CẬP NHẬT 19/08/2026: lịch làm theo tuần TRƯỚC ĐÂY cũng nằm trong danh sách ngoại lệ này ("7 cột
     ngày, bản chất phải rộng"), nhưng chủ quán yêu cầu đổi — nay đã thu gọn vừa màn hình bằng
     .schedule-week-table (nhãn ca rút gọn + ẩn nút, bấm vào ô để xem chi tiết/thêm ca). Xem khối
     ".schedule-week-table" trong media query. */
  #ingredient-list-table,
  #sfp-list-table,
  #packaging-list-table,
  .cart-table,
  .menu-items-table,
  .mobile-stacked-table {
    display: table;
    overflow-x: visible;
    white-space: normal;
  }
  /* Chỉ nút và badge giữ nowrap (chữ trên nút không được vắt dọc từng chữ). KHÔNG ép nowrap cả ô
     — làm vậy tên người duyệt / ghi chú dài sẽ kéo cột phình ra, bóp cột 1 (tên) còn ~120px. */
  .mobile-stacked-table thead th,
  .mobile-stacked-table .btn,
  .mobile-stacked-table button,
  .mobile-stacked-table .badge {
    white-space: nowrap;
  }
  /* Hàng phụ mở ra bằng nút (td colspan phủ hết bảng) luôn phải cho xuống dòng lại — bên trong là
     form nhiều ô, nowrap sẽ đẩy chúng tràn ra ngoài thẻ trắng. */
  .mobile-stacked-table tbody td[colspan] {
    white-space: normal;
  }
  /* Mặc định trình duyệt canh <td> giữa theo chiều dọc (vertical-align: middle). Cột 1 sau khi gom
     thường cao hơn hẳn cột 2 (nhiều dòng muted chồng lên nhau, có khi cả list tiến độ dài) — nếu
     để mặc định thì nút ở cột 2 (vốn thấp) bị đẩy xuống canh giữa toàn hàng, trông như dính lơ lửng
     vào giữa nội dung cột 1 thay vì nằm ngay đầu hàng. Ép cả 2 cột về top để nút luôn ở đúng vị trí
     ngang hàng với dòng đầu tiên (tên/tiêu đề) của cột 1. */
  .mobile-stacked-table td {
    vertical-align: top;
  }
  /* Cột nút giữ nowrap để chữ trên nút không bị vắt dọc từng chữ. Cột tồn kho KHÔNG nowrap cả ô:
     số + đơn vị đã có span nowrap riêng trong template, còn badge "Sắp hết"/"Âm kho" phải được
     xuống dòng — nếu bắt cả ô nowrap thì "3 cái Sắp hết" nằm 1 hàng làm cột rộng 158px, đẩy bảng
     tràn ra ngoài thẻ trắng. */
  #ingredient-list-table thead th,
  #sfp-list-table thead th,
  #packaging-list-table thead th,
  .cart-table thead th,
  .menu-items-table thead th,
  #ingredient-list-table tbody td:nth-child(3),
  #sfp-list-table tbody td:nth-child(3),
  #packaging-list-table tbody td:nth-child(3),
  .cart-table tbody td:nth-child(2),
  .cart-table tbody td:nth-child(3),
  .menu-items-table tbody td:nth-child(2),
  .menu-items-table tbody td:nth-child(3) {
    white-space: nowrap;
  }
  /* Hàng nhập giảm giá của từng món (td colspan=3) phải cho xuống dòng lại — bên trong có select
     + ô số + nút, nowrap sẽ đẩy chúng tràn ra ngoài. */
  .cart-table tbody tr[id^="cart-discount-"] td {
    white-space: normal;
  }
  /* Bảng ƯU TIÊN GIAO DIỆN MÁY TÍNH (Danh sách nhân viên, Bảng lương): khác với
     .mobile-stacked-table (đổi hẳn cấu trúc HTML, mất cột trên cả máy tính), bảng loại này giữ
     NGUYÊN cấu trúc nhiều cột — HTML render đủ cột cho cả 2 khổ màn hình, ở đây chỉ ẩn bớt cột
     phụ bằng CSS. Cột nào có class `desktop-col` (thường là số liệu đã có bản tóm tắt riêng trong
     cột đầu) sẽ ẩn hẳn trên điện thoại; khối `.mobile-summary` nằm trong cột đầu (ẩn trên máy
     tính) sẽ hiện ra thay thế. Ưu điểm: 1 lần sửa dữ liệu qua JS phải cập nhật CẢ 2 nơi (desktop
     + mobile) — xem `submitUpdateEmployee`, `submitPayrollKpi`, `submitPayrollWorkdays`,
     `submitAddAdjustment` trong hr.html, mỗi id đều có bản sao `-m` cho mobile. Dùng khi chủ quán
     cần xem đối chiếu nhiều cột cùng lúc trên máy tính (bảng lương, danh sách lương nhân viên) —
     khác với các bảng thao tác nhanh trên điện thoại (Giao việc, Vệ sinh...) vốn hợp với
     .mobile-stacked-table hơn vì gần như chỉ dùng trên điện thoại. */
  .responsive-desktop-table {
    display: table;
    overflow-x: visible;
    white-space: normal;
  }
  .responsive-desktop-table .desktop-col {
    display: none;
  }
  .responsive-desktop-table .mobile-summary {
    display: block;
    margin-top: 4px;
  }
  /* Đổi chiều cặp nhãn đầy đủ/rút gọn — xem ghi chú ở .mini-only phía trên file. */
  .full-only {
    display: none;
  }
  .mini-only {
    display: inline;
  }
  .responsive-desktop-table td {
    vertical-align: top;
  }
  /* Bảng .responsive-desktop-table ở khổ này là display:table + overflow-x:visible (xem rule trên,
     cố ý để Safari/iOS không bóp bảng hẹp hơn thẻ chứa — bẫy #4 trong CLAUDE.md). Hệ quả: nội dung
     rộng hơn khung thì bảng PHÌNH RA NGOÀI thẻ trắng chứ không cuộn, không cắt. Hàng nút hành động
     (vd "Đặt lại mật khẩu / Khoá / Xoá" ở Tài khoản nhân viên) xếp flex 1 hàng + .btn nowrap nên
     luôn vượt 375px và lòi hẳn ra ngoài thẻ — chủ quán đã báo lỗi này thật.
     Cho phép các nhóm nút trong ô xuống dòng để bảng luôn vừa khung. flex-wrap không có tác dụng
     lên div thường (không phải flex container) nên rule này vô hại với mọi ô khác. */
  .responsive-desktop-table td > div {
    flex-wrap: wrap;
  }
  /* Bảng nào thực sự phải cuộn ngang thì giữ cột đầu dính lại (cột đầu luôn là tên món / nguyên
     liệu / nhân viên / hạng mục — tức là danh tính của dòng). Cuộn sang phải mà mất cột này thì
     không biết đang nhập số cho dòng nào — nguy hiểm nhất ở "Kiểm tồn cuối ngày", nơi nhân viên
     gõ số đếm được vào đúng dòng nguyên liệu. Class has-x-scroll do markScrollableTables()
     trong base.html gắn, chỉ gắn cho bảng bị tràn thật nên bảng vừa khung không bị viền thừa. */
  table.has-x-scroll thead th:first-child,
  table.has-x-scroll tbody td:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
  }
  table.has-x-scroll tbody td:first-child {
    background: #fff;
    box-shadow: 2px 0 3px -1px rgba(0, 0, 0, 0.12);
  }
  table.has-x-scroll tr:hover td:first-child {
    background: #fbf6ef;
  }
  .sticky-first-col {
    max-height: 70vh;
    overflow-y: auto;
  }
  .sticky-first-col th:first-child,
  .sticky-first-col td:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
    box-shadow: 2px 0 3px -1px rgba(0, 0, 0, 0.12);
  }
  .sticky-first-col td:first-child {
    background: #fff;
  }
  .sticky-first-col th:first-child {
    background: var(--cream-100);
  }
  .sticky-first-col thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--cream-100);
    box-shadow: 0 2px 3px -1px rgba(0, 0, 0, 0.12);
  }
  .sticky-first-col thead th:first-child {
    z-index: 3;
  }
  /* Dai tab con trong 1 trang (vd Nhap kho/Danh sach/Ban thanh pham... trong Ton kho) chuyen thanh
     thanh icon noi (floating pill) day man hinh kieu Threads -- cach le trai/phai/day mot khoang
     thay vi dinh sat canh, ten tab hien len H1 dau trang qua JS (gkSyncTabTitle trong app.js),
     khong con hien chu ngay tren thanh nay nua. */
  .tabs {
    position: fixed;
    left: 12px;
    right: 12px;
    bottom: calc(12px + env(safe-area-inset-bottom, 0px));
    z-index: 50; /* duoi .sidebar/.sidebar-backdrop (55/60), tren .mobile-topbar (45) */
    margin-bottom: 0;
    display: flex;
    flex-wrap: nowrap; /* khong wrap -- qua nhieu tab (vd settings.html 9 tab) thi cuon ngang */
    gap: 0;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 999px; /* dang vien thuoc/pill noi, khong dinh sat canh man hinh nhu truoc */
    box-shadow: 0 4px 16px rgba(74, 26, 34, 0.18);
    padding: 6px 6px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .tab-btn {
    position: relative; /* diem neo cho badge */
    flex: 1 0 48px; /* chia deu, khong co lai duoi 48px -- qua nhieu tab thi cuon thay vi bop */
    min-width: 48px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 9px 4px;
    border: none;
    border-radius: 999px;
    background: transparent;
    color: var(--text-muted);
  }
  .tab-btn.active,
  .tab-btn.active.tab-income,
  .tab-btn.active.tab-expense {
    background: var(--maroon-100); /* chip nen sau icon dang chon, giong Threads */
    border-color: transparent;
    color: var(--maroon-700);
  }
  .tab-btn.active.tab-income {
    color: var(--green);
  }
  .tab-btn.active.tab-expense {
    color: var(--red);
  }
  .tab-icon {
    width: 24px; /* tu 16px -- to hon, de bam hon, ngang tam Instagram */
    height: 24px;
    vertical-align: 0;
  }
  /* Luon chi hien icon (ca tab dang active) -- khac voi truoc day chi an chu tab CHUA chon. */
  .tab-btn .tab-label {
    display: none;
  }
  /* Badge doi tu nam canh chu (sap an) sang cham/so ghim o goc icon -- khuon chuan bottom-nav. */
  .tab-btn .badge {
    position: absolute;
    top: 2px;
    left: calc(50% + 6px);
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 999px;
    font-size: 10px;
    line-height: 16px;
    text-align: center;
  }
  /* Chua cho noi dung trang de thanh tab moi khong de len the cuoi cung. :has() chi khoanh vung
     dung trang co .tabs la con truc tiep -- trang khong co tab (Tong quan...) khong bi anh huong. */
  .main-content:has(> .tabs) {
    padding-bottom: calc(84px + env(safe-area-inset-bottom, 0px));
  }
  /* gk-update-banner cung dinh day 12px -- nang len tren thanh tab moi de khong de len nhau. */
  .gk-update-banner {
    bottom: calc(84px + env(safe-area-inset-bottom, 0px));
  }
}

/* Đang chờ duyệt chấm công: trên máy tính hiện sẵn khối 4 nút xử lý (Giữ giờ thực tế / Dùng giờ
   theo ca / Tự nhập số giờ khác / Tự nhập giờ vào-ra) ngay từ đầu, không cần bấm "Xử lý" trước —
   chủ quán xử lý hàng trăm dòng liên tiếp nên bớt 1 lần bấm mỗi dòng đỡ tốn thao tác hơn hẳn. Chỉ
   áp dụng ở >780px: điện thoại vẫn giữ hành vi cũ (ẩn mặc định, bấm "Xử lý" để mở qua toggleEdit())
   vì không đủ chỗ hiện sẵn cho mỗi dòng. !important để thắng inline style="display:none" mà
   toggleEdit() đang thao tác trực tiếp trên dòng này. */
@media (min-width: 781px) {
  .attendance-resolve-row {
    display: table-row !important;
  }
  .attendance-resolve-toggle-btn {
    display: none;
  }
}

.txn-row:hover td {
  background: var(--cream-100);
}

.txn-edit-summary:hover {
  background: var(--cream-100);
}

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(43, 26, 31, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: 20px;
}

.modal-box {
  background: #fff;
  border-radius: 14px;
  max-width: 640px;
  width: 100%;
  max-height: 85vh;
  overflow-y: auto;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}

/* Modal dùng chung (#txn-modal-overlay) mặc định 640px — đủ cho phần lớn nội dung dạng
   txn-detail-row, nhưng 1 số bảng nhiều cột (VD Chi tiết bảng lương) bị bóp quá chật ở 640px
   trên máy tính. Bảng nào cần .responsive-desktop-table thật sự có chỗ để hiện cột thì gắn thêm
   class này lên .modal-box khi mở (xem showPayrollDetail trong hr.html), và closeTxnModal() ở
   base.html gỡ lại khi đóng để không ảnh hưởng các modal khác dùng chung 1 overlay. */
.modal-box.modal-box-wide {
  max-width: 960px;
}

/* Popup goi y go ten tu viet (thay <input list> + <datalist> goc trinh duyet, xem attachSuggest/
   registerSuggest trong app.js) -- 1 div dung chung, dinh vi bang JS (getBoundingClientRect) va
   gan thang vao document.body nen khong bi cat hinh boi overflow cua bang/the card chua input. */
.gk-suggest-popup {
  position: absolute;
  z-index: 1200;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(74, 26, 34, 0.18);
  overflow: hidden;
}

.gk-suggest-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  padding: 9px 12px;
  cursor: pointer;
  font-size: 14px;
  border-bottom: 1px solid var(--cream-100);
}

.gk-suggest-item:last-child {
  border-bottom: none;
}

.gk-suggest-item-label {
  color: var(--text-dark);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.gk-suggest-item-sub {
  color: var(--text-muted);
  font-size: 12px;
  flex-shrink: 0;
}

.gk-suggest-item:hover,
.gk-suggest-item.active {
  background: var(--cream-50);
}

/* Dung cho ten doi/nguoi choi nam LAN VOI VAN BAN khac trong cung 1 dong/1 o (vd "A vs B", danh
   sach bang dau) -- khong the click ca hang nhu .txn-row vi 1 dong co nhieu thuc the bam rieng
   duoc. Dung LAI dung mau hover cua .txn-row (background: var(--cream-100)) thay vi tu nghi kieu
   gach chan rieng, de bao hieu "bam duoc" dung theo quy uoc app da dung o moi noi khac. */
.tournament-team-link {
  cursor: pointer;
  border-radius: 4px;
  padding: 0 3px;
  margin: 0 -3px;
}

.tournament-team-link:hover,
.tournament-team-link:active {
  background: var(--cream-100);
}

/* Vong quay may man (quay chon thanh vien / quay boc tham bang, xem runSpinWheel() trong
   page-tournament.js) -- dung lai .modal-overlay lam nen mo, phan dia quay dung 1 the div rieng
   voi background la conic-gradient dung JS tinh, xoay bang transition transform 5s. */
.spin-wheel-box {
  background: #fff;
  border-radius: 16px;
  padding: 22px 20px 26px;
  text-align: center;
  max-width: 92vw;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}

.spin-wheel-pointer {
  font-size: 26px;
  line-height: 1;
  color: var(--maroon-700);
  margin-bottom: -6px;
  position: relative;
  z-index: 2;
}

.spin-wheel {
  position: relative;
  width: 260px;
  height: 260px;
  border-radius: 50%;
  margin: 0 auto;
  border: 6px solid var(--maroon-700);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.25);
  transition: transform 5s cubic-bezier(0.17, 0.67, 0.1, 0.99);
}

.spin-wheel-labels {
  position: absolute;
  inset: 0;
}

.spin-wheel-label {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 116px;
  height: 16px;
  line-height: 16px;
  margin-top: -8px;
  font-size: 11px;
  font-weight: 700;
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: center;
  box-sizing: border-box;
  transform-origin: 0% 50%;
}

/* Nua dia ben trai (centerAngle > 180, xem runSpinWheel()) can lat neo sang canh phai + doi chieu doc
   (rtl) -- neu khong, chu se hien nguoc/doc nguoc, va text-overflow:ellipsis (chi xu ly canh phai
   theo logic LTR mac dinh) se cat cut ten dai o canh trai ma khong hien dau "..." dung cho. */
.spin-wheel-label.spin-wheel-label-flip {
  left: auto;
  right: 50%;
  text-align: center;
  direction: rtl;
  transform-origin: 100% 50%;
}

.spin-wheel-fireworks {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.spin-wheel-spark {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 50%;
  animation: spin-wheel-spark-burst 900ms ease-out forwards;
}

@keyframes spin-wheel-spark-burst {
  0% {
    transform: translate(0, 0) scale(1);
    opacity: 1;
  }
  100% {
    transform: translate(var(--dx), var(--dy)) scale(0.2);
    opacity: 0;
  }
}

.spin-wheel-result {
  margin-top: 16px;
  font-size: 16px;
  font-weight: 700;
  color: var(--maroon-700);
  min-height: 24px;
}

.spin-wheel-confirm-btn {
  margin-top: 14px;
}

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  background: #fff;
}

.modal-header h3 {
  margin: 0;
  color: var(--maroon-700);
  font-size: 17px;
}

.modal-close {
  border: none;
  background: transparent;
  font-size: 22px;
  cursor: pointer;
  color: var(--text-muted);
  line-height: 1;
}

.modal-body {
  padding: 20px;
}

.txn-detail-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
}

.txn-detail-row span:first-child {
  color: var(--text-muted);
}

.txn-image-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 10px;
  margin-top: 4px;
}

.txn-image-grid a img {
  width: 100%;
  height: 120px;
  object-fit: cover;
  border-radius: 8px;
  border: 1px solid var(--border);
}

.star-rating {
  position: relative;
  display: inline-block;
  font-size: 13px;
  line-height: 1;
  letter-spacing: 1px;
  vertical-align: middle;
}

.star-rating-track {
  color: var(--border);
}

.star-rating-fill {
  position: absolute;
  top: 0;
  left: 0;
  overflow: hidden;
  width: var(--pct, 0%);
  white-space: nowrap;
  color: var(--gold);
}

/* Dải nhắc "đã có bản cập nhật mới" — chỉ hiện khi app tự phát hiện bản mới mà chưa nạp lại được
   ngay (đang gõ dở/đang mở popup/đang lưu). Xem applyUpdateIfSafe trong app/static/js/app.js. */
.gk-update-banner {
  position: fixed;
  left: 12px;
  right: 12px;
  bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  max-width: 520px;
  margin: 0 auto;
  padding: 10px 14px;
  border-radius: 10px;
  background: var(--maroon-700);
  color: #fff;
  font-size: 14px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.22);
}

.gk-update-banner button {
  flex: 0 0 auto;
  padding: 6px 14px;
  border: none;
  border-radius: 8px;
  background: #fff;
  color: var(--maroon-700);
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}
