/* CSS dùng chung cho toàn bộ site VPath — xem DESIGN_SYSTEM.md để biết quy tắc thiết kế. */

/* ---- Chống nháy trang lúc mới mở (v-cloak) ----
   Mỗi trang là 1 file HTML tĩnh, Vue dựng lại giao diện ngay trên chính khối HTML đó. Trong khoảnh
   khắc HTML đã tải xong nhưng Vue chưa chạy, trình duyệt vẽ nguyên bản thô: chữ {{ ... }} hiện ra,
   mọi khối v-if bung hết, các modal "fixed inset-0" đè kín màn hình (trang Khóa Học bung một lúc
   cả giỏ hàng, hộp chi tiết khóa và hộp "Đánh Giá & Nhận Xét") — và vì Vue chưa gắn sự kiện nên
   bấm nút đóng không ăn, phải F5. Thuộc tính v-cloak đặt trên #app giữ trang ẩn cho tới khi Vue
   dựng xong; Vue tự gỡ thuộc tính này ngay lúc mount, nên trang chỉ hiện ra khi đã đúng hình. */
[v-cloak] { display: none !important; }
/* Trong lúc #app còn ẩn, giữ nền giấy phủ kín màn hình để không lóe ra một mảng trắng. */
body:has(> [v-cloak]) { min-height: 100vh; }
/* Nền GỐC của tài liệu cũng phải là giấy kem: body mang màu qua class bg-paper, nhưng khoảnh
   khắc giữa hai trang (hoặc khi cuộn quá đà kiểu bật lò xo trên macOS/iOS) trình duyệt lộ nền
   của <html> — mặc định trắng tinh, chính là cú "nháy trắng" lâu lâu thấy khi chuyển trang
   (25/08/2026). Đặt về cùng màu giấy thì khung chờ nào lọt ra cũng lẫn vào trang. */
html { background-color: var(--paper, #FAF6EE); }

/* ---- Khối prerender (sinh bởi scripts/sinh-trang-tinh.js) ----
   Đứng TRƯỚC #app trong DOM, là bản tĩnh của nội dung vốn phải chờ JS gọi API (danh mục khóa
   học, thống kê kho đề, FAQ) — DÀNH RIÊNG cho bot/trình duyệt không chạy JS (khi đó #app bị
   v-cloak ẩn vĩnh viễn, khối tĩnh là toàn bộ trang).

   Người dùng thật KHÔNG được thấy khối này: bản thử đầu tiên cho hiện nó rồi swap sang app
   sau khi API về — chủ dự án báo ngay "trang giật giật, hiện cái gì đấy trước" (21/08).
   Cách làm hiện tại: một dòng <script> inline nằm NGAY TRÊN khối (sinh-trang-tinh.js chèn)
   chạy đồng bộ trước khi khối được vẽ, thêm class co-js vào <html> — rule dưới đây ẩn khối
   từ khung hình đầu tiên, người dùng chỉ thấy đúng luồng cũ của app (v-cloak -> app).
   Bot không chạy JS -> không có co-js -> khối hiện nguyên vẹn. */
.co-js #prerender-khoa-hoc, .co-js #prerender-kho-de, .co-js #prerender-faq { display: none !important; }

/* .reveal + .reveal.is-visible ĐÃ XOÁ 28/08/2026 (gói chuyển động, đợt 1 mục 1.10). Đã grep xác
   nhận trước khi xoá: 0 file HTML dùng, 0 file JS thêm class is-visible. Cặp luật này là CSS chết
   từ lâu, nhưng .reveal{opacity:0} nằm TRẦN trong file mà cả 35 trang đều nạp, nên ai lỡ gắn nhầm
   tên class đó là khối biến mất vĩnh viễn mà không có gì báo. Cần hiệu ứng hiện dần thì dùng cơ
   chế .hien/.ra của trang chủ (assets/css/pages/trang-chu.css), nó có IntersectionObserver thật. */

/* ---- Focus ring thống nhất toàn site (Phase 2 gói 2.4, chuẩn AA) ----
   :focus-visible = CHỈ hiện khi điều hướng bằng bàn phím (Tab), bấm chuột không nổi vòng —
   nên áp một luật chung được mà không làm bẩn giao diện chuột. Trước đây 11 chỗ tắt
   focus:outline-none mà không thay bằng gì: người dùng bàn phím mất dấu hoàn toàn.
   !important để thắng cả các utility focus:outline-none còn sót; :where giữ specificity 0
   cho phần selector nên không phá rule nào khác. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
    outline: 2px solid var(--navy-500) !important;
    outline-offset: 2px;
}
/* Trên tấm navy-900 của thẻ đội ngũ (team.html) vòng navy-500 chìm nghỉm — đổi sang gold-400.
   Phải kèm !important vì luật gốc ở trên cũng !important; specificity cao hơn nên luật này thắng. */
.the-doi-ngu a:focus-visible {
    outline-color: var(--gold-400) !important;
}
/* Người PNG tách nền đứng thẳng trên tấm navy của thẻ đội ngũ. drop-shadow bám theo ĐƯỜNG VIỀN
   alpha của ảnh (khác box-shadow bám hộp), ba lớp từ trong ra: viền sáng 1px màu giấy kem để áo
   sẫm màu không lẫn vào navy-900, bóng TIẾP XÚC gắt ngay sau lưng (8px) tạo cảm giác người đứng
   TRƯỚC tấm navy một quãng, và bóng MÔI TRƯỜNG loang xa (48px) cho khối tròn. Nâng ba lớp
   31/08 tối theo yêu cầu "trông 3D, nổi ra hơn so với text". rgba ghi thô vì var() không nhét
   được vào rgba() — giá trị là paper #FAF6EE và đen, cùng lý do o-chu-dau::before.
   z-index 1 để ảnh nổi trên hai phần tử giả của .vp-be-nguoi bên dưới. */
.vp-nguoi-noi {
    filter: drop-shadow(0 0 1px rgba(250, 246, 238, 0.5))
            drop-shadow(0 8px 12px rgba(0, 0, 0, 0.45))
            drop-shadow(0 28px 48px rgba(0, 0, 0, 0.4));
    z-index: 1;
}
/* Bệ 3D quanh người — gắn lên CỬA SỔ chứa ảnh (div overflow-hidden trong khoiDoiNgu). Cả hai
   đều là KHỐI ĐẶC làm mờ bằng blur, không phải gradient — hệ này cấm gradient trang trí nhưng
   bóng/vầng mờ là ngôn ngữ chiều sâu sẵn có (cùng họ --ao-bong).
   ::before — vầng sáng navy-500 mờ sau lưng: áo sẫm màu tách hẳn khỏi tấm navy-900, như có đèn
   hắt sau lưng. ::after — bóng chạm đất dưới chân, nửa dưới bị overflow-hidden của cửa sổ cắt
   nên chỉ còn vệt tối áp sát mép đáy thẻ, đọc ra là người đứng CÓ MẶT SÀN chứ không lơ lửng. */
.vp-be-nguoi::before {
    content: "";
    position: absolute;
    left: 50%;
    bottom: 6%;
    width: 58%;
    aspect-ratio: 1;
    transform: translateX(-50%);
    border-radius: 50%;
    background: var(--navy-500, #2C4A6E);
    opacity: 0.4;
    filter: blur(36px);
}
.vp-be-nguoi::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: -12px;
    width: 78%;
    height: 26px;
    transform: translateX(-50%);
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.55);
    filter: blur(10px);
}

/* ---- Toast (assets/js/thong-bao.js) — trượt vào từ mép phải, mờ dần khi đóng ---- */
.vp-toast-vao { animation: vp-toast-vao 0.22s ease-out; }
.vp-toast-ra { opacity: 0; transform: translateX(12px); transition: opacity 0.18s ease-in, transform 0.18s ease-in; }
@keyframes vp-toast-vao {
    from { opacity: 0; transform: translateX(16px); }
    to { opacity: 1; transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce) {
    .vp-toast-vao { animation: none; }
    .vp-toast-ra { transition: none; }
}

.label { letter-spacing: 0.1em; }

/* Gạch chân tay vẽ cho từ khóa trong H1 — thay cho gradient-text kiểu SaaS */
.hand-underline { position: relative; white-space: nowrap; }
.hand-underline::after {
    content: '';
    position: absolute;
    left: -2px; right: -4px; bottom: 0.02em;
    height: 0.3em;
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 16' preserveAspectRatio='none'%3E%3Cpath d='M2 11 C 40 4, 80 14, 100 8 S 160 3, 198 9' stroke='%23C08A2E' stroke-width='5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat center / 100% 100%;
    z-index: -1;
}

/* Vân chấm mờ cho khối nền tối, thay cho blob gradient */
.grain-dark {
    background-image: radial-gradient(rgba(255,255,255,0.06) 1px, transparent 1px);
    background-size: 18px 18px;
}

/* ---- Hoa văn giấy kẻ ô (hạng mục T1, sandbox/ban-chi-dan-hinh-anh.md mục 5.1) 28/08/2026 ----
   Một file duy nhất assets/anh/hoa-van/ke-o.svg dùng cho cả ba chỗ: nền bìa khóa học (navy), ô
   chữ cái đầu (navy) và dải nền lớn (giấy kem). Nó là thứ khiến ba thứ đó đọc ra là cùng một bộ.

   CƠ CHẾ: mask-image + background-color, KHÔNG phải background-image. Vì sao chọn cách này:
   một file SVG nạp qua url() là một TÀI LIỆU RIÊNG, nó không kế thừa currentColor cũng không đọc
   được var() của trang (đúng cái bẫy đã ghi ở assets/css/pages/gia-su.css dòng 100). Nạp bằng
   background-image thì nét kẻ chết cứng một màu, tức phải nuôi hai file hoặc hai data URI: một
   bản nét trắng cho nền navy, một bản nét navy cho nền giấy kem. Nuôi hai bản là nuôi hai nguồn
   sự thật cho cùng một hoa văn, sửa một bản quên bản kia là bộ lệch nhau.
   Dùng mask thì trình duyệt chỉ đọc KÊNH ALPHA của file, còn màu do background-color của chính
   phần tử quyết định. Đặt background-color: currentColor thì hoa văn tự đi theo màu chữ: nằm
   trong khối navy chữ trắng thì nét trắng, nằm trên nền giấy kem chữ navy thì nét navy. Một file,
   hai nền, không có bản sao nào để lệch.
   ĐÁNH ĐỔI ĐÃ BIẾT: (1) mask cắt luôn cả nội dung con của phần tử, nên class này CHỈ được đặt lên
   một LỚP PHỦ RỖNG, tuyệt đối không đặt lên khối có chữ bên trong (khác .grain-dark ở trên, cái
   đó dùng background-image nên đặt đâu cũng được). Có sẵn biến thể --phu bên dưới để khỏi làm sai.
   (2) đường parabol vàng trong file bị mask kéo về đúng màu nét kẻ. Đây lại là điều ĐÚNG theo luật
   "mỗi hình chỉ một điểm vàng" của DESIGN_SYSTEM.md: trên bìa khóa học điểm vàng đã là dải gold
   dưới đáy, trong ô chữ cái đầu điểm vàng đã là chữ cái, nên hoa văn không được giành thêm. Màu
   vàng trong file chỉ sống khi mở file trực tiếp hoặc nhúng bằng thẻ img.

   BA BIẾN THỂ ĐỘ MỜ: độ mờ cuối = độ mờ nét ghi trong file NHÂN alpha của background-color. File
   ghi sẵn nét nhạt 0,10 nét đậm 0,18 parabol 0,22, tức mức nền (alpha 100%) đã đúng bằng 0,10 mà
   mục 5.1 đòi cho ô rỗng. Hai biến thể kia chỉ là hạ alpha: 60% ra 0,06 cho bìa khóa học, 40% ra
   0,04 cho dải nền lớn. Viết background-color: currentColor một lần ở luật gốc để trình duyệt nào
   chưa có color-mix vẫn hiện ra hoa văn ở mức nền thay vì mất trắng. */
.hoa-van-ke-o,
.o-chu-dau::after {
    -webkit-mask-image: url("../anh/hoa-van/ke-o.svg");
            mask-image: url("../anh/hoa-van/ke-o.svg");
    /* Cỡ ghi tuyệt đối đúng bằng khổ file: ô kẻ phải luôn là 24px thật, không được co giãn theo
       kích thước phần tử, nếu không thì ô kẻ trên bìa 1200px và ô kẻ trong ô 84px ra hai cỡ khác
       nhau và cả bộ hết đồng nhất. */
    -webkit-mask-size: 480px 360px;
            mask-size: 480px 360px;
    -webkit-mask-repeat: repeat;
            mask-repeat: repeat;
    -webkit-mask-position: 0 0;
            mask-position: 0 0;
    background-color: currentColor;
}
.hoa-van-ke-o--bia { background-color: color-mix(in srgb, currentColor 60%, transparent); }
.hoa-van-ke-o--dai { background-color: color-mix(in srgb, currentColor 40%, transparent); }
/* Lớp phủ dựng sẵn: dùng cái này khi cần rải hoa văn lên một khối đã có nội dung. Khối cha phải
   là position:relative. Đặt hoa văn lên một lớp rỗng riêng là bắt buộc, xem đánh đổi (1) ở trên. */
.hoa-van-ke-o--phu { position: absolute; inset: 0; pointer-events: none; }

/* Tông ảnh đồng nhất theo màu navy — thay cho ảnh stock rời rạc mỗi ảnh một tông màu khác nhau.
   Cách dùng: bọc <img> trong 1 container "relative" có thêm class "brand-photo".

   HẠ TAY 28/08/2026 (mục 6.1 của sandbox/ban-chi-dan-hinh-anh.md). Bản cũ là grayscale(30%) cộng
   một lớp phủ navy-700 blend color độ mờ 0,28. Nó sinh ra để cứu một bộ ảnh stock hỗn tạp mỗi tấm
   một tông, và với bộ ảnh đó nó đúng. Nhưng bộ hình mới do VPath tự làm đã đúng bảng màu từ lúc
   vẽ, nên hai lớp đó quay ra phá: grayscale kéo gold #C08A2E về xám, còn lớp phủ blend color thì
   thay HẲN sắc độ của mọi điểm ảnh bằng sắc navy, tức nó xóa nốt phần vàng còn sót. Đúng một
   điểm vàng là toàn bộ ngôn ngữ của bộ hình này, mất nó là mất bộ.
   Nay hạ xuống mức chỉ còn "kéo về cùng một tông": grayscale 12% đủ dịu những ảnh chụp thật hơi
   rực, lớp phủ 0,10 đủ giữ mạch navy mà không nuốt màu vàng.
   GIỮ NGUYÊN TÊN LỚP, không đổi thành tên khác: có đúng 10 chỗ đang gọi nó (9 chỗ trong
   courses.html và scripts/sinh-trang-tinh.js dòng 470). Việc gỡ class khỏi những chỗ không còn
   cần (bìa khóa học tự sinh đã đúng tông sẵn) là việc của người sửa markup, không phải việc của
   file này. contrast(1.05) giữ nguyên vì nó không đụng tới sắc độ, chỉ tăng tương phản. */
.brand-photo img { filter: grayscale(12%) contrast(1.05); }
.brand-photo::after {
    content: '';
    position: absolute; inset: 0;
    background: var(--navy-700);
    mix-blend-mode: color;
    opacity: 0.10;
    pointer-events: none;
}

/* ---- Ô CHỮ CÁI ĐẦU dùng chung (hạng mục T3, ban-chi-dan-hinh-anh.md mục 5.3) 28/08/2026 ----
   VÌ SAO CÓ LỚP NÀY: chỗ nào chưa có ảnh chân dung thì hiện một ô mang chữ cái đầu của tên. Bản
   cũ ở mỗi trang một kiểu và đều là nền navy-50 chữ navy-700, nhạt tới mức người xem đọc ra là
   "ô đang chờ tải ảnh" chứ không phải một quyết định. Mà VPath chưa có gia sư thật và khối vinh
   danh thì vĩnh viễn không đăng ảnh học sinh (mục 7.1), nên ô này là phương án CHÍNH dùng lâu
   dài, không phải phương án dự phòng vài ngày. Nó phải trông có chủ ý.

   CẤU TẠO: nền navy-900 đặc, chữ cái gold-400 đậm 700, một vành gold-500 mờ 0,35 lùi vào 3px, và
   hoa văn T1 phủ mờ 0,10. Chữ cái là ĐIỂM VÀNG DUY NHẤT của ô, nên hoa văn phải là màu giấy kem
   chứ không phải màu vàng (xem luật một điểm vàng trong DESIGN_SYSTEM.md).

   LỚP NÀY KHÔNG ĐẶT CỠ. Cỡ là chuyện bố cục của từng trang: trang chủ đặt 84/66/46 trong
   .tc .chu-dau, trang tài khoản đặt 40 bằng w-10 h-10 của Tailwind, trang gia sư đặt 160/128/96
   trong .tutor-card__photo. Nếu lớp dùng chung cũng đặt cỡ thì nó đánh nhau với ba chỗ đó. Ai cần
   cỡ dựng sẵn thì lấy năm biến thể --160 --84 --66 --46 --40 bên dưới, cỡ chữ trong đó đã tính
   sẵn đúng 42 phần trăm đường kính như mục 5.3 đòi.

   BA BẬC HÌNH THỨC THEO CỠ, vì hoa văn thu nhỏ quá thì không còn đọc ra là giấy kẻ ô mà thành
   vệt xước trên mặt navy:
     từ 84px trở lên  : giữ đủ vành và hoa văn (chính là luật gốc dưới đây)
     quanh 66px       : bỏ hoa văn, giữ vành và chữ            -> --gon (hoặc --66)
     46px và nhỏ hơn  : chỉ nền navy, vành mảnh 1px và chữ     -> --nho (hoặc --46 --40)
   Tách bậc hình thức khỏi bậc cỡ là để trang nào tự đặt cỡ vẫn chọn được đúng bậc: account.html
   đặt cỡ bằng w-10 h-10 rồi gắn thêm --nho. */
.o-chu-dau {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    /* flex:none để ô không bị bóp méo khi nằm trong một hàng flex chật */
    flex: none;
    border-radius: 50%;
    overflow: hidden;
    background-color: var(--navy-900, #0D1F33);
    color: var(--gold-400, #D1A544);
    font-weight: 700;
    line-height: 1;
}
/* Vành trong. Dùng phần tử giả chứ không dùng box-shadow inset vì spec đòi vành LÙI VÀO 3px so
   với mép chứ không nằm sát mép. rgba() ghi thô vì var() không nhét được vào trong rgba(); giá
   trị là gold-500 #C08A2E ở độ mờ 0,35. border-radius:inherit để vành đồng tâm với ô, kể cả khi
   ô là bản vuông bo góc. */
.o-chu-dau::before {
    content: "";
    position: absolute;
    inset: 3px;
    border: 1.5px solid rgba(192, 138, 46, 0.35);
    border-radius: inherit;
    pointer-events: none;
}
/* Hoa văn T1. Phần mask nằm chung luật với .hoa-van-ke-o ở khối trên để chỉ có MỘT chỗ khai cơ
   chế; ở đây chỉ thêm phần dựng lớp phủ và màu nét. color đặt thành giấy kem để background-color:
   currentColor của luật kia lấy đúng màu đó (không lấy màu vàng của chữ cái).
   Hai phần tử giả này nằm ĐÈ LÊN chữ cái theo thứ tự vẽ của phần tử được định vị. Chấp nhận được
   vì nét kẻ chỉ mờ 0,10 và vành thì lùi hẳn vào mép, còn chữ cái cỡ 42 phần trăm nằm gọn giữa ô. */
.o-chu-dau::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    color: var(--paper, #FAF6EE);
}
/* Bậc 66px và bậc nhỏ: tắt hẳn hoa văn bằng content:none (gỡ luôn phần tử giả, không chỉ giấu). */
.o-chu-dau--gon::after,
.o-chu-dau--66::after,
.o-chu-dau--nho::after,
.o-chu-dau--46::after,
.o-chu-dau--40::after { content: none; }
/* Bậc nhỏ: vành mảnh lại còn 1px và lùi vào 2px, vì trên ô 40px thì vành 1,5px lùi 3px chiếm gần
   một phần tư đường kính, nhìn ra là hai vòng tròn lồng nhau chứ không phải một chi tiết. */
.o-chu-dau--nho::before,
.o-chu-dau--46::before,
.o-chu-dau--40::before { inset: 2px; border-width: 1px; }
/* Năm cỡ dựng sẵn. Cỡ chữ = 42 phần trăm đường kính, ghi số tuyệt đối chứ không dùng em vì cỡ chữ
   gốc của khối cha không cố định. */
.o-chu-dau--160 { width: 160px; height: 160px; font-size: 67px; }
.o-chu-dau--84  { width: 84px;  height: 84px;  font-size: 35px; }
.o-chu-dau--66  { width: 66px;  height: 66px;  font-size: 28px; }
.o-chu-dau--46  { width: 46px;  height: 46px;  font-size: 19px; }
.o-chu-dau--40  { width: 40px;  height: 40px;  font-size: 17px; }
/* Bản vuông cho trang gia sư: ảnh đại diện thật ở đó bo 14px theo áo mới 25/08, nên ô chữ phải
   cùng dáng, nếu không một danh sách vừa có gia sư đã có ảnh vừa có gia sư chưa có ảnh sẽ so le
   hai kiểu bo góc. Vành trong bo 11px = 14px trừ 3px lùi vào, để hai đường bo đồng tâm. */
.o-chu-dau--vuong { border-radius: var(--ao-r-nho, 14px); }
.o-chu-dau--vuong::before { border-radius: 11px; }

/* Hai hiệu ứng linh vật (mascot-bounce, mascot-point) ĐÃ XOÁ 28/08/2026 (gói chuyển động, đợt 1
   mục 1.10). Đã grep xác nhận trước khi xoá: 0 file HTML/JS nào gắn hai class đó. Chúng là hai
   animation lặp VÔ HẠN duy nhất còn sót trong file dùng chung, mà nội dung tự chuyển động quá 5
   giây phải có cơ chế dừng ngay trong trang theo WCAG 2.2.2 mức A. Không dùng thì không giữ.
   Cần lại thì lấy ở mốc v168. */

/* =====================================================================================
   ÁO MỚI 25/08/2026 — áp NGÔN NGỮ THỊ GIÁC CỦA TRANG CHỦ lên toàn bộ site.
   Chủ dự án chốt 25/08: cuốn toàn bộ website theo hướng trang chủ v3 (bo góc 14-26px, nút
   viên thuốc, nền trắng + dải ấm/lạnh, bóng mềm, tiêu đề đậm 800 âm tracking) — chính là
   "đề xuất đang treo" ghi trong DESIGN_SYSTEM.md, nay không còn treo nữa.

   CÁCH LÀM: đè bằng CSS tại đây thay vì sửa class trong 34 file HTML. tailwind.css build ra
   hex cứng nhưng nạp TRƯỚC theme.css, nên override cùng specificity ở đây thắng theo cascade.
   Đổi lại phải chấp nhận: tên class không còn tả đúng giá trị (.rounded-sm giờ tròn 14px).
   Đó là cái giá của một diff nhỏ đảo ngược được — muốn "sạch tên" thì đổi markup 34 trang.

   Phạm vi: MỌI trang trừ thân trang chủ (.tc tự quản, đừng đụng — xem NGOẠI LỆ TRANG CHỦ
   trong DESIGN_SYSTEM.md). Các selector .nut/.o-nhap ở đây thua .tc .nut về specificity nên
   không rò vào trang chủ.
   ===================================================================================== */

:root{
    /* TONE KEM 25/08/2026 (chủ dự án chốt sau khi xem bản trắng): giữ nguyên HÌNH KHỐI của áo
       mới (bo tròn, viên thuốc, bóng mềm) nhưng trả MÀU NỀN về tone kem vàng như hệ cũ.
       Hai giá trị dưới đây KHÔNG phải màu chế thêm — đúng hai token paper/paper.dim đã có sẵn
       trong tailwind.config.js, nên vẫn giữ luật "một nguồn màu duy nhất" của DESIGN_SYSTEM.
       Trước đó cặp này là #FBF8F2 / #F4F7FC (ấm-gần-trắng và lạnh) để đi với nền trắng. */
    --ao-vien:#E8E6E1; --ao-nen-am:#F2EBDC; --ao-nen-lanh:#FAF6EE;
    --ao-chu:#3F4A56; --ao-chu-nhat:#6B7684;
    --ao-bong:0 2px 4px rgba(13,31,51,.04), 0 12px 28px rgba(13,31,51,.07);
    --ao-bong-cao:0 8px 16px rgba(13,31,51,.07), 0 24px 48px rgba(13,31,51,.13);
    --ao-r-the:16px; --ao-r-nho:14px; --ao-r-vien-thuoc:999px;
    --ao-ease:cubic-bezier(.22,.61,.36,1);

    /* NHỊP CHUYỂN ĐỘNG 28/08/2026 (gói chuyển động, đợt 1 mục 1.1): nguồn duy nhất của thời
       lượng, đúng như tailwind.config.js là nguồn duy nhất của màu. Trước bản này mọi thời lượng
       viết cứng rải rác năm chỗ (.18s ở nút, .2s ở thẻ, .22s ở toast, .55s ở trang chủ, .12s ở
       chuyển trang) nên muốn chỉnh nhịp cả site phải đi sửa từng nơi.
         --ao-nhanh  đổi màu chữ/viền/nền: link, chip, tab, ô nhập. Phản hồi phải tức thì.
         --ao-vua    thẻ, dropdown, modal, gạch chân vàng, nhịp chấm câu.
         --ao-cham   TRẦN TRÊN, không hạng mục nào được vượt. Chỉ dùng cho hiện dần khi cuộn.
       Hai con số cứng đi kèm, đừng phá: nhấc thẻ đúng -3px, nhấc nút đúng -2px. Và không một
       @keyframes mới nào được mang `infinite` trừ nhịp mờ của khung chờ. */
    --ao-nhanh:150ms; --ao-vua:220ms; --ao-cham:420ms;
    /* Chỉ dành cho hiệu ứng XUẤT HIỆN: vọt vào nhanh rồi dừng êm. Khác --ao-ease ở đuôi. */
    --ao-ease-vao:cubic-bezier(.16,1,.3,1);
}

/* ---- Nền: GIỮ NGUYÊN tone kem của hệ cũ ----
   Bản đầu của áo mới đổi .bg-paper thành trắng và .bg-paper-dim thành #FBF8F2 để chép y hệt nền
   trang chủ. Chủ dự án xem xong chốt 25/08: hình khối thì giữ, nhưng tone phải là kem vàng như
   cũ. Nên ba luật remap nền ở đây đã được GỠ BỎ — .bg-paper (#FAF6EE), .bg-paper-dim (#F2EBDC)
   và .bg-paper/95 nay chạy thẳng giá trị gốc của tailwind.config.js, không ai đè nữa.
   ĐỪNG remap lại chúng: thẻ trắng + bóng mềm của áo mới nổi lên nền kem là đúng chủ ý, đó chính
   là thứ tạo ra chiều sâu mà bản nền-trắng không có (thẻ trắng trên nền trắng phải dựa hết vào
   bóng). Riêng thân trang chủ .tc vẫn tự quản nền trắng của nó — xem NGOẠI LỆ TRANG CHỦ. */

/* ---- Viền: stone -> màu viền ấm của trang chủ ---- */
.border-stone-300{border-color:var(--ao-vien)}
.border-stone-300\/70{border-color:var(--ao-vien)}
.border-stone-200{border-color:#EFEDE8}
.border-stone-200\/80{border-color:#EFEDE8}

/* Biến thể hover: của hai nền vừa remap — không đè thì lúc rê chuột màu bật về xám lạnh
   tailwind gốc, thoát khỏi bộ dải ấm/lạnh (lượt rà 25/08 chỉ ra ở bang-gia, dashboard, account) */
.hover\:bg-stone-100:hover{background-color:var(--ao-nen-lanh)}
.hover\:bg-stone-200:hover{background-color:#EFEDE8}

/* ---- Bo góc: hộp/thẻ tròn 14px, nút và huy hiệu thành viên thuốc ----
   Tách theo PHẦN TỬ vì cùng một class .rounded-sm đang gánh hai vai:
   khối chứa (div/section...) là "thẻ", còn a/button/span nhỏ là "nút/chip". */
div.rounded-sm, section.rounded-sm, article.rounded-sm, aside.rounded-sm, ul.rounded-sm,
ol.rounded-sm, li.rounded-sm, form.rounded-sm, table.rounded-sm, img.rounded-sm, figure.rounded-sm,
iframe.rounded-sm, video.rounded-sm, details.rounded-sm, nav.rounded-sm, p.rounded-sm,
blockquote.rounded-sm{border-radius:var(--ao-r-nho)}
a.rounded-sm, button.rounded-sm, span.rounded-sm, label.rounded-sm, summary.rounded-sm,
small.rounded-sm, strong.rounded-sm, b.rounded-sm, em.rounded-sm, i.rounded-sm,
code.rounded-sm, time.rounded-sm, dt.rounded-sm, dd.rounded-sm{border-radius:var(--ao-r-vien-thuoc)}
/* Ô nhập liệu: tròn 11px như form trang chủ, KHÔNG viên thuốc (chữ dài bị cắt xấu) */
input.rounded-sm, select.rounded-sm, textarea.rounded-sm{border-radius:11px}

/* ---- Thẻ trắng có viền: bo tròn + bóng mềm; là link thì nổi lên khi rê chuột ----
   Hệ cũ cố ý KHÔNG gắn class bo góc cho thẻ lớn (góc sắc là chuẩn cũ), nên không thể đè qua
   .rounded-sm — phải bắt thẳng cặp class "thẻ" (.bg-white.border / .border-2). */
.bg-white.border, .bg-white.border-2{border-radius:var(--ao-r-the);box-shadow:var(--ao-bong)}
/* NGOẠI LỆ của luật thẻ (lượt rà 25/08 bắt được): chip/nút/badge trắng có viền — a/button/span
   MANG SẴN rounded-sm — bị luật thẻ 0-2-0 thắng nhầm luật viên thuốc 0-1-1, thành hộp 16px có
   bóng (chip tài khoản trên header MỌI trang, badge "Mã môn" trang khoa-hoc, chip chủ đề
   prerender practice). Selector 3 class (0-3-1) đòi lại đúng vai chip, và tắt luôn hiệu ứng
   thẻ-nhấc-lên cho chúng — chip không phải thẻ. */
a.rounded-sm.bg-white, button.rounded-sm.bg-white, span.rounded-sm.bg-white{
    border-radius:var(--ao-r-vien-thuoc);box-shadow:none;
}
a.rounded-sm.bg-white:hover{transform:none;box-shadow:none;border-color:var(--navy-500,#2C4A6E)}
a.bg-white.border{transition:transform var(--ao-vua) var(--ao-ease),box-shadow var(--ao-vua) var(--ao-ease),border-color var(--ao-nhanh)}
/* border-color đổi từ transparent sang gold-500 (luật V6 của khối VÀNG DẪN MẮT cuối file):
   29 thẻ-link khi rê chuột nay có viền vàng thay vì mất viền. Không dùng inset box-shadow vì 8
   thẻ khóa học có ảnh bìa h-40 overflow-hidden sát mép trên sẽ che mất vạch inset.

   TÁCH LÀM HAI 28/08/2026 (gói chuyển động, đợt 1 mục 1.3). Trước bản này cả luật nằm chung, mà
   trình duyệt cảm ứng giả lập :hover sau mỗi cú chạm rồi GIỮ NGUYÊN trạng thái đó tới khi chạm
   chỗ khác, nên trên điện thoại thẻ bị nhấc lên 3px rồi treo lơ lửng. Phần ĐỔI MÀU để ngoài
   (máy cảm ứng vẫn được phản hồi, và màu thì không treo hình), phần TRANSFORM vào trong khối
   @media. Đây là khối @media (hover:hover) đầu tiên của dự án; toàn bộ assets/css trước 28/08 có
   đúng 0 khối, xem mục 1.3 trong sandbox/ke-hoach-chuyen-dong.md. */
a.bg-white.border:hover{border-color:var(--gold-500,#C08A2E)}
@media (hover: hover) and (pointer: fine){
    a.bg-white.border:hover{transform:translateY(-3px);box-shadow:var(--ao-bong-cao)}
}

/* ---- Chữ: tiêu đề đậm 800, tracking âm — và hero bỏ serif Lora, về Jakarta 800
   đúng h1 trang chủ (trang chủ không dùng Lora cho tiêu đề). ---- */
h1{font-weight:800;letter-spacing:-.028em}
h2{font-weight:800;letter-spacing:-.024em}
h3{letter-spacing:-.012em}
h1.font-serif, h2.font-serif, .font-serif.text-4xl, .font-serif.text-5xl{
    font-family:"Plus Jakarta Sans",system-ui,sans-serif;font-weight:800;
}

/* ---- Nút component (.nut, gói 2.2): sang viên thuốc + nhấc lên khi rê, đúng .nut trang chủ.
   Màu giữ nguyên hệ navy/gold; nut--vang đổi chữ navy -> TRẮNG theo .nut--chinh trang chủ. ---- */
.nut{
    border-radius:var(--ao-r-vien-thuoc);font-weight:800;
    transition:transform .18s var(--ao-ease),box-shadow .18s var(--ao-ease),
               background-color .18s,border-color .18s,color .18s;
}
.nut--vang{color:#fff;box-shadow:0 6px 18px rgba(192,138,46,.32)}
.nut--vien{border-color:var(--ao-vien)}
/* Phần ĐỔI MÀU của hover: để ngoài khối @media để máy cảm ứng vẫn có phản hồi khi chạm. */
.nut--vien:hover:not(:disabled){border-color:var(--navy-500,#2C4A6E)}
/* Phần NHẤC LÊN: chỉ cho máy có chuột thật. Xem chú thích dài ở a.bg-white.border phía trên. */
@media (hover: hover) and (pointer: fine){
    .nut--vang:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 10px 26px rgba(192,138,46,.42)}
    .nut--navy:hover:not(:disabled){transform:translateY(-2px)}
    .nut--vien:hover:not(:disabled){transform:translateY(-2px)}
}

/* ---- Ô nhập component (.o-nhap): nền trắng, viền ấm, tròn 11px như form trang chủ ---- */
.o-nhap{background-color:#fff;border-color:var(--ao-vien);border-radius:11px}

/* ---- Thẻ chọn bấm được (.the-chon), 29/08/2026 ----
   Một Ô LỰA CHỌN cỡ thẻ: có icon, tiêu đề và câu mô tả, bấm để chọn. Lần đầu dùng là ô "Tôi là
   học sinh / Tôi là gia sư" ở form đăng ký; cổng gia sư sắp tới còn cần nữa.

   VÌ SAO PHẢI CÓ TÊN RIÊNG chứ không dùng rounded-sm: luật bo góc phía trên tách theo PHẦN TỬ,
   nên button.rounded-sm thành viên thuốc 999px. Đúng cho nút và chip, nhưng ô lựa chọn cỡ thẻ
   thì thành hình bầu dục, chữ mô tả bị hai đầu cong cắt vào. Đã nhìn thấy tận mắt ở bản đầu của
   form đăng ký hai lựa chọn.

   Bán kính lấy thẳng --ao-r-the, cùng con số với mọi thẻ khác, nên vẫn đúng luật "một nguồn duy
   nhất" của DESIGN_SYSTEM.md. Không tự chế hex, không tự chế px.

   Trạng thái ĐANG CHỌN do trang tự gắn qua :class (viền navy + vòng sáng), không nhốt vào đây:
   mỗi trang có màu nhấn riêng, còn hình khối thì dùng chung. */
button.the-chon, label.the-chon, a.the-chon{
    border-radius:var(--ao-r-the);
    box-shadow:none;
    transition:border-color var(--ao-nhanh) var(--ao-ease),
               box-shadow var(--ao-nhanh) var(--ao-ease),
               transform var(--ao-nhanh) var(--ao-ease);
}
@media (hover: hover) and (pointer: fine){
    button.the-chon:hover, label.the-chon:hover, a.the-chon:hover{ transform:translateY(-2px) }
}

/* ---- Modal + toast: khung tròn, bóng cao ---- */
.vp-modal-khung{border-radius:var(--ao-r-the);box-shadow:var(--ao-bong-cao)}

/* ---- bg-stone-100 (xám LẠNH của tailwind) kéo về họ kem cho khỏi lệch tông giữa trang kem.
   Đây là màu của máng thanh tiến độ, chip nhỏ, ô trống... — để nguyên xám lạnh thì giữa một
   trang toàn kem vàng chúng hiện ra hơi xanh xám, lạc họ. ---- */
.bg-stone-100{background-color:var(--ao-nen-lanh)}

/* ---- Thanh tab (9 chỗ: admin 7, login, community, practice) về đúng mẫu .tc .tab-cap của
   trang chủ: MÁNG TRẮNG CÓ VIỀN + tab đang chọn NỀN NAVY CHỮ TRẮNG.
   Trước bản này máng là nền ấm còn tab chọn là bg-white — hồi nền ấm còn là kem đậm #F2EBDC thì
   trắng-trên-kem tách rõ, nhưng áo mới kéo nền ấm về #FBF8F2 nên hai màu gần như trùng, tab đang
   chọn đọc không ra (lượt rà 25/08 bắt được ở khu admin).

   Sửa ở CSS chứ không sửa ~30 chuỗi :class động trong HTML — vừa ít rủi ro hơn, vừa không thể bỏ
   sót chỗ nào. Cách nhận diện MÁNG: nền ấm/lạnh + rounded-full + padding ĐỀU (p-1 / p-1.5).
   Padding đều là chi tiết phân biệt sống còn — chip và thanh tiến độ cũng mang rounded-full kèm
   hai nền đó, nhưng chúng dùng px- và py- riêng lẻ hoặc không có padding, nên không lọt vào đây.
   (Đừng viết "px-<sao>/py-<sao>" trong comment CSS: cặp ký tự sao-gạch-chéo đóng comment SỚM và
   nuốt luôn luật ngay bên dưới — chính bẫy này vừa cắn ở bản nháp đầu, máng tab không đổi màu
   trong khi tab con thì đổi.) */
.rounded-full.p-1.bg-paper-dim, .rounded-full.p-1\.5.bg-paper-dim,
.rounded-full.p-1.bg-stone-100, .rounded-full.p-1\.5.bg-stone-100{
    background-color:#fff;border:1px solid var(--ao-vien);
}
/* Thanh tab CHÍNH của khu admin: 12 mục nên trượt ngang là "trượt trượt tìm rất khó" (chủ dự
   án 25/08) — gắn thêm class này cho máng để mọi mục XUỐNG DÒNG thấy hết một lượt. Bo 18px
   thay cho viên thuốc: máng hai tầng mà bo 999px thì hai đầu phình thành nửa hình tròn to.
   Thắng .overflow-x-auto và .rounded-full của tailwind nhờ theme.css nạp SAU. */
.tab-xuong-dong{flex-wrap:wrap;overflow-x:visible;border-radius:18px}

/* Tab đang chọn = con TRỰC TIẾP mang .bg-white (tab thường không có class nền nên không dính).
   Selector 4 class thắng .bg-white và .text-navy-900 của tailwind, nên không cần !important. */
.rounded-full.p-1.bg-paper-dim > .bg-white, .rounded-full.p-1\.5.bg-paper-dim > .bg-white,
.rounded-full.p-1.bg-stone-100 > .bg-white, .rounded-full.p-1\.5.bg-stone-100 > .bg-white{
    background-color:var(--navy-900,#0D1F33);color:#fff;box-shadow:none;
}

/* =====================================================================================
   DẢI LIÊN HỆ TRÊN CÙNG (25/08/2026) — dải navy có giờ hỗ trợ / số điện thoại / Chat Zalo /
   Nhận tư vấn, trước chỉ có ở trang chủ, nay chạy trên mọi trang khách nhìn thấy.
   Markup do scripts/dong-bo-lien-he.js nhét vào giữa mốc LIENHE:BAT-DAU/KET-THUC, và lấy thẳng
   từ bộ dựng trang chủ (render().lienHe) nên không bao giờ lệch với trang chủ.

   VÌ SAO SÁU LUẬT NÀY NẰM Ở ĐÂY chứ không nạp cả assets/css/pages/trang-chu.css: file kia có
   đúng một dòng KHÔNG bọc trong .tc là html{scroll-behavior:smooth}. Nạp toàn site thì dòng đó
   bẻ gãy chỗ practice.page.js:711 và :2378 cố ý gọi window.scrollTo({behavior:'auto'}) để tôn
   trọng prefers-reduced-motion — theo spec 'auto' nghĩa là "hỏi CSS", mà CSS lúc đó bảo smooth.
   Đã đo trên trình duyệt: nạp cả file kia KHÔNG xê dịch trang nào (mọi rule khác đều bọc .tc),
   nhưng cái giá là bịt lặng lẽ một lựa chọn trợ năng — không đáng cho 6 dòng.

   Viết var(--navy-900,#0D1F33): TRÊN TRANG CHỦ biến lấy từ khối .tc{} của trang-chu.css, TRANG
   KHÁC rơi về hex dự phòng. Một selector .tc .lien-he phục vụ cả hai bên, không nhân đôi luật.
   Sáu dòng gốc đã được GỠ khỏi trang-chu.css để chỉ còn một nguồn sự thật.
   ===================================================================================== */
.tc .lien-he{background:var(--navy-900,#0D1F33);color:var(--navy-300,#8FA4BB);font-size:13px;line-height:1.65}
.tc .lien-he .wrap{max-width:1180px;margin:0 auto;padding:9px 22px;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px 20px}
/* transition thêm 28/08/2026 (đợt 1 mục 1.11): dải này nằm TRÊN cả header và chạy trên 29 trang,
   tức là thứ đầu tiên con trỏ chạm vào khi vào site. Trước bản này màu bật phựt từ trắng sang
   vàng vì có khai báo hover mà không có transition. Chỉ đổi màu chữ nên không cần @media hover. */
.tc .lien-he a{display:inline-flex;align-items:center;gap:7px;color:#fff;font-weight:600;text-decoration:none;transition:color var(--ao-nhanh,150ms) var(--ao-ease)}
.tc .lien-he a:hover{color:var(--gold-300,#E3B95C)}
.tc .lien-he .nhom{display:flex;flex-wrap:wrap;gap:8px 20px;align-items:center}
.tc .lien-he svg{width:15px;height:15px}

/* =====================================================================================
   DẢI NỀN TRÀN VIỀN (25/08/2026) — mang NHỊP DẢI của trang chủ sang các trang còn lại.
   Chủ dự án 25/08: "đừng có làm full trắng, tôi muốn cảm giác sang trọng và hút mắt, dùng tone
   màu vàng, trắng như bên trang chủ".

   CHẨN ĐOÁN trước khi sửa (đo trên production, không đoán): các trang KHÔNG hề bị trắng toát —
   nền chung đã là kem #FAF6EE và thẻ đã trùng chất liệu trang chủ từ v114 (trắng, viền 1px
   #E8E6E1, bo 16px, bóng 0 2px 4px/.04 + 0 12px 28px/.07 — đo ra giống hệt). Thứ DUY NHẤT còn
   thiếu so với trang chủ là NHỊP: trang chủ xen kẽ 11 dải trắng / kem ấm #FBF8F2 / kem lạnh
   #F4F7FC / navy đặc nên mắt không bao giờ nhìn quá hai dải cùng màu liền nhau, còn các trang
   kia chạy MỘT nền kem suốt từ đầu tới chân — không xấu, nhưng phẳng và không "hút mắt".

   VÌ SAO PHẢI DÙNG MẸO NÀY chứ không đặt background thẳng: đã soi cấu trúc cả 8 trang chính,
   trang nào <main> cũng CHÍNH LÀ khung bó hẹp (max-w-3xl/5xl/7xl mx-auto px-4). Không trang nào
   có thẻ bọc chạy hết bề ngang để tô. Tô thẳng vào con của <main> chỉ ra một hộp màu nằm giữa
   trang, hai bên hở nền kem — trông như lỗi. Muốn dải thật thì phải bẻ lại markup <main> của
   hơn 20 trang, tức diff to và rủi ro cao.

   Mẹo: box-shadow spread 100vmax vẽ màu tràn ra ngoài phần tử, clip-path inset(0 -100vmax) cắt
   phần tràn theo CHIỀU DỌC nhưng để nguyên chiều ngang. Kết quả: dải chạy hết bề ngang màn hình
   mà KHÔNG đụng một chút nào vào luồng bố cục (không thêm thẻ bọc, không đổi chiều rộng, không
   xê dịch gì). Gỡ class ra là về y như cũ.
   ===================================================================================== */
.dai-trang, .dai-am{
    position:relative;
    padding-top:48px;padding-bottom:48px;margin-top:48px;margin-bottom:48px;
}
/* Vẽ nền bằng MỘT LỚP GIẢ nằm dưới (z-index:-1) rộng đúng bề ngang khung nhìn, thay vì bằng
   box-shadow spread + clip-path. Bản đầu dùng cặp kia và nó SAI ở chỗ ít ai ngờ: clip-path
   inset(0 -100vmax) nới hai bên nhưng KẸP CỨNG trên–dưới, nên mọi thứ xổ ra khỏi khối theo chiều
   dọc đều bị cắt cụt — đúng cái dropdown gợi ý của ô "Tìm khóa học" trong khối lọc courses.html
   (và nó còn tạo containing block mới, nhốt luôn position:fixed nếu có modal bên trong).
   Lớp giả thì không cắt gì, không tạo containing block, không đụng luồng bố cục.
   left:50% + translateX(-50%) neo theo tâm khối; khối nào cũng nằm trong .max-w-* mx-auto nên
   tâm khối TRÙNG tâm trang, dải ra cân hai bên. Dùng 100% chiều cao khối, không dùng 100vh. */
.dai-trang::before, .dai-am::before{
    content:"";position:absolute;z-index:-1;top:0;bottom:0;left:50%;
    width:100vw;transform:translateX(-50%);pointer-events:none;
}
/* 100vw ĐÃ TÍNH CẢ BỀ RỘNG THANH CUỘN, còn phần nhìn thấy thì không — nên lớp giả rộng hơn khung
   nhìn đúng bằng thanh cuộn (đo được: 701 so với 686) và trang mọc thanh cuộn ngang 7px. Dùng
   overflow-x:clip chứ KHÔNG dùng hidden: hidden biến body thành khung cuộn và làm chết
   position:sticky của header 23 trang, clip thì chỉ cắt phần thừa và giữ nguyên sticky.
   Cuộn ngang ở cấp trang vốn luôn là lỗi trong bố cục này; các khung cuộn ngang có chủ ý (bảng
   dữ liệu, máng tab) nằm ở thẻ con với overflow-x:auto riêng nên không bị ảnh hưởng. */
html, body{overflow-x:clip}
/* Nền chung của mọi trang đang là kem #FAF6EE (bg-paper). Cặp màu tạo nhịp vì thế là kem <-> trắng
   và kem <-> kem đậm, đúng "tone vàng, trắng" chủ dự án chỉ, chỉ đảo vai so với trang chủ (bên đó
   nền trắng, dải kem). KHÔNG dùng var(--ao-nen-lanh): biến đó đang bằng ĐÚNG #FAF6EE tức bằng nền
   trang, tô lên thì dải tàng hình — đã suýt đi vào bẫy đó.
   Kinh nghiệm đo thật: dải TRẮNG làm thẻ trắng mất ranh giới (chênh 1.08:1). Khối nào có thẻ thì
   dùng .dai-am cho thẻ trắng bật lên; .dai-trang để dành cho khối chỉ có chữ. */
.dai-trang::before{background-color:#FFFFFF}
.dai-am::before{background-color:var(--ao-nen-am)}

/* =====================================================================================
   VÀNG DẪN MẮT 25/08/2026 — BẮT ĐẦU
   Chủ dự án 25/08: "muốn cảm giác SANG TRỌNG và HÚT MẮT, dùng tone màu VÀNG, TRẮNG như bên
   trang chủ". Khối trên đã lo phần NHỊP; khối này lo phần VÀNG.

   CHẨN ĐOÁN: đo thật thì trang chủ KHÔNG dùng nền vàng. Vàng ở đó chiếm dưới 2% diện tích và
   chỉ đóng ba vai: MỰC (nhãn mục), NÚT, và bốn HỘP NHỎ nền gold-50. Cái làm trang chủ sang là
   nhịp dải cộng navy đậm chứ không phải lượng vàng. Nên khối này KHÔNG tô vàng lên bất kỳ mặt
   phẳng nào; nó chỉ đặt vàng thành VẠCH và MỰC.

   BỘ QUY TẮC, giữ nguyên khi thêm luật mới:
   ĐƯỢC dùng vàng ở 6 chỗ: nhãn mục viết hoa; vạch dẫn trước nhãn mục; gạch chân dưới h1; viền
   trái khối nhấn nền gold-50; chỉ trên đầu chân trang navy; nền nút chính và viền thẻ khi rê chuột.
   CẤM: không dải nền vàng; không thẻ nền vàng to hơn một hộp callout; không chữ gold-500 trở lên
   trên nền trắng hay kem (đo được 3.04:1 và 2.82:1, trượt AA); không dùng vàng làm màu trạng
   thái (đã có emerald/red/amber); không thêm vàng trang trí vào 4 trang quản trị.

   NGOẠI LỆ DUY NHẤT của luật "không dùng vàng làm màu trạng thái", chủ dự án quyết 02/09/2026:
   ô buổi học ĐANG TỚI GIỜ trong thời khoá biểu (.su-kien.dang-toi-gio) phát sáng bằng vàng.
   Lý do: emerald đã mang nghĩa "buổi đã học" trong CHÍNH lưới đó, hai trạng thái khác hẳn nhau
   mà cùng một màu thì phân biệt chỉ còn dựa vào độ đậm của vầng sáng. Phạm vi ngoại lệ đóng
   khung ở đúng ô đang tới giờ, KHÔNG lan sang badge, chip, pill hay bất kỳ nhãn trạng thái nào
   khác. Thấy vàng ở đó thì đừng "sửa cho đúng chuẩn", đọc DESIGN_SYSTEM.md mục Màu ngữ nghĩa.
   ===================================================================================== */

/* V1 — Mực nhãn mục. gold-600 trên nền kem chỉ 3.79:1, mà đây là chữ 11-14px nên ngưỡng AA là
   4.5:1; gold-700 cho 5.22:1. Bọc trong .label để 101 icon fa-solid text-gold-600 KHÔNG đổi
   (icon là đồ hoạ, ngưỡng chỉ 3:1). Tiện thể dẹp cảnh 17 nhãn dùng gold-600 và 5 nhãn dùng
   gold-700 cho ĐÚNG cùng một vai. */
.label.text-gold-600{color:var(--gold-700,#8A5F19)}

/* V2 — Vạch dẫn 20x2px trước nhãn mục: biến nhãn thành mốc mở khối, đúng chi tiết trang chủ có
   mà các trang kia không. Vạch là đồ hoạ nên gold-500 3.04:1 vẫn qua ngưỡng 3:1.
   :not([class*="bg-"]) loại mấy chip có nền gold-50 ra, vì chip không phải nhãn mục. */
.label.uppercase.text-gold-600:not([class*="bg-"]):not([class*="border"])::before,
.label.uppercase.text-gold-700:not([class*="bg-"]):not([class*="border"])::before,
.label.uppercase.text-gold-300:not([class*="bg-"]):not([class*="border"])::before{
    content:"";display:inline-block;vertical-align:middle;
    width:20px;height:2px;margin-right:10px;border-radius:2px;background:var(--gold-500,#C08A2E);
}
.label.uppercase.text-gold-300:not([class*="bg-"]):not([class*="border"])::before{background:var(--gold-300,#E3B95C)}
/* Nhãn dạng "label flex items-center gap-2" đã có gap 8px sẵn, cộng margin nữa là dôi 18px */
.label.flex.uppercase.text-gold-600:not([class*="bg-"]):not([class*="border"])::before,
.label.flex.uppercase.text-gold-700:not([class*="bg-"]):not([class*="border"])::before{margin-right:0}

/* V3 — Gạch chân tiêu đề trang: điểm neo mắt mạnh nhất mà rẻ nhất, và là chi tiết vàng duy nhất
   nằm ngay trong khung hình đầu tiên. Hero canh giữa có tổ tiên .text-center, hero canh trái thì
   không, nên gạch tự canh theo. */
h1.tracking-tight::after{
    content:"";display:block;width:64px;height:3px;border-radius:3px;
    margin:18px 0 0;background:var(--gold-500,#C08A2E);
}
.text-center h1.tracking-tight::after{margin-left:auto;margin-right:auto}

/* V4 — Chỉ vàng trên đầu chân trang navy. Footer là khối DÙNG CHUNG nên luật này áp cả cho trang
   chủ — đó chính là đồng bộ, không phải rò rỉ. Vàng trên navy 5.49:1.
   :not(.border-t) chừa footer nội bộ của worker.html, giữ luật cấm vàng ở khu quản trị. */
footer.bg-navy-900:not(.border-t){border-top:3px solid var(--gold-500,#C08A2E)}

/* V5 — Chuẩn hoá khối nhấn: đang có hai kiểu cho cùng một vai (callout "bg-gold-50 border p-4"
   và vài chỗ viết tay "border-l-4 border-gold-500"). Ràng p-3/p-4 để KHÔNG dính ô icon 56x56
   "w-14 h-14 bg-gold-50 border border-gold-200" — thêm vạch trái vào ô icon là hỏng. */
.bg-gold-50.border.p-4, .bg-gold-50.border.p-3{border-left:3px solid var(--gold-500,#C08A2E)}

/* V8 — Dải liên hệ trên điện thoại. .wrap để flex-wrap nên dưới 640px dải xuống HAI hàng, chiếm
   gần 70px đầu MỌI trang — dải vừa thêm hôm nay sẽ thành thứ chiếm chỗ nhất trên màn hình nhỏ.
   Ẩn câu giờ hỗ trợ (span con TRỰC TIẾP duy nhất của .wrap; span.num chứa số điện thoại nằm
   trong thẻ a nên không dính). Áp cả cho trang chủ vì dùng chung selector — đúng ý muốn, trang
   chủ đang bị y hệt. */
@media (max-width:639px){
    .tc .lien-he{font-size:12px}
    .tc .lien-he .wrap{padding:7px 10px;justify-content:center}
    .tc .lien-he .wrap > span{display:none}
    /* Ẩn câu giờ hỗ trợ thôi CHƯA đủ: đo trên production ở 375px thì ba link còn lại chiếm
       102+77+139=318px, cộng hai khe 20px và padding 16px hai bên là 390px, vẫn thừa 15px nên
       dải vẫn xuống hai hàng (cao 62px). Siết khe xuống 10px, padding xuống 10px và khe
       icon–chữ xuống 5px: tổng còn khoảng 344px, vừa cả máy 360px chứ không riêng 375px. */
    .tc .lien-he .nhom{gap:4px 10px}
    .tc .lien-he a{gap:5px}
}
/* VÀNG DẪN MẮT 25/08/2026 — KẾT THÚC */

/* ---- CHUYỂN TRANG MƯỢT 25/08/2026 (góp ý chủ dự án: bấm link nhảy phát sang trang mới, thô) ----
   Site này là nhiều trang HTML rời, mỗi cú bấm là một lần tải trang thật. View Transitions cho
   điều hướng cùng nguồn bảo trình duyệt: chụp lại trang cũ, tải trang mới xong thì hoà mờ sang
   (~0.2s) thay vì cắt phựt. Chỉ cần CSS, không JS; luật phải có mặt ở CẢ trang đi lẫn trang đến —
   theme.css nằm trên mọi trang nên tự đạt. Chrome/Edge 126+ và Safari 18.2+ hiểu; trình duyệt
   khác gặp at-rule lạ thì bỏ qua, quay về nhảy trang như cũ — không hỏng gì, không cần polyfill. */
@view-transition { navigation: auto; }
/* 0.12s chứ không phải 0.25s mặc định: bản 0.2s đầu tiên bị chủ dự án chê "hơi chậm" (25/08) —
   chuyển trang phải cảm giác tức thì, hoà mờ chỉ để xoá cú cắt phựt chứ không phải để ngắm. */
::view-transition-old(root) { animation-duration: .12s; }
::view-transition-new(root) { animation-duration: .12s; }
/* Đi kèm luật trên: script pages/*.page.js ở MỌI trang v-cloak mang thêm blocking="render"
   (sửa hàng loạt 25/08/2026). Không có nó, khung hình đầu của trang mới thường là trang TRỐNG
   (nội dung còn nằm dưới v-cloak vì Vue chưa chạy) — hoà mờ sang một trang trống rồi nội dung
   bật ra, thành cú nháy còn khó chịu hơn không có hiệu ứng. blocking="render" bảo trình duyệt
   đợi script trang chạy xong (Vue mount xong, v-cloak đã gỡ) mới vẽ khung hình đầu — vốn chẳng
   mất gì vì trước đó có gì để vẽ đâu. index.html KHÔNG gắn: thân trang chủ là HTML tĩnh dựng
   sẵn, chặn vẽ ở đó là bắt người dùng nhìn màn kem trắng lâu hơn thật. */

/* Nhún nhẹ lúc đang bấm giữ nút — phản hồi tức thì trong nhịp chờ trang mới, đỡ cảm giác "bấm
   xong chết một nhịp". Phần nhả ra đã có transition .18s sẵn ở .nut. Đặt CUỐI file cho thắng
   các luật :hover translateY(-2px) cùng bậc ưu tiên phía trên. */
.nut:active:not(:disabled) { transform: translateY(0) scale(.97); }

/* Người dùng đã bật "giảm chuyển động" trong hệ điều hành thì tắt hoà mờ chuyển trang. */
@media (prefers-reduced-motion: reduce) {
    ::view-transition-old(root), ::view-transition-new(root) { animation: none !important; }
}

/* =====================================================================================
   GÓI CHUYỂN ĐỘNG 28/08/2026: thành phần dùng chung.
   Kế hoạch đầy đủ kèm lý do từng lựa chọn: sandbox/ke-hoach-chuyen-dong.md
   Bản thử bấm được: sandbox/thu-chuyen-dong.html
   Bảng token nhịp (--ao-nhanh/--ao-vua/--ao-cham/--ao-ease-vao) ở khối :root đầu phần ÁO MỚI.
   ===================================================================================== */

/* ---- KHUNG CHỜ (skeleton), đợt 4 mục 4.1 --------------------------------------------
   Thay cho vòng xoay giữa khoảng trắng. Khung chờ mang sẵn HÌNH DẠNG của nội dung sắp tới nên
   mắt không phải thích nghi lại khi dữ liệu về, và bố cục không nhảy.
   DÙNG NHỊP MỜ, KHÔNG dùng vệt sáng trượt (shimmer): trên nền giấy kem #FAF6EE một vệt trắng
   quét ngang hàng chục khối cùng lúc ở practice là nhiễu đúng lúc học sinh đang chờ đọc đề.
   BẮT BUỘC khi dùng: vùng khung chờ mang aria-hidden="true", và cạnh nó một phần tử
   role="status" aria-live="polite" mang chữ "Đang tải ...". Nếu không thì người dùng trình đọc
   màn hình mất sạch thông tin, vì khung chờ thuần tuý là hình. */
.vp-khung-cho{
    background:var(--ao-nen-am);border-radius:var(--ao-r-nho);
    animation:vp-cho-mo 1600ms ease-in-out infinite;
}
@keyframes vp-cho-mo{ 0%,100%{opacity:1} 50%{opacity:.55} }

/* ---- THANH TIẾN TRÌNH ĐỈNH TRANG, đợt 4 mục 4.2 ------------------------------------
   Cao 3px, chỉ bật khi thao tác kéo dài quá 400ms (xem assets/js/lien-he.js) nên không chồng
   lên hoà mờ chuyển trang đã chạy từ 25/08. Dùng transform:scaleX chứ KHÔNG dùng width: width
   là thuộc tính bố cục, mỗi khung hình là một lần tính lại bố cục.
   z-index 60 = dưới toast (70) và dưới lớp nền modal, trên header sticky. */
.vp-tien-trinh{
    position:fixed;left:0;top:0;height:3px;width:100%;z-index:60;
    background:var(--gold-500,#C08A2E);
    transform:scaleX(0);transform-origin:left;opacity:0;pointer-events:none;
    transition:transform .25s var(--ao-ease),opacity .3s ease;
}
.vp-tien-trinh.dang-chay{opacity:1}

/* ---- HỘP THOẠI CÓ NHỊP VÀO, đợt 3 mục 3.5 ------------------------------------------
   Làm bằng @starting-style CSS thuần, KHÔNG bọc <Transition> của Vue. Lý do kỹ thuật: vp-modal
   (assets/js/thanh-phan-chung.29d3a822.js:64) dùng teleport to="body" và bị trang cha huỷ bằng v-if, nên
   <Transition> đặt trong template không bao giờ chạy được nhịp RA: component chết ngay và DOM
   teleport đi theo. Muốn nhịp ra thật phải bọc ở cả 5 chỗ gọi trong trang cha, gấp năm công sức.
   Nhịp RA vẫn cắt phựt, chấp nhận, đó là đúng thứ tự ưu tiên.
   Giữ backdrop-filter ở giá trị CỐ ĐỊNH (lớp nền đã có backdrop-blur-sm của Tailwind): đổi bán
   kính làm mờ theo từng khung hình buộc trình duyệt lấy mẫu lại toàn bộ nội dung phía sau mỗi
   frame, đúng lúc luồng chính đang bận dựng nội dung modal. Kết quả thấy được trên máy yếu là
   modal mở GIẬT chứ không mượt hơn.
   @starting-style PHẢI đặt ngay sau luật gốc trong CÙNG file, nếu tách ra là không ăn. */
.vp-modal-nen{ transition:opacity var(--ao-nhanh) var(--ao-ease); opacity:1 }
@starting-style{ .vp-modal-nen{ opacity:0 } }
.vp-modal-khung{ transition:opacity 190ms var(--ao-ease),transform 190ms var(--ao-ease); opacity:1; transform:none }
@starting-style{ .vp-modal-khung{ opacity:0; transform:scale(.98) } }

/* ---- HEADER ĐỔ BÓNG KHI CUỘN, đợt 3 mục 3.6 ----------------------------------------
   Làm bằng scroll-driven animation CSS thuần. VÌ SAO PHẢI LÀ CSS: đã grep, toàn bộ assets/js có
   đúng 0 addEventListener('scroll'), và không có file JS nào chạy trên cả 35 trang, làm bằng JS
   phải sửa hơn 20 thẻ head cộng khuôn trong scripts/sinh-trang-tinh.js. theme.css thì đã có mặt
   trên đủ 35 trang.
   KHÔNG animate box-shadow (thuộc tính vẽ, mà phần tử này đang mang backdrop-filter): đặt sẵn
   bóng trên một lớp giả rồi chỉ chuyển opacity của nó.
   Hai bẫy cú pháp đã tính: animation-timeline phải viết SAU shorthand `animation` (shorthand
   reset nó về auto), và phải có animation-fill-mode:both.
   Firefox bản ổn định 154 (28/08/2026) chưa hỗ trợ; @supports chắn lại và suy biến là giữ nguyên
   hiện trạng hôm nay, không hỏng gì. Đặc tả nằm trong trọng tâm Interop 2026 nên khi Firefox
   ship, người dùng Firefox tự được nâng cấp mà không phải sửa dòng nào. */
@media (prefers-reduced-motion: no-preference){
    @supports (animation-timeline: scroll()){
        header.sticky.top-0{ position:sticky }
        header.sticky.top-0::after{
            content:"";position:absolute;left:0;right:0;top:100%;height:14px;
            pointer-events:none;opacity:0;
            background:linear-gradient(rgba(13,31,51,.09),transparent);
            animation:vp-header-bong linear both;
            animation-timeline:scroll();
            animation-range:0 72px;
        }
        @keyframes vp-header-bong{ from{opacity:0} to{opacity:1} }
    }
}

/* ---- GẠCH CHÂN VÀNG DƯỚI TIÊU ĐỀ THẺ, đợt 3 mục 3.1 --------------------------------
   Rê chuột vào CẢ thẻ thì tiêu đề tự vẽ một vạch vàng từ trái sang phải. Đây là phần thay thế
   cho việc nâng biên độ nhấc thẻ: thẻ đứng yên hơn, chữ phản hồi nhiều hơn.
   BẮT BUỘC bọc chữ trong <span>: h2/h3 là phần tử KHỐI chiếm hết bề ngang thẻ, đặt nền trực
   tiếp lên nó sẽ vẽ ra một vạch chạy suốt thẻ chứ không nằm dưới chữ.
   box-decoration-break:clone để tên môn dài xuống hai dòng vẫn gạch đúng từng dòng. */
/* Kiểu NỀN tách hẳn khỏi ngữ cảnh, chỉ bám vào chính class .gach-vang. Bản đầu bắt
   `a.bg-white.border h2 > span.gach-vang` và nó là CSS chết: thẻ khóa học thật ra là
   `div.course-card`, không phải thẻ a, và chữ còn nằm lồng trong một <button> nên cũng không phải
   con TRỰC TIẾP của h2. Sai hai chỗ cùng lúc, mà sai kiểu này không có gì báo, chỉ là không có
   hiệu ứng. Tách ra thế này thì thêm một chỗ dùng chỉ tốn đúng một dòng kích hoạt. */
span.gach-vang{
    display:inline;box-decoration-break:clone;-webkit-box-decoration-break:clone;
    padding-bottom:2px;
    background-image:linear-gradient(var(--gold-500,#C08A2E),var(--gold-500,#C08A2E));
    background-size:0 2px;background-position:0 100%;background-repeat:no-repeat;
    transition:background-size var(--ao-vua) var(--ao-ease);
}
/* Dòng KÍCH HOẠT, một dòng cho mỗi loại thẻ. Thẻ khóa học ở assets/css/pages/courses.css. */
@media (hover: hover) and (pointer: fine){
    a.bg-white.border:hover span.gach-vang{ background-size:100% 2px }
}

/* ---- LỚP BẢO VỆ GIẢM CHUYỂN ĐỘNG, PHỔ QUÁT: đợt 1 mục 1.2 --------------------------
   Đặt CUỐI FILE có chủ ý: nó phải thắng mọi luật phía trên. Trước bản này chỉ có 4 chỗ rời rạc
   tôn trọng prefers-reduced-motion, phần lớn hiệu ứng không có lớp bảo vệ nào.

   DÙNG .01ms CHỨ KHÔNG DÙNG `none`: với `none` thì sự kiện transitionend và animationend KHÔNG
   bao giờ bắn, nên bất kỳ đoạn logic nào chờ chúng sẽ treo vĩnh viễn. .01ms thì hiệu ứng biến
   mất với mắt người nhưng sự kiện vẫn bắn đúng.

   NGOẠI LỆ ngay bên dưới: hai thanh mang THÔNG TIN chứ không phải trang trí (thanh XP ở
   dashboard.html:169 và .criteria-bar__fill ở gia-su.css). Tắt nhịp của chúng thì thanh nhảy
   thẳng tới giá trị cuối: vẫn đúng thông tin, nhưng người dùng mất cảm giác "nó vừa tăng".
   Giữ lại là có chủ ý, không phải bỏ sót. */
@media (prefers-reduced-motion: reduce){
    *, *::before, *::after{
        animation-duration:.01ms !important;
        animation-iteration-count:1 !important;
        transition-duration:.01ms !important;
        scroll-behavior:auto !important;
    }
    /* Hai giá trị KHÁC NHAU có chủ ý, đừng gộp về một số. Mỗi cái phải bằng đúng thời lượng mà
       chính phần tử đó vốn khai, nếu không thì cái "ngoại lệ" lại thành ra làm chậm hơn cả bản
       thường: thanh XP ở dashboard dùng class transition-all của Tailwind tức 150ms, ép nó về
       0.6s là bắt đúng nhóm người vừa xin ít chuyển động đi phải ngồi nhìn lâu gấp bốn. */
    [role="progressbar"] > *{ transition-duration:.15s !important }
    .criteria-bar__fill{ transition-duration:.6s !important }
}


/* =========================================================================
   THẺ GÓI ĐĂNG KÝ (.goi-the) — dùng chung cho MỌI bảng giá gói Topical
   Chuyển từ assets/css/pages/practice.css sang đây ngày 31/08/2026, khi bảng
   giá có bản thứ hai trong modal của paper-viewer.html. Đặt ở theme.css vì
   đây là file cả hai trang đều nạp; giữ nguyên trong practice.css rồi chép
   thêm một bản là đúng kiểu nhân bản màn mời mua mà dự án đang mắc.
   ========================================================================= */
/* ---- Thẻ giá trong modal Gói Luyện Theo Chủ Đề (25/08/2026) ----
   Nhãn nổi ("Được nhiều học sinh tin dùng nhất") cưỡi lên mép trên thẻ như khối giá trang chủ.
   Viết CSS riêng vì bản tailwind.css build sẵn KHÔNG có -top-3 / left-1/2 / -translate-x-1/2 —
   thêm class mới vào build là bắt cả dự án chạy lại npm run build:css chỉ vì một cái nhãn. */
.goi-the { position: relative; }
.goi-the__nhan {
    position: absolute;
    top: -0.75rem;
    left: 50%;
    transform: translateX(-50%);
    white-space: nowrap;
}

/* ---- ĐỢT 3 MỤC 3.3 (phần .goi-the, bổ sung 28/08/2026) ----
   Hạng mục 3.3 ghi phạm vi là HAI file: trang-chu.css và file này. Lượt đầu chỉ làm bên
   trang-chu.css, nên bốn nhóm thẻ giá cùng loại ngoài trang chủ đều đã phản hồi khi rê chuột,
   riêng thẻ gói trong modal ở đây vẫn hoàn toàn bất động dù nó cũng là thẻ BÁN HÀNG và cũng
   bấm được. Đây là chỗ bỏ sót, không phải chỗ cố ý bỏ.
   Chỉ đổi viền sang vàng và nền sang kem, KHÔNG transform, nên KHÔNG cần bọc
   @media (hover: hover): màu dính lại một nhịp trên máy cảm ứng thì vô hại.
   Đã kiểm hai biến dùng ở dưới, cả hai đều GIẢI ĐƯỢC trên trang này: --gold-500 khai ở :root
   trong assets/css/tailwind.ea465e5f.css (khác --xanh-nen ở cuối file này: biến đó thì đúng là chỉ có
   trong khối .tc{} của trang-chu.css nên buộc phải có dự phòng), còn --ao-nen-am khai ở :root
   trong theme.css. Giá trị dự phòng #C08A2E vẫn viết kèm cho đồng nhất với chính file này
   (.cau-the--da-chon::before) và với theme.css, chứ không phải vì biến thiếu.
   LOẠI TRỪ thẻ gói nổi bật: markup gắn động 'border-gold-500 ring-2 ring-gold-500' cho thẻ
   goiNoiBat, nó đã mang sẵn viền vàng làm tín hiệu riêng, nên tô thêm viền vàng lúc rê chuột là
   xoá mất tín hiệu đó, đúng lý do trang chủ loại trừ .goi.noi-bat và .goi.uu-dai. */
.goi-the {
    transition: border-color var(--ao-nhanh) var(--ao-ease),
                background-color var(--ao-nhanh) var(--ao-ease);
}
.goi-the:not(.border-gold-500):hover {
    border-color: var(--gold-500, #C08A2E);
    background-color: var(--ao-nen-am);
}

/* ============================================================================
   QUẢ CHUÔNG NƯỚNG TĨNH (31/08/2026) — nút chuông nằm sẵn trong HTML của mọi
   trang (khối CHUONG do scripts/dong-bo-header.js và sinh-trang-tinh.js dựng),
   không còn là DOM do JS cắm muộn. Che/mở bằng lớp `da-dang-nhap` trên <html>,
   lớp đó do assets/js/phien-som.js gắn TRƯỚC nét vẽ đầu tiên (script chặn trong
   <head>), nên: khách không bao giờ thấy chuông, người đăng nhập thấy chuông
   ngay từ điểm ảnh đầu tiên và nó không nháy mất khi Vue vá lại header — markup
   là một phần của template nên Vue vẽ lại thì chuông vẫn ở nguyên chỗ.
   display dùng inline-flex + position:relative thay cho style inline mà bản JS
   cũ từng gắn: bảng thông báo thả neo absolute theo đúng khối này. */
#vpath-chuong-goc { display: none; position: relative; }
html.da-dang-nhap #vpath-chuong-goc { display: inline-flex; }

/* ==========================================================================
   DẢI ẢNH LỚP HỌC CHẠY NGANG
   Đây là MỘT VÙNG CUỘN THẬT, không phải hoạt ảnh giả cuộn. Lý do chọn cách này
   thay vì @keyframes translateX (bản đầu đã viết rồi bỏ):

   Với translateX, người dùng vuốt tay thì vùng cuộn đi một đằng còn transform
   kéo một nẻo, hai thứ đánh nhau và không cách nào hoà giải bằng CSS. Ở đây cả
   hoạt ảnh lẫn ngón tay cùng tác động lên đúng MỘT đại lượng là scrollLeft, nên
   không có gì để xung đột: assets/js/dai-anh.1cfd8401.js cộng dần scrollLeft mỗi khung
   hình, người dùng vuốt thì cộng vào chính con số đó.

   Danh sách ảnh được lặp HAI LẦN trong DOM (bản thứ hai mang aria-hidden). Nhờ
   vậy khi trôi hết nửa đầu, script trừ đi đúng nửa chiều rộng và khung hình mới
   trùng khít khung hình cũ: mắt không thấy điểm nối. Bỏ bản lặp là dải chạy tới
   mép rồi đứng.
   ========================================================================== */
.gs-dai {
    overflow-x: auto;
    overflow-y: hidden;
    /* Cuộn tức thì. Nếu trang cha bật scroll-behavior: smooth thì mỗi lần script
       gán scrollLeft trình duyệt lại chạy hoạt ảnh cuộn riêng của nó, và dải
       giật từng nấc thay vì trôi đều. */
    scroll-behavior: auto;
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.gs-dai::-webkit-scrollbar { display: none; }

.gs-dai__chay {
    display: flex;
    gap: 14px;
    /* max-content: các ô giữ nguyên bề ngang thật và xếp thành một hàng dài, thay
       vì bị bóp cho vừa khung. Thiếu dòng này là cả dải co lại thành một cột. */
    width: max-content;
    padding-inline: 1rem;
}

@media (min-width: 640px) { .gs-dai__chay { padding-inline: 1.5rem; } }
@media (min-width: 1024px) { .gs-dai__chay { padding-inline: 2rem; } }

.gs-dai__o {
    flex: 0 0 auto;
    width: 300px;
    margin: 0;
}

.gs-dai__o img {
    display: block;
    width: 100%;
    height: 200px;
    object-fit: cover;
    border-radius: var(--ao-r-nho, 14px);
    background: var(--ao-nen-am, #F2EBDC);
    border: 1px solid var(--ao-vien, #E8E6E1);
}

.gs-dai__o figcaption {
    margin-top: 8px;
    font-size: 12px;
    line-height: 1.5;
    color: var(--ao-chu-nhat, #6B7684);
}

@media (max-width: 639px) {
    .gs-dai__o { width: 240px; }
    .gs-dai__o img { height: 160px; }
}
