---
url: https://docs.nemo12.com/design-system/ocean-world-dark-surfaces.md
description: >-
  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.
---

# 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](./index.md). 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:

| Token | Giá 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` | `#0C1524` | nền trang tối / dải tối mặc định |
| `--n12-deep-surface` | `#12203A` | bề 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.
