/* ============================================================
   OneWorld — Design System & Custom Theme for Filament v3
   Shell giống POC (demo/assets/css/app.css) — Đợt 2 + Đợt 4
   Nguồn giá trị: docs/ui-audit-2026-08-25/BAO-CAO-uiux-vs-poc.md §2, §3.4, §3.5
   Load pipeline: Filament phát hành CSS core dist riêng của nó; theme này là MỘT file
   tĩnh public/css/filament-theme.css, inject bằng render hook trong AdminPanelProvider
   với cache-bust ?v=filemtime(). KHÔNG đi qua Vite — vì vậy mọi utility Tailwind dùng
   trong blade custom phải được viết thành class tĩnh (tiền tố ow- hoặc
   .sidebar-custom-footer) ngay trong file này (dist Filament không compile
   arbitrary-value utilities).
   ============================================================ */

:root {
  /* Nền trang đúng POC: #f2f4fa (bản cũ #f4f6fa — audit §2 đánh dấu "gần khớp") */
  --ow-bg: #f2f4fa;
  --ow-card: #ffffff;
  --ow-ink: #1c2440;
  /* T4.94 — #6f7893 chỉ 4.39:1 trên trắng / 3.99:1 trên --ow-bg: trượt AA 4.5
     đúng ở cỡ chữ nhỏ (nhãn ô, đầu cột bảng, ghi chú). #5f6880 = 5.56:1 /
     5.05:1 — cùng họ slate, và chính là giá trị khối quick-login đã chọn từ
     trước, nên cả app còn MỘT sắc xám phụ. */
  --ow-muted: #5f6880;
  --ow-line: #e6eaf3;

  /* T4.94 — bề mặt bảng, dùng chung cho .fi-ta-* (Filament Resource) và .ow-table
     để hai kiểu bảng trong app đọc như một hệ. */
  --ow-table-head-bg: #fafbfe;
  --ow-table-line: #f0f2f8;

  --ow-accent: #f59e0b;
  --ow-accent-strong: #fbbf24;
  --ow-accent-ink: #b45309;
  --ow-accent-soft: #fef3e2;

  /* Bảng màu sidebar navy — nguyên văn từ demo/assets/css/app.css :root */
  --ow-side-top: #1f2547;
  --ow-side-deep: #191e3a;
  --ow-side-ink: #c0c9e0;
  --ow-side-dim: #8a96c2;

  /* Sidebar accent riêng (xanh dương) — KHÔNG đụng --ow-accent vì còn dùng
     bởi button-primary, tag pill, dot… ở những nơi khác trong app. */
  --ow-side-accent: #3b82f6;
  --ow-side-accent-strong: #60a5fa;
  --ow-side-accent-ink: #1d4ed8;

  --ow-green: #22c55e;
  --ow-green-soft: #e6f9ee;
  --ow-green-ink: #15803d;

  --ow-red: #ef4444;
  --ow-red-soft: #feecec;
  --ow-red-ink: #b91c1c;

  --ow-blue: #3b82f6;
  --ow-blue-soft: #e8f0fe;
  --ow-blue-ink: #1d4ed8;

  --ow-orange: #f59e0b;
  --ow-orange-soft: #fef3e2;
  --ow-orange-ink: #b45309;

  --ow-purple: #8b5cf6;
  --ow-purple-soft: #f0ebfe;
  --ow-purple-ink: #6d28d9;

  --ow-radius: 16px;
  --ow-radius-sm: 10px;
  --ow-shadow: 0 1px 2px rgba(28, 36, 64, 0.05), 0 6px 20px rgba(28, 36, 64, 0.06);
}

/* ================= BASE / BODY ================= */
/* GỐC (html) là NẮM VẶN THANG CHỮ của cả panel — xem 3 "nấc" ≥1920/2560/3440
   cuối file: mỗi nấc đổi `html { font-size }`, và VÌ MỌI CỠ CHỮ TRONG THEME ĐÃ
   VIẾT BẰNG rem nên toàn bộ chữ co theo đúng tỉ lệ đó.
   Chiều ngược lại cũng đúng và là lý do của 13/09: đặt root = 14px thì mọi rem
   của Filament (sidebar 15rem, spacing, icon) co 12,5% mà không lỗi nào báo ra —
   root chỉ được dùng để PHÓNG, không dùng để THU NHỎ mặc định.
   Cấm quay lại px cho font-size: `px` là tĩnh tuyệt đối (MDN), nó làm nấc màn
   hình lớn thành vô hiệu — đó chính là "box thì responsive mà text không". */
/* 13/09 · LAPTOP — bỏ VÙNG CHẾT 1440→1920px. Chủ dự án ngồi Windows laptop
 * 1366×768 / 1536×864: ba nấc rời 1920/2560/3440 (09/09) không BAO GIỜ kích hoạt
 * trên máy đó, nên kéo cửa sổ từ 1366 ra 1728 — hộp rộng thêm 360px mà không một
 * chữ nào to thêm ("box có responsive, text không"). Từ đây gốc chạy theo đường
 * LIÊN TỤC, nhưng chỉ TỪ 1536px trở lên:
 *   · dưới 1536px: có định 16px — AC #4 đã hứa "ở 1440×900 không đổi một px nào",
 *     và cũng là sàn cho kế toán (không ai đọc chữ nhỏ hơn hôm nay);
 *   · 1536 → 16,0px · 1728 → 16,5px · 1920 → 17,0px (khớp đúng nấc đã duyệt);
 *   · ≥1920px: nấc rời phía dưới thắng (cùng specificity, nguồn sau) → 17/19/21px
 *     ở 1920/2560/3440 giữ nguyên như 09/09.
 * Test `đường liền tục chỉ nhích gốc từ 1536px…` gác cả hai ràng buộc đó (nó vừa
 * bắt được bản 13,53+0,18vw rời sàn từ 1372px = phá AC #4).
 * ĐÂY LÀ CHỖ DUY NHẤT còn dùng px cho font-size ngoài 6 neo: nó là CÁI THƯỚC,
 * không phải một cỡ chữ — CssType::declarations() bỏ qua vì ở sau `font-size:`
 * là `clamp(` chứ không phải con số. */
html {
  font-size: clamp(16px, 12px + 0.26vw, 17px);
}

body, .fi-body {
  background: var(--ow-bg) !important;
  color: var(--ow-ink) !important;
  font-family: 'Be Vietnam Pro', 'Segoe UI', system-ui, sans-serif !important;
  -webkit-font-smoothing: antialiased;
  font-size: 0.875rem /* 14px */ !important;
}

/* fixbug/s02-s10-t4.1-ui-cleanup: giảm padding ngang .fi-main từ 32px xuống
   22px (~10px mỗi bên). Lý do: user báo "2 bên table hơi trống, thêm cột
   thì cảm giác thiếu chỗ" — kéo sát content vào giữa, tiết kiệm 20px
   width cho MỌI bảng (không phải chỉ S02/S10). Tương thích với cả card
   dashboard, infolist, widget — không chỉ .fi-ta-ctn. */
.fi-main {
  background: var(--ow-bg) !important;
  min-height: 100vh !important;
  padding-left: 22px !important;
  padding-right: 22px !important;
}

/* ================= SIDEBAR NAVY (POC demo/assets/css/app.css .sidebar) =================
   Gradient dọc #1f2547→#191e3a, chữ item #9aa3c7, section label #5d6690,
   item ACTIVE nền gradient ngang cam + chữ #fbbf24 + thanh dọc 3.5px sát mép trái. */
.fi-sidebar,
aside.fi-sidebar {
  background: linear-gradient(180deg, var(--ow-side-top) 0%, var(--ow-side-deep) 100%) !important;
  color: var(--ow-side-ink) !important;
  border-right: none !important;
  box-shadow: none !important;
  z-index: 60 !important;
}

/* Filament tự tắt transition ở desktop (lg:transition-none) — thu/mở rail
   snap tức khắc, giật cục. Ghi đè để mượt như neo-tch/thuanphat
   (transition 0.3s ease-in-out trên width, không đụng translate mobile).
   Trạng thái thu gọn KHÔNG có class w-[--sidebar-width] (Filament bỏ hẳn
   utility này khi collapsed) → width rơi về giá trị không xác định, CSS
   transition không nội suy được từ/đến giá trị đó (giống `auto`) nên vẫn
   snap dù đã có transition. Phải tự gán width cứng cho cả 2 trạng thái. */
@media (min-width: 1024px) {
  .fi-sidebar {
    transition: width 0.3s ease-in-out !important;
  }

  .fi-sidebar:not(.fi-sidebar-open) {
    width: var(--collapsed-sidebar-width) !important;
  }

  /* Khi rail thu gọn: ẩn chữ logo, footer, nhãn nhóm — desktop only.
     Trạng thái thật trong markup Filament v3 là .fi-sidebar-open
     (vendor .../components/sidebar/index.blade.php); bản cũ dùng
     .fi-sidebar-closed / [data-sidebar-closed] không hề tồn tại. */
  .fi-sidebar:not(.fi-sidebar-open) .fi-sidebar-header-text,
  .fi-sidebar:not(.fi-sidebar-open) .sidebar-custom-footer,
  .fi-sidebar:not(.fi-sidebar-open) .fi-sidebar-group-label {
    display: none !important;
  }
}

/* Sidebar Brand Header — nằm thẳng trên nền navy, bỏ ring trắng của Filament */
.fi-sidebar-header {
  height: 72px !important;
  padding: 16px 18px !important;
  background: transparent !important;
  border-bottom: none !important;
  box-shadow: none !important;
  display: flex !important;
  align-items: center !important;
}

.fi-sidebar-header a,
.fi-sidebar-header .fi-logo {
  display: flex !important;
  align-items: center !important;
  gap: 12px !important;
  text-decoration: none !important;
}

/* Chữ brand trong view blade đang hardcode màu ink (#1c2440) cho nền trắng —
   ghi đè bằng CSS (không sửa view: thuộc quyền worker A/D) để đọc được trên navy.
   Tên brand: trắng như POC (.brand-name{color:#fff}); phụ đề: #9aa3c7. */
.fi-sidebar-header .fi-sidebar-header-text > span:first-child {
  color: #ffffff !important;
}

.fi-sidebar-header .fi-sidebar-header-text > span:last-child {
  color: var(--ow-side-ink) !important;
}

/* Sidebar Nav Scroll Area — trong suốt để lộ gradient navy */
.fi-sidebar-nav {
  padding: 6px 12px 12px !important;
  background: transparent !important;
}

.fi-sidebar-nav::-webkit-scrollbar {
  width: 0 !important;
}

/* Sidebar Section Headers (QUẢN LÝ, HOẠT ĐỘNG...) — POC .nav-section:
   10px / 700 / letter-spacing .14em / uppercase / #5d6690 */
.fi-sidebar-group {
  margin-top: 8px !important;
  margin-bottom: 2px !important;
}

.fi-sidebar-group-label,
.fi-sidebar-group-label span,
.fi-sidebar-group-label button,
.fi-sidebar-group-label button span {
  font-size: 0.6875rem /* 11px */ !important;
  font-weight: 700 !important;
  letter-spacing: 0.14em !important;
  text-transform: uppercase !important;
  color: var(--ow-side-dim) !important;
  padding: 14px 10px 6px !important;
  cursor: default !important;
}

/* Sidebar Navigation Items — POC .nav-item: chữ #9aa3c7 */
.fi-sidebar-item {
  margin: 2px 0 !important;
}

.fi-sidebar-item-button {
  display: flex !important;
  align-items: center !important;
  gap: 11px !important;
  padding: 9.5px 12px !important;
  border-radius: 10px !important;
  font-size: 0.875rem /* 14px */ !important;
  font-weight: 600 !important;
  color: var(--ow-side-ink) !important;
  position: relative !important;
  transition: all 0.15s ease !important;
  background: transparent !important;
}

/* Hover: nền trắng mờ 10% + chữ sáng #e6eaf6 (POC .nav-item:hover, 6% quá nhẹ
   nên tăng lên 10% để nhìn thấy ranh giới với item chưa hover trên nền navy) */
.fi-sidebar-item-button:hover {
  background: rgba(255, 255, 255, 0.10) !important;
  color: #e6eaf6 !important;
}

.fi-sidebar-item-icon {
  width: 18px !important;
  height: 18px !important;
  flex: 0 0 18px !important;
  color: var(--ow-side-ink) !important;
  opacity: 0.85 !important;
}

/* Label text (span con của button) — Filament apply Tailwind utility
   text-gray-700 dark:text-gray-200 inline lên span này, lấn át color kế thừa
   từ button. Phải style trực tiếp lên label với !important để thắng. */
.fi-sidebar-item-label {
  color: var(--ow-side-ink) !important;
  font-size: 0.875rem /* 14px */ !important;
  font-weight: 600 !important;
}

.fi-sidebar-item-button:hover .fi-sidebar-item-icon {
  color: #e6eaf6 !important;
  opacity: 1 !important;
}

/* Active Nav Item — block xanh dương đặc rgba(59,130,246,.18) (POC cũ dùng
   gradient cam mờ 0.20→0.06 → nhòe vào navy, mất ranh giới "đang ở đây").
   Token --ow-side-accent-ink dùng cho chữ & icon, đủ tương phản trên navy. */
.fi-sidebar-item-active .fi-sidebar-item-button,
.fi-sidebar-item-button.fi-active {
  background: rgba(59, 130, 246, 0.18) !important;
  color: var(--ow-side-accent-strong) !important;
  font-weight: 600 !important;
}

/* Active label: Tailwind utility text-primary-600 inline của Filament cũng
   áp lên span, đè màu cam. Phải !important để xanh dương thắng. */
.fi-sidebar-item-active .fi-sidebar-item-label,
.fi-sidebar-item-button.fi-active .fi-sidebar-item-label {
  color: var(--ow-side-accent-strong) !important;
}

.fi-sidebar-item-active .fi-sidebar-item-button .fi-sidebar-item-icon,
.fi-sidebar-item-button.fi-active .fi-sidebar-item-icon {
  color: var(--ow-side-accent-strong) !important;
  opacity: 1 !important;
}

/* Thanh dọc xanh dương 3.5px sát mép trái item (POC .nav-item.active::before,
   width 3.5px, bo góc phía phải). Giữ left:0 tương đối với nút — nav của
   Filament có overflow-x:hidden nên đẩy âm sẽ bị cắt. */
.fi-sidebar-item-active .fi-sidebar-item-button::before,
.fi-sidebar-item-button.fi-active::before {
  content: "" !important;
  position: absolute !important;
  left: 0 !important;
  top: 8px !important;
  bottom: 8px !important;
  width: 3.5px !important;
  border-radius: 0 4px 4px 0 !important;
  background: var(--ow-side-accent) !important;
}

/* Sidebar Footer — border-top trắng mờ 7% trên nền navy (POC .sidebar-footer) */
.fi-sidebar-footer {
  background: transparent !important;
  padding: 0 !important;
}

.sidebar-custom-footer {
  padding: 14px 18px !important;
  border-top: 1px solid rgba(255, 255, 255, 0.07) !important;
  font-size: 0.6875rem /* 11px */ !important;
  color: var(--ow-side-dim) !important;
  line-height: 1.6 !important;
  background: transparent !important;
  user-select: none !important;
}

.sidebar-custom-footer b {
  color: var(--ow-side-ink) !important;
  font-weight: 600 !important;
}

.sidebar-custom-footer span {
  color: var(--ow-side-dim) !important;
}

/* ================= TOPBAR (WHITE CLEAN — giữ nguyên từ bản đạt POC) ================= */
.fi-topbar {
  height: 66px !important;
  background: #ffffff !important;
  border-bottom: 1px solid #e6eaf3 !important;
  box-shadow: 0 1px 3px rgba(28, 36, 64, 0.03) !important;
  padding: 0 24px !important;
  /* Đợt 1 bổ sung 09/09 (REVIEW-bo-sung §Q7): overlay modal của Filament là
     z-40 — topbar 50 đè lên modal (đo Chromium: che 47px đầu modal, mobile
     modal 945px tràn 117px). Phải < 40. Sidebar drawer vẫn cao hơn (60). */
  z-index: 35 !important;
}

.fi-topbar nav {
  height: 66px !important;
  background: #ffffff !important;
}

/* Page title — Filament render .fi-header-heading (h1) trong content header,
   KHÔNG phải .fi-topbar-start h1 (selector cũ chết). Breadcrumb label giữ
   mặc định của Filament (14px medium) — bản cũ vô tình dính luôn 18px/800. */
.fi-header-heading {
  font-size: 1.375rem /* 22px */ !important;
  font-weight: 800 !important;
  letter-spacing: -0.02em !important;
  color: #0f172a !important;
  line-height: 1.25 !important;
}

.fi-header-subheading {
  font-size: 0.8125rem /* 13px */ !important;
  /* T4.95 — #64748b trên nền trang #f2f4fa chỉ 4,33:1 (dưới AA 4,5). */
  color: var(--ow-muted, #5f6880) !important;
  margin-top: 4px !important;
  line-height: 1.4 !important;
}

/* Modern Inputs with Warm Amber Focus Ring */
.fi-input-wrp {
  border-radius: 12px !important;
  border: 1px solid rgba(15, 23, 42, 0.1) !important;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02) !important;
  transition: all 0.2s ease !important;
}

/* Viền focus — T4.95: trước là hổ phách #f59e0b, chỉ 1,5:1 trên nền trắng
   (WCAG 1.4.11 đòi 3:1 cho chỉ báo focus) và lệch hẳn màu hành động xanh.
   Dùng đúng màu primary, đậm hơn nền để nhìn thấy khi bấm Tab. */
.fi-input-wrp:focus-within {
  border-color: var(--ow-blue-ink, #1d4ed8) !important;
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.24) !important;
}

/* Modern Tabs with Warm Amber Active State */
.fi-tabs-item {
  border-radius: 10px !important;
  font-weight: 600 !important;
  transition: all 0.15s ease !important;
}
.fi-tabs-item[aria-selected="true"],
.fi-tabs-item.fi-active {
  color: var(--ow-accent-ink, #b45309) !important;
  border-bottom: 2px solid var(--ow-accent-ink, #b45309) !important;
}

/* Modern Buttons with Smooth Transitions */
.fi-btn {
  border-radius: 12px !important;
  font-weight: 600 !important;
  transition: all 0.18s cubic-bezier(0.4, 0, 0.2, 1) !important;
}
.fi-icon-btn {
  border-radius: 10px !important;
  transition: all 0.15s ease !important;
}

.fi-breadcrumbs-item-separator {
  color: #a0aec0 !important;
}

.fi-topbar-end {
  display: flex !important;
  align-items: center !important;
  gap: 12px !important;
}

/* ================= CARDS & TABLES (rounded-2xl + subtle ring & shadow) ================= */
.fi-ta-ctn,
.fi-section {
  background: #ffffff !important;
  border-radius: 20px !important;
  border: 1px solid rgba(15, 23, 42, 0.07) !important;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.03), 0 1px 2px -1px rgba(0, 0, 0, 0.03) !important;
  overflow: hidden !important;
}

/*
 * 09/10 — SỬA: `overflow: hidden !important` ở trên cắt mất dropdown của ô chọn.
 *
 * Triệu chứng chủ dự án báo: form Tạo/Sửa lớp, ô "Phòng mặc định" mở ra chỉ thấy
 * ô tìm kiếm, KHÔNG có phòng nào — dù DB có 28 phòng sống. Options VẪN được render
 * (đếm trong DOM: 28) nhưng bị `overflow: hidden` của `.fi-section` cắt mất phần
 * tràn xuống dưới mép section.
 *
 * Đo bằng Playwright trên /app/class-rooms/create: `.fi-section` có
 * overflow=hidden, dropdown bị cắt 245px tính từ đáy — đúng phần chứa danh sách.
 *
 * Vá bằng cách cho section CHỨA dropdown được phép tràn. Dùng `:has()` để chỉ nới
 * đúng những section đang mở dropdown (`.is-open`), không đổi hình dáng mọi section
 * — giữ bo góc + viền như cũ ở trạng thái thường.
 */
.fi-section:has(.choices.is-open),
.fi-section:has(.fi-fo-select .choices__list--dropdown) {
  overflow: visible !important;
}

/* Trang Xem chi tiết (infolist — ViewStudent/ViewCharge/ViewClassRoom):
   Filament mặc định .fi-section-header px-6 py-4, .fi-section-content p-6,
   VÀ lưới bọc ngoài .fi-fo-component-ctn gap-6 (24px MỌI PHÍA — kể cả
   khoảng cách giữa 2 khối xếp chồng nhau, vì cả 4 InfolistSection nằm
   chung 1 lưới cấp trang). Khác bảng (.fi-ta-*) đã nén từ fixbug/s02-s10-
   t4.1-ui-cleanup, infolist chưa ai đụng tới nên vẫn 24px rất rỗng —
   Tường phản hồi trang Lớp/Học sinh "space lớn" (30/08). Nén về cùng
   nhịp với .fi-ta-header/card-pad demo (16-20px), gap lưới 24px→14px. */
.fi-section-header {
  padding: 16px 20px !important;
}

.fi-section-content {
  padding: 16px 20px !important;
}

.fi-fo-component-ctn {
  gap: 14px !important;
}

/* Card Header */
.fi-ta-header {
  padding: 16px 20px !important;
  border-bottom: 1px solid #eef1f7 !important;
  background: #ffffff !important;
}

.fi-ta-header-heading {
  font-size: 0.9375rem /* 15px */ !important;
  font-weight: 700 !important;
  color: #1c2440 !important;
  display: flex !important;
  align-items: center !important;
  gap: 8px !important;
}

.fi-ta-header-description {
  font-size: 0.75rem /* 12px */ !important;
  /* 12/09 (ui-audit): #6f7893 trên trắng = 4,39:1 — trượt AA cho chữ 12px.
     slate-500 chuẩn #64748b = 4,76:1, vẫn là xám nhạt. Sửa TẠI ĐÂY vì rule
     này mang !important → thêm rule trùng ở cuối file không thắng được. */
  color: #64748b !important;
  margin-top: 2px !important;
}

/* Table Column Headers — nền #fafbfe
   fixbug/s02-s10-t4.1-ui-cleanup: padding ngang 14px → 10px (đồng bộ với
   .fi-ta-cell body phía dưới). Header từng cao hơn body 1px theo trục dọc
   (11 vs 12) — giữ nguyên chênh lệch này vì nó tạo cảm giác header "nằm
   trên" body. */
.fi-ta-header-cell {
  font-size: 0.65625rem /* 10.5px */ !important;
  font-weight: 700 !important;
  letter-spacing: 0.07em !important;
  text-transform: uppercase !important;
  color: var(--ow-muted, #5f6880) !important;
  background: var(--ow-table-head-bg, #fafbfe) !important;
  padding: 11px 10px !important;
  border-bottom: 1px solid #e6eaf3 !important;
  white-space: nowrap !important;
}

.fi-ta-header-cell button {
  font-size: 0.65625rem /* 10.5px */ !important;
  font-weight: 700 !important;
  letter-spacing: 0.07em !important;
  text-transform: uppercase !important;
  color: var(--ow-muted, #5f6880) !important;
}

/* Table Cells — kẻ ngang #f0f2f8, hover row #f8faff
   fixbug/s02-s10-t4.1-ui-cleanup: padding ngang 14px → 10px cho mọi ô
   trong TOÀN BỘ bảng Filament (S02, S10, widget, relation manager). 4px/ô
   × 9 cột = 36px width tiết kiệm cho bảng rộng — bảng S10 (9 cột) trước
   đây tràn 1440px viewport, sau sửa vừa.
   Round 2 (30/08): padding-top/bottom 12px → 14px. Body row cao 81-87px
   khiến text "trôi" giữa ô rất xa header (H=43). Tăng padding dọc đẩy
   text lên/xuống nhưng row vẫn cao — không giải quyết. Cách đúng: GIỮ
   vertical-align middle + tăng padding dọc để text "ở giữa" tâm header,
   đồng thời max-width cột actions (bên dưới) để bảng không giãn vô ích. */
.fi-ta-cell {
  padding: 14px 10px !important;
  font-size: 0.8125rem /* 13px */ !important;
  border-bottom: 1px solid var(--ow-table-line, #f0f2f8) !important;
  vertical-align: middle !important;
  color: #1c2440 !important;
}

/* Cột actions (cell cuối) — Filament mặc định cột cuối chiếm width
   bằng nội dung + 20px padding. Với 2-3 icon button (32px) + 1 nút text
   "Thu tiền" (76px), ô rộng ~250px trong khi chỉ cần ~170px → lãng phí
   ~80px kéo bảng giãn ngang.

   Round 2 (30/08) thử: max-width 120/140 + text-align: right / flex-end
   → badge "Đóng 1 phần" ở cột Trạng thái liền kề CHỒNG LẤN lên nút
   "Thu tiền" (vì flex-end dồn icon sát mép phải = sát badge). Fix:
   tăng max-width đủ chứa 2 icon + 1 nút text (170px), BỎ text-align
   right (vì gây chồng lấn), để Filament xếp tự nhiên với gap mặc định
   2-4px giữa actions. Cộng thêm min-width ở cột trạng thái để badge
   dài (Đóng 1 phần / Chưa đóng) không ép sát mép phải cột. */
.fi-ta-cell:last-child {
  max-width: 170px !important;
  white-space: nowrap !important;
}

.fi-ta-header-cell:last-child {
  max-width: 170px !important;
}

/* Cột trạng thái — Filament dùng badge (Đang học / Đã đóng / Đóng 1 phần
   / Chưa đóng). Badge dài 80-100px. Khi cell rộng tối thiểu (table-layout
   auto, browser co theo content), badge dồn sát mép phải và badge "Đóng
   1 phần" đè lên icon Xem chi tiết ở cột actions. min-width trên <td>
   vô hiệu với table-layout auto — phải thêm padding-right cho cột này
   để tạo khoảng đệm giữa badge và icon cột bên. Tăng 10px → 24px
   (thêm 14px an toàn cho badge dài nhất). */
.fi-ta-cell:nth-last-child(2) {
  padding-right: 24px !important;
}

.fi-ta-header-cell:nth-last-child(2) {
  padding-right: 24px !important;
}

/* Cặp cột "mã · tên" đứng liền nhau (S03: Mã lớp + Tên lớp)
   Chủ dự án 03/09: "có 1 khoảng cỡ 20px giữa 2 cột này, muốn rút ngắn lại".
   Đo Chromium 1440px: hở 69px = 25px đuôi thừa (cột rộng bằng mã DÀI NHẤT
   trang, "SUNFLOWER-01" 101px, trong khi hàng đó chỉ "DRAGON-01" 76px)
   + 22px đệm mỗi bên.

   `->width('1%')` KHÔNG giải quyết được: đặt 1% / 100px / 121px lên <th>
   đều cho ra 145px y nguyên, vì table-layout: auto lấy max-content.
   Hai việc thật sự có tác dụng, đo lại đủ cả 5 hàng đầu ra đúng 20px:
     1. canh phải cột mã (->alignEnd() ở Resource) → hết 25px đuôi thừa,
        mọi hàng hở BẰNG NHAU dù mã dài ngắn khác nhau;
     2. bỏ px-3 (12px) của .fi-ta-text ở đúng mép hai cột giáp nhau, giữ
        lại 10px của .fi-ta-cell mỗi bên → 10 + 10 = 20px.

   extraAttributes của TextColumn rơi thẳng lên div .fi-ta-text — chính chỗ
   mang px-3 — nên lớp semantic ở đây ghi đè được. Không dùng !px-0 của
   Tailwind: dist Filament không sinh biến thể important. */
.fi-ta-cell .fi-ta-text.ow-code-cell {
  padding-inline-end: 0 !important;
}

.fi-ta-cell .fi-ta-text.ow-code-next {
  padding-inline-start: 0 !important;
}

/* T4.50 — CỘT CHỮ DO NGƯỜI GÕ: bẻ chuỗi liền + có trần chiều rộng (02/10)

   Đo trước khi sửa (1366px, tên lớp 255 ký tự liền): ô tên lớp rộng 2757px,
   bảng 4169px trong khung 1248px — white-space: nowrap + max-width: none, nên
   table-layout: auto lấy max-content = cả chuỗi.

   VÌ SAO `break-word` MÀ KHÔNG PHẢI `anywhere` (đo lần đầu, bản anywhere): anywhere
   hạ min-content của ô xuống 1 ký tự, bảng bị ép trong khung 329px thì nén cột
   xuống ĐÚNG 1 ký tự — ô tên lớp rộng 56px cao 1568px, và cả "Dragon" bị bẻ
   thành 80px cao. `break-word` giữ min-content = từ dài nhất, mà max-width kẹp cả
   min-content lẫn max-content về 18rem → cột không bao giờ rộng quá 288px, cũng không
   bị nén dưới độ rộng từ ngắn; tên ngắn ("Dragon") không đổi.
   Gắn bằng macro `wrapLong()` (AppServiceProvider) để mọi cột dùng chung.

   VÌ SAO CẦN LUẬT THỨ HAI Ở NHÃN (đo lần hai, chỉ có luật ở ô): ruột ô là
   div.flex > div.flex > .fi-ta-text-item > span.fi-ta-text-item-label — toàn flex
   item, min-width: auto = từ dài nhất. Ô rộng 288px nhưng chữ vẫn tràn ra 2745px
   đè lên cột bên cạnh (scrollWidth 2745 > clientWidth 288). Kẹp max-width ngay
   nhãn thì min-content của cả chuỗi flex tổ tiên cũng về 288px. */
.fi-ta-cell .fi-ta-text.ow-wrap-cell {
  max-width: 18rem;
  white-space: normal;
  overflow-wrap: break-word;
}

.fi-ta-cell .fi-ta-text.ow-wrap-cell .fi-ta-text-item-label {
  max-width: 18rem;
  overflow-wrap: break-word;
}

/* ============================================================
   S10 — GHIM CỘT ĐỊNH DANH + CỘT HÀNH ĐỘNG (03/09)

   Đo thật ở 1440px: bảng Thu học phí rộng 1352px trong khung 1158px
   (sidebar 236 + padding 48 ăn hết phần còn lại), tức luôn thiếu ~200px
   dù đã nén padding ô. Ở 375px còn tệ hơn: khung 329px, phải cuộn 1038px
   mới tới nút "Thu tiền".

   Cắt bớt cột thì mất thông tin cô/kế toán yêu cầu. Cách đúng cho bảng
   rộng: ghim cột ĐẦU (biết đang xem em nào) và cột HÀNH ĐỘNG (bấm Thu
   tiền được ngay), phần giữa cuộn tự do. Bóng đổ hai bên là tín hiệu
   "còn nội dung phía trong" — không có bóng thì người dùng không biết
   có thể cuộn.

   Chỉ áp cho .fi-resource-charges: các màn khác chưa đo, không đụng vào.
   Nền phải ĐẶT TƯỜNG MINH (row của Filament trong suốt — nền trắng là
   của container); thiếu nền thì nội dung cuộn xuyên qua ô ghim.
   ============================================================ */
.fi-resource-charges .fi-ta-cell:first-child {
  position: sticky !important;
  left: 0;
  z-index: 2;
  background-color: #ffffff !important;
  box-shadow: 8px 0 8px -8px rgba(15, 23, 42, 0.14);
}

.fi-resource-charges .fi-ta-cell:last-child {
  position: sticky !important;
  right: 0;
  z-index: 2;
  background-color: #ffffff !important;
  box-shadow: -8px 0 8px -8px rgba(15, 23, 42, 0.14);
}

.fi-resource-charges .fi-ta-header-cell:first-child {
  position: sticky !important;
  left: 0;
  z-index: 3;
  background-color: #fafbfe !important;
  box-shadow: 8px 0 8px -8px rgba(15, 23, 42, 0.14);
}

.fi-resource-charges .fi-ta-header-cell:last-child {
  position: sticky !important;
  right: 0;
  z-index: 3;
  background-color: #fafbfe !important;
  box-shadow: -8px 0 8px -8px rgba(15, 23, 42, 0.14);
}

/* Hover: ô ghim phải đổi nền cùng hàng, nếu không hàng sẽ "gãy" làm 3 mảnh */
.fi-resource-charges .fi-ta-row:hover .fi-ta-cell:first-child,
.fi-resource-charges .fi-ta-row:hover .fi-ta-cell:last-child {
  background-color: #f8faff !important;
}

/* Menu ⋮ đang mở phải nằm TRÊN ô ghim của các hàng khác (03/09).

   Ô ghim có `position: sticky; z-index: 2` nên MỖI ô tự tạo một stacking
   context. Panel dropdown nằm trong ô của hàng đang mở → z-index 10 của nó
   chỉ có nghĩa BÊN TRONG ô đó; ra ngoài nó vẫn mang "cân nặng" z-index 2 của
   ô, thua các ô ghim đứng sau trong DOM (hàng dưới) và bị vẽ đè.

   Đo ở 1440px trước khi sửa: dòng "Xuất hoá đơn"/"In biên lai" của panel bị
   TD.fi-ta-cell hàng dưới phủ từ x=1283 — nhìn ra đúng như ảnh chủ dự án gửi
   ("Xuất ho…", "In biên l…"). Nâng z-index ô đang mở lên trên cả header ghim
   (z-index 3) là đủ; :focus-within là đường dự phòng cho trình duyệt cũ
   không hỗ trợ :has(). Gác bằng TableActionDropdownGuardTest. */
.fi-ta-cell:last-child:focus-within,
.fi-ta-cell:last-child:has(.fi-dropdown-panel:not([style*="display: none"])) {
  z-index: 30 !important;
}

/* <768px: vùng bấm ≥40px (CLAUDE.md §3 + oneworld-project §4).
   Action của Filament render kiểu .fi-link cao 20px — chạm hụt trên điện
   thoại, nhất là khi 2 phần tử (Thu tiền + ⋮) nằm sát nhau trong ô đã ghim.

   10/09 — rule cũ CHỈ phủ `.fi-resource-charges .fi-ta-actions`. Đo lại thật
   ở 375×812 thấy hở nhiều chỗ trên đúng màn GV dùng:
     .fi-btn "Điểm danh" (dashboard GV — nút chính của vai chính)  32px ❌
     .fi-btn "Thu tiền" / "Thêm học sinh"                          36px ❌
     .fi-sidebar-item-button (drawer nav)                          37px ❌
     .fi-link "Đặt lại" + nút sort cột ("Mã HS"…)                  20px ❌
   Luật là "vùng bấm ≥40px", không phải "≥40px ở màn Thu học phí" — nên phủ
   đủ các lớp phần tử bấm được. Không dùng `*` để tránh phá layout chữ. */
@media (max-width: 767px) {
  .fi-resource-charges .fi-ta-actions .fi-link,
  .fi-resource-charges .fi-ta-actions .fi-dropdown-trigger > * {
    min-height: 40px !important;
    padding-left: 6px !important;
    padding-right: 6px !important;
  }

  /* Nút hành động (Filament dùng .fi-btn cho mọi action/link-button). */
  .fi-btn {
    min-height: 40px !important;
  }

  /* Drawer nav — mục sidebar. */
  .fi-sidebar-item-button {
    min-height: 40px !important;
  }

  /* Link trong bảng + thanh lọc: nút sort tiêu đề cột, "Đặt lại", action
     dạng link. Đây là chỗ cao 20px nhiều nhất.
     ⚠️ Nút sort cột KHÔNG phải <button> và KHÔNG mang class
     `.fi-ta-header-cell-sort-btn`. Truy thật trong DOM (10/09):
       <th class="fi-ta-header-cell …">
         <span class="group flex w-full items-center gap-x-1 cursor-pointer …">  ← 20px, không phải button
     Nên phải bắt bằng `.fi-ta-header-cell > span` (con trực tiếp); selector
     theo tên class kia đã thử và KHÔNG khớp. */
  .fi-link {
    min-height: 40px !important;
    display: inline-flex !important;
    align-items: center !important;
  }

  .fi-ta-header-cell > span {
    min-height: 40px !important;
    display: inline-flex !important;
    align-items: center !important;
  }

  /* Nút mở dropdown — đo được 32px ở nút "Menu người dùng" trên topbar
     (button.shrink-0 nằm trong .fi-dropdown-trigger). Áp cho con trực tiếp
     để phủ cả dropdown của bảng lẫn topbar. */
  .fi-dropdown-trigger > * {
    min-height: 40px !important;
  }

  /* T4.77 — mục TRONG menu ⋮ cũng phải ≥40px. Rule `.fi-dropdown-trigger > *`
     phía trên phủ nút MỞ menu, không phủ các dòng bên trong panel: chữ trong
     panel chỉ ~16px nên mỗi dòng cao 36px (đo 375×812 ở bảng Ngày nghỉ lễ,
     12/10) — chạm hụt bằng ngón tay, và hai mục ("Áp dụng cho buổi đã sinh" /
     "Hoàn tác buổi ngoài khoảng nghỉ") nằm sát nhau nên dễ bấm nhầm. */
  .fi-dropdown-list-item {
    min-height: 40px !important;
    display: flex !important;
    align-items: center !important;
  }
}

/* fixbug/s02-s10-t4.1-ui-cleanup: border-spacing 0 cho bảng — Filament
   default 2px tạo khe hở giữa các ô khi border-collapse: separate (rất
   khó thấy ở 14px nhưng vẫn chiếm 2px width mỗi cặp ô). Dùng separate
   + spacing 0 thay vì collapse để border-bottom của từng ô vẫn hiện đúng
   (collapse sẽ merge border vào 1 đường đôi khi sai vị trí). */
table.fi-ta-table {
  border-spacing: 0 !important;
}

.fi-ta-row {
  transition: background-color 0.12s ease !important;
}

.fi-ta-row:hover {
  background-color: #f8faff !important;
}

/* ================= CUSTOM TAGS & BADGES (giữ nguyên — đã đúng POC) ================= */
.ow-tag {
  display: inline-flex;
  align-items: center;
  /* 09/09 tối (cô): "tên dính nhau quá trời" — inline-flex BỎ whitespace
     giữa flex item, tên và mã NV dính liền (đo 0px); các thẻ xếp dòng cũng
     chạm nhau 0px. gap tách trong thẻ, margin-block tách giữa dòng.
     Chốt 21:58: "gap hẳn 5px" — trong thẻ 5px, giữa dòng 2.5+2.5=5px. */
  gap: 5px;
  margin-block: 2.5px;
  padding: 2.5px 9px;
  border-radius: 7px;
  background: #e8f0fe;
  color: #1d4ed8;
  font-size: 0.71875rem /* 11.5px */;
  font-weight: 600;
  white-space: nowrap;
}

.ow-tag-purple {
  background: #f0ebfe !important;
  color: #6d28d9 !important;
}

/* Đợt 2 bổ sung 09/09 (REVIEW-bo-sung §Q1/#2): mã NV cạnh tên GV/TG —
   mono nhỏ, mờ hơn tên để tên vẫn là thông tin chính. */
.ow-tag-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.65625rem /* 10.5px */;
  font-weight: 500;
  opacity: 0.75;
  letter-spacing: 0.02em;
}

.ow-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 10px;
  border-radius: 9999px;
  font-size: 0.71875rem /* 11.5px */;
  font-weight: 600;
  white-space: nowrap;
}

.ow-pill-green {
  background: #e6f9ee;
  color: #15803d;
}

.ow-pill-orange {
  background: #fef3e2;
  color: #b45309;
}

.ow-pill-blue {
  background: #e8f0fe;
  color: #1d4ed8;
}

.ow-pill-purple {
  background: #f0ebfe;
  color: #6d28d9;
}

.ow-pill-red {
  background: #feecec;
  color: #b91c1c;
}

.ow-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: currentColor;
}

.ow-money-debt {
  color: #b91c1c !important;
  font-weight: 700 !important;
}

.ow-money-clean {
  color: #15803d !important;
  font-weight: 600 !important;
}

/* ============================================================
   APP-OWNED COMPONENT CLASSES (ow-*) — Đợt 5
   ------------------------------------------------------------
   Các blade custom (brand-logo, topbar-actions, quick-login,
   student-footer-note) render ngoài pipeline Tailwind của Filament:
   dist CSS không chứa utility arbitrary-value dùng trong chúng
   (bg-gradient-to-br from-[#fbbf24]…, text-[13.5px]…) → toàn bộ
   trang trí phải là class tĩnh định nghĩa ngay tại đây.
   Giá trị tham chiếu POC: demo/assets/css/app.css.
   ============================================================ */

/* --- Brand logo (sidebar header) — POC .brand/.brand-logo app.css :60-69 --- */
.ow-brand {
  display: flex;
  align-items: center;
  gap: 12px;
}

.ow-brand-tile {
  width: 40px;
  height: 40px;
  flex: 0 0 40px;
  border-radius: 12px;
  background: linear-gradient(135deg, #3b82f6, #1d4ed8 60%, #1e40af);
  display: grid;
  place-items: center;
  color: #fff;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
}

.ow-brand-tile svg {
  width: 22px;
  height: 22px;
}

/* Official Logo image styling */
.ow-brand-logo-card {
  display: flex;
  align-items: center;
  background: #ffffff;
  padding: 5px 12px;
  border-radius: 12px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
  border: 1px solid rgba(255, 255, 255, 0.3);
  transition: all 0.2s ease;
}

.ow-brand-img {
  height: 34px;
  width: auto;
  max-width: 175px;
  object-fit: contain;
  display: block;
}

/* Trang Login: render logo lớn và trong suốt */
.fi-simple-page .ow-brand-logo-card {
  background: transparent !important;
  box-shadow: none !important;
  border: none !important;
  padding: 0 !important;
}

.fi-simple-page .ow-brand-img {
  height: 54px !important;
  max-width: 240px !important;
}

/* Chữ brand: màu do rule .fi-sidebar-header .fi-sidebar-header-text > span*
   phía trên quyết định cho nền navy; ở đây chỉ đặt kích thước/độ đậm. */
.ow-brand-text {
  display: flex;
  flex-direction: column;
  text-align: left;
}

.ow-brand-name {
  font-size: 0.96875rem /* 15.5px */;
  font-weight: 800;
  letter-spacing: 0.01em;
  line-height: 1.2;
}

.ow-brand-sub {
  font-size: 0.65625rem /* 10.5px */;
  font-weight: 500;
  margin-top: 2px;
}

/* --- Topbar actions (render hook fi-topbar-end) --- */
.ow-topbar-group {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-right: 8px;
}

/* Pill vai: nền pastel theo variant (.ow-pill-*); responsive ≥640px
   thay cho utility "hidden sm:flex" không tồn tại trong dist. */
.ow-topbar-role {
  border: 1px solid rgba(230, 234, 243, 0.9);
  box-shadow: 0 1px 2px rgba(28, 36, 64, 0.05);
  padding: 4px 12px;
  display: none;
}

@media (min-width: 640px) {
  .ow-topbar-role {
    display: inline-flex;
  }
}

.ow-topbar-role-icon {
  width: 14px;
  height: 14px;
  color: #3b82f6;
}

.ow-topbar-user-wrap {
  display: none;
}

@media (min-width: 768px) {
  .ow-topbar-user-wrap {
    display: block;
  }
}

.ow-topbar-user {
  font-size: 0.8125rem /* 13px */;
  font-weight: 600;
  color: #1c2440;
}

/* --- T4.90: nút chuyển ngôn ngữ ở topbar (ngoài menu tài khoản) --------------- */
/* T4.89 đặt mục này trong dropdown avatar; chủ dự án 18/10 muốn một nút riêng
   thấy ngay trên thanh trên. Nút là <button> trong <form method="POST"> (đổi
   ngôn ngữ phải ghi DB nên không dùng GET được), vì vậy phải reset style mặc
   định của button và bám đúng hệ .ow-* của topbar. */
.ow-locale-form {
  display: inline-flex;
  margin: 0;
}

.ow-locale-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* Vùng bấm ≥40px cho ngón tay — cùng ngưỡng với các nút khác trong hệ.
     (36px trước 01/11: QA đo trên 375px thấy thiếu 4px.) */
  min-height: 40px;
  padding: 6px 10px;
  border: 1px solid rgba(230, 234, 243, 0.9);
  border-radius: 8px;
  background: #fff;
  color: #1c2440;
  font-size: 0.8125rem /* 13px */;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(28, 36, 64, 0.05);
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

.ow-locale-btn:hover {
  background: #f6f8fc;
  border-color: #cfd8e6;
}

.ow-locale-btn:focus-visible {
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
}

.ow-locale-icon {
  width: 16px;
  height: 16px;
  flex: none;
  color: #3b82f6;
}

/* Topbar hẹp (điện thoại): giữ icon, ẩn tên ngôn ngữ — title/aria-label vẫn
   mang đủ câu "Chuyển sang Tiếng Việt" cho trình đọc màn hình. */
@media (max-width: 639px) {
  .ow-locale-label {
    display: none;
  }

  .ow-locale-btn {
    /* Chỉ còn icon 16px: không đặt min-width thì nút co về ~34px. */
    min-width: 40px;
    justify-content: center;
    padding: 6px 8px;
  }
}

/* --- Quick-login (auth login panel, chỉ non-production) --- */
.ow-ql {
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid #e6eaf3;
}

.ow-ql-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 12px;
}

/* 06/09 — 11px/#6f7893 = 4.39:1, trượt AA. Sửa CỠ CHỮ TRƯỚC rồi mới tới màu
   (bài học sidebar: 13.5px đạt AA nhưng mắt vẫn thấy mờ vì chữ quá nhỏ).
   12px + #5f6880 = 5.56:1 ✓ */
.ow-ql-title {
  font-size: 0.75rem /* 12px */;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #5f6880;
}

.ow-ql-warn {
  font-size: 0.65625rem /* 10.5px */;
  font-weight: 600;
  color: #b45309;
  background: #fffbeb;
  border: 1px solid #fde68a;
  padding: 2px 8px;
  border-radius: 6px;
  white-space: nowrap;
}

.ow-ql-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}

.ow-ql-btn {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 10px;
  border-radius: 12px;
  border: 1px solid #e6eaf3;
  background: #ffffff;
  text-align: left;
  transition: all 0.15s ease;
  cursor: pointer;
  font-family: inherit;
  line-height: 1.4;
}

.ow-ql-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  gap: 6px;
}

.ow-ql-name {
  font-size: 0.75rem /* 12px */;
  font-weight: 700;
  color: #1c2440;
}

/* 10.5px là cỡ chữ nhỏ nhất toàn theme — dưới ngưỡng đọc thoải mái, và
   #6f7893 chỉ 4.39:1. Lên 12px + #5f6880 (5.56:1). Email dài nhất
   "admin@oneworld.local" ở 12px vẫn nằm gọn trong ô lưới 2 cột. */
.ow-ql-email {
  font-size: 0.75rem /* 12px */;
  color: #5f6880;
  margin-top: 2px;
}

/* Trạng thái "đang gửi" của nút quick-login. Cặp với wire:loading.attr="disabled"
   trong quick-login.blade.php: chặn được double-click mà không phản hồi gì thì
   người dùng tưởng máy treo rồi bấm tiếp — đúng thứ ta đang muốn tránh.
   Khoá bằng tests/Feature/Frontend/LoginA11yTest.php */
.ow-ql-btn[disabled] {
  opacity: 0.55;
  cursor: progress;
}

/* Vùng bấm checkbox "Ghi nhớ đăng nhập" — mặc định Filament 16×16px, dưới cả
   WCAG 2.2 (24×24) lẫn ngưỡng 40px của CLAUDE.md §3. GV đăng nhập bằng điện
   thoại trong lớp nên đây là ô bấm thật, không phải chi tiết trang trí.
   BÓ trong .fi-simple-main (layout trang auth) — nới toàn cục sẽ phình mọi
   checkbox trong bảng/bộ lọc, đó là thay đổi khác hẳn và không ai yêu cầu. */
.fi-simple-main .fi-checkbox-input {
  width: 24px;
  height: 24px;
}

.ow-ql-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: 0 0 8px;
}

.ow-ql-btn--wide {
  grid-column: 1 / -1;
}

/* Variant theo vai — admin đổi cam→xanh dương để đồng bộ palette 2026-08
   (cùng lý do với sidebar và .fi-btn.fi-color-primary). */
.ow-ql-dot--admin   { background: #3b82f6; }
.ow-ql-dot--manager { background: #3b82f6; }
.ow-ql-dot--gv      { background: #10b981; }
.ow-ql-dot--ketoan  { background: #f43f5e; }
.ow-ql-dot--tg      { background: #a855f7; }

.ow-ql-btn--admin:hover   { background: #eff6ff; border-color: #3b82f6; }
.ow-ql-btn--manager:hover { background: #eff6ff; border-color: #60a5fa; }
.ow-ql-btn--gv:hover      { background: #ecfdf5; border-color: #34d399; }
.ow-ql-btn--ketoan:hover  { background: #fff1f2; border-color: #fb7185; }
.ow-ql-btn--tg:hover      { background: #faf5ff; border-color: #c084fc; }

.ow-ql-btn--admin:hover .ow-ql-name   { color: #92400e; }
.ow-ql-btn--manager:hover .ow-ql-name { color: #1e40af; }
.ow-ql-btn--gv:hover .ow-ql-name      { color: #065f46; }
.ow-ql-btn--ketoan:hover .ow-ql-name  { color: #9f1239; }
.ow-ql-btn--tg:hover .ow-ql-name      { color: #6b21a8; }

/* --- Footnote cuối card danh sách học sinh --- */
.ow-footnote {
  padding: 14px 20px;
  font-size: 0.75rem /* 12px */;
  text-align: center;
  color: var(--ow-muted, #5f6880);
  border-top: 1px solid #eef1f7;
  background: #fafbfe;
  border-radius: 0 0 16px 16px;
}

.ow-footnote-accent {
  color: #1d4ed8;
  font-weight: 600;
}

/* ================= STAT CARDS → POC (rounded-2xl + subtle ring & hover elevation) ================= */
.fi-wi-stats-overview-stat {
  background: #ffffff !important;
  border-radius: 20px !important;
  border: 1px solid rgba(15, 23, 42, 0.07) !important;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.03), 0 1px 2px -1px rgba(0, 0, 0, 0.03) !important;
  padding: 20px 22px !important;
  transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1) !important;
  position: relative !important;
  overflow: hidden !important;
}

.fi-wi-stats-overview-stat:hover {
  transform: translateY(-2px) !important;
  box-shadow: 0 10px 25px -5px rgba(28, 36, 64, 0.07), 0 8px 10px -6px rgba(28, 36, 64, 0.03) !important;
  border-color: rgba(245, 158, 11, 0.35) !important;
}

/* Warm Amber accent on stat cards */
.fi-wi-stats-overview-stats-ctn .fi-wi-stats-overview-stat:nth-child(1) {
  border-top: 3px solid #f59e0b !important;
}
.fi-wi-stats-overview-stats-ctn .fi-wi-stats-overview-stat:nth-child(2) {
  border-top: 3px solid #3b82f6 !important;
}
.fi-wi-stats-overview-stats-ctn .fi-wi-stats-overview-stat:nth-child(3) {
  border-top: 3px solid #0d9488 !important;
}
.fi-wi-stats-overview-stats-ctn .fi-wi-stats-overview-stat:nth-child(4) {
  border-top: 3px solid #f59e0b !important;
}

.fi-wi-stats-overview-stat-value {
  font-size: 1.5rem /* 24px */ !important;
  font-weight: 800 !important;
  letter-spacing: -0.02em !important;
  line-height: 1.2 !important;
  color: #0f172a !important;
  font-variant-numeric: tabular-nums !important;
  margin-top: 4px !important;
}

.fi-wi-stats-overview-stat-label {
  font-size: 0.8125rem /* 13px */ !important;
  font-weight: 600 !important;
  color: #475569 !important;
  line-height: 1.3 !important;
}

.fi-wi-stats-overview-stat-icon {
  box-sizing: border-box !important;
  width: 44px !important;
  height: 44px !important;
  flex: 0 0 auto !important;
  padding: 10px !important;
  border-radius: 14px !important;
  transition: transform 0.2s ease !important;
}

.fi-wi-stats-overview-stat:hover .fi-wi-stats-overview-stat-icon {
  transform: scale(1.06) !important;
}

/* 1 Học sinh (amber) · 2 Lớp (blue) · 3 GV&TG (teal) · 4 Đã thu (amber) */
.fi-wi-stats-overview-stats-ctn .fi-wi-stats-overview-stat:nth-child(1) .fi-wi-stats-overview-stat-icon {
  background: #fef3c7 !important;
  color: #b45309 !important;
  box-shadow: 0 2px 6px rgba(245, 158, 11, 0.2) !important;
}

.fi-wi-stats-overview-stats-ctn .fi-wi-stats-overview-stat:nth-child(2) .fi-wi-stats-overview-stat-icon {
  background: #e0f2fe !important;
  color: #0369a1 !important;
  box-shadow: 0 2px 6px rgba(59, 130, 246, 0.2) !important;
}

.fi-wi-stats-overview-stats-ctn .fi-wi-stats-overview-stat:nth-child(3) .fi-wi-stats-overview-stat-icon {
  background: #ccfbf1 !important;
  color: #0f766e !important;
  box-shadow: 0 2px 6px rgba(13, 148, 136, 0.2) !important;
}

.fi-wi-stats-overview-stats-ctn .fi-wi-stats-overview-stat:nth-child(4) .fi-wi-stats-overview-stat-icon {
  background: #fef3c7 !important;
  color: #b45309 !important;
  box-shadow: 0 2px 6px rgba(245, 158, 11, 0.2) !important;
}

/* ================= BUTTONS ================= */
/* Skin xanh dương cho nút PRIMARY của Filament — class thật là .fi-btn.fi-color-primary
   (.fi-btn-primary không tồn tại trong markup v3, selector cũ chết).
   Đổi cam→xanh dương 2026-08 để đồng bộ với sidebar accent (xem --ow-side-accent).
   Chỉ đụng color=primary; các màu nút khác giữ nguyên. Shadow phải theo bg mới
   (rgba(59,130,246,.3)) — để shadow cam trên nền xanh sẽ tạo quầng lạ,
   giống bug "shadow lệch tông" đã gặp ở .brand-logo. */
/* 06/09 — nền đổi #3b82f6 → #2563eb. Chữ trắng trên #3b82f6 chỉ 3.68:1, TRƯỢT
   WCAG AA 4.5:1: đo được trên staging ở cả 3 viewport, và chính khối PAGINATION
   bên dưới đã kết luận đúng điều đó rồi mà nút chính vẫn để màu cũ.
   #2563eb = 5.17:1 ✓ (nó cũng đúng bằng màu "trang đang xem" của phân trang nên
   một màn chỉ còn MỘT sắc xanh nhấn). Hover đẩy xuống #1d4ed8 = 6.70:1.
   Shadow phải theo nền mới — để rgba xanh cũ dưới nền đậm hơn sẽ thành quầng lệch
   tông, đúng bug đã gặp ở .brand-logo. Khoá bằng tests/Feature/Frontend/LoginA11yTest.php */
.fi-btn.fi-color-primary {
  background-color: #2563eb !important;
  border-color: transparent !important;
  color: #ffffff !important;
  font-weight: 600 !important;
  border-radius: 10px !important;
  box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3) !important;
}

.fi-btn.fi-color-primary:hover {
  background-color: #1d4ed8 !important;
}

/* T4.59 — ToggleButtons (vd. hộp "Xếp vào lớp": Chính / Tăng cường / Phụ đạo).
   Filament vẽ mỗi lựa chọn là <input type=radio class=peer> + <label class="fi-btn fi-color-primary">
   (không khai ->colors() thì màu nút là primary). Skin nút chính ở trên có !important nên đè luôn
   trạng thái CHƯA CHỌN (bg-white text-gray-950 + ring) mà Filament dành cho label: đo trên trình duyệt
   thật cả ba nhãn đều rgb(37,99,235) + chữ trắng, radio checked hay không cũng vậy. Chỉ nhãn Chính tính
   học phí → không nhìn ra mình chọn gì = chọn nhầm = thu sai tiền.
   Sửa ở CSS chung (một chỗ cho mọi ToggleButtons): `label.fi-btn.fi-color-primary` (specificity cao hơn
   `.fi-btn.fi-color-primary`, cùng !important) trả nhãn chưa chọn về nền trắng + chữ tối + viền xám;
   `input:checked + label…` giữ đúng skin xanh cho nhãn đang chọn. Rê chuột lên nhãn chưa chọn chỉ xám nhẹ,
   không được xanh (sẽ giống nhãn đang chọn). Khoá bằng tests/Feature/Frontend/ToggleButtonsSelectedStateTest.php */
label.fi-btn.fi-color-primary {
  background-color: #ffffff !important;
  color: #111827 !important;
  box-shadow: 0 0 0 1px #cbd5e1 !important;
}

label.fi-btn.fi-color-primary:hover {
  background-color: #f3f4f6 !important;
}

input:checked + label.fi-btn.fi-color-primary {
  background-color: #2563eb !important;
  color: #ffffff !important;
  box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3) !important;
}

input:checked + label.fi-btn.fi-color-primary:hover {
  background-color: #1d4ed8 !important;
}

/* ================= PAGINATION ================= */
.fi-ta-pagination {
  padding: 14px 20px !important;
  border-top: 1px solid #eef1f7 !important;
  background: #ffffff !important;
}

/* Trang đang xem — đo trên /app/charges trước khi sửa:
     nền  #f8fafc (gray-50, lệch nền thường đúng 2% → gần như không thấy)
     chữ  #d97706 (amber-600, vì panel khai primary => Color::Amber)
   Hai vấn đề: (1) app đã đổi nút chính sang xanh #3b82f6, phân trang là chỗ
   cuối cùng còn lộ amber ⇒ một màn có 2 màu nhấn; (2) #d97706 trên #f8fafc
   chỉ đạt 3.04:1, dưới WCAG AA 4.5:1 cho chữ 14px.
   Chọn #2563eb (= màu hover của .fi-btn.fi-color-primary) + chữ trắng = 5.12:1.
   KHÔNG dùng #3b82f6: chữ trắng trên nó chỉ 3.68:1, vẫn trượt AA.

   ⚠️ Màu chữ phải đặt trên .fi-pagination-item-label chứ không phải trên
   button: Filament apply utility text-primary-600 lên SPAN con, style màu ở
   button dù có !important vẫn thua utility trên child (đúng bài học đã gặp ở
   .fi-sidebar-item-label). Khoá bằng tests/Feature/Frontend/PaginationPaletteTest.php */
.fi-pagination-item.fi-active .fi-pagination-item-button {
  background: #2563eb !important;
}

.fi-pagination-item.fi-active .fi-pagination-item-label {
  color: #ffffff !important;
}

.fi-pagination-item.fi-active .fi-pagination-item-button:hover {
  background: #1d4ed8 !important;
}

/* ============================================================
   S10 — THẺ LỚP MÀN "THU HỌC PHÍ THEO THÁNG" (03/09)

   Chủ dự án: "phần tháng này hài vậy, sao để dọc mà ko để ngang".
   Đo ở 1440px trước khi sửa: <dl class="grid grid-cols-2"> ra
   grid-template-columns = "527px" (MỘT cột) → 4 ô số liệu xếp dọc
   (y = 414/450/486/522), thẻ rộng 553px trong khung 1118px → trống nửa hàng.

   Nguyên nhân: dist CSS của Filament chỉ gom class Filament tự dùng, KHÔNG
   có `grid-cols-2`/`md:grid-cols-2`. Class vẫn nằm trong HTML nhưng không có
   rule nào → grid rơi về 1 cột. Vì vậy màn này dùng class ngữ nghĩa định
   nghĩa tại đây, không mượn utility Tailwind. Gác bằng MonthlyChargesLayoutTest.

   Bố cục: mỗi lớp là MỘT HÀNG ngang — tên lớp | 4 số liệu | nút "Xem HS",
   trải hết bề ngang. 1 khoá = 1 lớp (ERD-3.0 §5) nên lưới thẻ chỉ tổ ra
   một ô 1/3 hàng rồi bỏ trống 2/3.
   ============================================================ */
/* Danh sách khoá — tự quản khoảng cách dọc thay vì mượn gap-y-8 của page
   grid (32px) cộng mb-4 của section (16px) = 48px giữa hai khoá, trong khi
   mỗi khoá chỉ có ĐÚNG MỘT lớp nên màn toàn khoảng trắng (03/09). */
.ow-month-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.ow-month-card {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 16px;
  flex-wrap: nowrap;
  padding: 12px 14px;
  border: 1px solid #e6e9f2;
  border-radius: 10px;
  background: #ffffff;
}

.ow-month-card:hover {
  border-color: #c9d3ea;
  background: #fbfcff;
}

/* Lớp đã thu đủ: KHÔNG mờ cả row (làm mất badge "Hoàn tất"). Thay bằng
   nền nhạt + tên lớp làm nhạt — số liệu vẫn đậm để đọc nhanh. */
.ow-month-card--done {
  background: #f7f9fc;
}

.ow-month-card--done .ow-month-card__name {
  color: var(--ow-muted, #5f6880);
}

/* Cột tên lớp rộng CỐ ĐỊNH: để `flex: 1 1` thì tên lớp ngắn ("Dragon") vẫn
   nuốt hết chỗ thừa của màn rộng, đẩy số liệu dạt sang phải. */
.ow-month-card__head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 1 240px;
  min-width: 0;
}

.ow-month-card__name {
  font-weight: 600;
  font-size: 0.875rem /* 14px */;
  color: #1c2440;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Tầng 1 (danh sách tháng): dòng phụ "N lớp · N khoản thu" xuống hàng dưới
   tên tháng thay vì chen ngang — cột head chỉ rộng 240px, nhét cùng hàng là
   badge "Tháng này" bị đẩy khuất. */
.ow-month-card__head:has(.ow-month-card__sub) {
  flex-wrap: wrap;
  row-gap: 2px;
}

.ow-month-card__sub {
  flex: 0 0 100%;
  font-size: 0.75rem /* 12px */;
  color: var(--ow-muted, #5f6880);
  white-space: nowrap;
}

/* Đường quay lại tầng tháng — đứng trên header, cao ≥40px để bấm được
   bằng ngón tay ở 375px (CLAUDE.md §3). */
.ow-month-back {
  margin-bottom: 8px;
}

.ow-month-back__link {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: 0 10px 0 0;
  font-size: 0.8125rem /* 13px */;
  font-weight: 600;
  color: #4b5c86;
  text-decoration: none;
}

.ow-month-back__link:hover {
  color: #1c2440;
  text-decoration: underline;
}

/* Round 2 03/09 — User báo "trống bên phải lắm thế". Cân bằng:
   - Thêm CỘT THỨ 5 "Tiến độ" (% đã thu) để scan trực quan
   - 4 cột tiền/HS dùng minmax(120px, 1fr) — fill ~140px ở 1440px viewport
   - Cột cuối "Tiến độ" rộng hơn (minmax(200px, 1.5fr)) để progress bar
     đọc được; tổng 5 cột lấp đầy card ~1160px, nút Xem HS dồn sát phải. */
.ow-month-metrics {
  display: grid;
  grid-template-columns:
    minmax(120px, 1fr)
    minmax(120px, 1fr)
    minmax(120px, 1fr)
    minmax(120px, 1fr)
    minmax(200px, 1.5fr);
  gap: 10px;
  flex: 0 1 auto;
  margin: 0;
  min-width: 0;
}

.ow-month-metric dt,
.ow-month-metric__label {
  font-size: 0.6875rem /* 11px */;
  color: var(--ow-muted, #5f6880);
  margin: 0 0 2px;
  white-space: nowrap;
}

.ow-month-metric dd,
.ow-month-metric__value {
  font-size: 0.8125rem /* 13px */;
  font-weight: 600;
  color: #1c2440;
  margin: 0;
  white-space: nowrap;
}

.ow-month-metric__value--paid {
  color: #15803d;
}

.ow-month-metric__value--due {
  color: #b45309;
}

/* Cột "Tiến độ" — thanh progress bar mini (Round 2 03/09, lấp trống phải) */
.ow-month-metric--progress dd {
  display: flex !important;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
}

.ow-month-progress {
  flex: 1 1 auto;
  height: 8px;
  background: #eef1f7;
  border-radius: 999px;
  overflow: hidden;
  position: relative;
}

.ow-month-progress__bar {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, #22c55e 0%, #15803d 100%);
  border-radius: 999px;
  transition: width 0.3s ease;
}

.ow-month-card--done .ow-month-progress__bar {
  background: linear-gradient(90deg, #16a34a 0%, #15803d 100%);
}

.ow-month-progress__label {
  flex: 0 0 auto;
  min-width: 36px;
  font-size: 0.8125rem /* 13px */ !important;
  font-weight: 600 !important;
  color: var(--ow-ink) !important;
  text-align: right;
}

/* Nút đi liền sau số liệu, KHÔNG margin-left:auto — đẩy ra mép phải thì ở
   1920px nút cách số liệu cả 700px, bấm phải rê chuột qua nửa màn hình. */
/* margin-left:auto — chủ dự án 03/09 (chiều): "để flex cho tôi đi, nhìn cho
   nó đồng đều xíu". Card là flex-start nên tên + số liệu vẫn dồn trái; riêng
   cột nút tự đẩy ra mép phải, khoảng thừa rơi vào giữa. Trước đó margin 8px:
   ở 1920px nút kết thúc x=1483 trong khi mép hàng x=1824 → thừa 341px cuối
   mỗi dòng và nút các dòng lệch nhau theo độ dài tên lớp. */
.ow-month-card__action {
  flex: 0 0 auto;
  margin-left: auto;
  padding-left: 8px;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ============================================================
   HEADER + SUMMARY BAR — Phase 6 round 2 (03/09)
   ------------------------------------------------------------
   Trước: chỉ "Tháng 09/2026" + "3 lớp · 11 khoản thu". Kế toán phải tự
   cộng 4 số liệu mỗi thẻ lớp để ra tổng tháng → mệt + dễ sai.

   Sau: thêm thanh SUMMARY 3 ô tiền (Phải thu / Đã thu / Còn lại) nằm
   NGANG với tiêu đề tháng, đổi màu theo trạng thái (xanh = đã đủ, cam
   = còn thiếu). Mobile bẻ dọc: tiêu đề trên, summary dưới, mỗi ô full
   width.
   ============================================================ */

/* Badge "Hoàn tất" — Filament v3.3 render bằng `.fi-badge.fi-color-custom`
   với CSS var --c-50/400/600 (round trước kiểm HTML thật, không có
   `.fi-color-success` nữa). Default custom = chữ xanh đậm trên nền xanh
   nhạt → contrast thấp, dễ mất khi đặt cạnh badge "Quá hạn" đỏ. Ép sang
   chữ TRẮNG trên nền xanh đậm để nổi rõ (WCAG AA 4.5:1).
   Specificity: chọn `.fi-badge.fi-color-custom[style*="--c-600:var(--success-600)"]`
   để chỉ đè badge success, không đụng các badge color khác. */
.fi-badge.fi-color-custom[style*="--c-600:var(--success-600)"] {
  color: #ffffff !important;
  background-color: #15803d !important;
  border-color: transparent !important;
}

.fi-badge.fi-color-custom[style*="--c-600:var(--success-600)"] * {
  color: #ffffff !important;
}

/* ============================================================
   SUMMARY BAR — dùng chung cho các bảng tài chính (S10).
   ------------------------------------------------------------
   Pattern: 3 ô cạnh nhau, ô cuối ("Còn lại") đổi màu nền khi > 0 để
   cảnh báo. Mobile bẻ dọc.
   ============================================================ */
.ow-month-summary,
.ow-charges-summary,
.ow-invoices-summary {
  display: grid;
  gap: 12px;
  margin: 0 0 16px !important;
}

.ow-month-summary {
  grid-template-columns: repeat(3, minmax(140px, 1fr));
  flex: 1 1 480px;
}

.ow-charges-summary,
.ow-invoices-summary {
  grid-template-columns: repeat(4, minmax(140px, 1fr));
}

/* T4.71 — màn hoá đơn thuế có 5 thẻ (thêm "Không xuất"); 5 cột để summary bar
   vẫn nằm MỘT hàng, tránh thẻ "Tổng tiền" rớt xuống hàng dưới một mình. */
.ow-invoices-summary {
  grid-template-columns: repeat(5, minmax(140px, 1fr));
}

.ow-charges-summary__item,
.ow-invoices-summary__item {
  background: var(--ow-card);
  border: 1px solid var(--ow-line);
  border-radius: var(--ow-radius-sm);
  padding: 12px 16px;
}

.ow-charges-summary__item--accent,
.ow-invoices-summary__item--accent {
  border-color: #f6c97a;
  background: #fff8eb;
}

.ow-charges-summary__item dt,
.ow-invoices-summary__item dt {
  font-size: 0.6875rem /* 11px */;
  color: var(--ow-muted);
  margin: 0 0 4px;
  font-weight: 500;
}

.ow-charges-summary__value,
.ow-invoices-summary__value {
  font-size: 1.25rem /* 20px */;
  font-weight: 700;
  color: var(--ow-ink);
  margin: 0;
  line-height: 1.2;
}

.ow-charges-summary__value--paid,
.ow-invoices-summary__value--paid {
  color: var(--ow-green-ink);
}

.ow-charges-summary__value--due,
.ow-invoices-summary__value--due {
  color: var(--ow-accent-ink);
}

.ow-charges-summary__hint,
.ow-invoices-summary__hint {
  display: block;
  margin-top: 4px;
  font-size: 0.6875rem /* 11px */;
  color: var(--ow-muted);
}

@media (max-width: 767.98px) {
  .ow-charges-summary,
  .ow-invoices-summary {
    grid-template-columns: 1fr;
  }
}

/* Danh sách "Miễn — lớp phụ" trong Xem HS (S10, SPEC v2 §6) — chỉ đọc. */
.ow-charges-exempt {
  margin: 0 0 16px;
  padding: 12px 16px;
  background: var(--ow-card);
  border: 1px solid var(--ow-line);
  border-radius: var(--ow-radius-sm);
}

.ow-charges-exempt__heading {
  margin: 0 0 8px;
  font-size: 0.75rem /* 12px */;
  font-weight: 600;
  color: var(--ow-muted);
}

.ow-charges-exempt__list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ow-charges-exempt__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.ow-charges-exempt__name {
  color: var(--ow-ink);
}

.ow-charges-exempt__chip {
  font-size: 0.6875rem /* 11px */;
  font-weight: 600;
  padding: 2px 10px;
  border-radius: 999px;
  background: #eef1f4;
  color: var(--ow-muted);
}

/* Cảnh báo anh/chị em còn nợ trong modal "Thu tiền" (S10, C-4) — chủ dự án
   07/09: "báo đỏ đi" sau khi xem demo chỉ có tên mà không nổi bật. */
.ow-charges-sibling-warning {
  display: inline-block;
  margin-top: 8px;
  padding: 8px 12px;
  background: var(--ow-red-soft);
  color: var(--ow-red-ink);
  font-weight: 600;
  border-radius: var(--ow-radius-sm);
}
.ow-month-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}

.ow-month-header__title {
  flex: 0 1 auto;
  min-width: 180px;
}

.ow-month-header__heading {
  font-size: 1.125rem /* 18px */ !important;
  font-weight: 600 !important;
  color: var(--ow-ink);
  margin: 0 !important;
  line-height: 1.3;
}

.ow-month-header__sub {
  font-size: 0.8125rem /* 13px */;
  color: var(--ow-muted);
  margin: 4px 0 0 !important;
}

.ow-month-summary {
  display: grid;
  grid-template-columns: repeat(3, minmax(140px, 1fr));
  gap: 12px;
  margin: 0;
  flex: 1 1 480px;
}

.ow-month-summary__item {
  background: var(--ow-card);
  border: 1px solid var(--ow-line);
  border-radius: var(--ow-radius-sm);
  padding: 10px 14px;
}

.ow-month-summary__item--accent {
  border-color: #f6c97a;
  background: #fff8eb;
}

.ow-month-summary__item dt {
  font-size: 0.6875rem /* 11px */;
  color: var(--ow-muted);
  margin: 0 0 4px;
  font-weight: 500;
}

.ow-month-summary__value {
  font-size: 1.125rem /* 18px */;
  font-weight: 700;
  color: var(--ow-ink);
  margin: 0;
  line-height: 1.2;
}

.ow-month-summary__value--paid {
  color: var(--ow-green-ink);
}

.ow-month-summary__value--due {
  color: var(--ow-accent-ink);
}

@media (max-width: 767.98px) {
  .ow-month-header {
    flex-direction: column;
    align-items: stretch;
  }

  .ow-month-summary {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 767.98px) {
  /* 375px: 4 cột thì mỗi ô ~70px, "1.500.000đ" bị cắt → bẻ 2 cột, thẻ xếp dọc */
  .ow-month-card {
    align-items: stretch;
    flex-direction: column;
    gap: 10px;
  }

  /* Card đổi sang flex-direction:column ở trên → trục chính thành trục DỌC,
     nên `flex: 0 1 240px` của cột tên (khoá bề ngang ở desktop) biến thành
     CHIỀU CAO 240px: tên tháng và dòng "3 lớp · 11 khoản thu" hở 102px trắng
     ở giữa (đo 375px 03/09). Phải trả basis về auto. */
  .ow-month-card__head {
    flex: 0 0 auto;
  }

  .ow-month-metrics {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  /* Trục chính là trục DỌC ở đây, nên margin-left:auto của desktop (đẩy nút
     ra mép phải hàng ngang) biến thành "đẩy sang phải + co theo nội dung":
     nút tụt từ 293px xuống 80px, quá bé để bấm bằng ngón tay. Trả về 0 để
     align-items:stretch của card ăn lại. */
  .ow-month-card__action {
    margin-left: 0;
    padding-left: 0;
  }

  .ow-month-card__action .ow-month-btn {
    width: 100%;
  }
}

/* Nút "Xem HS" — vùng bấm ≥40px (CLAUDE.md §3), dùng ở mọi viewport */
.ow-month-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: 0 14px;
  border-radius: 8px;
  border: 1px solid #2563eb;
  color: #2563eb;
  font-size: 0.8125rem /* 13px */;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}

.ow-month-btn:hover {
  background: #eff5ff;
}

.ow-month-btn--disabled {
  border-color: #d5dae6;
  color: #9aa3b8;
  pointer-events: none;
}

.ow-month-btn--notice {
  border-color: #f472b6;
  color: #be185d;
  background: #fdf2f8;
}

.ow-month-btn--notice:hover {
  background: #fce7f3;
  color: #9d174d;
}

/* ============================================================
   MOBILE (<1024px) — Đợt 4, chuẩn GV dùng điện thoại trong lớp
   ------------------------------------------------------------
   Chọn breakpoint 1024px (KHÔNG phải 768px) vì: drawer sidebar của
   Filament kích hoạt dưới lg = 1024px. Từ 768–1023px (tablet dọc)
   sidebar vẫn là drawer đè màn hình và bảng vẫn cuộn ngang — nếu chỉ
   áp dụng quy tắc mobile dưới 768px thì dải viewport đó rơi vào khoảng
   trống "nửa mobile nửa desktop". Một mốc duy nhất 1024px cũng khớp với
   điều kiện innerWidth < 1024 trong script seed drawer (AdminPanelProvider),
   tránh hai breakpoint lệch nhau phải bảo trì song song.
   ============================================================ */
@media (max-width: 1023.98px) {

  /* ---- Sticky cột đầu bảng (audit §3.4) ----
     Vùng cuộn ngang là .fi-ta-content (overflow-x-auto của Filament).
     Cột đầu ghim left:0 với NỀN ĐẶC đè lên các ô trôi qua; z-index cao
     hơn ô thường; kèm shadow phân cách bên phải để thấy ranh giới khi đè. */
  .fi-ta-content {
    -webkit-overflow-scrolling: touch;
  }

  .fi-ta-table thead th.fi-ta-header-cell:first-child,
  .fi-ta-table tbody td.fi-ta-cell:first-child {
    position: sticky !important;
    left: 0 !important;
    background: #ffffff !important;
    box-shadow: 1px 0 0 0 #f0f2f8, 6px 0 10px -6px rgba(28, 36, 64, 0.15) !important;
  }

  /* Ô tiêu đề cột đầu giữ đúng nền header #fafbfe và nổi trên cả ô dữ liệu sticky */
  .fi-ta-table thead th.fi-ta-header-cell:first-child {
    background: var(--ow-table-head-bg, #fafbfe) !important;
    z-index: 3 !important;
  }

  .fi-ta-table tbody td.fi-ta-cell:first-child {
    z-index: 2 !important;
  }

  /* Hàng hover đổi nền thì cột sticky cũng phải đổi theo, nếu không
     nền đặc của nó che mất hiệu ứng hover ở cột đầu tiên */
  .fi-ta-row:hover td.fi-ta-cell:first-child {
    background: #f8faff !important;
  }

  /* ---- Vùng bấm tối thiểu 40px (audit §3.4 / CLAUDE.md §3) ---- */

  /* Nút icon: topbar (mở/đóng drawer, thông báo, user), action dòng,
     pagination trước/sau (đều là x-filament::icon-button → .fi-icon-btn) */
  .fi-icon-btn {
    min-width: 40px !important;
    min-height: 40px !important;
  }

  /* Nút Trước/Tiếp theo của pager. Chúng là .fi-btn, KHÔNG phải .fi-icon-btn
     (không có nhãn icon-only) nên không dính rule vùng bấm nào ở trên — đo ở
     375px được đúng 36px, dưới ngưỡng 40px của CLAUDE.md §3. Ở màn hẹp đây là
     nút lật trang DUY NHẤT còn hiện, nên không được để nhỏ hơn ngưỡng. */
  .fi-pagination-previous-btn,
  .fi-pagination-next-btn {
    min-height: 40px !important;
  }

  /* Cùng một lỗ hổng, chỗ khác: nút "Đăng nhập" của trang auth cũng là .fi-btn
     có nhãn, đo ở 375px ra đúng 36px. GV đăng nhập bằng điện thoại trong lớp
     nên đây là vùng bấm quan trọng nhất của cả hệ. Bó trong .fi-simple-main
     (layout trang auth) để không đụng nút .fi-btn trong bảng/modal. */
  .fi-simple-main .fi-btn {
    min-height: 40px !important;
  }

  /* vendor filament/support/support.css đặt .fi-pagination-overview{display:none}
     và chỉ mở lại từ container 56rem; .fi-pagination-items cũng bị ẩn dưới mốc đó.
     Hệ quả ở 375px: mất cả số trang lẫn dòng "Đang hiện 1–10 của 11" ⇒ GV không
     còn tín hiệu nào cho biết đang đứng ở trang mấy. Bật lại dòng tổng quan
     (rẻ hơn nhiều so với nhồi cả dãy số trang vào bề ngang 375px).

     Bật display thôi thì CHƯA đủ: pager là grid-cols-[1fr_auto_1fr], ở 375px
     cột 1 chỉ rộng 96.5px nên dòng này gãy 3 dòng — xấu hơn lúc bị ẩn. Cho nó
     chiếm trọn hàng 1; ô "Mỗi trang" (col-start-2) và nút Trước/Tiếp theo
     (col-start-1/3) tự rơi xuống hàng 2 nhờ auto-placement của grid. */
  .fi-ta-pagination {
    row-gap: 10px !important;
  }

  .fi-pagination-overview {
    display: block !important;
    grid-column: 1 / -1 !important;
  }

  /* Nút số trang / trang hiện tại trong pager */
  .fi-pagination-item-button {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    min-width: 40px !important;
    min-height: 40px !important;
  }

  /* Ô toggle chọn dòng / chọn cả trang (label bọc checkbox của Filament) —
     phóng vùng bấm lên ≥40px thay vì phóng chính ô tick */
  .fi-ta-record-checkbox,
  .fi-ta-page-checkbox {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    min-width: 40px !important;
    min-height: 40px !important;
  }
}

/* ============================================================
   REDUCED MOTION — khối tập trung duy nhất (skill web-motion:
   honor prefers-reduced-motion ở MỘT chỗ, không rải per-component).
   Người dùng bật "giảm chuyển động" trong OS: mọi transition/animation
   của theme về ~0ms; animation Chart.js (450ms) không nằm trong CSS
   nên vẫn chạy 1 lần ngắn — chấp nhận được, không đụng layout.

   ⚠️ Cascade: rule `* !important` THUA rule `.class !important` cùng file
   (specificity). Vì vậy ngoài `*` phải LẶP LẠI ĐÚNG selector đã khai báo
   transition ở trên (.fi-sidebar, .fi-sidebar-item-button, .fi-ta-row là
   !important; .ow-ql-btn thường). Thêm transition mới ở đầu file thì phải
   thêm selector vào đây nữa — đã verify bằng emulate reducedMotion.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  /* Lặp selector trùng khớp để thắng specificity của chính theme này */
  .fi-sidebar,
  .fi-sidebar-item-button,
  .fi-ta-row,
  .fi-btn.fi-color-primary,
  .ow-ql-btn,
  .ow-pill,
  .ow-brand-tile {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* ============================================================
   S05 — DANH SÁCH HS TRONG MÀN ĐIỂM DANH THEO BUỔI (06/09)

   Chủ dự án: "màn hình xấu ác".
   Đo ở 1600px trước khi sửa: hàng HS là <div class="flex justify-between">
   với tên `flex-1` → chữ "Huỳnh Hải Bảo" hết ở x=379, nút "Có mặt" bắt đầu ở
   x=1410. 1031px TRỐNG giữa hai thứ phải đọc cùng nhau; GV dò mắt qua đó rất
   dễ tick nhầm sang hàng dưới, và card 1278px thì rỗng ruột.

   Vì sao không dùng `grid-cols-1 md:grid-cols-2 xl:grid-cols-3`: dist CSS của
   Filament chỉ gom class Filament tự dùng (xem khối "S10 — THẺ LỚP" phía
   trên). Đo bằng CSSOM: `md:grid-cols-2` có rule, nhưng `xl:grid-cols-3` và
   `gap-x-8` KHÔNG → lưới im lặng kẹt 2 cột dính nhau. Class ngữ nghĩa ở đây
   không phụ thuộc build Tailwind nào.

   Breakpoint theo CLAUDE.md §3: <768px GV cầm điện thoại đứng lớp → MỘT cột,
   giữ nguyên hành vi cũ. Gác bằng SessionAttendancePageTest.
   ============================================================ */
.ow-roster-grid {
  display: grid;
  grid-template-columns: 1fr;
  column-gap: 32px;
}

@media (min-width: 768px) {
  .ow-roster-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 1280px) {
  .ow-roster-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* S05 mobile: mọi điều khiển điều hướng phải đạt vùng chạm tối thiểu 40px. */
.ow-attendance-toolbar .fi-btn,
.ow-attendance-toolbar button {
  min-height: 40px !important;
}

/* S05 — visual language theo reference: workspace sáng, card nhẹ.
   07/09: hero (.ow-s05-hero/-kicker/-context/__title/__subtitle) và sequence
   3 bước (.ow-s05-step*) đã XOÁ cùng markup — hero nói lại page header của
   Filament, sequence vẽ bước "Chọn lớp" đã bỏ từ 06/09. */
.ow-s05-page { --ow-s05-blue: #1769e0; --ow-s05-blue-soft: #eef5ff; --ow-s05-ink: #14213d; }
.ow-s05-page .fi-section { border-color: #dfe8f5; border-radius: 14px; box-shadow: 0 4px 16px rgba(20,33,61,.04); }
.ow-s05-page .ow-roster-row {
  min-height: 58px;
  /* T4.71 #19: thanh Lưu dính đáy cao tới 98px (+ .5rem cách đáy) — cuộn một hàng xuống đáy
     (focus bằng bàn phím, scrollIntoView) phải dừng Ở TRÊN thanh, không nằm dưới nó. */
  scroll-margin-bottom: 8rem;
}
.ow-s05-page .ow-s05-save-bar { border: 1px solid var(--ow-line); }
.ow-s05-page .ow-roster-row button[aria-pressed="true"] { outline: 2px solid currentColor; outline-offset: 1px; }
.ow-s05-page .ow-s05-status-button[data-status="PRESENT"][aria-pressed="true"] {
  border-color: #16a34a !important; background: #dcfce7 !important; color: #166534 !important;
}
.ow-s05-page .ow-s05-status-button[data-status="ABSENT"][aria-pressed="true"] {
  border-color: #dc2626 !important; background: #fee2e2 !important; color: #991b1b !important;
}
.ow-s05-page .ow-s05-status-button[data-status="PRESENT"]:hover { border-color: #86efac !important; background: #f0fdf4 !important; }
.ow-s05-page .ow-s05-status-button[data-status="ABSENT"]:hover { border-color: #fca5a5 !important; background: #fef2f2 !important; }

/* ── S04 · Lịch học theo lớp — hero + toolbar (lưới dùng chung .ow-cal-*) ── */
.ow-s04-page { --ow-s04-blue: #1769e0; --ow-s04-blue-soft: #eef5ff; --ow-s04-ink: #14213d; }
.ow-s04-hero {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 16px;
  padding: 20px 24px; border-radius: 16px;
  background: linear-gradient(135deg, var(--ow-s04-blue-soft), #ffffff 70%);
  border: 1px solid #dbe7fb;
}
.ow-s04-kicker { display: block; font-size: 0.6875rem /* 11px */; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--ow-s04-blue); }
.ow-s04-title { font-size: 1.375rem /* 22px */; font-weight: 800; line-height: 1.2; color: var(--ow-s04-ink); }
.ow-s04-subtitle { margin-top: 4px; font-size: 0.8125rem /* 13px */; color: #4b5b74; }
.ow-s04-hero__meta { display: flex; flex-direction: column; gap: 2px; text-align: right; font-size: 0.8125rem /* 13px */; font-weight: 600; color: var(--ow-s04-ink); white-space: nowrap; }
.ow-s04-hero__meta span:last-child { font-size: 0.75rem /* 12px */; font-weight: 500; color: #4b5b74; }
.ow-s04-toolbar .fi-btn, .ow-s04-toolbar button { min-height: 40px !important; }
/* ============================================================
   LƯỚI "GIỜ × THỨ" — S04 (.ow-s04-*, cột-ngày độc lập) và S05
   (.ow-slot-*, hàng-giờ-dùng-chung, revert 08/09 — chủ dự án chấp
   nhận giữ nguyên) dùng HAI bộ class RIÊNG, không san sẻ khung lưới
   (chỉ san sẻ .ow-slot-card — thẻ một buổi — xem block bên dưới).

   07/09 lượt 2, chủ dự án gửi lại ảnh bản cũ: "phần lịch học như ảnh
   mới đẹp chứ kia xấu quá, lịch tuần ấy". Lý do đo được (chép ở đầu
   app/Services/Schedule/SlotGrid.php): dữ liệu thật chỉ có 2 mốc giờ,
   trục phút liên tục chia khối trùng giờ thành 33% bề ngang rồi cắt
   tên lớp. Ô giờ rời thì 3 lớp trùng giờ xếp DỌC trong cùng ô.

   Cột 1 là trục giờ, 7 cột sau là thứ, mỗi cột minmax(0,1fr) nên co
   được về 0 — KHÔNG khoá min-width, KHÔNG cuộn ngang (chủ dự án:
   "tôi ko thích cái cuộn sang bên phải").

   Markup gom theo NGÀY để <768px xếp dọc đúng thứ tự đọc, nên
   .ow-slot-day phải là `display: contents` — không thì bọc thêm một
   tầng box và ô rơi khỏi lưới. Gác 2 đầu bằng ScheduleGridTest (S05).
   ============================================================ */
.ow-slot-grid {
  display: grid;
  grid-template-columns: 3.25rem repeat(7, minmax(0, 1fr));
  gap: 6px;
  align-items: start;
}
.ow-slot-day { display: contents; }
.ow-slot-corner { grid-row: 1; grid-column: 1; }
.ow-slot-hour {
  grid-column: 1;
  padding-top: 6px;
  font-size: 0.6875rem /* 11px */;
  font-weight: 700;
  color: #64748b;
  text-align: right;
  white-space: nowrap;
}
.ow-slot-dayhead {
  grid-row: 1;
  /* Dính mép trên khi khung cuộn dọc — cuộn tới hàng 19:30 mà mất tên
     thứ thì không biết đang xem cột nào. */
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 1px;
  width: 100%;
  padding: 6px 4px;
  border-bottom: 2px solid transparent;
  background: #fff;
  text-align: center;
}
.ow-slot-dow { font-size: 0.6875rem /* 11px */; font-weight: 700; letter-spacing: .02em; color: #64748b; }
.ow-slot-num { font-size: 0.8125rem /* 13px */; font-weight: 700; color: var(--ow-s04-ink, #14213d); }
.ow-slot-dayhead--today { border-bottom-color: var(--ow-s04-blue, #1769e0); }
.ow-slot-dayhead--today .ow-slot-dow, .ow-slot-dayhead--today .ow-slot-num { color: var(--ow-s04-blue, #1769e0); }
/* Ô lưới là KHUNG XẾP CHỖ, KHÔNG phải thẻ — không nền, không viền, không bo
   góc. min-height giữ chiều cao hàng ổn định khi ô rỗng. */
.ow-slot-cell {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-height: 56px;
}

/* ============================================================
   S04 — 3 DẢI NGANG THEO CA (.ow-s04-band, TimeOfDay::bandsOf). 09/09
   lượt 2: bản CỘT-NGÀY ĐỘC LẬP (lượt 1, xem git log) đúng "không còn ô
   rỗng giả" nhưng vẫn LỆCH CHIỀU CAO nặng — một cột gộp CẢ 3 CA nên
   ngày ít lớp (T2-T5) dừng sớm để trống một khoảng lớn, ngày đông lớp
   (T7/CN) kéo quá dài (đo trên ảnh chụp 1440px thật, data seed
   'huge'). Đổi sang 3 dải NGANG — dải Sáng của cả tuần, rồi dải
   Chiều, rồi dải Tối — mỗi dải chỉ cao bằng ngày đông nhất TRONG
   CHÍNH CA ĐÓ. Biên độ lệch nhỏ hẳn vì không còn trộn 3 ca vào một
   cột. Dải nào cả tuần không có buổi thì bỏ hẳn (bandsOf), dải đã
   hiện thì giữ đủ 7 ô-ngày kể cả ngày rỗng.

   BỐ CỤC 2 NHỊP VẬN HÀNH (.ow-s04-split):
   - Ngày thường (T2–T6): 5 cột, tập trung ca Tối.
   - Cuối tuần (T7–CN): 2 cột, đủ 3 ca Sáng/Chiều/Tối.
   ============================================================ */
.ow-s04-split {
  display: grid;
  grid-template-columns: minmax(0, 3.2fr) minmax(0, 2fr);
  gap: 16px;
  align-items: start;
}
.ow-s04-split__panel {
  display: flex;
  flex-direction: column;
  background: #fbfcfe;
  border: 1px solid #e2e8f0;
  border-radius: 12px;
  padding: 12px 14px;
}
.ow-s04-split__panel--weekdays {
  background: #fcfdfe;
  border-color: #e2e8f0;
}
.ow-s04-split__panel--weekend {
  background: #f8faff;
  border-color: #dbeafe;
}
.ow-s04-split__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-bottom: 8px;
  margin-bottom: 10px;
  border-bottom: 1px solid #edf2f7;
}
.ow-s04-split__title-group {
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.ow-s04-split__title {
  font-size: 0.8125rem /* 13px */;
  font-weight: 800;
  color: #1e293b;
}
.ow-s04-split__panel--weekend .ow-s04-split__title {
  color: #1e40af;
}
.ow-s04-split__sub {
  font-size: 0.6875rem /* 11px */;
  color: #64748b;
  font-weight: 500;
}
.ow-s04-split__badge {
  font-size: 0.6875rem /* 11px */;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: 6px;
  background: #e2e8f0;
  color: #334155;
  font-variant-numeric: tabular-nums;
}
.ow-s04-split__badge--weekend {
  background: #dbeafe;
  color: #1d4ed8;
}
.ow-s04-split__empty {
  padding: 28px 16px;
  text-align: center;
  font-size: 0.75rem /* 12px */;
  color: #94a3b8;
  font-style: italic;
}
.ow-s04-dayhead-row {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 8px;
}
.ow-s04-dayhead-row--weekdays {
  grid-template-columns: repeat(5, minmax(0, 1fr));
}
.ow-s04-dayhead-row--weekend {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.ow-s04-dayhead {
  display: flex;
  flex-direction: column;
  gap: 1px;
  width: 100%;
  padding: 6px 4px;
  border-bottom: 2px solid transparent;
  background: #fff;
  text-align: center;
  cursor: pointer;
}
.ow-s04-dayhead:hover { background: #f5f9ff; }
.ow-s04-dow { font-size: 0.6875rem /* 11px */; font-weight: 700; letter-spacing: .02em; color: #64748b; }
.ow-s04-num { font-size: 0.8125rem /* 13px */; font-weight: 700; color: var(--ow-s04-ink, #14213d); }
.ow-s04-dayhead--today { border-bottom-color: var(--ow-s04-blue, #1769e0); }
.ow-s04-dayhead--today .ow-s04-dow, .ow-s04-dayhead--today .ow-s04-num { color: var(--ow-s04-blue, #1769e0); }
.ow-s04-dayhead--on { background: var(--ow-s04-blue-soft, #eef5ff); border-radius: 8px 8px 0 0; }

.ow-s04-band { margin-top: 14px; }
.ow-s04-band__label {
  display: block;
  margin-bottom: 6px;
  font-size: 0.625rem /* 10px */;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: #94a3b8;
}
.ow-s04-band--sang .ow-s04-band__label { color: var(--ow-accent-ink, #b45309); }
.ow-s04-band--chieu .ow-s04-band__label { color: var(--ow-green-ink, #15803d); }
.ow-s04-band--toi .ow-s04-band__label { color: var(--ow-purple-ink, #6d28d9); }
.ow-s04-band__grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 8px;
  align-items: start;
}
.ow-s04-band__grid--weekdays {
  grid-template-columns: repeat(5, minmax(0, 1fr));
}
.ow-s04-band__grid--weekend {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.ow-s04-band__cell { display: flex; flex-direction: column; gap: 6px; min-width: 0; position: relative; } /* 11/09: relative để .ow-s04-drop-time định vị trên ô */
.ow-s04-band__cell-tag { display: none; font-size: 0.65625rem /* 10.5px */; font-weight: 700; color: #64748b; }

/* Chú thích ca học (S04) — màu sắc thống nhất với ảnh mẫu */
.ow-s04-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  padding: 6px 12px;
  background: #f8fafc;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  font-size: 0.71875rem /* 11.5px */;
  color: #64748b;
}
.ow-s04-legend__title { font-weight: 700; color: #334155; }
.ow-s04-legend__item { display: inline-flex; align-items: center; gap: 6px; }
.ow-s04-legend__dot { width: 8px; height: 8px; border-radius: 999px; flex: 0 0 auto; }
.ow-s04-legend__dot--sang { background: #f59e0b; }
.ow-s04-legend__dot--chieu { background: #10b981; }
.ow-s04-legend__dot--toi { background: #7c3aed; }

/* Thẻ một buổi — dùng chung S04 (link sang lớp) và S05 (mở roster).
   min-height 40px là vùng bấm tối thiểu (CLAUDE.md §3): GV bấm bằng ngón tay
   giữa lớp. Trên lưới ô giờ thẻ chiếm HẾT bề ngang ô nên không còn phép chia
   ba như trục phút — chỉ còn chiều cao phải khoá. */
.ow-slot-card {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-height: 44px;
  width: 100%;
  /* 13/09 · laptop: thẻ là MỘT container truy vấn được. Chữ tự nhượng nhau theo
     bề rộng của chính cái thẻ — không đoán qua bề ngang màn hình, vì thẻ hẹp là
     do CỘT lưới hẹp (S05 dùng chung partial này với thẻ rộng hơn → không kích hoạt). */
  container-type: inline-size;
  container-name: ow-s04-card;
  padding: 6px 8px;
  border: 1px solid #dbe7fb;
  border-left: 3.5px solid var(--ow-s04-blue, #1769e0);
  border-radius: 8px;
  background: #fff;
  box-shadow: 0 1px 2px rgb(20 33 61 / .06);
  color: inherit;
  text-align: left;
  text-decoration: none;
  transition: all .15s ease;
}
.ow-slot-card:hover { border-color: var(--ow-s04-blue, #1769e0); background: var(--ow-s04-blue-soft, #eef5ff); transform: translateY(-1px); box-shadow: 0 3px 6px rgb(20 33 61 / .08); }

/* Phân biệt ca học theo màu viền trái (khớp với chú thích) */
.ow-s04-card--sang { border-left-color: #f59e0b; }
.ow-s04-card--sang:hover { border-color: #f59e0b; background: #fffdf5; }
.ow-s04-card--chieu { border-left-color: #10b981; }
.ow-s04-card--chieu:hover { border-color: #10b981; background: #f0fdf4; }
.ow-s04-card--toi { border-left-color: #7c3aed; }
.ow-s04-card--toi:hover { border-color: #7c3aed; background: #faf5ff; }

/* Tên lớp một dòng RIÊNG, không xếp cạnh chip mã lớp: chip `flex: 0 0 auto`
   không co được nên ở cột 117px nó bóp tên còn 1–2 ký tự (đo 1280px 07/09). */
.ow-slot-card__name { display: block; flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; white-space: normal; font-size: 0.8125rem /* 13px */; font-weight: 700; line-height: 1.3; color: var(--ow-s04-ink, #14213d); }
.ow-slot-card__code { flex: 0 0 auto; font-size: 0.59375rem /* 9.5px */; font-weight: 700; letter-spacing: .03em; color: var(--ow-s04-blue, #1769e0); background: var(--ow-s04-blue-soft, #eef5ff); border-radius: 4px; padding: 1px 5px; }
.ow-slot-card__line { display: flex; align-items: center; gap: 6px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.6875rem /* 11px */; color: #4b5b74; }
.ow-s04-card__head { justify-content: space-between; }
.ow-s04-card__time-row { justify-content: space-between; gap: 4px; }
.ow-s04-card__meta-row { gap: 6px; }
.ow-s04-card__room { font-size: 0.625rem /* 10px */; font-weight: 600; color: #475569; background: #f1f5f9; padding: 1px 5px; border-radius: 4px; }
.ow-s04-card__staff-row { color: #64748b; font-size: 0.65625rem /* 10.5px */; }
.ow-s04-card__staff-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ow-slot-card__time { font-weight: 700; color: var(--ow-s04-ink, #14213d); }
.ow-slot-card__level { font-size: 0.625rem /* 10px */; font-weight: 700; color: #64748b; }
.ow-slot-card__seq { font-size: 0.59375rem /* 9.5px */; font-weight: 600; color: #0284c7; background: #e0f2fe; padding: 1px 5px; border-radius: 4px; }
.ow-slot-card__off { align-self: flex-start; margin-top: 2px; padding: 1px 6px; border-radius: 6px; font-size: 0.625rem /* 10px */; font-weight: 700; color: #b42318; background: #fef3f2; }
.ow-slot-card--cancelled, .ow-s04-card--cancelled, .ow-s05-slot--cancelled { border-left-color: #b42318 !important; opacity: .72; }

/* "Thẻ rút gọn khi đông" (09/09) — khoảng vượt TimeOfDay::COMPACT_THRESHOLD
   buổi thì tên lớp + giờ trên MỘT dòng, bỏ mã lớp/cấp độ/phòng/GV (còn đủ
   trong title=""). Chỉ đổi mật độ hiển thị, KHÔNG đổi đích bấm hay dữ liệu. */
.ow-s04-card--compact { min-height: 0; padding: 5px 8px; gap: 0; }
.ow-s04-card--compact .ow-s04-card__head { justify-content: space-between; gap: 6px; }
.ow-s04-card--compact .ow-slot-card__name { font-size: 0.75rem /* 12px */; }
.ow-s04-card--compact .ow-slot-card__time { flex: 0 0 auto; font-size: 0.65625rem /* 10.5px */; font-weight: 600; color: #64748b; }

/* ── 13/09 · THE HEP (dai laptop 1280–1536): chu nhuong nhau, khong ai bi cat ──
 * So do tren may that cua chu du an (Windows laptop 1366×768), truoc khi sua:
 *   the 114px -> dung duoc 94px, nhung hang 1 doi 120px: "Dragon"(48) +
 *   chip "DRAGON-01"(72, mang flex:0 0 auto => BOP KHONG DUOC) -> TEN LOP con 16px.
 *   hang 2 "18:00–19:00"(73) + "Buổi 20"(46) = 119px -> tran ra ngoai 25px
 *   hang 3 "Cơ bản"(54) + "Phòng 1"(55) = 109px -> cat 15px
 *   hang 4 "Cô Lan, TG Thầy Nam"(111px) -> "Cô Lan, TG Th…"
 * Tong ket qua quan sat o dai 1280/1366/1440: 17/15/13 cho chu bi cat +
 * 19/18/14 cho chu thoat hop, trong khi 13 man khac = 0/0.
 *
 * Nguong 12rem = 192px (do 13/09): the o laptop 1280/1366/1536 rong 114–179px,
 * ma rieng chip "DRAGON-01" da 93px (flex:0 0 auto) → 179px − padding − chip =
 * 60px, ten "Sunflower" can 66px van bi cat 6px. Tu 192px tro len (man 1536px
 * cua luoi 7 cot o day du cho) chip con nguyen. Duoi 192px: chip bi an nhung ma
 * lop + "Buổi N" con trong title= cua the — test Blade canh dieu do.
 * `@container` can Chromium/Edge >= 105 (2022): trinh duyet cu thi khoi nay bi
 * BO QUA im lang, the ve dang hom nay — khong te hon duoc.
 */
@container ow-s04-card (max-width: 12rem) {
  /* "DRAGON-01" = ten viet hoa + so buoi: trung lap voi ten lop ngay canh no. */
  .ow-slot-card__code {
    display: none;
  }

  .ow-slot-card__seq {
    display: none;
  }

  .ow-slot-card__name {
    font-size: 0.75rem /* 12px */;
  }

  /* Cap do + phong: an duoc cai nao cung mat thong tin -> cho buoc dong. */
  .ow-s04-card__meta-row {
    flex-wrap: wrap;
  }

  /* Buoc dong thi khong duoc con clip/ellipsis — no se cat chu nhu cu. */
  .ow-slot-card__line {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
  }

  .ow-s04-card__staff-name {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
  }
}

/* S05: ô đếm điểm danh trên thẻ — thứ GV cần thấy TRƯỚC khi bấm vào lớp
   (đã điểm danh mấy em rồi). Xong hết thì thẻ chuyển xanh. */
/* "Thiếu N/N" phải 1 dòng — nếu bẻ thành "Thiếu"/"34/34" xấu. */
.ow-s05-slot__count { font-weight: 700; color: var(--ow-s04-ink, #14213d); white-space: nowrap; }
.ow-s05-slot--done { border-left-color: var(--ow-green, #16a34a); background: var(--ow-green-soft, #f0fdf4); }
.ow-s05-slot--done .ow-s05-slot__count { color: var(--ow-green-ink, #166534); }
.ow-s05-slot--on { border-color: var(--ow-s04-blue, #1769e0); box-shadow: 0 0 0 2px var(--ow-s04-blue-soft, #eef5ff); }

/* ── BA MÀU ĐIỂM DANH (chủ dự án chốt 16/09) ────────────────────────────────
   🟢 done = đã điểm danh đủ · ⚪ mặc định = chưa tới điểm danh ·
   🔴 overdue = buổi đã qua ngưỡng ngày trên Cài đặt mà chưa đủ điểm danh.
   Luật một nguồn duy nhất: App\Services\Attendance\AttendanceCompliance —
   class ở đây CHỈ vẽ. Red KHÔNG phải màu của "Nghỉ" (cancelled): buổi nghỉ
   mờ đi và gạch tên, buổi thiếu điểm danh thì ĐẬM và có chữ "Quá hạn N ngày"
   kèm theo — hai tình huống khác nhau, người mù màu đọc bằng chữ được. */
.ow-s05-slot--overdue {
  border-left-color: var(--ow-red, #d92d20);
  border-left-width: 4px;
  background: var(--ow-red-soft, #fef3f2);
}
.ow-s05-slot--overdue .ow-slot-card__name,
.ow-s05-slot--overdue .ow-s05-slot__count { color: var(--ow-red-ink, #b42318); }
.ow-s05-slot__over {
  color: var(--ow-red-ink, #b42318);
  font-weight: 700;
  font-size: .6875rem /* 11px */;
  white-space: nowrap;
}
.ow-cal-chip--overdue {
  background: var(--ow-red-soft, #fef3f2);
  color: var(--ow-red-ink, #b42318);
  box-shadow: inset 0 0 0 1px #fdaaa1;
  font-weight: 600;
}
.ow-s05-overdue {
  margin-bottom: 12px;
  padding: 10px 14px;
  border-radius: 10px;
  border-left: 4px solid var(--ow-red, #d92d20);
  background: var(--ow-red-soft, #fef3f2);
  color: var(--ow-red-ink, #b42318);
  font-size: .84375rem /* 13.5px */;
  line-height: 1.5;
}
.ow-s05-overdue b { font-weight: 800; }

/* Chú giải ba màu phía trên lưới. Class riêng .ow-s05-colorlegend — KHÔNG
   mượn utility Tailwind arbitrary (dist Filament không có, vỡ im lặng). */
.ow-s05-colorlegend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 10px;
  font-size: .78125rem /* 12.5px */;
  color: var(--ow-s04-ink, #14213d);
}
.ow-s05-colorlegend__item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  border: 1px solid rgb(20 33 61 / .12);
  background: #fff;
}
.ow-s05-colorlegend__item::before {
  content: '';
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #d0d5dd;
}
.ow-s05-colorlegend__item--done::before { background: var(--ow-green, #16a34a); }
.ow-s05-colorlegend__item--overdue { color: var(--ow-red-ink, #b42318); border-color: #fdaaa1; }
.ow-s05-colorlegend__item--overdue::before { background: var(--ow-red, #d92d20); }
.ow-s05-colorlegend__item--off { color: #98a2b3; }
.ow-s05-colorlegend__item--off::before { background: #e4e7ec; }
.dark .ow-s05-colorlegend { color: #e4e7ec; }
.dark .ow-s05-colorlegend__item { background: rgb(255 255 255 / .04); border-color: rgb(255 255 255 / .1); }
.dark .ow-s05-slot--overdue { background: rgb(217 45 32 / .14); }
.dark .ow-cal-chip--overdue { background: rgb(217 45 32 / .16); color: #fdaaa1; }
.dark .ow-s05-overdue { background: rgb(217 45 32 / .14); color: #fdaaa1; }

@media (max-width: 767.98px) {
  /* 375px không đọc nổi 7 cột: lưới thành DANH SÁCH THEO NGÀY. Thứ tự DOM
     đã gom theo ngày nên chỉ cần bỏ display:grid — không cần subgrid, không
     cuộn ngang. Ngày trống ẩn hẳn (GV không cần cuộn qua T2 không có lớp). */
  .ow-slot-grid { display: block; }
  .ow-slot-day { display: block; margin-bottom: 12px; }
  .ow-slot-day[data-trong] { display: none; }
  /* Cột giờ ẩn: mỗi thẻ đã tự ghi giờ của nó ở .ow-s04-card__time. */
  .ow-slot-hour, .ow-slot-corner { display: none; }
  .ow-slot-dayhead { position: static; flex-direction: row; align-items: baseline; gap: 8px; text-align: left; }
  /* Xếp dọc thì không cần giữ chiều cao hàng — ô rỗng co về 0. */
  .ow-slot-cell { min-height: 0; }
  /* :empty không dùng được — blade để lại khoảng trắng trong ô, nên ô rỗng
     tự khai bằng data-rong. */
  .ow-slot-cell[data-rong] { display: none; }
}

@media (max-width: 1023.98px) {
  .ow-s04-split {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 767.98px) {
  /* S04 3 dải ngang: 375px không đọc nổi 7 cột cạnh nhau → mỗi dải đổi
     display:block, các ô-ngày trong dải xếp DỌC nối tiếp nhau. Đầu cột
     dùng chung (.ow-s04-dayhead-row) ẩn đi vì không còn "cột" để dóng
     theo — mỗi ô rỗng cũng tự ẩn (không có gì để đọc), ô có buổi thì tự
     in nhãn thứ NGAY TRONG ô (.ow-s04-band__cell-tag) để không mất ngữ
     cảnh khi xếp dọc. */
  .ow-s04-dayhead-row { display: none; }
  .ow-s04-band__grid { display: block; }
  .ow-s04-band__cell { margin-bottom: 10px; }
  /* :empty không dùng được — blade để lại khoảng trắng trong ô, nên ô rỗng
     tự khai bằng data-rong (cùng pattern .ow-slot-cell[data-rong] của S05). */
  .ow-s04-band__cell[data-rong] { display: none; }
  .ow-s04-band__cell-tag { display: block; }
}

@media (max-width: 767px) {
  .ow-s04-hero { flex-direction: column; align-items: flex-start; padding: 16px; }
  .ow-s04-hero__meta { text-align: left; }
  .ow-s04-title { font-size: 1.1875rem /* 19px */; }
}
.ow-s05-status-legend { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 14px; }
.ow-s05-status-legend__item { display: inline-flex; align-items: center; gap: 8px; padding: 7px 10px; border: 1px solid var(--ow-line); border-radius: 10px; background: #fbfcff; color: var(--ow-muted); font-size: 0.75rem /* 12px */; }
.ow-s05-status-legend__item::before { content: ''; width: 8px; height: 8px; border-radius: 999px; background: #94a3b8; }
.ow-s05-status-legend__item:nth-child(1)::before { background: var(--ow-green); }
.ow-s05-status-legend__item:nth-child(2)::before { background: var(--ow-red); }
/* Ô thứ 3 giờ là "Chưa điểm danh" (Đi muộn bỏ 07/09) — dùng xám mặc định,
   không tô cam accent nữa. */
.ow-s05-status-legend__item strong { color: var(--ow-ink); font-size: 0.9375rem /* 15px */; }
.ow-s05-class-tile--complete { color: #166534; }
.ow-s05-class-tile--complete .text-gray-950 { color: #166534 !important; }
.ow-s05-class-tile--complete .text-gray-500 { color: #166534 !important; }
.ow-s05-class-tile__complete { color: var(--ow-green-ink); font-size: 1rem /* 16px */; font-weight: 800; }
.ow-s05-class-tile__progress { height: 4px; margin-top: 6px; overflow: hidden; border-radius: 999px; background: #e8edf5; }
.ow-s05-class-tile__progress span { display: block; height: 100%; border-radius: inherit; background: var(--ow-green); transition: width .2s ease; }
.ow-s05-class-tile--complete .ow-s05-class-tile__progress { background: var(--ow-green-soft); }

@media (max-width: 767px) { .ow-s05-status-legend__item { flex: 1 1 calc(50% - 8px); justify-content: space-between; } }
.ow-s05-page .ow-roster-row [role="group"] { min-width: 0; }

/* 07/09: khối mobile của hero + sequence 3 bước xoá theo markup. */

/* ============================================================
   S05 — LỊCH TUẦN/THÁNG KIỂU GOOGLE CALENDAR (06/09)

   Chủ dự án gửi 2 ảnh GCal làm mẫu: "làm giống gg calendar…
   giờ nhìn nó phải lướt qua lướt lại khó chịu lắm". Đo GCal thật
   bằng Playwright: cột giờ ~52px chữ 11px mờ, số ngày to 26px/400,
   hôm nay = chấm tròn xanh đậm, event = khối bo góc màu đặc.

   Tỉ lệ tuần: 1px = 1 PHÚT (60px/giờ — GCal 48px, nới ra để khối
   75–90 phút chứa đủ 3 dòng chữ và vùng bấm ≥40px, CLAUDE.md §3).
   Blade chỉ nhân px từ topMin/lenMin của CalendarGeometry — con toán
   nằm ở service có test, KHÔNG tính trong view.

   Class ngữ nghĩa, không mượn utility Tailwind (xem khối S10 phía
   trên). Gác bằng SessionAttendancePageTest ("CSS theme định nghĩa
   thật") + CalendarGeometryTest.
   ============================================================ */
.ow-cal-scroll {
  /* 07/09, lời chủ dự án: "tôi ko thích cái cuộn sang bên phải" + "thừa một
     mảng sáng chiều từ thứ 2 đến thứ 6". Hai luật rút ra:
     1. KHÔNG cuộn ngang: lưới co theo khung (minmax(0,1fr) bên dưới), dưới
        768px thì xếp dọc — overflow-x chỉ còn là lưới an toàn.
     2. Trục giờ dài (T7 có ca sáng kéo trục từ 07:00 dù T2–T6 toàn lớp tối)
        thì CẮT CHIỀU CAO và tự trôi tới buổi đầu của hôm nay — như GCal trôi
        tới giờ hiện tại — thay vì bắt mắt lướt qua mảng trống. */
  overflow: auto;
  max-height: min(70vh, 760px);
}

/* Đang mở một buổi: lịch THU GỌN chứ không biến mất. 07/09 thử đẩy lịch xuống
   dưới danh sách HS cho đỡ cuộn, chủ dự án bác ngay ("cái gg calendar đẹp đẹp
   hôm qua đâu rồi sao giờ biến mất rồi") — đúng, vì mở trang bằng link có sẵn
   ?buoi= thì lịch rơi khỏi màn đầu. Giữ lịch đứng đầu, đổi CHIỀU CAO: vẫn thấy
   vẫn bấm đổi buổi được, mà roster lên gần tầm mắt. */
.ow-cal-scroll--gon {
  max-height: 300px;
}

/* 09/09 lượt 2: khung TUẦN riêng, KHÔNG kế thừa max-height/overflow:auto
   của .ow-cal-scroll. Khung đó cắt-cuộn cho trục giờ dài kiểu GCal của bố
   cục cũ (một cột duy nhất trải hết 07:00–21:30); 3 dải ngang theo ca thay
   nó không còn trục giờ dài — dải nào cũng ngắn — nên cắt chiều cao chỉ
   còn giấu mất dải Tối dưới fold của khung cuộn con, trong khi trang thật
   thừa khoảng trắng dưới thẻ (chủ dự án 1440px: "responsive đang khá tệ
   khi màn hình to ra"). Cao đúng bằng nội dung, trang tự cao theo. */
.ow-s04-week-scroll {
  overflow: visible;
}

/* ── TUẦN ──
   07/09 lượt 2: khối trục phút đã XOÁ — gồm ow-cal-week, ow-cal-day,
   ow-cal-gutter, ow-cal-hour, ow-cal-daycol, ow-cal-event, ow-cal-num.
   Chủ dự án gửi lại ảnh bản cũ ("phần lịch học như ảnh mới đẹp chứ kia xấu
   quá, lịch tuần ấy") và chốt cả S04 lẫn S05 dùng lưới ô giờ ow-slot-* phía
   trên. Giữ ow-cal-dow vì hàng đầu của lưới THÁNG còn dùng.

   Viết tên class trong comment KHÔNG kèm dấu sao ở cuối: "ow-cal-event" +
   "*" + "/" ghép thành dấu đóng comment ngay giữa câu, chữ còn lại rơi ra
   ngoài thành CSS rác và trình duyệt bỏ luôn mọi rule phía sau (ow-quick-grid
   mất display:grid, 4 thẻ S01 xếp dọc). Khoá bằng ThemeCssSyntaxTest. */

.ow-cal-dow {
  display: block;
  font-size: 0.6875rem /* 11px */;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #6b7280;
}

/* ── THÁNG ── */
/* Không min-width: 7 ô chia đều khung (ô đã minmax(0,1fr)), điện thoại bóp
   chip lại chỉ còn giờ (media query cuối khối) — KHÔNG cuộn ngang (07/09). */

.ow-cal-month-week {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
}

.ow-cal-month-headrow {
  border-bottom: 1px solid #e5e9f2;
  padding-bottom: 6px;
  text-align: center;
}

.ow-cal-mcell {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-height: 104px;
  padding: 4px;
  border-bottom: 1px solid #eef1f6;
  border-left: 1px solid #eef1f6;
}

.ow-cal-mcell:first-child {
  border-left: 0;
}

.ow-cal-mcell--out {
  background: #fafbfd;
}

.ow-cal-mcell--out .ow-cal-mnum {
  color: #9aa3b2;
}

.ow-cal-mnum {
  align-self: center;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  height: 28px;
  padding: 0 6px;
  border-radius: 9999px;
  font-size: 0.78125rem /* 12.5px */;
  font-weight: 500;
  color: #374151;
  cursor: pointer;
}

.ow-cal-mnum:hover {
  background: #eef2f9;
}

.ow-cal-mcell--today .ow-cal-mnum {
  background: #1d4ed8;
  color: #ffffff;
}

.ow-cal-chip {
  display: flex;
  /* S04 dùng thẻ <a> (link sang S05), S05 dùng <button> — cùng một lớp vẽ */
  text-decoration: none;
  align-items: center;
  gap: 4px;
  min-height: 28px;
  padding: 2px 7px;
  border-radius: 6px;
  background: #e8f0fe;
  color: #174ea6;
  font-size: 0.75rem /* 12px */;
  text-align: left;
  cursor: pointer;
  overflow: hidden;
}

.ow-cal-chip:hover {
  background: #d7e5fd;
}

.ow-cal-chip--done {
  background: #dcfce7;
  color: #166534;
}

.ow-cal-chip--done:hover {
  background: #c8f5d8;
}

.ow-cal-chip--cancelled {
  background: #fef2f2;
  color: #b91c1c;
}

.ow-cal-chip--cancelled .ow-cal-chip__name {
  text-decoration: line-through;
}

.ow-cal-chip__time {
  flex-shrink: 0;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.ow-cal-chip__name {
  /* T4.51: xuống dòng thay vì cắt "…" — tên lớp là thứ duy nhất nói chip này là lớp nào. */
  min-width: 0;
  white-space: normal;
  overflow-wrap: anywhere;
}

.ow-cal-more {
  align-self: flex-start;
  min-height: 24px;
  padding: 2px 7px;
  border-radius: 6px;
  font-size: 0.71875rem /* 11.5px */;
  font-weight: 500;
  color: #4b5563;
  cursor: pointer;
}

.ow-cal-more:hover {
  background: #eef2f9;
}

/* Điện thoại (<768px) — KHÔNG cuộn ngang (chủ dự án 07/09):

   TUẦN: phần xếp dọc chuyển sang khối .ow-slot-* phía trên cùng lúc bỏ trục
   phút (07/09 lượt 2). Các ghi đè cũ của .ow-cal-week/.ow-cal-daycol/
   .ow-cal-event ở đây đã xoá theo — trong đó có bản vá `align-items: stretch`
   (media query đổi flex-direction thành column thì `align-items: end` của bản
   rộng thành trục NGANG, dồn cả tuần sang mép phải). Lưới ô giờ không dùng
   flex trục nên bẫy đó không còn.

   THÁNG giữ đủ 7 cột (ô ~49px ở 375px) nhưng chip chỉ còn GIỜ — tên lớp không
   nhét nổi 49px, cắt còn 2 ký tự thì tệ hơn là giấu; bấm chip/ô vẫn mở đúng
   buổi, tên đầy đủ nằm ở bước 3. Chuẩn vùng bấm ≥40px (CLAUDE.md §3). */
@media (max-width: 767.98px) {
  .ow-cal-scroll {
    max-height: none;
  }

  .ow-cal-chip {
    min-height: 40px;
    justify-content: center;
    padding: 2px 2px;
  }

  .ow-cal-chip__name {
    display: none;
  }

  .ow-cal-more {
    min-height: 40px;
    align-self: stretch;
    text-align: center;
    padding: 2px 2px;
  }

  .ow-cal-mnum {
    min-width: 40px;
    height: 40px;
  }
}

/* ============================================================
   S01 — WIDGET "HÔM NAY DẠY GÌ" (gv/tg, 07/09)

   Ra đời khi gộp màn GV: "Lớp của tôi" + "Lịch dạy của tôi" xoá,
   dashboard hiện thẳng buổi hôm nay kèm nút mở đúng buổi trên S05.
   Class ngữ nghĩa — view widget không có pipeline Tailwind riêng,
   utility responsive/arbitrary sẽ vô hình (xem khối S10 phía trên).
   Gác 2 đầu bởi TodayTeachingWidgetTest.
   ============================================================ */
.ow-today-list {
  display: flex;
  flex-direction: column;
}

.ow-today-row {
  display: flex;
  align-items: center;
  gap: 14px;
  /* ≥48px cả dòng: GV bấm trên điện thoại trước giờ dạy (CLAUDE.md §3). */
  min-height: 48px;
  padding: 9px 2px;
  border-bottom: 1px solid #eef1f6;
}

.ow-today-row:last-child {
  border-bottom: 0;
}

.ow-today-row--cancelled {
  opacity: 0.75;
}

.ow-today-row--cancelled .ow-today-info b {
  text-decoration: line-through;
}

.ow-today-time {
  flex-shrink: 0;
  min-width: 88px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: #1d4ed8;
}

.ow-today-info {
  display: flex;
  flex: 1;
  min-width: 0;
  flex-direction: column;
  gap: 1px;
}

.ow-today-info b {
  font-size: 0.875rem /* 14px */;
  color: #111827;
}

.ow-today-info small {
  font-size: 0.75rem /* 12px */;
  color: #6b7280;
}

.ow-today-empty {
  padding: 6px 2px;
  font-size: 0.84375rem /* 13.5px */;
  color: #6b7280;
}

.dark .ow-today-row { border-color: rgb(255 255 255 / .08); }
.dark .ow-today-info b { color: #f3f4f6; }
.dark .ow-today-time { color: #93b4f5; }

/* Quick Actions Deck (S01) */
.ow-quick-deck {
  background: #ffffff;
  border: 1px solid rgba(15, 23, 42, 0.07);
  border-radius: 20px;
  padding: 16px 20px 20px;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.03), 0 1px 2px -1px rgba(0, 0, 0, 0.03);
  margin-bottom: 24px;
}
.ow-quick-deck-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 14px;
  flex-wrap: wrap;
}
.ow-quick-deck-title {
  font-size: 0.9375rem /* 15px */;
  font-weight: 700;
  color: #0f172a;
  letter-spacing: -0.01em;
  flex-shrink: 0;
  white-space: nowrap;
}
.ow-quick-deck-desc {
  font-size: 0.8125rem /* 13px */;
  color: #64748b;
  flex-shrink: 1;
  min-width: 0;
}
.ow-quick-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
}
.ow-quick-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-radius: 14px;
  background: #f8fafc;
  border: 1px solid rgba(226, 232, 240, 0.8);
  text-decoration: none;
  transition: all 0.18s ease-in-out;
  min-height: 54px;
}
.ow-quick-card:hover {
  background: #ffffff;
  border-color: #f59e0b;
  transform: translateY(-2px);
  box-shadow: 0 4px 12px -2px rgba(245, 158, 11, 0.12), 0 2px 4px -2px rgba(0, 0, 0, 0.04);
}
.ow-quick-card-icon {
  width: 36px;
  height: 36px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.ow-quick-card--amber .ow-quick-card-icon {
  background: #fef3c7;
  color: #d97706;
}
.ow-quick-card--blue .ow-quick-card-icon {
  background: #dbeafe;
  color: #2563eb;
}
.ow-quick-card--indigo .ow-quick-card-icon {
  background: #e0e7ff;
  color: #4f46e5;
}
.ow-quick-card--emerald .ow-quick-card-icon {
  background: #d1fae5;
  color: #059669;
}
.ow-quick-card--slate .ow-quick-card-icon {
  background: #f1f5f9;
  color: #475569;
}
.ow-quick-card-text {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
  min-width: 0;
}
.ow-quick-card-label {
  font-size: 0.84375rem /* 13.5px */;
  font-weight: 600;
  color: #1e293b;
  /* 13/09 · laptop 1280px: hop 121px, chu can 125px → ban nowrap+ellipsis cat
     thanh "Tiếp nhận học sin…". Lưới ô ĐÃ responsive (auto-fit), chỉ chữ là bị
     khoá một dòng. Cho bước dòng: thẻ cao thêm, không mất chữ nào. */
  white-space: normal;
  overflow-wrap: anywhere;
  line-height: 1.25;
}
.ow-quick-card-sub {
  font-size: 0.71875rem /* 11.5px */;
  color: #64748b;
  /* 13/09: "Phê duyệt các kỳ học phí tháng" cần 176px trong hộp 121px ở 1280px.
     Mô tả là câu dài nhất trên thẻ → bắt buộc phải cuộn được 2 dòng. */
  white-space: normal;
  overflow-wrap: anywhere;
  line-height: 1.3;
}
.ow-quick-card-arrow {
  color: #94a3b8;
  transition: transform 0.18s ease;
  flex-shrink: 0;
}
.ow-quick-card:hover .ow-quick-card-arrow {
  color: #f59e0b;
  transform: translateX(3px);
}

/* Mobile (< 768px): header không xuống dòng — title giữ nguyên 1 dòng,
 * mô tả cuộn 1 dòng tối đa. Mobile theo CLAUDE.md §3: sidebar drawer,
 * bảng cuộn ngang, vùng bấm ≥40px. */
@media (max-width: 767px) {
  .ow-quick-deck-header {
    flex-direction: column;
    gap: 3px;
    margin-bottom: 10px;
  }
  .ow-quick-deck-title {
    white-space: nowrap;
    font-size: 0.875rem;
  }
  .ow-quick-deck-desc {
    font-size: 0.75rem;
    line-height: 1.3;
  }
  .ow-quick-grid {
    gap: 8px;
  }
  .ow-quick-card {
    padding: 10px 14px;
    min-height: 48px;
  }
}


/* ── Màn hình lớn (24"–32"): fill theo tỉ lệ — 09/09 tối ────────────────
 * Số đo Chromium lúc 21:07: .fi-main bị khoá max-w-screen-2xl = 1536px ở
 * CẢ 1920/2560/3440 (mainW=1536 hằng định) — màn 32" chỉ dùng 44% bề ngang,
 * ~1900px trống bên phải (chủ dự án: "box không phóng to thu nhỏ fill theo
 * tỉ lệ màn hình").
 *
 * Hai tầng, giữ đúng chốt 03/09 (ContentWidthTest):
 * 1. .fi-main bỏ trần từ ≥1920px — BẢNG/LƯỚI/DASHBOARD fill toàn bề ngang.
 *    Dưới 1920 KHÔNG đụng: cap 1536 vẫn là hành vi đã duyệt ở màn nhỏ.
 * 2. Form/infolist ĐƠN CỘT (.fi-fo-component-ctn) KHÔNG theo trang kéo căng —
 *    dòng input dài >1500px không đọc nổi; cap 768px (max-w-3xl) căn giữa,
 *    form nhiều cột (grid 2 cột modal thu tiền) vẫn fill theo grid.
 */
@media (min-width: 1920px) {
  .fi-main {
    max-width: 100%;
  }

  /* KHÔNG áp max-width cho Settings page (grid 2-column) và các form có grid layout —
     chúng cần fill đầy màn hình lớn. Chỉ giữ cap 768px cho form ĐƠN CỘT thật sự
     (modal, dialog, simple single-column form). Settings có .grid class. */
  .fi-main > .fi-fo-component-ctn:not(.grid),
  .fi-main form > .fi-fo-component-ctn:not(.grid) {
    /* rem (không px) — phóng theo root font cùng nấc viewport bên dưới */
    max-width: 48rem;
    margin-inline: auto;
  }

  /* Nấc 1 (24"): chữ nền +15%, mọi rem (bảng, nút, sidebar) đi theo.
     body phải có .fi-body kèm theo — global 14px dùng "body, .fi-body",
     specificity (0,1,0) ăn CSS "body" trần cùng !important (bẫy đã ghi). */
  html {
    font-size: 17px;
  }

  /* NEO PX có chủ đích: 15px là chữ nền ĐÃ DUYỆT cho nấc này (09/09).
     Toàn bộ cỡ chữ có rule riêng trong theme đã chuyển sang rem nên tự đi theo
     gốc; rule này lo phần tử kế thừa (không có font-size riêng).
     global là "body, .fi-body" + !important — phải lặp đúng cặp selector mới
     thắng (specificity giữa các !important — bẫy đã ghi ở 11/09). */
  body,
  .fi-body {
    font-size: 15px !important;
  }
}

@media (min-width: 2560px) {
  /* Nấc 2 (27"): +21%. Global 14px là "body, .fi-body" + !important —
     phải lặp đúng cặp selector mới thắng (specificity giữa các !important). */
  html {
    font-size: 19px;
  }

  /* NEO PX có chủ đích: 16.5px là chữ nền ĐÃ DUYỆT cho nấc này (09/09).
     Toàn bộ cỡ chữ có rule riêng trong theme đã chuyển sang rem nên tự đi theo
     gốc; rule này lo phần tử kế thừa (không có font-size riêng).
     global là "body, .fi-body" + !important — phải lặp đúng cặp selector mới
     thắng (specificity giữa các !important — bẫy đã ghi ở 11/09). */
  body,
  .fi-body {
    font-size: 16.5px !important;
  }
}

@media (min-width: 3440px) {
  /* Nấc 3 (32"): +30% — "chữ bé đi nhiều quá" của cô */
  html {
    font-size: 21px;
  }

  /* NEO PX có chủ đích: 18px là chữ nền ĐÃ DUYỆT cho nấc này (09/09).
     Toàn bộ cỡ chữ có rule riêng trong theme đã chuyển sang rem nên tự đi theo
     gốc; rule này lo phần tử kế thừa (không có font-size riêng).
     global là "body, .fi-body" + !important — phải lặp đúng cặp selector mới
     thắng (specificity giữa các !important — bẫy đã ghi ở 11/09). */
  body,
  .fi-body {
    font-size: 18px !important;
  }
}

/* ── Filter + search: MỘT HÀNG gọn trên bảng — 09/09 22:00 ──────────────
 * Audit Chromium 21:59–22:06 (4 màn × 1440/1920): toolbar search cao 63px
 * (padding 12px/12px quanh 38px nội dung); search + nút Lọc trôi hẳn phải
 * (x=1137/1440) để trống ~880px bên trái; màn có AboveContent (S03/S10)
 * thành 2 hàng rời — hàng select, hàng search lơ lửng.
 *
 * Fix CSS thuần, KHÔNG đổi DOM (Livewire morph an toàn): trong .fi-ta của
 * màn có filter AboveContent, filter-ctn và toolbar chia nhau một hàng
 * flex-wrap [selects ····· search], mọi con khác (bảng, footer, bulk bar)
 * chiếm trọn hàng sau. :has() chỉ áp đúng màn có above-content — màn không
 * filter (S15 nghỉ lễ) giữ nguyên layout mặc định. S02 vừa chuyển
 * AboveContent nên hưởng theo (StudentResource::filtersLayout).
 *
 * CẤU TRÚC DOM ĐO ĐƯỢC 22:11: filter-ctn + toolbar là 2 con LIỀN nhau
 * trong .fi-ta-header-ctn (con của .fi-ta-ctn), bảng ở .fi-ta-content —
 * ANH EM của header-ctn, không phải con của .fi-ta. Flex đặt trên
 * .fi-ta-header-ctn: [selects ··· search] một hàng, bảng tự nằm dưới.
 * Chỉ áp ≥1024px — dưới đó giữ layout mặc định xếp dọc cho tablet/mobile.
 */
@media (min-width: 1024px) {
  .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-ta-header-ctn {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    /* Triệt tiêu gap trên header-ctn để loại bỏ vạch gap thừa giữa header và filter */
    gap: 0;
    padding: 0;
  }

  /* .fi-ta-header (khối tiêu đề trang trong bảng) chiếm trọn dòng đầu */
  .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-ta-header-ctn > .fi-ta-header {
    flex: 0 0 100%;
    padding-block: 14px;
  }

  /* filter-ctn giãn chiếm CHỖ CÒN LẠI (basis 0, không theo max-content của
   * grid con — basis auto từng làm grid kéo dài cả dòng, toolbar bị wrap) */
  .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-ta-filters-above-content-ctn {
    flex: 1 1 0%;
    min-width: 0;
    padding-block: 8px;
    padding-left: 1.5rem;
    padding-right: 0.75rem;
  }

  .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-ta-header-toolbar {
    flex: 0 0 auto;
    padding-block: 6px;
    padding-right: 1.5rem;
    padding-left: 0.75rem;
    align-self: flex-end;
  }

  /* CẤU TRÚC BÊN TRONG: .fi-ta-filters flex ngang, các select và nút đặt lại
   * căn đáy chuẩn xác (align-items: flex-end) để chung baseline 36px với search */
  .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-ta-filters {
    display: flex;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: 12px;
  }

  /* Grid layout cho form filters bên trong để các select align đều */
  .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-ta-filters > div:last-child {
    order: 1;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 12px;
    flex: 1 1 0%;
  }

  /* Ẩn tiêu đề h4 "Bộ lọc" thừa thải (đã có nhãn trên từng ô select) —
   * triệt tiêu hoàn toàn lỗi dính chữ "Bộ lọcĐặt Lại" */
  .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-ta-filters > div:first-child h4 {
    display: none;
  }

  /* Nút "Đặt lại" đứng sau các ô filter, căn chuẩn chiều cao 36px ngang bằng các input */
  .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-ta-filters > div:first-child {
    order: 2;
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    height: 36px;
  }

  .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-ta-filters > div:first-child button {
    height: 36px;
    padding: 0 12px;
    border-radius: 8px;
    font-size: 0.8125rem /* 13px */;
    font-weight: 500;
    border: 1px solid #e2e8f0;
    background-color: #ffffff;
    color: #64748b;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
    display: inline-flex;
    align-items: center;
    gap: 6px;
    transition: all 0.15s ease;
  }

  .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-ta-filters > div:first-child button::before {
    content: '';
    display: inline-block;
    width: 14px;
    height: 14px;
    background-color: currentColor;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.75a.75.75 0 00-.75.75v4.482a.75.75 0 001.5 0v-2.298l.454.454a7 7 0 101.408-9.84.75.75 0 00-1.06 1.06 5.5 5.5 0 0110.01 4.787z' clip-rule='evenodd'/%3E%3C/svg%3E") no-repeat center / contain;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.75a.75.75 0 00-.75.75v4.482a.75.75 0 001.5 0v-2.298l.454.454a7 7 0 101.408-9.84.75.75 0 00-1.06 1.06 5.5 5.5 0 0110.01 4.787z' clip-rule='evenodd'/%3E%3C/svg%3E") no-repeat center / contain;
  }

  .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-ta-filters > div:first-child button:hover {
    background-color: #fef2f2;
    color: #ef4444;
    border-color: #fecaca;
  }

  /* Dark mode */
  .dark .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-ta-filters > div:first-child button {
    border-color: rgba(255, 255, 255, 0.1);
    background-color: rgba(255, 255, 255, 0.05);
    color: #94a3b8;
  }

  .dark .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-ta-filters > div:first-child button:hover {
    background-color: rgba(239, 68, 68, 0.15);
    color: #f87171;
    border-color: #7f1d1d;
  }

  /* Label trên select: span con giữ Tailwind 'text-sm leading-6' (24px) —
   * phải trúng đúng span đó bằng !important (utility trên child thắng mọi
   * rule cha, bẫy đã ghi). */
  .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-fo-field-wrp-label > span {
    font-size: 0.75rem /* 12px */ !important;
    line-height: 16px !important;
    font-weight: 500 !important;
    color: #475569 !important;
  }

  .dark .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-fo-field-wrp-label > span {
    color: #cbd5e1 !important;
  }

  .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-fo-field-wrp > div {
    row-gap: 4px;
  }

  .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-fo-component-ctn {
    gap: 12px !important;
  }

  /* Grid layout for CheckboxList to balance rows (CSS columns fill column-first which looks unbalanced) */
  .fi-fo-checkbox-list {
    display: grid !important;
    grid-template-columns: repeat(var(--cols-lg, 4), minmax(0, 1fr)) !important;
    column-count: 1 !important;
    gap: 8px 16px !important;
  }

  /* Checkbox option label: căn giữa checkbox + text, tránh kéo giãn */
  .fi-fo-checkbox-list .fi-fo-checkbox-list-option-label {
    align-items: flex-start !important;
    padding: 4px 0 !important;
  }

  /* Ô tìm kiếm: chiều cao 36px, tối thiểu 260px không bị cắt chữ placeholder */
  .fi-ta:has(.fi-ta-filters-above-content-ctn) .fi-ta-search-field input {
    min-width: 260px;
    height: 36px;
  }
}

/* ─── S04 11/09 — nút "+ Xếp buổi" trên ô trống (admin, DOCS §S04 dòng 792) ─── */
.ow-s04-add {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: 34px;
    margin-top: 4px;
    padding: 2px 6px;
    border: 1px dashed #94a3b8;
    border-radius: 6px;
    background: transparent;
    color: #64748b;
    font-size: 0.78125rem /* 12.5px */;
    line-height: 1.2;
    cursor: pointer;
    opacity: .72;
    transition: opacity .15s ease, border-color .15s ease, color .15s ease, background .15s ease;
}
/* Chỉ hiện khi rê chuột vào ô — lưới không bị rợp nút; cảm ứng không có hover
   nên <768px luôn hiện mờ (mobile: bấm được ngay). */
.ow-s04-band__cell:hover .ow-s04-add,
.ow-s04-add:focus-visible {
    opacity: 1;
}
.ow-s04-add:hover,
.ow-s04-add:focus-visible {
    color: #b45309;
    border-color: #b45309;
    background: rgba(245, 158, 11, .06);
}
.ow-s04-add--month {
    width: auto;
    min-height: 20px;
    margin-top: 2px;
    padding: 1px 6px;
    font-size: 0.6875rem /* 11px */;
    opacity: .55;
}
.ow-s04-add--month:hover,
.ow-s04-add--month:focus-visible {
    opacity: 1;
}

/* ── 16/09 — ô CÓ CA cũng cần nút "+ Xếp buổi" (dải kín không được "tắt" thao tác);
 * nhỏ gọn, nằm dưới đáy chồng thẻ, không lấn chỗ của buổi đã xếp. */
.ow-s04-add--mini {
    min-height: 26px;
    margin-top: 2px;
    padding: 1px 6px;
    font-size: 0.6875rem /* 11px */;
    opacity: .45;
}
.ow-s04-add--mini:hover,
.ow-s04-add--mini:focus-visible {
    opacity: 1;
}
@media (max-width: 767.98px) {
    .ow-s04-add {
        opacity: .55; /* cảm ứng không có hover — luôn hiện mờ, vùng bấm đủ to */
    }
    /* 16/09 — nút nhỏ trên ô có ca cũng phải đủ vùng bấm cảm ứng (≥40px). */
    .ow-s04-add--mini {
        min-height: 40px;
        opacity: .6;
    }
}

/* ── S04 · phản hồi kéo-thả + chip quyền (11/09) ─────────────────────────── */
/* Alpine đang kéo thẻ: đánh dấu trạng thái trên <main> để CSS khoanh ô đích thả.
 * 16/09 — khóa vào data-drop="1" (mọi ô đều là đích thả), KHÔNG phải data-rong
 * (chỉ ô trống): dải kín 5/5 ô (vd Tối) không còn ô trống nào thì data-rong
 * không khớp gì → cả dải tối đen, không có dấu hiệu "thả vào đây được". */
.ow-s04-dragging .ow-s04-band__cell[data-drop="1"] {
  outline: 1px dashed #93c5fd;
  outline-offset: -1px;
  cursor: copy;
}
/* Ô đang là đích thả: viền đậm + nền xanh nhạt. */
.ow-s04-drop-on {
  outline: 2px dashed #2563eb;
  outline-offset: -2px;
  background: #eff6ff;
  border-radius: 6px;
}
/* Giờ đích hiện ngay trên ô đang trỏ. */
.ow-s04-drop-time {
  position: absolute;
  top: 4px;
  right: 4px;
  z-index: 5;
  background: #2563eb;
  color: #ffffff;
  font-size: 0.6875rem /* 11px */;
  font-weight: 700;
  line-height: 1.4;
  padding: 2px 8px;
  border-radius: 999px;
  white-space: nowrap;
}
/* Dòng hướng dẫn hiện khi đang kéo. */
.ow-s04-drag-hint {
  padding: 8px 14px;
  background: #eff6ff;
  border: 1px dashed #93c5fd;
  border-radius: 8px;
  color: #1e40af;
  font-size: 0.78125rem /* 12.5px */;
  font-weight: 600;
}
/* Chip quyền trên toolbar — NHÃN TRẠNG server-render, không phải cơ chế chặn. */
.ow-s04-perms { display: flex; align-items: center; gap: 8px; margin-left: auto; flex-wrap: wrap; }
.ow-s04-perm {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.71875rem /* 11.5px */;
  font-weight: 700;
  white-space: nowrap;
}
/* Chữ xanh đậm #166534 (~7,1:1) đạt AA — không dùng #22c55e (~2,3:1 trên trắng). */
.ow-s04-perm--ok { background: #dcfce7; color: #166534; }
.ow-s04-perm--ro { background: #f1f5f9; color: #475569; }
[x-cloak] { display: none !important; }

/* ============================================================
   BADGE TƯƠNG PHẢN CHỮ — đo bằng ui-audit 12/09, không đoán bằng mắt
   ------------------------------------------------------------
   Filament vẽ badge = chữ shade-600 trên nền shade-50, cỡ 12px. Với
   danger/warning/primary cặp đó TRƯỢT WCAG AA 4.5:1 cho chữ thường:
     danger   #e11d48 / #fff1f2 = 4,28   (badge "Đã đóng" — màn S03)
     warning  #ea580c / #fff7ed = 3,35   (badge "Nháp")
     primary  #d97706 / #fffbeb = 3,07   (badge đếm bộ lọc, badge mặc định)
   Nền giữ nguyên (màu nhạt là design system), chỉ đẩy chữ xuống shade-700:
     danger   #be123c = 5,72   warning #c2410c = 4,88   primary #b45309 = 4,84
   Cùng bài học đã ghi với xanh lá (#22c55e 2,3:1 → #166534 7,1:1).
   Success KHÔNG đụng: nó là nền đặc trắng/xanh (#15803d) đã đạt.
   Specificity: .fi-badge.fi-color-x (0-2-0) thắng utility text-custom-500
   (0-1-0) ngay trên phần tử, nên không cần !important.
   ============================================================ */
.fi-badge.fi-color-danger,
.fi-in-badge.fi-color-danger { color: #be123c; }
.fi-badge.fi-color-warning,
.fi-in-badge.fi-color-warning { color: #c2410c; }
.fi-badge.fi-color-primary,
.fi-in-badge.fi-color-primary { color: #b45309; }

/* Chữ "—" của ô trống (placeholder cột) đo được 2,46:1 — không đọc được
   trên máy chiếu văn phòng. Đẩy lên slate-600 = 4,55:1. */
.fi-ta-placeholder { color: #64748b; }

/* Chữ phụ trợ cũng trượt AA cùng lúc với badge (đo 12/09, ui-audit):
   breadcrumb #64748b trên nền #f2f4fa = 4,33 ; mô tả đầu bảng #6f7893 trên
   trắng = 4,39. Đẩy lên một bậc palette — slate-600 = 6,89 và slate-500
   chuẩn = 4,76. Hai rule này sửa MỌI màn (không riêng màn nào vừa dựng). */
.fi-breadcrumbs-item a,
.fi-breadcrumbs-item span { color: #475569; }

/* ── 13/09 · mobile: chữ hiển thị phải co theo hộp, không giữ bằng màn 27″ ──
 * Baseline Chromium 375×720 (trước khi sửa): tên màn 22px, số tiền thẻ S01 24px,
 * tổng tiền S10/S11 20px — y hệt ở 2560px trong khi hộp đã co còn 375px; và nhãn
 * cột `Buổi vắng gần nhất` nowrap tràn ra NGOÀI Ô của nó 17px ngay trên /app.
 * WCAG 1.4.10 cho phép BẢNG cuộn ngang, nhưng chữ không được thoát khỏi ô.
 *
 * Vẫn viết bằng rem để ba "nấc" màn lớn phía dưới phóng tiếp; ở đây chỉ đổi
 * hệ số, không đổi đơn vị. Cỡ nhỏ nhất 18px — WebAIM/WCAG 1.4.3: dưới ngưỡng
 * "large text" thì phải giữ 4,5:1, các màu đang dùng (#0f172a · #14213d trên
 * trắng ≈ 15-17:1) dư chuẩn đó.
 */
@media (max-width: 767.98px) {
  .fi-header-heading {
    font-size: 1.1875rem !important; /* 22px → 19px */
  }

  .fi-wi-stats-overview-stat-value {
    font-size: 1.25rem !important; /* 24px → 20px */
  }

  .ow-charges-summary__value,
  .ow-invoices-summary__value,
  .ow-month-summary__value {
    font-size: 1.125rem !important; /* 20px → 18px */
  }

  /* Ô cột bảng co theo màn nhưng chữ nowrap thì không → tràn 17px. Cho phép
     nhãn tự xuống dòng; chiều cao hàng không khoá nên không cắt chữ. */
  .fi-ta-header-cell-label {
    white-space: normal;
  }
}

/* ── 23/09 · T4.7 bằng cấp học sinh: hai nhãn minh chứng phải NHÌN KHÁC NHAU ──
 * Đo trên trình duyệt thật (Chromium, /app/students/1): khối "Bằng cấp" in
 * ".ow-qualification__evidence" và ".ow-qualification__no-evidence" ra CÙNG màu
 * rgb(2,6,23) và cùng weight 400 — vì hai class này chưa từng tồn tại trong theme.
 * Hai trạng thái đó NGƯỢC NHAU về nghiệp vụ: em đã có ảnh bằng cấp trên Drive,
 * so với em mới nhập tên bằng mà chưa có gì kiểm chứng. Nhìn giống nhau là mất
 * luôn thông tin, dù CSS hợp lệ và request vẫn 200.
 *
 * Dùng palette sẵn có của theme (slate-500 #64748b đã bị loại 12/09 vì 4,33:1
 * không đạt AA): #475569 = slate-600 → 6,89:1 trên trắng, đạt AA. Nhãn "chưa có"
 * cố ý nhạt hơn vì nó là THIẾU sót cần chú ý, không phải thông tin khẳng định.
 *
 * Khoá bằng tests/Feature/Frontend/QualificationEvidenceStyleTest.php — thêm
 * class semantic mới vào blade thì phải thêm rule ở đây, không thì lại là class
 * rỗng. */
.ow-qualification__evidence {
  color: #047857; /* emerald-700 — 5,84:1 trên trắng, đạt AA */
  font-weight: 600;
}

.ow-qualification__no-evidence {
  color: #b45309; /* amber-700 — 4,63:1 trên trắng, đạt AA */
  font-style: italic;
}

/* ══════════════════════════════════════════════════════════════════════════
   S04 — DRAWER 3 TAB (Buổi học · Lớp mới · Phòng mới). Chốt 27/09.

   Chủ dự án 26/09: "chưa có tạo lớp ngay trên trang đó và chưa cho tạo phòng
   ngay trên trang luôn thế lại phải thoát ra để tạo hả? ... sao lại ko có thêm
   1 thanh bên".

   Màu lấy từ bộ biến --ow-* của chính theme này (--ow-card, --ow-ink, --ow-line,
   --ow-muted, --ow-blue), KHÔNG dùng biến xám/xanh của Tailwind: theme này không
   khai chúng, nên fallback cũng không áp và drawer render ra TRONG SUỐT (đo được
   27/09: backgroundColor = rgba(0,0,0,0)). Dark mode override bằng .dark +
   rgb(255 255 255 / .x) đúng khuôn các khối ow-* khác trong file này.

   Cỡ chữ viết bằng rem, không px — luật của ThemeTypeScalesWithRootTest (chỉ
   `html` và `body, .fi-body` được px, vì px là tĩnh tuyệt đối theo MDN).

   Vị trí: thanh trượt BÊN PHẢI, rộng 26rem, tối đa 92vw cho màn 375px.
   ══════════════════════════════════════════════════════════════════════════ */

.ow-drawer-scrim {
  position: fixed;
  inset: 0;
  z-index: 40;
  background-color: rgb(0 0 0 / 0.45);
}

.ow-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 50;
  display: flex;
  flex-direction: column;
  width: 26rem;
  max-width: 92vw;
  overflow-y: auto;
  background-color: var(--ow-card, #ffffff);
  border-left: 1px solid var(--ow-line, #e6eaf3);
  box-shadow: -8px 0 24px rgb(0 0 0 / 0.16);
}

.dark .ow-drawer {
  background-color: #14161f;
  border-left-color: rgb(255 255 255 / .1);
}

.ow-drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--ow-line, #e6eaf3);
}

.dark .ow-drawer__head {
  border-bottom-color: rgb(255 255 255 / .1);
}

.ow-drawer__title {
  margin: 0;
  font-size: 1.0625rem;
  font-weight: 600;
  color: var(--ow-ink, #1c2440);
}

.dark .ow-drawer__title {
  color: #f3f4f6;
}

.ow-drawer__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 2.5rem ≈ 40px: vùng bấm tối thiểu trên mobile (CLAUDE.md §3). */
  width: 2.5rem;
  height: 2.5rem;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--ow-muted, #6f7893);
  cursor: pointer;
  background: transparent;
  border: 0;
  border-radius: var(--ow-radius-sm, 10px);
}

.ow-drawer__close:hover,
.ow-drawer__close:focus-visible {
  color: var(--ow-ink, #1c2440);
  background-color: var(--ow-bg, #f2f4fa);
}

.dark .ow-drawer__close {
  color: #9aa3bd;
}

.dark .ow-drawer__close:hover,
.dark .ow-drawer__close:focus-visible {
  color: #f3f4f6;
  background-color: rgb(255 255 255 / .08);
}

.ow-drawer__tabs {
  display: flex;
  gap: 0.25rem;
  padding: 0.75rem 1.25rem 0;
  border-bottom: 1px solid var(--ow-line, #e6eaf3);
}

.dark .ow-drawer__tabs {
  border-bottom-color: rgb(255 255 255 / .1);
}

.ow-drawer__tab {
  flex: 1 1 0;
  /* 2.75rem ≈ 44px: vùng bấm thoải mái khi ngón tay thao tác. */
  min-height: 2.75rem;
  padding: 0.5rem 0.75rem;
  font-family: inherit;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--ow-muted, #6f7893);
  cursor: pointer;
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: var(--ow-radius-sm, 10px) var(--ow-radius-sm, 10px) 0 0;
}

.dark .ow-drawer__tab {
  color: #9aa3bd;
}

.ow-drawer__tab:hover {
  background-color: var(--ow-bg, #f2f4fa);
}

.dark .ow-drawer__tab:hover {
  background-color: rgb(255 255 255 / .08);
}

.ow-drawer__tab--active {
  color: var(--ow-blue, #1769e0);
  border-bottom-color: var(--ow-blue, #1769e0);
}

.dark .ow-drawer__tab--active {
  color: #93b4f5;
  border-bottom-color: #93b4f5;
}

.ow-drawer__body {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1.25rem;
}

.ow-drawer__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.875rem;
}

/* Hai cột khi đủ chỗ; dưới 640px tự về một cột (không dùng sm:/md: của Tailwind
   vì dist Filament không chứa chúng — xem đầu file). */
@media (min-width: 640px) {
  .ow-drawer__grid {
    grid-template-columns: 1fr 1fr;
  }

  .ow-drawer__field--wide {
    grid-column: 1 / -1;
  }
}

.ow-drawer__field {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: 0.8125rem;
  font-weight: 500;
}

.ow-drawer__field > span {
  color: var(--ow-ink, #1c2440);
}

.dark .ow-drawer__field > span {
  color: #d4d4d8;
}

.ow-drawer__field small {
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--ow-muted, #6f7893);
}

.dark .ow-drawer__field small {
  color: #9aa3bd;
}

/* 09/10 — cảnh báo "phòng đang bảo trì" trong drawer S04 (chủ dự án chốt hướng (a):
   giữ chọn được + cảnh báo). Đè màu muted của .ow-drawer__field small để câu cảnh báo
   đọc ra là CẢNH BÁO, không lẫn với dòng gợi ý thường. */
.ow-drawer__field small.ow-drawer__warn {
  color: #b45309;
  font-weight: 500;
}

.dark .ow-drawer__field small.ow-drawer__warn {
  color: #fbbf24;
}

.ow-drawer__field input,
.ow-drawer__field select,
.ow-drawer__field textarea {
  /* 2.5rem ≈ 40px: ngón tay bấm được trên 375px. */
  min-height: 2.5rem;
  padding: 0.5rem 0.625rem;
  font-family: inherit;
  font-size: 0.875rem;
  font-weight: 400;
  color: var(--ow-ink, #1c2440);
  background-color: var(--ow-card, #ffffff);
  border: 1px solid var(--ow-line, #e6eaf3);
  border-radius: var(--ow-radius-sm, 10px);
}

.dark .ow-drawer__field input,
.dark .ow-drawer__field select,
.dark .ow-drawer__field textarea {
  color: #f3f4f6;
  background-color: #14161f;
  border-color: rgb(255 255 255 / .14);
}

.ow-drawer__field textarea {
  min-height: 4rem;
  resize: vertical;
}

/* T4.43 (01/10): ô chọn NHIỀU (vd Thứ trong drawer Xếp lịch) — mũi tên dropdown
   lặp kín ô. forms.css của Filament "select:not(.choices)" (0,1,1) đè luật gỡ
   mũi tên của app.css "select:where([multiple])" (0,0,1). Selector dưới đây là
   0,2,1 nên thắng. Guard: tests/Feature/Ui/MultiSelectNoChevronTest.php */
select[multiple]:not(.choices),
select[size]:not([size="1"]):not(.choices) {
  background-image: none;
  padding-right: 0.75rem;
}

.ow-drawer__field input:focus-visible,
.ow-drawer__field select:focus-visible,
.ow-drawer__field textarea:focus-visible {
  outline: 2px solid var(--ow-blue, #1769e0);
  outline-offset: 1px;
}

.ow-drawer__save {
  min-height: 2.75rem;
  padding: 0.625rem 1rem;
  font-family: inherit;
  font-size: 0.9375rem;
  font-weight: 600;
  color: #ffffff;
  cursor: pointer;
  /* T4.95 — trắng trên #3b82f6 chỉ 3,68:1 (dưới AA 4,5 cho chữ 15px);
     nền lùi về blue-700 để chữ trắng đạt 6,4:1. */
  background-color: var(--ow-blue-ink, #1d4ed8);
  border: 0;
  border-radius: var(--ow-radius-sm, 10px);
}

.ow-drawer__save:hover {
  background-color: #1259c3;
}

.ow-drawer__ok,
.ow-drawer__err {
  margin: 0;
  padding: 0.5rem 0.75rem;
  font-size: 0.8125rem;
  border-radius: var(--ow-radius-sm, 10px);
}

.ow-drawer__ok {
  color: var(--ow-green-ink, #15803d);
  background-color: var(--ow-green-soft, #f0fdf4);
}

.dark .ow-drawer__ok {
  color: #6ee7a7;
  background-color: rgb(34 197 94 / .16);
}

.ow-drawer__err {
  color: var(--ow-red-ink, #b91c1c);
  background-color: var(--ow-red-soft, #fef2f2);
}

.dark .ow-drawer__err {
  color: #fdaaa1;
  background-color: rgb(239 68 68 / .16);
}

/* ══════════════════════════════════════════════════════════════════════════
   S03 — BẢNG TO GHI CHÚ LỚP + drawer phải. Task 2 (27/09).

   Chủ dự án 26/09: "phần ghi chú lớp nó phèn quá, lúc mà bấm vào thì phải hiện
   cái bảng to cho cả trợ giảng lẫn giảng viên nếu có trong màn hình Lớp của tôi
   vào buổi s05 ấy, chứ mà để trong task điểm danh thì nó kì cục quá".

   Bản cũ là form 43 dòng trôi giữa trang, không khung, không xem lại được. Bản này
   là DRAWER PHẢI rộng rãi, có tiêu đề, có lịch sử ghi chú.

   Màu lấy từ bộ biến --ow-* của theme; cỡ chữ viết bằng rem (luật
   ThemeTypeScalesWithRootTest — chỉ `html` và `body, .fi-body` được px).
   Vùng bấm ≥2.5rem ≈ 40px cho ngón tay trên 375px (CLAUDE.md §3).
   ══════════════════════════════════════════════════════════════════════════ */

.ow-note-drawer-scrim {
  position: fixed;
  inset: 0;
  z-index: 40;
  background-color: rgb(0 0 0 / 0.45);
}

.ow-note-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 50;
  width: 34rem;
  max-width: 94vw;
  overflow-y: auto;
  background-color: var(--ow-bg, #f2f4fa);
  border-left: 1px solid var(--ow-line, #e6eaf3);
  box-shadow: -8px 0 24px rgb(0 0 0 / 0.16);
}

.dark .ow-note-drawer {
  background-color: #14161f;
  border-left-color: rgb(255 255 255 / .1);
}

.ow-note-drawer__close {
  position: sticky;
  top: 0;
  /* Nằm trên nội dung cuộn, nên phải tự có nền — không thì chữ lọt qua nút. */
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 2.5rem;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--ow-muted, #6f7893);
  cursor: pointer;
  background-color: var(--ow-bg, #f2f4fa);
  border: 0;
  border-bottom: 1px solid var(--ow-line, #e6eaf3);
}

.dark .ow-note-drawer__close {
  color: #9aa3bd;
  background-color: #14161f;
  border-bottom-color: rgb(255 255 255 / .1);
}

.ow-note-drawer__close:hover,
.ow-note-drawer__close:focus-visible {
  color: var(--ow-ink, #1c2440);
}

.ow-note-panel {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1.25rem;
}

.ow-note-panel__title {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--ow-ink, #1c2440);
}

.dark .ow-note-panel__title {
  color: #f3f4f6;
}

.ow-note-panel__hint {
  margin: 0.25rem 0 0;
  font-size: 0.8125rem;
  color: var(--ow-muted, #6f7893);
}

.dark .ow-note-panel__hint {
  color: #9aa3bd;
}

.ow-note-panel__form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1rem;
  background-color: var(--ow-card, #ffffff);
  border: 1px solid var(--ow-line, #e6eaf3);
  border-radius: var(--ow-radius, 16px);
}

.dark .ow-note-panel__form {
  background-color: #1b1e29;
  border-color: rgb(255 255 255 / .1);
}

.ow-note-panel__row {
  display: grid;
  /* T4.71 #19: minmax(0, 1fr) — `1fr` trần = minmax(auto, 1fr) nên cột phình theo bề rộng tự nhiên
     của <input type=file> (321px) và đẩy nút ghi âm/ô chọn tệp thò 5px ngoài viewport 375px. */
  grid-template-columns: minmax(0, 1fr);
  gap: 1rem;
}

@media (min-width: 640px) {
  .ow-note-panel__row {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

.ow-note-panel__field {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
  font-size: 0.8125rem;
  font-weight: 500;
}

.ow-note-panel__field > span {
  color: var(--ow-ink, #1c2440);
}

.dark .ow-note-panel__field > span {
  color: #d4d4d8;
}

.ow-note-panel__field small {
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--ow-muted, #6f7893);
}

.dark .ow-note-panel__field small {
  color: #9aa3bd;
}

.ow-note-panel__field textarea {
  min-height: 7rem;
  padding: 0.625rem;
  font-family: inherit;
  font-size: 0.9375rem;
  font-weight: 400;
  color: var(--ow-ink, #1c2440);
  background-color: var(--ow-card, #ffffff);
  border: 1px solid var(--ow-line, #e6eaf3);
  border-radius: var(--ow-radius-sm, 10px);
  resize: vertical;
}

.ow-note-panel__field input[type="file"] {
  box-sizing: border-box;
  width: 100%;
  min-width: 0;
  max-width: 100%;
  min-height: 2.5rem;
  padding: 0.5rem;
  font-family: inherit;
  font-size: 0.8125rem;
  color: var(--ow-ink, #1c2440);
  background-color: var(--ow-card, #ffffff);
  border: 1px dashed var(--ow-line, #e6eaf3);
  border-radius: var(--ow-radius-sm, 10px);
}

.dark .ow-note-panel__field textarea,
.dark .ow-note-panel__field input[type="file"] {
  color: #f3f4f6;
  background-color: #14161f;
  border-color: rgb(255 255 255 / .14);
}

.ow-note-panel__field textarea:focus-visible,
.ow-note-panel__field input[type="file"]:focus-visible {
  outline: 2px solid var(--ow-blue, #1769e0);
  outline-offset: 1px;
}

.ow-note-panel__rec-controls {
  display: flex;
  gap: 0.5rem;
}

.ow-note-panel__rec-btn {
  flex: 1 1 0;
  min-width: 0;
  min-height: 2.5rem;
  padding: 0.5rem 0.75rem;
  font-family: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  color: #ffffff;
  cursor: pointer;
  background-color: var(--ow-red, #ef4444);
  border: 0;
  border-radius: var(--ow-radius-sm, 10px);
}

.ow-note-panel__rec-btn--stop {
  background-color: var(--ow-ink, #1c2440);
}

.ow-note-panel__actions {
  display: flex;
  justify-content: flex-end;
}

.ow-note-panel__save {
  min-height: 2.75rem;
  padding: 0.625rem 1.25rem;
  font-family: inherit;
  font-size: 0.9375rem;
  font-weight: 600;
  color: #ffffff;
  cursor: pointer;
  background-color: var(--ow-blue, #1769e0);
  border: 0;
  border-radius: var(--ow-radius-sm, 10px);
}

.ow-note-panel__save:hover {
  background-color: #1259c3;
}

.ow-note-panel__saved,
.ow-note-panel__err,
.ow-note-panel__loading {
  display: block;
  margin: 0;
  font-size: 0.8125rem;
  font-style: normal;
}

.ow-note-panel__saved {
  color: var(--ow-green-ink, #15803d);
}

.dark .ow-note-panel__saved {
  color: #6ee7a7;
}

.ow-note-panel__err {
  color: var(--ow-red-ink, #b91c1c);
}

.dark .ow-note-panel__err {
  color: #fdaaa1;
}

.ow-note-panel__loading {
  color: var(--ow-muted, #6f7893);
}

.ow-note-panel__history {
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
}

.ow-note-panel__subtitle {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--ow-ink, #1c2440);
}

.dark .ow-note-panel__subtitle {
  color: #f3f4f6;
}

.ow-note-panel__empty {
  margin: 0;
  font-size: 0.8125rem;
  color: var(--ow-muted, #6f7893);
}

.ow-note-panel__list {
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
  padding: 0;
  margin: 0;
  list-style: none;
}

.ow-note-panel__item {
  padding: 0.75rem;
  background-color: var(--ow-card, #ffffff);
  border: 1px solid var(--ow-line, #e6eaf3);
  border-radius: var(--ow-radius-sm, 10px);
}

.dark .ow-note-panel__item {
  background-color: #1b1e29;
  border-color: rgb(255 255 255 / .1);
}

.ow-note-panel__meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  font-size: 0.75rem;
  color: var(--ow-muted, #6f7893);
}

.ow-note-panel__meta b {
  font-size: 0.8125rem;
  color: var(--ow-ink, #1c2440);
}

.dark .ow-note-panel__meta b {
  color: #d4d4d8;
}

.ow-note-panel__text {
  margin: 0.375rem 0 0;
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--ow-ink, #1c2440);
  white-space: pre-wrap;
}

.dark .ow-note-panel__text {
  color: #e4e7ec;
}

.ow-note-panel__media {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  margin-top: 0.5rem;
}

.ow-note-panel__badge {
  padding: 0.125rem 0.5rem;
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--ow-blue, #1769e0);
  background-color: var(--ow-blue-soft, #eef5ff);
  border-radius: 999px;
}

.dark .ow-note-panel__badge {
  color: #93b4f5;
  background-color: rgb(23 105 224 / .2);
}

/* ══════════════════════════════════════════════════════════════════════════
   S05 — Khối ghi chú lớp nằm DƯỚI khối điểm danh (27/09).

   Chủ dự án: "là điểm danh ở trên đầu là 1 list các tên thôi … rồi sau đó ở bên
   dưới sẽ là phần note phần ảnh phần note gì gì đó như cái modal thanh bên hiện
   tại đang làm nè".

   Không khung, không nền riêng: ghi chú là phần CUỐI của luồng buổi học, dán sau
   thanh Lưu điểm danh. Đường kẻ ngăn cách đủ để mắt biết "hết điểm danh, sang
   ghi chú" — không dựng thêm card lồng card (Filament đã có section bao ngoài).

   Chừa dưới cùng ~4rem cho thanh Lưu điểm danh đang dính đáy (position:
   sticky; bottom: 0.5rem) — không chừa thì nút Lưu đè lên ô ghi chú, đúng cái
   lỗi "bấm nhầm" mà màn này cảnh báo.
   ══════════════════════════════════════════════════════════════════════════ */

.ow-s05-note {
  margin-top: 1.5rem;
  padding-top: 1.5rem;
  padding-bottom: 4.5rem;
  border-top: 1px solid var(--ow-line, #e6eaf3);
}

.dark .ow-s05-note {
  border-top-color: rgb(255 255 255 / .12);
}

/* Nhóm ghi chú khi xem TOÀN BỘ ghi chú của lớp — mỗi buổi một khối, có tiêu đề
   "Buổi N · Thứ X dd/mm" để không đọc nhầm ghi chú sang buổi bên cạnh. */
.ow-note-group + .ow-note-group {
  margin-top: 1.25rem;
}

.ow-note-group__title {
  margin: 0 0 0.5rem;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--ow-blue, #1769e0);
}

.dark .ow-note-group__title {
  color: #93b4f5;
}

/* ══════════════════════════════════════════════════════════════════════════
   Ghi chú dùng chung giữa GV và TG (27/09) — khối "đang gõ".

   Khối này là thứ NGƯỜI KHÁC vừa gõ, nên nó phải đọc là "việc đang diễn ra" chứ
   không phải "ghi chú đã lưu". Vì vậy: viền trái màu, nền rất nhạt, và chữ nội
   nghiêng nhẹ — đọc là biết ngay đây là bản nháp, chưa chốt.

   `aria-live="polite"` trong blade: màn hình đọc phải đọc được khi nội dung đổi,
   không phải im lặng rồi tự nhảy — người dùng màn hình đọc ngồi cùng lớp với GV
   nhiều khi. Không phải chi tiết trang trí.
   ══════════════════════════════════════════════════════════════════════════ */

.ow-note-panel__draft {
  padding: 0.75rem 0.75rem 0.75rem 0.875rem;
  background-color: var(--ow-green-soft, #eefaf2);
  border-left: 3px solid var(--ow-green, #16a34a);
  border-radius: var(--ow-radius-sm, 10px);
}

.dark .ow-note-panel__draft {
  background-color: rgb(22 163 74 / .16);
  border-left-color: #4ade80;
}

.ow-note-panel__text--draft {
  font-style: italic;
  opacity: 0.92;
}

/* ══════════════════════════════════════════════════════════════════════════
   S03 — Thẻ lớp ĐANG CÓ BUỔI: viền xanh + nhãn HÔM NAY (27/09).

   Chủ dự án: "bấm vào buổi cái ra… cho nó hiện rõ ko bị bấm nhầm hoặc là tiết
   này sẽ dạy lớp này để tránh việc bị bấm nhầm vào lớp khác".

   Vì sao viền chứ không đổi màu nền: thẻ lớp nằm trong lưới 3 cột, GV nhìn
   thấy CẢ lớp cùng lúc và tay đi tới ô giữa màn. Đổi nền cả thẻ thì tưởng đang
   chọn/thêu; VIỀN + NHÃN thì đọc là "trạng thái", không đọc là "hành động".

   Độ đậm viền 2px là cố ý: dưới 1px trên nền trắng là dễ bị bỏ sót khi liếc
   nhanh trên điện thoại — chính trường hợp cần chặn.
   ══════════════════════════════════════════════════════════════════════════ */

.ow-class-card__now {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}

/* ══════════════════════════════════════════════════════════════════════════
   T4.45 — HIỆU ỨNG XANH cho thẻ có buổi HÔM NAY (chủ dự án 01/10: "thêm phần
   hiện ứng cho đẹp màu xanh"). Buổi đã xong vẫn điểm danh được tới hết ngày
   nên thẻ phải nổi lên từ xa.

   Nhịp sáng chỉ đổi `opacity` của lớp phủ ::after (compositor) — KHÔNG animate
   box-shadow/background trực tiếp: điện thoại GV phải repaint mỗi khung hình.
   prefers-reduced-motion thì đứng yên.
   ══════════════════════════════════════════════════════════════════════════ */
/* ⚠️ `.fi-ta-ctn, .fi-section` ở đầu file đặt nền/viền/bóng/overflow đều
   !important → phải ghép `.fi-section.ow-class-card--live` + !important, nếu
   không thẻ vẫn trắng (đo 01/10: viền xanh 27/09 chưa từng hiện vì lý do này).
   overflow: hidden cắt mọi thứ tràn ra ngoài → quầng nhịp sáng dùng inset shadow. */
.fi-section.ow-class-card--live {
  position: relative;
  background: linear-gradient(135deg, rgb(22 163 74 / .13) 0%, rgb(22 163 74 / .03) 60%), #f7fdf9 !important;
  border: 1px solid rgb(22 163 74 / .40) !important;
  border-left: 4px solid #16a34a !important;
  box-shadow: 0 0 0 1px rgb(22 163 74 / .12), 0 10px 24px -10px rgb(22 163 74 / .55) !important;
}

.dark .fi-section.ow-class-card--live {
  background: linear-gradient(135deg, rgb(22 163 74 / .28) 0%, rgb(22 163 74 / .08) 60%), #0f1f16 !important;
  border-color: rgb(74 222 128 / .45) !important;
  border-left-color: #4ade80 !important;
}

.ow-class-card--live::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  box-shadow: inset 0 0 0 2px rgb(34 197 94 / .55), inset 0 0 28px rgb(34 197 94 / .28);
  opacity: 0;
  animation: ow-live-glow 2.4s ease-in-out infinite;
}

@keyframes ow-live-glow {
  0%, 100% { opacity: 0; }
  50% { opacity: 1; }
}

.ow-class-card__now::before {
  content: "";
  display: inline-block;
  width: 0.45rem;
  height: 0.45rem;
  margin-right: 0.3rem;
  border-radius: 9999px;
  background-color: #22c55e;
  vertical-align: 0.05em;
  animation: ow-live-dot 1.6s ease-in-out infinite;
}

@keyframes ow-live-dot {
  0%, 100% { opacity: 1; }
  50% { opacity: .25; }
}

.ow-class-card__mark {
  color: #b45309;
  font-weight: 600;
}

.ow-class-card__mark--done {
  color: #15803d;
}

.dark .ow-class-card__mark { color: #fbbf24; }
.dark .ow-class-card__mark--done { color: #4ade80; }

/* ══════════════════════════════════════════════════════════════════════════
   W-3 — NHÃN "BUỔI HÔM NAY" đặt ngay dưới tên lớp, TRƯỚC "Buổi tiếp theo".

   GV mở điện thoại lúc 17:49 thấy ngay việc hôm nay còn dở, không phải cuộn
   xuống cuối thẻ. Màu báo đúng nghĩa: CAM = buổi đã xong mà chưa điểm danh
   (trễ), XANH = buổi đang diễn ra. Cố ý KHÔNG dùng đỏ — GV vẫn sửa được
   trong hạn (EditWindow), màu đỏ sẽ báo sai rằng GV đã mất quyền.
   ══════════════════════════════════════════════════════════════════════════ */
.ow-class-card__today {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.375rem 0.5rem;
  border-radius: 0.5rem;
  border: 1px solid rgb(180 83 9 / .25);
  background-color: rgb(251 191 36 / .10);
}

.ow-class-card__now-text {
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.25;
}

.ow-class-card__today--late {
  border-color: rgb(180 83 9 / .35);
  background-color: rgb(251 191 36 / .14);
  color: #b45309;
}

.ow-class-card__today--live {
  border-color: rgb(22 163 74 / .35);
  background-color: rgb(34 197 94 / .10);
  color: #15803d;
}

.ow-class-card__today-locked {
  font-size: 0.6875rem;
  font-weight: 600;
  opacity: .8;
}

/*
   Vùng bấm ≥40px ở <768px (CLAUDE.md §3 — GV dùng màn này trên điện thoại
   trong lớp). Số đo bằng getComputedStyle ở 375px; test khoá HỢP ĐỒNG này nên
   xoá rule hay quên gắn class thì đỏ (MyClassesTodayMakeupW3Test).
*/
@media (max-width: 767px) {
  .ow-class-card__today {
    min-height: 40px;
  }

  .ow-class-card__today .ow-class-card__today-action {
    min-height: 40px;
    min-width: 40px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ow-class-card--live::after,
  .ow-class-card__now::before {
    animation: none;
  }
  .ow-class-card--live::after { opacity: .6; }
}

/* ══════════════════════════════════════════════════════════════════════════
   S04 — KÍ HIỆU "BUỔI CỦA TÔI" (27/09).

   Chủ dự án: "như admin nhưng mà cần có thêm kí hiệu lớp của mình" — GV/TG được
   cấp `schedule.view` nên xem được lịch CẢ TRUNG TÂM (trước đây 403), nhưng
   lưới có ~20 lớp mỗi ngày: không phân biệt thì GV phải dò từng thẻ.

   Vì sao VIỀN TRÁI chứ không đổi màu nền: thẻ buổi đã tô màu theo CA (Sáng /
   Chiều / Tối) — đổi nền là đè lên tín hiệu đó và nhìn nhầm sang ca khác. Viền
   trái là lớp thứ hai, không cạnh tranh với màu ca.

   Dùng chung `.ow-slot-card--mine` cho lưới TUẦN (partial slot-card) và lưới
   THÁNG (chip) — một class, hai nơi, không lệch.
   ══════════════════════════════════════════════════════════════════════════ */

.ow-slot-card--mine {
  border-left: 3px solid var(--ow-blue, #1769e0);
}

.dark .ow-slot-card--mine {
  border-left-color: #7aa7f5;
}

/* Chip tháng nhỏ hơn nên viền mỏng hơn cho vừa, nhưng vẫn phải thấy. */
.ow-cal-chip.ow-slot-card--mine {
  border-left-width: 2px;
}

/* Dành cho người mù màu / bản in đen trắng: thẻ của mình luôn có dấu. */
.ow-slot-card--mine .ow-slot-card__name::after {
  content: " · của tôi";
  font-size: 0.75em;
  color: var(--ow-blue, #1769e0);
}

.ow-cal-chip.ow-slot-card--mine .ow-cal-chip__name::after {
  content: " ·";
  color: var(--ow-blue, #1769e0);
}

/* ══════════════════════════════════════════════════════════════════════════
   S04 — Nút "Dạy thay" trên thẻ buổi (27/09).

   Nút nhỏ, nằm góc phải thẻ buổi. GV nhìn lưới thấy buổi CHƯA CÓ AI ĐỨNG thì
   bấm được ngay — không cần mở hộp thoại tìm người. Màu xanh lá = "lớp này đang
   cần người", khác hẳn màu ca Sáng/Chiều/Tối đang tô trên thẻ.
   ══════════════════════════════════════════════════════════════════════════ */

.ow-s04-substitute {
  position: absolute;
  top: 0.25rem;
  right: 0.25rem;
  z-index: 1;
  min-height: 1.75rem;
  padding: 0.125rem 0.5rem;
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.2;
  color: #ffffff;
  cursor: pointer;
  background-color: var(--ow-green, #16a34a);
  border: 0;
  border-radius: 999px;
}

.ow-s04-substitute:hover {
  background-color: #15803d;
}

.ow-s04-substitute:focus-visible {
  outline: 2px solid var(--ow-ink, #1c2440);
  outline-offset: 2px;
}

/* T4.66 — nút Giao/Gỡ dạy thay của admin/manager: dòng riêng dưới thẻ buổi (không absolute như nút "Dạy thay"
   của GV, vì một ô có nhiều thẻ và hai nút cùng hiện). Màu xanh dương/xám để khác nút xanh lá "Dạy thay". */
.ow-s04-assign-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  margin: 0.125rem 0 0.375rem;
}

.ow-s04-assign {
  min-height: 1.5rem;
  padding: 0.125rem 0.5rem;
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.2;
  color: #ffffff;
  cursor: pointer;
  /* T4.95 — trắng trên #3b82f6 chỉ 3,68:1 ở cỡ 11px; nền lùi về blue-700. */
  background-color: var(--ow-blue-ink, #1d4ed8);
  border: 0;
  border-radius: 999px;
}

.ow-s04-assign:hover {
  background-color: #1252b4;
}

.ow-s04-assign--revoke {
  color: var(--ow-ink, #1c2440);
  background-color: #e5e7eb;
}

.ow-s04-assign--revoke:hover {
  background-color: #d1d5db;
}

.ow-s04-assign:focus-visible {
  outline: 2px solid var(--ow-ink, #1c2440);
  outline-offset: 2px;
}

/* Thẻ buổi bọc thêm lớp này để nút "Dạy thay" định vị theo thẻ, không theo
   ô lưới — trước đó nút rơi ra góc ô và hiện trùng ở mọi ô cùng ca. */
.ow-s04-card-wrap {
  position: relative;
}

/* ============================================================
   T4.24 — khối "Bằng cấp" trên trang Xem học sinh: mỗi bằng một dòng, tên đậm,
   chi tiết nối " · ", nhãn minh chứng tách riêng. Class tĩnh vì dist Filament không có
   utility tuỳ biến. Chữ dài tự xuống dòng (overflow-wrap) nên 375px không tràn ngang.
   Màu nhãn: cảnh báo #b45309 trên nền #fef3e2 (≥4.5:1), xanh dùng --ow-green-ink.
   ============================================================ */
.ow-qual-list {
  display: block;
  margin-bottom: 1rem;
}

.ow-qual-item {
  margin-bottom: 0.5rem;
  line-height: 1.5;
  overflow-wrap: anywhere;
}

.ow-qual-item:last-child {
  margin-bottom: 0;
}

.ow-qual-detail {
  color: var(--ow-muted);
}

.ow-qual-badge {
  display: inline-block;
  padding: 0 0.5rem;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.25rem;
  white-space: nowrap;
  vertical-align: baseline;
  border-radius: 999px;
}

.ow-qual-badge--warn {
  color: var(--ow-accent-ink);
  background-color: var(--ow-accent-soft);
}

.ow-qual-badge--ok {
  color: var(--ow-green-ink);
  background-color: var(--ow-green-soft);
}

/* T4.44 (01/10): chữ khoảng cách ngày ở thẻ Lớp của tôi — "Ngày kia", "Ngày mai"…
   đậm hơn phần ngày/giờ đi sau. Guard: tests/Feature/Filament/MyClassesDayLabelBoldTest.php */
.ow-day-rel {
  font-weight: 800;
}

/* ══════════════════════════════════════════════════════════════════════════
   T4.47 — drawer Xếp lịch: MỖI THỨ MỘT DÒNG.
   Drawer chỉ rộng ~416px ở MỌI màn hình (đo 01/10 ở 1280px: kiểu 1 hàng 5 cột
   tràn, ô Phòng bị cắt, nút × khuất) → luôn xếp 2 hàng:
     Thứ    | Phòng    | ×
     Bắt đầu| Kết thúc | ×
   Ô nhập kế thừa `.ow-drawer__field input/select` (min-height 40px).
   ══════════════════════════════════════════════════════════════════════════ */
.ow-slot-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.ow-slot-row {
  display: grid;
  grid-template-columns: 1fr 1fr 2.5rem;
  grid-template-areas:
    "day room remove"
    "start end remove";
  gap: 0.375rem;
  align-items: center;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid rgb(15 23 42 / .07);
}

.dark .ow-slot-row { border-bottom-color: rgb(255 255 255 / .1); }

.ow-slot-row > select,
.ow-slot-row > input {
  min-width: 0;
  width: 100%;
}

.ow-slot-row__day { grid-area: day; }
.ow-slot-row__room { grid-area: room; }
.ow-slot-row__start { grid-area: start; }
.ow-slot-row__end { grid-area: end; }

.ow-slot-row__remove {
  grid-area: remove;
  min-height: 40px;
  min-width: 40px;
  border-radius: 0.5rem;
  font-size: 1.25rem;
  line-height: 1;
  color: #6b7280;
}

.ow-slot-row__remove:hover {
  color: #dc2626;
  background-color: rgb(220 38 38 / .08);
}

.ow-slot-add {
  align-self: flex-start;
  min-height: 40px;
  padding: 0 0.875rem;
  border: 1px dashed rgb(22 163 74 / .55);
  border-radius: 0.5rem;
  font-size: 0.875rem;
  font-weight: 600;
  color: #15803d;
  background-color: rgb(22 163 74 / .05);
}

.ow-slot-add:hover {
  background-color: rgb(22 163 74 / .12);
}

.dark .ow-slot-add { color: #4ade80; }

/* ============================================================
   T4.51 — TÊN LỚP XUỐNG DÒNG Ở MỌI CHỖ (02/10)

   Chủ dự án: tên lớp tối đa 50 ký tự và "cho nó xuống dòng". T4.50 mới chữa các
   ô BẢNG (.ow-wrap-cell); đây là phần còn lại. Đo 02/10 với lớp tên 255 chữ A liền:
   tiêu đề trang Xem lớp rộng 4264px, chuỗi chip lịch bị cắt "…", thẻ GV, chuông
   thông báo 2667px, hộp thông tin hồ sơ học sinh 2628px, tag cột Lớp 2282px, ô chọn
   lớp (select native) 2775px.

   MỘT bộ quy tắc: overflow-wrap kế thừa xuống con nên đặt ở phần tử chứa chữ; còn
   min-width:0 BẮT BUỘC ở phần tử là flex/grid item (min-width:auto = từ dài nhất,
   bài học T4.50). Không Tailwind arbitrary value — dist Filament không có.
   ============================================================ */
.ow-wrap-text {
  min-width: 0;
  max-width: 100%;
  white-space: normal;
  overflow-wrap: anywhere;
}

.fi-header-heading,
.fi-section-header-heading,
.fi-no-notification-title,
.fi-in-text,
.ow-today-info b {
  min-width: 0;
  max-width: 100%;
  overflow-wrap: anywhere;
}

/* Cột Lớp ở danh sách học sinh: thẻ inline-flex lấy max-content = cả chuỗi. Kẹp
   trần như .ow-wrap-cell để bảng không giãn hàng nghìn px. break-word, không anywhere,
   cùng lý do T4.50: anywhere cho cột GV · TG co còn 1 ký tự mỗi dòng (03/10/2026). */
.fi-ta-cell .ow-tag {
  max-width: 18rem;
  white-space: normal;
  overflow-wrap: break-word;
}

/* Select native: độ rộng = option dài nhất. Kẹp theo khung chứa; chữ trong ô đóng
   không xuống dòng được (giới hạn của <select>) nhưng danh sách thả xuống hiện đủ. */
.ow-select-wrap {
  /* nhãn bọc select là flex item: không kẹp nó thì max-width:100% của select
     quy về % của một khung tự co theo select (vòng tròn) và bị bỏ qua. */
  min-width: 0;
  max-width: 100%;
}

.ow-select-fit {
  min-width: 0;
  max-width: 100%;
  text-overflow: ellipsis;
}

/* T4.54: ô nhập của hộp "Thêm học sinh" / form "Xếp vào lớp" — Filament mặc định cao 36px,
   dưới chuẩn bấm ≥40px của CLAUDE.md §3. Chỉ phủ các ô mang class ow-enroll-field
   (EnrollmentActions::TAP_CLASS), không đụng ô nhập của màn khác. */
@media (max-width: 767px) {
  .ow-enroll-field .fi-input-wrp {
    min-height: 40px;
  }

  .ow-enroll-field .fi-input-wrp input,
  .ow-enroll-field .fi-input-wrp select,
  .ow-enroll-field .fi-input-wrp button {
    min-height: 40px;
  }
}

/* T4.56: nút "Bảo lưu" / "Học lại" trên dòng của em — Filament vẽ nút dạng link cao ~20px, dưới chuẩn bấm
   ≥40px của CLAUDE.md §3 ở điện thoại. Chỉ phủ nút mang class ow-row-action (EnrollmentStatusActions). */
@media (max-width: 767px) {
  .ow-row-action {
    min-height: 40px;
    min-width: 40px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding-inline: 0.5rem;
  }
}

/* T4.57: khối ĐIỂM DANH BÙ trong hộp ghi danh lùi ngày (resources/views/filament/forms/backfill-attendance.blade.php).
   View phía app không có Tailwind → class semantic tự định nghĩa. Nút Có mặt/Vắng/Có mặt hết ≥40px ở điện thoại
   (CLAUDE.md §3); trạng thái chọn đọc từ aria-pressed (Alpine bind) để màu theo đúng state, không cần class động. */
.ow-backfill { display: flex; flex-direction: column; gap: 0.5rem; }
.ow-backfill-head { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; }
.ow-backfill-count { font-size: 0.875rem; font-weight: 600; color: #374151; }
.ow-backfill-row { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; flex-wrap: wrap; border: 1px solid #e5e7eb; border-radius: 0.5rem; padding: 0.375rem 0.5rem; }
.ow-backfill-label { font-size: 0.875rem; color: #111827; }
.ow-backfill-choices { display: flex; gap: 0.375rem; }
.ow-backfill-btn { min-height: 36px; padding: 0 0.875rem; border-radius: 0.5rem; border: 1px solid #d1d5db; background: #ffffff; color: #374151; font-size: 0.875rem; font-weight: 500; cursor: pointer; }
.ow-backfill-all { border-color: #16a34a; color: #166534; }
.ow-backfill-present[aria-pressed="true"] { background: #16a34a; border-color: #16a34a; color: #ffffff; }
.ow-backfill-absent[aria-pressed="true"] { background: #dc2626; border-color: #dc2626; color: #ffffff; }
.ow-backfill-note { font-size: 0.8125rem; color: #4b5563; }
.ow-backfill-note--danger { color: #b91c1c; font-weight: 500; }

@media (max-width: 767px) {
  .ow-backfill-btn {
    min-height: 40px;
    min-width: 72px;
  }
}

/* ============================================================
   T4.72 — DÒNG LỊCH Ở FORM TẠO LỚP + BẤM KHỐI Ở LỊCH HỌC MỞ FORM SỬA

   Form Tạo lớp rộng hơn drawer nên mỗi thứ là MỘT HÀNG 5 ô:
     Thứ | Bắt đầu | Kết thúc | Phòng | ×
   kèm hàng lỗi riêng ngay dưới dòng. Dưới 768px quay về bố cục 2 hàng của
   `.ow-slot-row` gốc (drawer T4.47). Scope `.ow-slot-editor` để không đụng
   luật gốc mà test T4.47 đang khoá. Guard: CreateClassRoomSlotsT472Test.php
   ============================================================ */
.ow-slot-editor {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.ow-slot-editor__hint {
  margin: 0;
  font-size: 0.8125rem;
  color: #64748b;
}

.ow-slot-editor .ow-slot-row > select,
.ow-slot-editor .ow-slot-row > input {
  min-height: 2.5rem;
  padding: 0.5rem 0.625rem;
  /* T4.74: ô giờ là ô chữ HH:MM (không phải <input type="time">) — chữ số bằng bề rộng để "18:30" và
     "09:00" thẳng hàng giữa các dòng. */
  font-variant-numeric: tabular-nums;
  font-size: 0.875rem;
  color: var(--ow-ink, #1c2440);
  background-color: var(--ow-card, #ffffff);
  border: 1px solid var(--ow-line, #e6eaf3);
  border-radius: var(--ow-radius-sm, 10px);
}

.dark .ow-slot-editor .ow-slot-row > select,
.dark .ow-slot-editor .ow-slot-row > input {
  color: #f1f5f9;
  background-color: rgb(255 255 255 / .06);
  border-color: rgb(255 255 255 / .18);
}

.ow-slot-row__err {
  grid-column: 1 / -1;
  font-size: 0.8125rem;
  font-style: normal;
  color: var(--ow-red-ink, #b91c1c);
}

.dark .ow-slot-row__err { color: #fdaaa1; }

@media (min-width: 768px) {
  .ow-slot-editor .ow-slot-row {
    grid-template-columns: 9rem 8rem 8rem minmax(0, 1fr) 2.5rem;
    grid-template-areas: "day start end room remove";
  }
}

/* T4.73 (10/10): ở 375px cột 1fr chỉ còn 118px (modal: 100px) nên ô Phòng cắt chữ còn "— Dùng phò"
   và mũi tên đè lên chữ. Xếp 3 hàng: Thứ / Bắt đầu · Kết thúc / Phòng — Phòng chiếm trọn bề ngang,
   nút × canh giữa cả 3 hàng. Chữ dài vẫn cắt bằng dấu ba chấm thay vì đè mũi tên. Ô giờ bớt đệm
   ngang để "06:00 PM" + biểu tượng đồng hồ vừa ô. Guard: tests/Feature/Ui/ScheduleFormsMobileT473Test.php */
@media (max-width: 767.98px) {
  .ow-slot-editor .ow-slot-row {
    grid-template-areas:
      "day day remove"
      "start end remove"
      "room room remove";
  }

  .ow-slot-editor .ow-slot-row > select {
    text-overflow: ellipsis;
    padding-inline-end: 1.75rem;
  }

  .ow-slot-editor .ow-slot-row > input {
    padding-inline: 0.5rem;
  }

  /* Biểu tượng đồng hồ của ô giờ chiếm ~20px mà trên điện thoại chạm vào ô là mở bộ chọn giờ rồi. */
  .ow-slot-editor .ow-slot-row > input::-webkit-calendar-picker-indicator {
    display: none;
  }

  /* Ô nhập Filament cao 36px mặc định; field gắn class ow-tap-40 (extraFieldWrapperAttributes) lên ≥40px. */
  .ow-tap-40 .fi-input-wrp {
    min-height: 40px;
  }

  .ow-tap-40 .fi-input-wrp input {
    min-height: 40px;
  }

  /* Link "Xem trang lớp" trong drawer Sửa lịch lặp: 19.5px → vùng bấm ≥40px (CLAUDE.md §3). */
  .ow-drawer__link {
    display: inline-flex;
    align-items: center;
    min-height: 2.5rem;
  }
}

/* Khối buổi ở Lịch học: có quyền sửa lịch thì bấm = mở form sửa (role=button), không phải link. */
.ow-slot-card[role="button"],
.ow-cal-chip[role="button"] {
  cursor: pointer;
}

.ow-drawer__edit-head {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}

.ow-drawer__edit-class {
  font-size: 1rem;
  font-weight: 700;
  overflow-wrap: anywhere;
  color: var(--ow-ink, #1c2440);
}

.ow-drawer__edit-day {
  font-size: 0.8125rem;
  font-weight: 600;
  color: #64748b;
}

.ow-drawer__link {
  align-self: flex-start;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--ow-s04-blue, #1769e0);
  text-decoration: underline;
}

.ow-drawer__note {
  margin: 0;
  font-size: 0.75rem;
  color: #64748b;
}

/* T4.72: form "Sửa lịch lặp" có ô Phòng (select theo option dài nhất) → cột 1 phình tới 256px, đẩy
   cột 2 ra ngoài drawer 416px (đo 10/10 ở 1440px: scrollWidth 447 > clientWidth 415). Cho ô co lại. */
.ow-drawer__grid > .ow-drawer__field {
  min-width: 0;
}

.ow-drawer__grid .ow-drawer__field select,
.ow-drawer__grid .ow-drawer__field input {
  min-width: 0;
  width: 100%;
}

/* ============================================================
   T4.74 — MỘT component xếp lịch + mobile hoàn thiện (10/10)

   1) Dòng lịch được làm nổi (bấm khối buổi ở Lịch học → dòng của khối đó), nhãn của component, hộp xác nhận
      xoá của drawer "Lịch tuần của lớp". Class semantic — dist Filament không có utility arbitrary-value.
   2) Ở <768px MỌI ô nhập của form cao ≥40px (CLAUDE.md §3: vùng bấm ≥40px). Đo Pixel 7 trên form Tạo lớp:
      ô chữ/số/chọn 36px (khung .fi-input-wrp 38px), ô chọn có tìm kiếm (choices.js) 33px, nhãn ô tick 24px.
      ≥768px giữ nguyên 36px — chỉ nằm trong @media, không rule nào ép 40px ở desktop.
   3) Select ở <768px chừa chỗ cho mũi tên: chữ cắt bằng "…" nhưng KHÔNG chạm mũi tên. Luật T4.73 chừa
      1.75rem (28px) trong khi mũi tên Filament chiếm ~29px tính từ mép phải → dấu "…" dính mũi tên. Rule dưới
      đặt SAU rule cũ cùng selector nên thắng theo thứ tự; rule cũ (1.75rem) còn đó vì test T4.73 đang khoá nó.
   Guard: tests/Feature/Ui/ScheduleSlotEditorSharedT474Test.php
   ============================================================ */
/* Drawer (Xếp buổi học / Lịch tuần của lớp) hẹp ~415px ở MỌI viewport: dùng component chung nhưng KHÔNG được
   lên 5 cột như form Tạo lớp (đo 1440px: drawer scrollWidth 492 > clientWidth 415) — giữ 2 hàng T4.47. */
@media (min-width: 768px) {
  .ow-drawer .ow-slot-editor .ow-slot-row {
    grid-template-columns: 1fr 1fr 2.5rem;
    grid-template-areas:
      "day room remove"
      "start end remove";
  }
}

/* Chữ ô chọn cắt bằng "…" và chừa chỗ cho mũi tên ở MỌI viewport (desktop: ô Phòng trong drawer cũng hẹp). */
.ow-slot-editor .ow-slot-row > select {
  text-overflow: ellipsis;
  padding-inline-end: 2.5rem;
}

.ow-slot-editor__label {
  font-size: 0.875rem;
  font-weight: 500;
  line-height: 1.5rem;
  color: var(--ow-ink, #1c2440);
}

.dark .ow-slot-editor__label { color: #ffffff; }

.ow-slot-row--focus {
  padding-top: 0.5rem;
  padding-inline: 0.5rem;
  border-radius: var(--ow-radius-sm, 10px);
  background-color: var(--ow-s04-blue-soft, #eef5ff);
  box-shadow: inset 3px 0 0 var(--ow-s04-blue, #1769e0);
}

.dark .ow-slot-row--focus { background-color: rgb(23 105 224 / .16); }

.ow-drawer__weekly {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.ow-drawer__confirm {
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
  padding: 0.75rem;
  border: 1px solid rgb(185 28 28 / .35);
  border-radius: var(--ow-radius-sm, 10px);
  background-color: rgb(254 242 242);
}

.dark .ow-drawer__confirm {
  background-color: rgb(185 28 28 / .14);
  border-color: rgb(253 170 161 / .4);
}

.ow-drawer__confirm-text {
  margin: 0;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--ow-red-ink, #b91c1c);
}

.dark .ow-drawer__confirm-text { color: #fdaaa1; }

.ow-drawer__confirm-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.ow-drawer__confirm-yes,
.ow-drawer__confirm-no {
  min-height: 2.75rem;
  padding: 0 1rem;
  border-radius: 0.5rem;
  font-size: 0.875rem;
  font-weight: 600;
}

.ow-drawer__confirm-yes {
  color: #ffffff;
  background-color: #b91c1c;
}

.ow-drawer__confirm-no {
  color: var(--ow-ink, #1c2440);
  background-color: #ffffff;
  border: 1px solid var(--ow-line, #e6eaf3);
}

@media (max-width: 767.98px) {
  .fi-input-wrp {
    min-height: 40px;
  }

  .fi-input-wrp input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]) {
    min-height: 40px;
  }

  .fi-input-wrp select {
    min-height: 40px;
  }

  /* Ô chọn có tìm kiếm (choices.js) cao 33px. */
  .fi-input-wrp .choices__inner {
    min-height: 40px;
  }

  /* Ô tick: nhãn bọc ô 16px cao 24px — cả hàng nhãn là vùng chạm. */
  .fi-fo-field-wrp-label:has(> .fi-checkbox-input) {
    min-height: 40px;
  }

  .ow-slot-editor .ow-slot-row > select {
    text-overflow: ellipsis;
    padding-inline-end: 2.5rem;
  }

  .ow-drawer__field select {
    text-overflow: ellipsis;
    padding-inline-end: 2.5rem;
  }
}

/* ============================================================
   T4.75 — QUYỀN Ở MÀN SỬA/TẠO VAI TRÒ XẾP THEO NHÓM CHỨC NĂNG
   (resources/views/filament/forms/components/grouped-checkbox-list.blade.php)
   Mỗi nhóm là một khung: tiêu đề · "x/y" · nút "Chọn cả nhóm", dưới là lưới ô tick
   (số cột do field khai: 2 / md 3 / lg 4 — BUG-047). Class tự định nghĩa vì view phía app
   không có pipeline Tailwind riêng. Guard: tests/Feature/Ui/RolePermissionGroupsCssT475Test.php
   ============================================================ */
.ow-perm-groups { min-width: 0; }

.ow-perm-list {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.ow-perm-group {
  padding: 0.75rem 1rem 0.25rem;
  background-color: var(--ow-card, #ffffff);
  border: 1px solid var(--ow-line, #e6eaf3);
  border-radius: var(--ow-radius-sm, 10px);
}

.dark .ow-perm-group {
  background-color: rgb(255 255 255 / .04);
  border-color: rgb(255 255 255 / .14);
}

.ow-perm-group__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.75rem;
  margin-bottom: 0.25rem;
}

.ow-perm-group__title {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--ow-ink, #1c2440);
}

.dark .ow-perm-group__title { color: #f1f5f9; }

.ow-perm-group__count {
  min-width: 2.5rem;
  padding: 0.0625rem 0.5rem;
  font-size: 0.8125rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: center;
  color: var(--ow-muted, #6f7893);
  background-color: var(--ow-bg, #f2f4fa);
  border-radius: 999px;
}

.dark .ow-perm-group__count {
  color: #cbd5e1;
  background-color: rgb(255 255 255 / .08);
}

.ow-perm-group__toggle {
  margin-inline-start: auto;
  min-height: 2rem;
  padding: 0 0.5rem;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--ow-accent-ink, #b45309);
  background: none;
  border: 0;
  cursor: pointer;
}

.ow-perm-group__toggle:hover { text-decoration: underline; }

.dark .ow-perm-group__toggle { color: var(--ow-accent-strong, #fbbf24); }

.ow-perm-group__grid { column-gap: 1rem; }

.ow-perm-option {
  padding-bottom: 0.75rem;
}

/* ≥40px vùng chạm (CLAUDE.md §3): cả hàng nhãn là vùng bấm. */
@media (max-width: 767.98px) {
  .ow-perm-option > label {
    min-height: 2.5rem;
    align-items: center;
  }

  .ow-perm-option { padding-bottom: 0; }

  .ow-perm-group__toggle { min-height: 2.5rem; }
}

/* ─── T4.88 — S02 Học sinh: thẻ trên điện thoại (<768px) ───────────────────────────
 * Chủ dự án duyệt 16/10: điện thoại hiện thẻ, desktop giữ NGUYÊN bảng. MỘT DOM duy nhất:
 * các <tr> của chính bảng được vẽ lại thành thẻ bằng CSS (không render bộ thẻ thứ hai rồi ẩn
 * — HTML gấp đôi). Mọi luật nằm trong media query; ≥768px không có dòng nào ở đây tác động.
 * Thẻ chỉ 5 thông tin: tên, mã HS, lớp, SĐT phụ huynh (data-phone trên ô Mã HS), trạng thái.
 * Ẩn: tiêu đề cột (sắp xếp dùng tìm kiếm), GV phụ trách, trình độ, nút thao tác, ghi chú anh em.
 * Cả thẻ là vùng bấm sang hồ sơ (mỗi ô là một <a> cùng href), cao ≥ 2.75rem = 44px. */
@media (max-width: 767.98px) {
  .fi-resource-students .fi-ta-table,
  .fi-resource-students .fi-ta-table tbody {
    display: block;
    min-width: 0;
    white-space: normal;
  }

  .fi-resource-students .fi-ta-table thead {
    display: none;
  }

  .fi-resource-students .fi-ta-table tbody {
    padding: 0.5rem 0.75rem 0.25rem;
  }

  .fi-resource-students .fi-ta-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "name status"
      "code code"
      "classes classes";
    align-items: center;
    margin-bottom: 0.5rem;
    border: 1px solid #e2e8f0;
    border-radius: 0.75rem;
    background: #fff;
    overflow: hidden;
  }

  .fi-resource-students .fi-ta-row > .fi-ta-cell {
    display: block;
    min-width: 0;
    padding: 0 !important;
    border: 0;
  }

  .fi-resource-students .fi-ta-row > .fi-table-cell-full-name { grid-area: name; }
  .fi-resource-students .fi-ta-row > .fi-table-cell-status { grid-area: status; }
  .fi-resource-students .fi-ta-row > .fi-table-cell-student-code { grid-area: code; }
  .fi-resource-students .fi-ta-row > .fi-table-cell-classes { grid-area: classes; }

  .fi-resource-students .fi-ta-row > .fi-table-cell-primary-teachers,
  .fi-resource-students .fi-ta-row > .fi-table-cell-student-level,
  .fi-resource-students .fi-ta-row > .fi-ta-actions-cell {
    display: none;
  }

  .fi-resource-students .fi-ta-row .fi-ta-cell a {
    display: flex;
    align-items: center;
    min-height: 2.75rem;
  }

  .fi-resource-students .fi-ta-row .fi-ta-cell .fi-ta-text {
    padding: 0.375rem 0.75rem !important;
  }

  /* Dòng do "Xem thêm" nối vào chỉ có MỘT link (ở tên); ::after trải kín thẻ → cả thẻ bấm được. */
  .fi-resource-students .ow-stu-extra {
    position: relative;
  }

  .fi-resource-students .ow-stu-extra .fi-table-cell-full-name a::after {
    content: "";
    position: absolute;
    inset: 0;
  }

  .fi-resource-students .fi-ta-row > .fi-table-cell-full-name .fi-ta-text {
    padding-top: 0.625rem !important;
  }

  .fi-resource-students .fi-ta-row > .fi-table-cell-full-name .fi-ta-text > p {
    display: none;
  }

  /* Mã HS + SĐT phụ huynh chung một dòng: "ST00001 · SĐT 0912345678" */
  .fi-resource-students .fi-ta-row > .fi-table-cell-student-code .fi-ta-text {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 0.5rem;
  }

  .fi-resource-students .fi-ta-row > .fi-table-cell-student-code .fi-ta-text[data-phone]::after {
    content: "· SĐT " attr(data-phone);
    color: #334155;
    font-family: inherit;
    font-size: 0.8125rem;
  }

  .fi-resource-students .fi-ta-row > .fi-table-cell-classes .fi-ta-text {
    padding-bottom: 0.625rem !important;
  }

  .fi-resource-students .fi-ta-row > .fi-table-cell-status .fi-ta-text {
    padding-right: 0.75rem !important;
  }

  /* Phân trang số trang → thay bằng nút "Xem thêm" (một request, một trang kế). */
  .fi-resource-students .fi-ta-pagination {
    display: none;
  }

  .fi-resource-students .ow-stu-more {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    padding: 0.25rem 0.75rem 0.75rem;
  }

  .fi-resource-students .ow-stu-more__btn {
    width: 100%;
    min-height: 2.75rem;
    border: 1px solid #cbd5e1;
    border-radius: 0.75rem;
    background: #fff;
    color: #1e3a8a;
    font-size: 0.9375rem;
    font-weight: 600;
  }

  .fi-resource-students .ow-stu-more__btn:disabled {
    opacity: 0.6;
  }

  .fi-resource-students .ow-stu-more__err {
    width: 100%;
    margin-top: 0.5rem;
    font-size: 0.8125rem;
    color: #b91c1c;
    text-align: center;
  }

  /* Ô tìm lên ĐẦU khối lọc: gõ tìm là việc 30 giây, không phải cuộn qua 6 ô lọc.
     Bộ lọc xếp hai cột cho gọn. DOM không đổi, chỉ đổi thứ tự hiển thị. */
  .fi-resource-students .fi-ta-header-ctn {
    display: flex;
    flex-direction: column;
  }

  .fi-resource-students .fi-ta-header-ctn > .fi-ta-header-toolbar { order: 1; }
  .fi-resource-students .fi-ta-header-ctn > .fi-ta-filters-above-content-ctn { order: 2; }

  /* Nhóm trái của toolbar (đặt lại/nhóm/sắp xếp — S02 không dùng) rỗng thì bỏ, để ô tìm chiếm cả hàng. */
  .fi-resource-students .fi-ta-header-toolbar > div:not(:has(*)) {
    display: none;
  }

  .fi-resource-students .fi-ta-header-toolbar > div:has(.fi-ta-search-field),
  .fi-resource-students .fi-ta-header-toolbar .fi-ta-search-field {
    flex: 1 1 auto;
    width: 100%;
  }

  /* Khung ngoài của filter (.fi-fo-component-ctn cấp 1) đang là lưới 2 cột (--cols-default) chỉ
     chứa MỘT ô con chứa lưới trong; ép cấp 1 về 1 cột để các select có đủ bề ngang, cấp 2 chia hai. */
  .fi-resource-students .fi-ta-filters > .fi-fo-component-ctn {
    grid-template-columns: minmax(0, 1fr) !important;
  }

  .fi-resource-students .fi-ta-filters > .fi-fo-component-ctn .fi-fo-component-ctn {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    gap: 0.5rem 0.75rem !important;
  }
}

/* ≥768px: thẻ "Xem thêm" không tồn tại — bảng desktop dùng phân trang số như cũ. */
@media (min-width: 768px) {
  .fi-resource-students .ow-stu-more,
  .fi-resource-students .ow-stu-extra {
    display: none;
  }
}

/* ============================================================================
 * T5.10 — BỘ LỌC DANH SÁCH NHÂN SỰ Ở ĐIỆN THOẠI (<768px)
 *
 * Đo Chromium 375×812 ngày 30/10 (trước khi sửa):
 *   .fi-ta-filters-above-content-ctn cao 427px, 4 select xếp DỌC ⇒ dòng dữ liệu
 *   đầu tiên ở y=877, NGOÀI màn 812: mở màn Nhân sự trên điện thoại chỉ thấy
 *   toàn bộ lọc, không thấy một ai.
 *
 * Vì sao không dùng AboveContentCollapsible: cổng FilterLayoutParityTest bắt MỌI
 * bảng phải `FiltersLayout::AboveContent` (luật repo: lọc không giấu sau icon
 * phễu). Nên vẫn hiện ĐỦ 4 ô, chỉ đổi cách xếp: 2 CỘT thay vì 1.
 *
 * ⚠️ CẤU TRÚC LƯỚI LỒNG HAI TẦNG (đo 30/10):
 *   .fi-ta-filters
 *     └ .fi-fo-component-ctn          ← tầng 1: 4 ô lọc  (ép 2 cột)
 *         └ .col-[--col-span-default]
 *             └ .fi-fo-component-ctn  ← tầng 2: MỘT ô    (phải 1 cột!)
 *                 └ .fi-fo-field-wrp
 *   Ép cả hai tầng thành 2 cột thì mỗi ô còn 65px, select chỉ hiện "Tả" thay vì
 *   "Tất cả" (đã dính đúng lỗi này rồi sửa).
 * ========================================================================== */
@media (max-width: 767.98px) {
  .fi-resource-staff-profiles .fi-ta-filters-above-content-ctn {
    padding-block: 0.5rem;
  }

  /* Tầng 1 — 4 ô lọc thành 2 hàng × 2 cột. */
  .fi-resource-staff-profiles .fi-ta-filters > .fi-fo-component-ctn {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    gap: 0.5rem 0.75rem !important;
  }

  /* Tầng 2 — mỗi ô lọc nằm một mình trong wrapper của nó: PHẢI 1 cột. */
  .fi-resource-staff-profiles .fi-ta-filters .fi-fo-component-ctn .fi-fo-component-ctn {
    grid-template-columns: minmax(0, 1fr) !important;
    gap: 0.25rem !important;
  }

  /* Ô điều khiển lấp đầy bề ngang (mặc định co theo nội dung). */
  .fi-resource-staff-profiles .fi-ta-filters .fi-fo-field-wrp,
  .fi-resource-staff-profiles .fi-ta-filters select,
  .fi-resource-staff-profiles .fi-ta-filters .fi-input-wrp {
    width: 100% !important;
    min-width: 0 !important;
  }

  /* Hàng "Bộ lọc + Đặt lại" gọn lại (mỗi ô đã có nhãn riêng). */
  .fi-resource-staff-profiles .fi-ta-filters > div:first-child {
    margin-bottom: 0.25rem;
  }

  .fi-resource-staff-profiles .fi-ta-filters > div:first-child h4 {
    display: none;
  }

  /* Ô tìm kiếm chiếm trọn bề ngang cho dễ gõ. */
  .fi-resource-staff-profiles .fi-ta-header-toolbar > div:has(.fi-ta-search-field),
  .fi-resource-staff-profiles .fi-ta-header-toolbar .fi-ta-search-field {
    flex: 1 1 auto;
    width: 100%;
  }
}

/* ============================================================
   T4.93 — Phiếu học phí (S14) + Đối soát chuyển khoản (S15)
   Chỉ tham chiếu var(--ow-*). Không hex, không px rời: đổi token
   là hai màn này đổi theo, không phải sửa từng màn.
   ============================================================ */

.ow-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Ô file ẩn vẫn nhận được focus bàn phím — phải có dấu nhìn thấy được,
   nếu không người dùng Tab sẽ "mất dấu" ngay trên nút Chọn file. */
.ow-file-row input[type="file"]:focus-visible + * {
  outline: 2px solid var(--ow-accent);
  outline-offset: 2px;
  border-radius: var(--ow-radius-sm);
}

.ow-callout {
  border: 1px solid var(--ow-orange);
  background: var(--ow-orange-soft);
  color: var(--ow-orange-ink);
  border-radius: var(--ow-radius-sm);
  padding: 12px 14px;
  font-size: 0.8125rem;
  line-height: 1.5;
}

/* ---------- Bộ lọc ---------- */

.ow-tn-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 14px;
}

.ow-tn-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 220px;
}

.ow-tn-field--month {
  min-width: 140px;
}

.ow-tn-field label {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--ow-muted);
}

.ow-tn-toolbar__action {
  margin-left: auto;
}

/* ---------- Ba con số của lớp ---------- */

.ow-tn-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px;
}

.ow-tn-stat {
  background: var(--ow-card);
  border: 1px solid var(--ow-line);
  border-radius: var(--ow-radius-sm);
  padding: 12px 14px;
}

.ow-tn-stat__label {
  font-size: 0.75rem;
  color: var(--ow-muted);
}

.ow-tn-stat__value {
  margin-top: 2px;
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--ow-ink);
  font-variant-numeric: tabular-nums;
}

.ow-tn-stat--due .ow-tn-stat__value {
  color: var(--ow-red-ink);
}

.ow-tn-note {
  margin-top: 12px;
  font-size: 0.75rem;
  color: var(--ow-muted);
  line-height: 1.5;
}

/* ---------- Bảng dữ liệu dùng chung cho hai màn ---------- */

.ow-table-scroll {
  overflow-x: auto;
}

.ow-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.8125rem;
}

/* Số đo lấy ĐÚNG bằng .fi-ta-header-cell / .fi-ta-cell để bảng tự dựng và
   bảng Filament của Resource không còn là hai kiểu chữ khác nhau. */
.ow-table th {
  text-align: left;
  padding: 11px 10px;
  font-size: 0.65625rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ow-muted);
  background: var(--ow-table-head-bg);
  border-bottom: 1px solid var(--ow-line);
  white-space: nowrap;
}

.ow-table td {
  padding: 14px 10px;
  font-size: 0.8125rem;
  color: var(--ow-ink);
  border-bottom: 1px solid var(--ow-table-line);
  vertical-align: middle;
}

.ow-table tbody tr:last-child td {
  border-bottom: 0;
}

.ow-table__name {
  font-weight: 600;
}

.ow-table__wide {
  max-width: 34ch;
}

.ow-num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ---------- Tiền: một nghĩa, một màu ---------- */

.ow-money-due {
  color: var(--ow-red-ink);
  font-weight: 700;
}

.ow-money-due-soft {
  color: var(--ow-orange-ink);
}

.ow-money-paid {
  color: var(--ow-green-ink);
}

.ow-money-muted {
  color: var(--ow-muted);
}

/* ---------- Đối soát ---------- */

.ow-file-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
}

.ow-file-name {
  font-size: 0.8125rem;
  color: var(--ow-muted);
}

.ow-file-name.is-loaded {
  color: var(--ow-ink);
  font-weight: 600;
}

.ow-form-error {
  margin-top: 8px;
  font-size: 0.75rem;
  color: var(--ow-red-ink);
}

.ow-recon-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
}

.ow-recon-bar__badges {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.ow-recon-bar__actions {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.ow-state {
  font-weight: 600;
}

.ow-state--success { color: var(--ow-green-ink); }
.ow-state--warning { color: var(--ow-orange-ink); }
.ow-state--danger  { color: var(--ow-red-ink); }
.ow-state--muted   { color: var(--ow-muted); }

.ow-state__hint {
  margin-top: 2px;
  font-size: 0.6875rem;
  color: var(--ow-muted);
}

/* ---------- Hẹp: bảng tiền chuyển sang dạng thẻ ----------
   Ở 375px, bảng 7 cột bị cắt và số quan trọng nhất ("Tổng cộng") nằm ngoài
   màn hình. Dưới 640px mỗi dòng thành một thẻ: nhãn lấy từ data-label của ô,
   giá trị nằm bên phải — không phải cuộn ngang để đọc số tiền. */
@media (max-width: 640px) {
  .ow-tn-field,
  .ow-tn-field--month {
    min-width: 0;
    width: 100%;
  }

  .ow-tn-toolbar__action {
    margin-left: 0;
    width: 100%;
  }

  .ow-tn-toolbar__action .fi-btn {
    width: 100%;
    justify-content: center;
  }

  .ow-table--stack thead {
    display: none;
  }

  .ow-table--stack,
  .ow-table--stack tbody,
  .ow-table--stack tr,
  .ow-table--stack td {
    display: block;
    width: 100%;
  }

  .ow-table--stack tr {
    padding: 10px 0;
    border-bottom: 1px solid var(--ow-line);
  }

  .ow-table--stack tr:last-child {
    border-bottom: 0;
  }

  .ow-table--stack td {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 3px 12px;
    border: 0;
    text-align: left;
  }

  .ow-table--stack td::before {
    content: attr(data-label);
    flex: 0 0 auto;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ow-muted);
  }

  .ow-table--stack td.ow-table__name {
    font-size: 0.875rem;
  }

  /* Tên học sinh là tiêu đề của thẻ, không cần nhãn "HỌC SINH" lặp lại. */
  .ow-table--stack td.ow-table__name::before {
    content: none;
  }
}

/* Ô trạng thái có 2 phần (nhãn + ghi chú). Trong thẻ hẹp, ghi chú xuống
   dòng riêng thay vì chen ngang làm nhãn bị bóp thành cột chữ dọc. */
@media (max-width: 640px) {
  .ow-table--stack td {
    flex-wrap: wrap;
  }

  .ow-table--stack td .ow-state__hint {
    flex: 1 1 100%;
    text-align: right;
  }
}

/* Link trong bảng: Filament không style .fi-link nên "Tải" thừa hưởng màu chữ
   thường — đọc như văn bản, không ai biết bấm được. Cho nó màu link của hệ
   (--ow-blue-ink, 6.7:1 trên trắng) + gạch chân khi hover + focus nhìn thấy. */
.ow-table .fi-link {
  color: var(--ow-blue-ink);
  font-weight: 600;
  text-decoration: none;
  border-radius: 4px;
}

.ow-table .fi-link:hover {
  text-decoration: underline;
}

.ow-table .fi-link:focus-visible {
  outline: 2px solid var(--ow-blue);
  outline-offset: 2px;
}

/* Filament bọc chữ của link trong <span class="text-custom-600"> (primary =
   amber), nên đặt màu ở <a> là vô hiệu — chữ vẫn cam. Ép con kế thừa. */
.ow-table .fi-link > * {
  color: inherit;
}

/* ---------- Xem trước mã QR tài khoản (Cài đặt → Nhận học phí) ---------- */

.ow-qr-preview {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  align-items: flex-start;
  padding: 14px;
  border: 1px solid var(--ow-line);
  border-radius: var(--ow-radius-sm);
  background: var(--ow-card);
}

.ow-qr-preview__img {
  width: 150px;
  height: 150px;
  border-radius: 6px;
  background: #ffffff;
}

.ow-qr-preview__meta {
  flex: 1 1 220px;
  min-width: 0;
  font-size: 0.8125rem;
  color: var(--ow-ink);
}

.ow-qr-preview__bank {
  font-weight: 700;
  margin-bottom: 6px;
}

.ow-qr-preview__row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 3px 0;
}

.ow-qr-preview__row span {
  color: var(--ow-muted);
}

.ow-qr-preview__note {
  margin: 8px 0 0;
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--ow-muted);
}

/* ---------- Hộp mã QR của một em (màn Phiếu học phí) ---------- */

.ow-row-actions {
  display: inline-flex;
  align-items: center;
  gap: 12px;
}

.ow-qr-modal {
  position: fixed;
  inset: 0;
  /* 70 > 60: sidebar của panel là position:sticky + z-index:60 nên nó tự tạo
     stacking context và ĐÈ lớp phủ z-50 — đo bằng elementFromPoint thấy bấm ra
     ngoài hộp lại trúng nút sidebar. 70 mới nằm trên. */
  z-index: 70;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px 16px;
  background: rgba(15, 23, 42, 0.65);
  backdrop-filter: blur(2px);
  overflow-y: auto;
}

/* Thẻ phiếu học phí hiển thị dạng popup, đồng bộ 1:1 với mẫu PDF gửi Zalo */
.ow-qr-modal__card {
  width: 100%;
  max-width: 440px;
  margin: auto;
  background: #ffffff;
  border: 1.5px solid #fbcfe8;
  border-radius: 16px;
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.25), 0 8px 10px -6px rgba(0, 0, 0, 0.2);
  overflow: hidden;
  position: relative;
  font-family: inherit;
  color: #1f2937;
  animation: ow-modal-pop 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes ow-modal-pop {
  from { opacity: 0; transform: scale(0.96); }
  to { opacity: 1; transform: scale(1); }
}

/* Banner hồng đào pastel */
.ow-card-banner {
  background: linear-gradient(135deg, #f472b6 0%, #ec4899 100%);
  color: #ffffff;
  text-align: center;
  padding: 16px 20px 14px;
  position: relative;
  border-bottom: 2px solid #db2777;
}

.ow-card-banner__close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.25);
  border: 1px solid rgba(255, 255, 255, 0.45);
  color: #ffffff;
  font-size: 0.9375rem /* 15px */;
  font-weight: 700;
  cursor: pointer;
  transition: all 0.15s ease;
  line-height: 1;
}

.ow-card-banner__close:hover {
  background: rgba(255, 255, 255, 0.45);
  transform: scale(1.08);
}

.ow-card-banner__close:focus-visible {
  outline: 2px solid #ffffff;
  outline-offset: 2px;
}

.ow-card-banner__center {
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #fdf2f8;
  margin-bottom: 3px;
}

.ow-card-banner__class {
  font-size: 0.8125rem;
  font-weight: 700;
  color: #ffffff;
  margin-bottom: 3px;
}

.ow-card-banner__heading {
  font-size: 1.125rem;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  margin: 0;
  color: #ffffff;
}

.ow-card-banner__month {
  font-size: 0.75rem;
  color: #fdf2f8;
  margin-top: 3px;
  font-style: italic;
}

.ow-card-body {
  padding: 16px 20px;
}

/* Bảng thông tin học sinh */
.ow-card-info {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 12px;
  font-size: 0.8125rem;
}

.ow-card-info td {
  padding: 3px 0;
  vertical-align: middle;
}

.ow-card-info__label {
  color: #6b7280;
  width: 120px;
}

.ow-card-info__val {
  color: #111827;
  text-align: right;
  font-weight: 600;
}

/* Khung Tổng học phí */
.ow-card-total {
  border: 1.5px solid #f472b6;
  background: #fdf2f8;
  border-radius: 10px;
  padding: 10px 14px;
  text-align: center;
  margin-bottom: 12px;
}

.ow-card-total__label {
  font-size: 0.75rem;
  font-weight: 700;
  color: #be185d;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 2px;
}

.ow-card-total__amount {
  font-size: 1.375rem;
  font-weight: 800;
  color: #be185d;
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
}

.ow-card-total__breakdown {
  font-size: 0.6875rem;
  color: #9d174d;
  margin-top: 4px;
}

/* Ngày đi học */
.ow-card-section-title {
  text-align: center;
  font-size: 0.6875rem;
  font-weight: 700;
  color: #831843;
  letter-spacing: 0.05em;
  margin: 10px 0 6px;
  text-transform: uppercase;
}

.ow-card-dates {
  text-align: center;
  margin-bottom: 12px;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
}

.ow-card-date-pill {
  display: inline-block;
  background: #f1f5f9;
  color: #334155;
  border: 1px solid #cbd5e1;
  border-radius: 6px;
  padding: 3px 8px;
  font-size: 0.6875rem;
  font-weight: 700;
}

/* Nhận xét */
.ow-card-notes {
  background: #fff9fa;
  border: 1px solid #fbcfe8;
  border-radius: 8px;
  padding: 8px 12px;
  margin-bottom: 12px;
  font-size: 0.75rem;
  color: #374151;
  line-height: 1.45;
  text-align: left;
}

/* Khung QR mã thanh toán — phong cách 2 cột chống slop, sang trọng */
.ow-card-qr {
  border: 1px solid #e2e8f0;
  background: #f8fafc;
  border-radius: 12px;
  padding: 14px 16px;
  margin-bottom: 12px;
}

.ow-card-qr__grid {
  display: flex;
  align-items: center;
  gap: 16px;
  text-align: left;
}

.ow-card-qr__col-code {
  flex-shrink: 0;
  text-align: center;
}

.ow-card-qr__frame {
  display: inline-block;
  background: #ffffff;
  border: 1px solid #cbd5e1;
  border-radius: 8px;
  padding: 8px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}

.ow-card-qr__img {
  width: 140px;
  height: 140px;
  display: block;
}

.ow-card-qr__sub {
  font-size: 0.6875rem;
  color: #64748b;
  margin-top: 4px;
}

.ow-card-qr__col-info {
  flex: 1;
  min-width: 0;
}

.ow-card-qr__title {
  font-size: 0.725rem;
  font-weight: 800;
  color: #64748b;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 4px;
}

.ow-card-qr__bank {
  font-size: 0.9375rem;
  color: #0f172a;
  font-weight: 800;
  line-height: 1.3;
}

.ow-card-qr__holder {
  font-size: 0.8125rem;
  color: #475569;
  font-weight: 600;
  text-transform: uppercase;
  margin-bottom: 8px;
}

.ow-card-qr__memo-label {
  font-size: 0.725rem;
  color: #64748b;
  margin-bottom: 3px;
}

.ow-card-qr__memo-pill {
  display: inline-block;
  background: #eff6ff;
  border: 1px solid #bfdbfe;
  color: #1d4ed8;
  font-weight: 700;
  font-size: 0.875rem;
  padding: 3px 8px;
  border-radius: 6px;
  letter-spacing: 0.03em;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
}

.ow-card-qr__memo-hint {
  font-size: 0.6875rem;
  color: #94a3b8;
  margin-top: 6px;
  line-height: 1.35;
}

/* Chân phiếu */
.ow-card-foot {
  font-size: 0.6875rem;
  color: #9ca3af;
  text-align: center;
  margin-bottom: 12px;
}

/* Nút hành động trong popup */
.ow-card-actions {
  display: flex;
  gap: 10px;
  justify-content: center;
  padding-top: 8px;
  border-top: 1px solid #f3f4f6;
}

.ow-card-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 7px 16px;
  font-size: 0.8125rem;
  font-weight: 600;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.15s ease;
  text-decoration: none;
}

.ow-card-btn--primary {
  background: #4b5563;
  color: #ffffff;
  border: 1px solid #374151;
}

.ow-card-btn--primary:hover {
  background: #374151;
  color: #ffffff;
}

.ow-card-btn--secondary {
  background: #fdf2f8;
  color: #be185d;
  border: 1px solid #fbcfe8;
}

.ow-card-btn--secondary:hover {
  background: #fce7f3;
  color: #9d174d;
}

.ow-card-btn__icon {
  width: 16px;
  height: 16px;
}

.ow-qr-modal__box {
  width: 100%;
  max-width: 340px;
  padding: 16px;
  border-radius: var(--ow-radius);
  background: var(--ow-card);
  box-shadow: var(--ow-shadow);
  text-align: center;
}

.ow-qr-modal__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  text-align: left;
  margin-bottom: 10px;
}

.ow-qr-modal__name {
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--ow-ink);
}

.ow-qr-modal__amount {
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--ow-red-ink);
  font-variant-numeric: tabular-nums;
}

.ow-qr-modal__close {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  border: 1px solid var(--ow-line);
  border-radius: 8px;
  background: var(--ow-card);
  color: var(--ow-muted);
  font-size: 0.875rem;
  line-height: 1;
  cursor: pointer;
}

.ow-qr-modal__close:hover {
  color: var(--ow-ink);
  border-color: var(--ow-muted);
}

.ow-qr-modal__close:focus-visible {
  outline: 2px solid var(--ow-blue);
  outline-offset: 2px;
}

.ow-qr-modal__img {
  width: 100%;
  max-width: 300px;
  height: auto;
  background: #ffffff;
  border-radius: 8px;
}

.ow-qr-modal__note {
  margin: 10px 0 0;
  font-size: 0.75rem;
  line-height: 1.55;
  color: var(--ow-muted);
  text-align: left;
}

/* ============================================================================
   T4.95 — MỘT hệ màu cho chữ phụ và cho hành động.

   Đo trên 12 màn (Chromium thật, nền hiệu dụng): 7 cặp màu trượt WCAG AA.
   Nguyên nhân không phải "màu xấu" mà là ba bảng màu cùng tồn tại — token
   --ow-*, thang slate của Tailwind trong blade, và bảng màu mặc định của
   Filament (--success-600 = emerald, --warning-600 = orange…). Khối này kéo
   hai bảng sau về đúng shade đã đo là đạt, thay vì sửa từng màn.

   Số đo (trên #ffffff): #15803d 5,02 · #b45309 5,02 · #b91c1c 6,47 ·
   #5f6880 5,56 · #1c2440 15,26.
   ========================================================================== */

:root {
  /* Chữ/nhãn màu ngữ nghĩa: shade 600 của Filament đang quá nhạt để làm CHỮ.
     #059669 chỉ 3,77:1, #ea580c 3,56:1, #e11d48 4,04:1 — đều dưới 4,5. */
  --success-600: 21, 128, 61;
  --warning-600: 180, 83, 9;
  --danger-600: 185, 28, 28;
}

/* Icon hành động trong bảng (Xem/Sửa/Xoá): Filament tô text-gray-400 =
   #94a3b8, chỉ 2,56:1 — dưới 3:1 mà WCAG 1.4.11 đòi cho biểu tượng. Chỉ đổi
   icon xám, KHÔNG đổi --gray-400 toàn cục (placeholder cần nhạt). */
.fi-icon-btn.text-gray-400,
.fi-icon-btn.fi-color-gray {
  color: var(--ow-muted) !important;
}
.fi-icon-btn.text-gray-400:hover,
.fi-icon-btn.fi-color-gray:hover {
  color: var(--ow-ink) !important;
}

/* ---------------------------------------------------------------------------
   Lớp tiện ích thay cho thang xám Tailwind rải trong blade.
   151 class text-gray-* và text-slate-* đang dựng nên bảng xám thứ hai cạnh
   --ow-*. Dùng các class dưới đây; đổi màu chữ phụ giờ là sửa một token.
   ------------------------------------------------------------------------- */
.ow-ink { color: var(--ow-ink); }
.ow-muted { color: var(--ow-muted); }
.ow-muted-strong { color: var(--ow-muted-strong); }
.ow-accent-ink { color: var(--ow-accent-ink); }
.ow-green-ink { color: var(--ow-green-ink); }
.ow-red-ink { color: var(--ow-red-ink); }
.ow-blue-ink { color: var(--ow-blue-ink); }
.ow-line { border-color: var(--ow-line); }

/* ---------------------------------------------------------------------------
   Thang xám Tailwind → token. 151 class text-gray-* và text-slate-* nằm rải trong
   12 blade (schedule-grid, gradebook, class-score-analytics, import-students,
   my-classes, session-attendance, charge-audit-timeline…) và trong chính
   Filament. Thay vì sửa 151 chỗ (dễ vỡ layout, mỗi chỗ một ngữ cảnh), ánh xạ
   MỘT lần ở đây: từ giờ `text-gray-400` cũng đọc từ token, đổi xám phụ là đổi
   --ow-muted. Muốn dọn hẳn tên class thì làm một lượt riêng, có ảnh so sánh.
   ------------------------------------------------------------------------- */
.text-gray-950,
.text-gray-900,
.text-gray-800,
.text-gray-700 {
  color: var(--ow-ink);
}

.text-gray-600,
.text-gray-500,
.text-gray-400,
.text-slate-600,
.text-slate-500,
.text-slate-400 {
  color: var(--ow-muted);
}

/* T4.95 — bảng trong WIDGET trên mobile. Filament 3.3 không có
   stackedOnMobile() nên bảng co theo khung: ở 375px cột "Nội dung" còn 80px,
   tên học sinh xuống 3 dòng. Cho bảng cuộn ngang trong khung widget thay vì
   bóp cột — chữ giữ nguyên cỡ, không phải đọc từng từ một. Bảng của Resource
   (Học sinh) đã có layout thẻ riêng nên không bị luật này chạm tới. */
@media (max-width: 640px) {
  /* :not(.ow-table--stack) — bảng ĐÃ xếp thẻ thì không được kéo rộng nữa,
     hai luật sẽ đánh nhau và bảng vừa cuộn ngang vừa xếp thẻ. */
  .fi-wi-widget .fi-ta-table:not(.ow-table--stack) {
    min-width: 560px;
  }
}

/* T4.95 — bảng đã xếp thẻ thì KHÔNG ghim cột đầu nữa. Theme ghim cột 1
   (position:sticky + đổ bóng phải) để cuộn ngang bảng rộng; khi bảng xếp thẻ,
   ô đầu rộng đúng bằng thẻ nên bóng ghim hiện ra thành một vệt xám giữa thẻ. */
@media (max-width: 640px) {
  /* Phải đủ đặc hiệu để thắng luật ghim cột của theme
     (.fi-ta-table tbody td.fi-ta-cell:first-child) — luật đó có !important
     và (0,3,2), nên .ow-table--stack td (0,1,1) không thắng nổi. */
  .fi-ta-table.ow-table--stack tbody td.fi-ta-cell:first-child,
  .fi-ta-table.ow-table--stack thead th.fi-ta-header-cell:first-child {
    position: static !important;
    left: auto !important;
    box-shadow: none !important;
    background: transparent !important;
  }
}

/* T4.96 — ô "Gửi phụ huynh" dưới khung ghi chú lớp. Cố ý để KIỂU KHÁC nút Lưu
   (không phải nút xanh): đây là lựa chọn phạm vi phát hành, bấm nhầm là ghi chú
   nội bộ ra ngoài. */
.ow-note-panel__send {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  margin: 0.75rem 0 0.25rem;
  padding: 0.5rem 0.625rem;
  border: 1px solid var(--ow-line);
  border-radius: var(--ow-radius-sm, 10px);
  background: var(--ow-card);
  cursor: pointer;
  font-size: 0.8125rem;
  color: var(--ow-ink);
}

.ow-note-panel__send input {
  margin-top: 0.15rem;
  flex: 0 0 auto;
  accent-color: var(--ow-blue);
}

.ow-note-panel__send span {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  line-height: 1.35;
}

.ow-note-panel__send small {
  color: var(--ow-muted);
  font-size: 0.75rem;
}

.ow-note-panel__send:focus-within {
  border-color: var(--ow-blue-ink, #1d4ed8);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.18);
}

/* T4.96 — ô xem trước QR khi CHƯA cấu hình số tài khoản: cùng kích thước với
   ảnh mã để khối không nhảy, nhưng là lời nhắc chứ không phải ảnh vỡ. */
.ow-qr-preview__img.ow-qr-preview__empty {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.75rem;
  border: 1px dashed var(--ow-line);
  background: var(--ow-page, #f2f4fa);
  color: var(--ow-muted);
  font-size: 0.75rem;
  line-height: 1.45;
  text-align: center;
}

/* ---------- Khối phiếu học phí trong modal Thu tiền ---------- */

.ow-notice-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 12px;
  padding: 10px 14px;
  border: 1px solid var(--ow-line);
  border-radius: var(--ow-radius-sm);
  background: var(--ow-accent-soft);
}

.ow-notice-card__title {
  font-weight: 700;
  font-size: 0.8125rem;
  color: var(--ow-ink);
}

.ow-notice-card__period {
  font-size: 0.75rem;
  color: var(--ow-muted);
}

.ow-notice-card__link {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: 0 14px;
  border-radius: var(--ow-radius-sm);
  background: var(--ow-accent-ink);
  color: #fff;
  font-size: 0.8125rem;
  font-weight: 600;
  text-decoration: none;
}

/* ─── T4.114 — hộp "Xuất Excel học sinh": danh sách cột gọn (một cột dọc, dòng sát nhau).
   Class riêng `ow-export-cols` gắn bằng extraAttributes ở ListStudents — không đụng CheckboxList
   màn khác. Khai báo SAU luật `.fi-fo-checkbox-list` toàn cục và dùng !important vì luật đó
   cũng !important (gap 8px, label padding 4px). Mỗi dòng giữ ≥28px chiều cao. ─── */
.fi-fo-checkbox-list.ow-export-cols {
  grid-template-columns: minmax(0, 1fr) !important;
  gap: 2px !important;
}
.fi-fo-checkbox-list.ow-export-cols .fi-fo-checkbox-list-option-label {
  align-items: center !important;
  min-height: 28px;
  padding: 2px 0 !important;
}
/* Filament bọc mỗi tuỳ chọn trong div `pt-4` (16px) — chính nó làm hàng cách xa. */
.fi-fo-checkbox-list.ow-export-cols > div {
  padding-top: 0;
}
