Skip to content

DS-001 · Ocean World: dải tối, mặt tối chuẩn và màn learn ​

Một phần của DS-001. Dải tối dùng chung, công thức mặt tối, ba giá trị sửa, learn bỏ nền sáng, thẻ màu đặc, coral/admin/mentors bật hệ màu chung.

2·1·11. Dải tối dùng chung + thanh điều hướng nền sẫm (SRC-804) ​

Chủ dự án 2026-09-18: "Chỉnh lại màu sắc chung của Design System, của trang HOME của www.nemo12.com, và các trang khác trong nemo12.com. Chỉnh NavBar thành màu nền sẫm." — và tiếp theo: "chỉnh màu sắc toàn bộ các trang trong learn.nemo12.com, để dùng Design System chung, hệ màu sắc chung."

Vấn đề thật là gì. Ocean Palette §2·1·3 khai đủ màu cho nền SÁNG, nhưng không khai màu nào cho nền TỐI — trong khi cả www lẫn learn đều có màn tối. Hậu quả: mỗi app tự pha. www lấy #0C1523 cho dải tối và #0A121E cho chân trang; learn lấy #0C1524 / #060C16 / #12203A; hero .n12-hero-ocean lại pha #2b6ea6 → #0a2c50. Bốn giá trị gần giống nhau mà không bằng nhau, tất cả đều xám-hơi-tím chứ không phải màu nước. Đi từ trang công khai vào app học là màu "nhảy" một nấc, và không ai chỉ ra được nấc ấy nằm ở đâu để sửa.

Dải tối nay là tài sản của DS, khai một lần ở tokens/base.css, bốn bậc, không đẻ thêm:

TokenGiá trịVai
--n12-abyss#060C16đáy: chân trang, cột trái, mép dưới dải chuyển sắc
--n12-deep-sea#0C1524nền trang tối / dải tối mặc định
--n12-deep-surface#12203Abề mặt NỔI trên nền ấy: thẻ, thanh điều hướng, ô nhập
--n12-lagoon#56AEDFđiểm lam trên nền tối: eyebrow, gạch chân, nét biểu đồ

Kèm --n12-deep-ink #EAF4FF, --n12-deep-ink-muted #A9C6E6, --n12-deep-line rgb(255 255 255 / .12) — vì "nền tối" mà không kèm màu chữ thì mỗi app lại tự chọn, tức là bài cũ lặp lại.

Lagoon không dùng được trên nền sáng. #56AEDF trên trắng đo 2,2:1 — đủ cho một nét gạch chân (ngưỡng đồ hoạ 3:1 vẫn hụt, nên nó chỉ dùng ở nét dày), không đủ cho chữ. Chữ lam trên dải sáng dùng --n12-ocean#12639A (6,41:1). Hai màu, hai dải, đừng hoán chỗ.

Thanh điều hướng nền sẫm. Sáu token riêng (--nav-surface, --nav-ink, --nav-ink-muted, --nav-line, --nav-active, --nav-active-ink), không viết màu thẳng trong từng app: thanh điều hướng có mặt trên mọi trang, nên nó phải đổi được ở một chỗ. Lý do đổi khỏi thanh trắng: phần lớn nội dung nằm trong dải tối, nên thanh trắng là mảng sáng nhất màn hình ở đúng chỗ không có nội dung — mắt bị kéo lên trước khi kịp đọc gì, và mỗi lần cuộn qua ranh giới sáng/tối là một cú chớp.

Mục đang chọn đảo lại: nền trắng, chữ sẫm (--nav-active / --nav-active-ink). Trên nền sẫm, vật sáng nhất là vật được nhìn trước, và nó không đụng màu coral của nút hành động ngay cạnh — đúng lý do đã bác nền coral cho mục điều hướng ở SRC-776.

learn dùng chung lớp semantic. apps/learn/index.html gắn n12-v1 lên <html>, nên màn sáng của app học đi theo Ocean Palette thay vì bảng v0.4. Màn tối vẫn do .n12-deep quyết — class ấy nằm sâu hơn trong cây nên luôn thắng — nhưng bốn bậc của nó nay trỏ vào dải tối chung thay vì tự pha. Kèm theo, .n12-v1 kéo --n12-sand-50 / --n12-sand-100 về --n12-foam / --n12-mist, vì bg-sand nằm rải khắp learn và marlins: không kéo thì một màn v1 có nền foam xanh nhạt mà thẻ bg-sand lại xám trung tính, lệch sắc ngay trong cùng một màn hình.

Chưa đụng tới: marlins, coral, admin, mentors, b21 và bốn site VitePress vẫn ở v0.4. Chúng bật n12-v1 được bất cứ lúc nào, nhưng mỗi app cần một vòng xem màn thật — đó là việc riêng, không gộp vào đây.

2·1·12. Mặt tối chuẩn: một CÔNG THỨC, không phải một hex (SRC-806) ​

Chủ dự án 2026-09-18, chỉ vào màn học IELTS: "Màu này của NavBar, các chấm da cam… đều siêu đẹp. Màu sắc này siêu đẹp. Hãy sửa Design System theo màu này."

Điều đáng ghi lại nhất: thứ được khen không nằm ở bảng màu. Bốn bậc --n12-abyss → --n12-lagoon ở §2·1·11 đã có sẵn từ vòng trước, và riêng chúng không dựng ra được màn ấy. Thứ dựng ra nó là một công thức ba lớp mà trước nay chỉ sống trong apps/learn/src/index.css, tức là app khác không với tới được:

  1. Ánh sáng rọi từ trên mặt nước xuống — một vầng lam lệch phải, tắt dần trong 60% chiều cao. Đây là lớp làm mặt phẳng "có chiều sâu". Bỏ nó thì còn lại một màn xanh đậm chết, và không cách nào bù bằng cách chọn hex khác — đó là lý do mục này tồn tại tách khỏi §2·1·11.
  2. Nước sẫm dần xuống đáy — --n12-deep-surface → --n12-deep-sea → --n12-abyss.
  3. Bề mặt nổi là KÍNH MỜ trắng 7%, không phải một màu tự chọn. Kính lấy màu từ nước bên dưới nên nó luôn đúng tông, ở mọi độ cao của trang. Thanh điều hướng trong ảnh chính là lớp này.

Nay công thức là tài sản của DS: --surface-deep (lớp 1+2), --surface-glass (lớp 3), và hai class .n12-surface-deep / .n12-surface-glass để một app lấy được cả mặt nước bằng một class. learn từ nay là người dùng của công thức, không phải chủ sở hữu.

--nav-surface #1B384F là phép tính, không phải gu. App nào có nền --surface-deep chạy suốt trang thì thanh dùng thẳng kính. www không có — nó xen kẽ dải sáng / dải tối, mà trắng 7% trên dải sáng ra một thanh gần trắng. Chỗ đó dùng --nav-surface, là đúng giá trị kính ấy đã hoà sẵn với nước rồi nướng thành màu đặc: #12203A + trắng 7% = #233048 — tính từ --n12-deep-surface chứ không phải --n12-deep-sea, vì thanh nằm ở ĐỈNH trang nên nước sau lưng nó là mặt nước, không phải đáy. Hai đường khác nhau, cùng một màu ra. Đổi --n12-deep-surface hoặc --surface-glass thì phải tính lại con số này.

Chấm da cam trong ảnh là --accent #F45B32 nguyên bản trên mặt nước — không có token mới nào. Ghi ra để lần sau không ai đi tìm "màu cam của màn học": nó là coral của §2·1·3, và thứ làm nó đẹp là nền sau lưng.

Một khác biệt www ↔ learn, đừng chép nhầm: learn đặt background-attachment: fixed vì cả trang là MỘT mặt nước. www thì fixed neo dải màu vào khung nhìn chứ không vào khối, nên cùng một dải tối sẽ đổi màu trong lúc người ta cuộn qua nó, từ tông mặt nước sang tông đáy. Ở www gradient neo vào chính khối: mỗi dải tối là một lát nước trọn vẹn, đứng yên khi cuộn.

2·1·13. Ba giá trị bị đẩy sai, và vì sao (SRC-808) ​

Vòng SRC-804/806 khai dải tối ở #05192C / #0A2942 / #11364F — xanh nước, sáng hơn hẳn. Chủ dự án xem rồi bác thẳng: "Màu này sai rồi, tôi không thích màu này đâu. Tôi thích màu ở ảnh 2 cơ, và tôi đã gửi 1 lần rồi đó." Ảnh 2 là navy gần đen — đúng ba giá trị #060C16 / #0C1524 / #12203A mà learn đã chạy từ SRC-349.

Sai ở đâu. Chủ dự án chỉ vào một màn learn và khen "màu này siêu đẹp". Tôi đọc thành "màu tôi vừa đổi đẹp" và đẩy tiếp về phía xanh nước. Thực tế màn ấy đang chạy màu CŨ. Luật rút ra, ghi ở đây vì nó không phải chuyện của riêng đợt này:

Khi chủ dự án chỉ vào một màn hình và khen, thứ được khen là màn đang chạy ở đó — không phải bản vừa sửa. Kiểm lại xem màn ấy đang dùng giá trị nào TRƯỚC khi đổi, nếu không là đổi mất đúng thứ được khen.

Kiến trúc của hai vòng trước giữ nguyên và vẫn đúng: một dải tối dùng chung, một công thức --surface-deep, kính --surface-glass, sáu token --nav-*. Chỉ giá trị đổi. Đó chính là thứ kiến trúc ấy mua được — sửa màu của cả ba app bằng ba dòng token, không phải đi lùng từng file.

Một bản vá rơi nhầm khối, cũng từ vòng trước. SRC-804 định đổi gradient của .n12-hero-ocean, nhưng .od-root cách đó 90 dòng có đúng cùng một chuỗi gradient, nên phép thay chuỗi "chỉ một lần" trúng od-root, và hero bị bỏ sót. Đã vá ở SRC-808. Bẫy còn nguyên cho lần sau: sửa bằng chuỗi thì phải chọn đoạn có gì đó RIÊNG của khối mình định sửa.

marlins bật hệ màu chung. Cổng này vốn đã là biển sâu (SRC-571), nên việc phải làm không phải "đổi sang tối" mà là ngừng tự pha: thẻ kính trắng 8%, viền trắng 26%, chữ #eaf4ff, đáy header #06213E — mỗi giá trị lệch một chút so với bản learn. Nay trỏ vào --surface-glass / --n12-deep-line / --n12-deep-ink / --nav-surface, và <html class="n12-v1"> để màn sáng đi theo Ocean Palette chung.

Cửa vào IELTS của learn: một dòng, một icon, MỘT chữ. Bốn thẻ có tiêu đề + câu mô tả đổi thành bốn dòng READING · LISTENING · WRITING · SPEAKING, đúng thứ tự chủ dự án đưa. Tên để tiếng Anh — ngoại lệ có chủ đích với luật "chữ người dùng đọc là tiếng Việt" ở CLAUDE.md, vì bốn chữ này là tên riêng của bài thi, in đúng như vậy trên phiếu điểm; dịch ra là bắt learner tự ánh xạ ngược mỗi lần nhìn điểm của mình. Icon vẽ bằng SVG, không dùng emoji (§2c).

2·1·14. learn không còn màn nền sáng nào (SRC-809) ​

Rà 16 trang learn bằng ảnh chụp từ bản build thật rồi đo pixel từng ảnh (không đọc bằng mắt: một màn hơi sáng hơn thì mắt bỏ qua, còn tỉ lệ điểm sáng thì không). Kết quả: 13 trang đúng họ navy, nhưng còn một nhóm màn vẫn ở thế giới sáng với navbar trắng — đi từ màn học sang là một cú chớp trắng.

Tôi báo cho chủ dự án là "năm màn". Con số đó sai, thực tế là chín: Lighthouse, Onboarding, OrcaHub, PortraitView, RoadmapPage, WhaleHub, màn chẩn đoán + màn Centered + màn "Đang tải" trong App.tsx, và components/ErrorBoundary. Ba màn cuối tôi chỉ tìm ra khi đã sửa xong sáu màn đầu và grep lại lần nữa — bài học: đếm bằng grep trên toàn thư mục trước khi báo số, đừng đếm từ danh sách ảnh mình vừa xem.

Cách sửa là đổi lớp gốc bg-sand → n12-deep, không đi sửa màu từng thành phần: .n12-deep đè token nên thẻ, chữ, viền, nền nhạt bên trong tự theo. Đó cũng là lý do một màn mới viết sau này chỉ cần đặt đúng lớp gốc.

ErrorBoundary đáng nói riêng: nó là màn dễ gặp nhất trong các màn hiếm gặp, và một màn trắng toát giữa một app nền tối đọc ra như app vừa sập — đúng lúc learner cần tin rằng mọi thứ còn trong tầm kiểm soát.

Sau khi sửa, cả chín màn đo được ≤1% điểm sáng (phần còn lại là chữ và icon). Không còn màn nền sáng nào trong learn.

2·1·15. Ô chọn môn hạ màu, và màn tối chỉ còn MỘT màu hành động (SRC-813) ​

Ô chọn môn. Bảy ô môn từng là bảy mảng màu đặc (xanh lá, xanh dương, đỏ, tím, teal…) cạnh nhau trên nền navy gần đen — thứ ồn nhất màn hình, ở đúng chỗ đáng ra chỉ cần liệt kê.

Luật SRC-261 — "mỗi môn một MÀU cố định để nhận ra bằng mắt" — không bị bỏ, màu chỉ đổi chỗ đứng: rời khỏi nền ô và về hai chỗ hẹp hơn mà mắt vẫn bắt được ngay, là vạch 3px ở mép trên và nét vẽ của hình môn. Nền ô nay là kính --surface-glass, nên ô môn đọc ra là cùng một loại vật thể với mọi thẻ khác trong app. Đây đúng cách v0.4 đã giải bài nhận diện school bằng --school-mark 2px thay vì nhuộm cả thẻ.

Một màu hành động. --primary của .n12-deep từ xanh nước #2E86D8 về coral. Trước đó một màn có thể mang cả nút xanh lẫn nút coral, tức learner phải học hai tín hiệu cho cùng một việc "chỗ này bấm được".

Dùng --accent-deep #CE4019 chứ không --accent #F45B32: coral gốc chỉ đạt 3,29:1 với chữ trắng, không cõng nổi chữ trên nền đặc; bản đậm đạt 4,80:1. Đây đúng lý do DS giữ hai biến coral (§2·1·3) — đừng gộp lại.

Hệ quả phải biết trước: --primary không chỉ nuôi nút. Nó còn nuôi thanh tiến độ, badge mặc định, chip đang chọn và màu bôi đen văn bản. Đổi sang coral nghĩa là coral xuất hiện rộng hơn luật "≤3% diện tích" của §2·1 — đây là một đánh đổi có chủ đích theo chỉ đạo, không phải một chỗ bị bỏ sót. Nếu sau này thấy coral quá nhiều thì chỗ đáng sửa là thanh tiến độ, không phải nút.

2·1·16. Thẻ trên nền tối là MÀU ĐẶC, và mỗi trang có vài icon coral đặc (SRC-814) ​

Thẻ không còn là kính. Chủ dự án 2026-09-18 gửi ba ảnh trang IELTS và yêu cầu mọi thẻ nhỏ trong mọi trang dùng đúng màu thẻ trong ảnh. Vấn đề thật không phải "chọn sai hex" mà là kính không cho ra một màu:

Kính --surface-glass lấy màu từ thứ nằm dưới nó. Trên nền tối — vốn là một dải chuyển sắc — thẻ ở đầu trang ra #233048, thẻ cuối trang ra #131A26. Năm thẻ giống hệt nhau về vai trò mà mỗi thẻ một màu, và càng cuộn xuống càng nhạt dần so với nhau. Mắt đọc ra là "trang chưa hoàn thiện" mà không chỉ được tên.

Nay thẻ dùng --card-deep #1D2533 — đúng tấm kính ấy đã hoà sẵn với nền trang rồi nướng thành màu đặc (#0C1524 + trắng 7%). Cùng phép tính với --nav-surface, chỉ khác nền để hoà: thanh hoà với mặt nước (--n12-deep-surface) vì nó đứng ở đỉnh và không cuộn; thẻ hoà với nền trang (--n12-deep-sea) vì thẻ nằm rải khắp trang. Kính vẫn đúng cho thanh điều hướng — đừng đổi chỗ hai thứ.

Icon coral đặc. Chỉ đạo: "trong mỗi trang đều có một vài icon màu da cam, đặc". Chỗ đúng để làm việc đó là .n12-icon — khung tròn đứng đầu mỗi thẻ việc, vốn đã là "một vài cái mỗi trang" (ba tới năm). Nên không phải thêm icon vào đâu cả, chỉ đổi màu khung từ đĩa xanh nhạt sang coral đặc.

Tô khung chứ không tô nét vẽ: nét vẽ ở learn phần lớn là tranh nhiều màu (rùa, hải đăng, sứa), tô coral lên chúng là mất tranh. Khung tròn là hình nền, tô đặc được mà không đụng tới hình.

Một thẻ trong hàng (Khoá học) tự dựng khung tròn bằng bg-muted thay vì dùng .n12-icon, nên phải sửa riêng sang bg-accent — không đổi nó sang chính lớp .n12-icon, vì cổng check-shadcn-adoption đang ở đúng trần 97 cho learn và thêm một chỗ dùng lớp cũ là đỏ.

Chưa đụng: hai khung icon lớn ở màn trống (--n12-ocean-100 khai inline) vẫn để nhạt — chúng là hình minh hoạ của màn trống, mỗi màn một cái, không thuộc "hàng vài icon" mà chỉ đạo nói tới.

2·1·16b. Thẻ năng lực: tiêu đề tiếng Anh, ba thẻ một hàng (SRC-818) ​

Chỉ đạo chủ dự án 2026-09-18, sau khi xem ba trang /ielts/skills/reading, /ielts/skills/listening và /ielts/courses: "trong các card nhỏ, sub-title cần dài không quá 10 từ", "mỗi dòng trình bày 3 cards", "nên dùng tiếng Anh là chính", "tiêu đề tiếng Anh, tiếng Việt xuống dòng dưới nhỏ hơn".

Tiêu đề hai dòng. Tên tiếng Anh đứng trên ở cỡ chữ tiêu đề, tên tiếng Việt xuống dòng dưới ở text-sm text-muted-foreground. Đây là một ngoại lệ có phạm vi của luật "chữ người dùng đọc là tiếng Việt" (CLAUDE.md): Main Idea, Gist, Note completion là chính chữ learner gặp trong đề thi, nên bắt nó đọc tiếng Việt trước là dạy một cái tên nó sẽ không thấy ở đâu nữa. Ngoại lệ chỉ áp cho tiêu đề của mạch IELTS; câu mô tả và mọi chức năng khác vẫn theo nút VI/EN như cũ.

Sub-title tối đa 10 từ, cắt ở tầng hiển thị. teaser() trong MicroSkill.tsx lấy câu đầu của what_vi, còn dài quá thì cắt ở từ thứ 10. Không sửa what_vi trong D1: cùng chuỗi ấy còn là đoạn mở đầu của trang lý thuyết, nơi nó cần nói đủ ý — cắt dữ liệu cho vừa cái thẻ là làm hỏng trang kia mà không ai thấy ngay.

Ba cột, và cục coral đầu thẻ là số thứ tự. lg:grid-cols-3 → sm:grid-cols-2 → một cột; ba cột trên điện thoại thì chữ vắt thành từng cột hẹp không đọc được. Chỉ đạo muốn "một cục màu da cam" đầu mỗi thẻ; chọn số thứ tự năng lực (01, 02…) chứ không phải pill tên tiếng Anh, vì tên ấy nay đã là tiêu đề — một pill lặp lại chính tiêu đề không nói thêm điều gì, còn số thì nói được thứ tự nên học.

Ô lớn của trang kỹ năng theo cùng luật. HubTile (apps/learn/src/ielts/Hub.tsx) dùng chung cho cả bốn kỹ năng: dòng eyebrow từ chữ mờ hoa nhỏ thành pill coral, tiêu đề là tên tiếng Anh, titleVi là dòng tiếng Việt nhỏ hơn, và câu mô tả đi qua cùng hàm teaser(). Nhãn pill luôn viết tiếng Anh, không đổi theo nút VI/EN: nó là một cái tag, mà tag thì phải giống nhau ở mọi ô. titleVi để trống được — ô "Keep going" lấy tiêu đề từ server và chỉ có một thứ tiếng, ép thêm một dòng rỗng ở đó thì tệ hơn là thiếu nó.

2·1·17. Màn "Hôm nay" tối om suốt một tháng — luật ngoài @layer đè position: fixed của tour (SRC-819) ​

Triệu chứng. /turtle/learn/math (và mọi môn) khi mở lần đầu: cả màn phủ tối, pixel sáng nhất chỉ (107,110,113), nút coral ra nâu đục, không có lỗ sáng, bấm ra ngoài không thoát. Có từ 7f4ad443 (SRC-369, 2026-08-19). Lỗi thật trên production — đã đo trên Google Chrome thật, và CSS đang phục vụ ở learn.nemo12.com có đúng cascade này.

Cơ chế (30 agent điều tra, 18/18 phản biện không bác được, tôi đo lại trên build sạch từ main):

  1. Tour learn-cockpit tự mở cho người mới; đèn rọi .n12-tour-spot vẽ vùng tối bằng box-shadow: 0 0 0 9999px color-mix(--n12-ocean-950 62%, transparent) — tức phủ 62% #10151A lên mọi thứ ngoài ô được rọi.
  2. DS khai .n12-tour-veil/-spot/-card { position: fixed } trong @layer components. learn có luật .n12-deep > * { position: relative; z-index: 1 } ngoài mọi @layer. Theo cascade layers, luật không-layer thắng luật trong layer bất kể specificity. <Tour> là con trực tiếp của <main class="n12-deep"> → ba phần tử tour bị ép thành relative, rơi xuống đáy trang, ngoài khung nhìn.
  3. Nhưng box-shadow toả 9999px thì vẫn phủ cả viewport. Header cũng bị chính luật ấy ép từ sticky xuống relative.

Số khớp từng kênh: 0,38 × trắng + 0,62 × #10151A = (106.8, 109.9, 113.0).

Vì sao các probe đầu không thấy: quét phần tử fixed/absolute rộng >1000px — nhưng thủ phạm là một vật thể 454×175 nằm ở y=1509 có cái bóng khổng lồ. elementFromPoint xuyên qua vì veil cao 0px và spot pointer-events: none. Bài học: khi cả màn tối mà không có filter/opacity/overlay nào, hãy tìm cái gì toả ra, không chỉ cái gì phủ lên.

Cách sửa. Không đụng tour. Đảo chiều luật SRC-369: thay vì NÂNG mọi con của <main> lên trên bong bóng, HẠ bong bóng xuống — .n12-deep { isolation: isolate } + .n12-sea { z-index: -1 }. Trong stacking context của main, phần tử z-index: -1 vẽ sau nền nhưng trước mọi nội dung trong dòng chảy: đúng "dưới chữ, trên nền" mà SRC-369 muốn, mà không ghi đè position của ai. Tour, header sticky, popover giữ nguyên luật của chúng.

Luật rút ra cho mọi app: CSS của app không được viết ngoài @layer để ghi đè thứ DS đã khai trong layer. "Thắng bất kể specificity" nghĩa là thắng cả những chỗ mình không định thắng — và không cổng nào bắt được, vì computed style của từng phần tử vẫn "hợp lệ".

2·1·18. coral · admin · mentors bật hệ màu chung — nội dung sáng, thanh sẫm (SRC-822) ​

Chủ dự án 2026-09-18: "bật màu mới cho coral, admin, mentors luôn đi." Ba app cuối cùng của phần React còn ở v0.4.

"Màu mới" nghĩa là gì ở ba app này. Không phải kéo vào thế giới tối như learn/marlins: đây là công cụ làm việc (bảng, form, trình soạn) — nội dung dày, đọc lâu, nên nền sáng là đúng và chưa ai yêu cầu đổi. "Bật màu mới" = hai việc: <html class="n12-v1"> để lớp semantic Ocean Palette áp cho cả app (foam làm nền, deep-ocean làm primary, coral v1 làm accent, radius v1), và thanh điều hướng nền sẫm dùng chung — cùng sáu token --nav-* với www, nên người đi giữa các cổng gặp cùng một thanh.

.n12-nav-deep — một lớp cho cả ba thanh. www đã có bộ override riêng (.theme-www > header …). Chép bộ đó vào ba app nữa là bốn bản của cùng một thứ, và bản thứ tư sẽ lệch từ tuần sau. Nên DS khai một lớp: gắn lên <header> là thanh đổi sang --nav-*, kèm remap các utility hay gặp trong header (text-ocean-900, text-muted-foreground, border-border, bg-card/95, mục đang chọn bg-ocean-900 → nền sáng chữ sẫm).

Hai chỗ đáng ghi vì không hiển nhiên:

  • Lớp này nằm trong @layer utilities, không viết ngoài layer. Đây là hệ quả trực tiếp của §2·1·17: luật ngoài @layer thắng "bất kể specificity" và thắng cả chỗ mình không định thắng. Ở đây các remap đứng cùng layer với utility của Tailwind, thắng bằng specificity (0,2,0) > (0,1,0) và thứ tự nguồn — cascade thường, không có gì lén. Bộ override của www viết trước bài học ấy nên vẫn ở ngoài layer; đó là nợ, ghi lại chứ chưa dọn.
  • Tên n12-nav-deep cố ý không khớp n12-(btn|card|input|badge) mà cổng check-shadcn-adoption đếm. Cả ba app đang ở đúng trần (admin 0, coral 33, mentors 37) — một chỗ dùng lớp cũ nữa là CI đỏ. Tên lớp mới trong DS từ nay phải soi regex ấy trước khi đặt.

Kiểm chứng tới đâu, nói rõ: ba app này chặn sau đăng nhập Google và không có bộ e2e (nợ đã ghi ở /e2e-check §2b). Mỗi app do một agent sửa và một agent khác kiểm độc lập: typecheck, build, dựng preview rồi chụp màn đăng nhập/màn vào và đo pixel (vùng header phải sẫm, nền trang phải foam), quét tĩnh mọi <header> để không có trang sẫm trang trắng, và chạy bốn cổng check-shadcn-adoption · check-no-emdash · check-vi-type · check-english-identifiers. Phần sau đăng nhập chỉ tới typecheck + build + quét tĩnh — chưa có mắt nào xem.