---
url: https://docs.nemo12.com/product/marlins-ia/courses-and-pages.md
description: >-
  Marlins: card khoá học, khoá cho bố mẹ, Parent Learning System, thư viện sách,
  câu bố mẹ hay nói, trang chỗ học, Unit, quiz, câu định danh.
---

# Marlins IA · Khoá học, thư viện sách, trang chỗ học và quiz

Một phần của [Marlins IA](./index.md). card khoá học, khoá cho bố mẹ, Parent Learning System, thư viện sách, câu bố mẹ hay nói, trang chỗ học, Unit, quiz, câu định danh.

## Khoá học: card nhỏ, chi tiết ở trang riêng (SRC-652)

```
/courses            danh sách — MỖI KHOÁ MỘT CARD NHỎ (môn · số buổi học thử · giờ · buổi tới)
/courses/{id}       chi tiết một khoá — dùng lại đúng thân đầy đủ của CoursesPanel
```

Trước đó mọi khoá trải hết trên một trang: bố mẹ phải cuộn qua toàn bộ mô tả khoá đầu mới thấy
khoá thứ hai tồn tại, và không có URL nào để gửi riêng một khoá cho nhau. Trang chi tiết **không
có bản dựng thứ hai** — nó lọc `CoursesPanel` còn một khoá, nên nội dung và nút đăng ký không thể
lệch nhau.

**Bố mẹ học thử Unit 1 (7 ngày) — CHƯA DỰNG, đã gỡ khỏi UI (SRC-656).** Bản SRC-652 có một nút
trỏ tạm vào `learn.nemo12.com`; trên production nó dẫn bố mẹ tới màn "nhập mã mời" của học sinh.
Hai thứ còn thiếu, đều là dữ liệu/kiến trúc chứ không phải giao diện:

1. `learn` chặn toàn app khi tài khoản không có learner đang hoạt động (`App.tsx:1348`) — bố mẹ
   theo định nghĩa thì không có. Cần một đường xem thử cho vai phụ huynh.
2. Bảng `courses` (lớp có lịch) **không có cột nào trỏ sang `curriculum_courses`**, nên không có
   dữ liệu nào nói "Toán 1 dạy Unit nào". Deep link đúng cũng không dựng được.

Luật rút ra: **không hứa trên UI một tính năng chưa có đường chạy.** Một nút dẫn tới ngõ cụt tệ
hơn là không có nút — bố mẹ bấm, gặp màn sai, rồi mất tin vào phần còn lại của trang.

## Khoá học cho bố mẹ: danh sách gọn → trang chi tiết có Unit/Lesson (SRC-664)

```
/parent-courses           3 thẻ gọn: bậc · tên · "n unit · m bài". KHÔNG giá, KHÔNG mô tả.
/parent-courses/{code}    chi tiết: giới thiệu · giá · Unit 1 miễn phí · danh sách Unit → Lesson
```

Thẻ danh sách bỏ giá và mô tả vì ở màn **chọn**, hai thứ đó không giúp chọn — chúng chỉ làm mỗi
thẻ cao gấp ba và đẩy khoá thứ ba xuống dưới màn hình. Cả hai còn nguyên ở trang chi tiết, nơi bố
mẹ đã chọn xong và đang cân nhắc thật.

**Unit 1 miễn phí** nói ngay trên đầu danh sách unit: đây là thứ quyết định bố mẹ có bắt đầu hay
không. Các unit sau gắn nhãn "Mở khi đăng ký".

**Ranh giới nội dung (QG-014, đã gỡ ở SRC-672).** `packages.ts` khi đó chỉ chứa **cấu trúc rỗng** — tên unit và tên bài.
CLAUDE.md cho phép đúng phần này ("cấu trúc rỗng mà xưởng cần để bám vào… khi chưa có workflow
sinh chúng") và cấm gõ tay phần còn lại: Big Idea, Essential Question, guiding question,
formative task, kho câu hỏi. Tầng Unit **chưa có workflow nào** — đo trên D1 ngày 2026-08-26,
`content_runs` có `lesson`=253, `items`=395, **`unit`=0**.

Bài chỉ có **tên**, không có nút "Vào học": dựng nút dẫn tới trang rỗng đúng là lỗi đã mắc ở
SRC-656. Nhãn "Đang soạn" từng hiện trên mỗi bài nay **bỏ hết** (SRC-670) — lặp lại một trạng
thái nội bộ trên 16 dòng liền nhau là nhiễu, và nó nói về việc của Nemo12 chứ không phải việc
của bố mẹ.

### Bố cục trang chi tiết (SRC-670)

```
THÂN (trái)                          PANEL PHẢI (sticky)
  giới thiệu                           Học phí
  "Có phải bạn đang gặp?"              [ Bắt đầu học ]
  "Học xong, bạn sẽ"                     Miễn phí 1 lesson
  Accordion: Unit → EQ → Lesson
```

Bản 2026-09-03 (SRC-672) đổi ba chỗ. **Nỗi đau đứng đầu thân trang**, viết bằng giọng thứ nhất của
chính bố mẹ: người đọc phải nhận ra mình trong một câu thì mới đọc tiếp. **Mọi unit đóng sẵn** —
mở sẵn Unit 1 thì bố mẹ đọc luôn nó và không còn thấy khung cả khoá, mà khung mới là thứ giúp
quyết; Essential Question vẫn hiện ngay dưới tên unit kể cả khi đóng, nên đọc lướt vẫn biết mỗi
unit đi trả lời câu gì. **Nhãn "Học thử"/"Khoá" trên từng dòng bài bị bỏ**: 162 dòng mà dòng nào
cũng đeo nhãn thì nhãn không còn nói gì, và thông tin "miễn phí một bài" đã nằm ngay trong nút.

Nút **"Bắt đầu học" / "Miễn phí 1 lesson"** làm được thật: nó mở Unit 1 rồi cuộn tới đúng chỗ học
miễn phí. Không trỏ sang màn hình chưa tồn tại — luật rút ra ở SRC-656.

**Mỗi unit một card, mỗi lesson một card** (2026-09-03). Gộp cả chương trình vào một tấm thì 4-5
unit đọc như một danh sách dài không có ranh giới; tách card là mắt thấy ngay unit bắt đầu và kết
thúc ở đâu. Lesson cũng vậy: một chỗ học là một việc bố mẹ ngồi xuống làm, nên nó phải đọc như một
đơn vị trọn vẹn chứ không như một dòng trong danh sách. Cả chương trình vẫn nằm trong **một card
lớn** bọc ngoài, nên ba tầng thẻ đọc ra ba tầng nội dung: khoá → unit → chỗ học.

Cạm bẫy đã sập hai lần ở đây: card của unit phải là component `Card` **bọc ngoài**
`AccordionItem`, không phải `asChild` và cũng không phải utility `bg-card` viết tay. Lớp theme
`.theme-marlins.od-root` bám vào `[data-slot="card"]`; `asChild` để props của `AccordionItem`
thắng nên `data-slot` thành `accordion-item`, còn utility thì không có `data-slot` nào — cả hai
cách đều cho ra thẻ TRẮNG ĐẶC, chữ trắng trên nền trắng, trên nền biển sâu.

### Bên trong một Unit (2026-09-03)

Một unit có **đúng MỘT Essential Question**, hiện ngay dưới tên unit kể cả khi đang đóng. Bản
trước để ba câu ngang hàng nối bằng dấu chấm giữa: ba câu cùng cấp thì không câu nào dẫn đường
nữa, người đọc lướt qua như lướt một danh sách. Ba câu cũ nay là ba **lesson** bên dưới, mỗi
lesson một card, gồm việc bố mẹ làm và một guiding question.

**Key Concepts** đứng giữa Big Idea và các lesson, mỗi khái niệm một chip bấm được, mở popover
chứa nghĩa và một ví dụ có thật. Thuật ngữ **luôn giữ tiếng Anh** (SRC-604): thuật ngữ là định
danh, mỗi nơi dịch một kiểu thì bố mẹ tra tiếp ở tài liệu khác không ra. Nghĩa và ví dụ thì song
ngữ, vì đó mới là phần để hiểu. Không trải nghĩa ra thẳng trang: 3 khái niệm × nửa màn hình mỗi
unit thì khung khoá lại chìm, mà khung mới là thứ giúp quyết.

Số liệu hiện tại: 15 khoá · 54 unit · 54 Essential Question · 162 Key Concept · 162 lesson,
tổng 897 mẩu chữ song ngữ.

Học phí và lịch là thứ bố mẹ **tra đi tra lại** trong lúc đọc chương trình; nằm trong dòng chảy
nội dung thì mỗi lần muốn xem lại giá phải cuộn ngược lên đầu.

Chương trình gom vào **accordion** vì 4 unit × 3 bài trải hết là 16 dòng, mà ở màn
quyết-định-có-học-không thì bố mẹ cần thấy **khung khoá** trước. `type="multiple"` chứ không
`"single"`: bố mẹ hay mở hai unit cạnh nhau để so xem khoá đi từ đâu tới đâu — đóng cái này để mở
cái kia là cản đúng việc đó.

Chuyển động dùng `FadeIn` của `Motion.tsx` (DS-001: `motion`, qua `LazyMotion` + `m`), biên độ
10px, so le 60ms.

## Parent Learning System: 15 khoá cho bố mẹ (SRC-671)

Ba gói cũ (Marlin Start · Compass · Mind) **gắn `retired`, không xoá**: người đã mua vẫn còn dấu
vết và link cũ không 404. Thay bằng 15 khoá nhóm theo **giai đoạn của một vòng học**, không xếp
phẳng theo số — xếp phẳng thì 15 thẻ đọc như một danh mục rời rạc.

| Nhóm | Khoá |
| --- | --- |
| Hiểu con | 1 Understanding Your Child as a Learner · 2 The Student Portrait · 3 The Competency Framework for Parents |
| Chọn điều đáng học | 4 What Should My Child Learn? · 5 Goals That Matter · 6 Building 21 for Parents |
| Làm việc học hiệu quả | 7 How Children Actually Learn · 8 Assessment · 9 Learning Math Well · 10 Learning English for Life |
| Dựng tự chủ và quan hệ | 11 Raising an Independent Learner · 12 Talking So Your Child Can Hear You · 13 From Pressure to Ownership |
| Sẵn sàng cho tương lai | 14 Future Skills · 15 Designing Your Child's Learning Journey |

Tổng **54 unit · 162 chỗ học**. Mỗi khoá mang `jobs` trỏ về bảng 10 Job-To-Be-Done, nên tra ngược
được "job này khoá nào phục vụ".

**Giá**: 2.900.000đ một khoá; mua từ **3 khoá bất kỳ** thì còn 1.900.000đ mỗi khoá. Giá nói ngay
đầu danh sách chứ không giấu trong từng khoá — combo là lý do bố mẹ đọc tiếp cả 15 khoá thay vì
dừng ở khoá đầu thấy hợp.

**Luật học thử**: mở đúng **chỗ học đầu tiên của Unit 1**, phần còn lại khoá. Cài trong đúng một
hàm `isFreePreview(unitIndex, questionIndex)` chứ không rải cờ trên dữ liệu: luật mở khoá chỉ nên
có một chỗ để sửa, và chỗ đó phải là nơi cả UI lẫn API sau này cùng gọi. Test khoá bất biến "mỗi
khoá đúng MỘT chỗ miễn phí".

### Provenance: nội dung do chủ dự án soạn

Toàn bộ course outcome, tên unit, Big Idea và Essential Question **do chủ dự án soạn** và gửi
trong hội thoại 2026-09-03. Khi đó việc này còn vướng QG-014; **cổng ấy đã gỡ hẳn ở SRC-672**, nên
nội dung soạn tay nay hợp lệ, không cần miễn trừ gì.

Điều **vẫn giữ** là ghi rõ nguồn gốc: mỗi khoá mang `provenance: "owner"` **thay cho** một
`content_runs.id`. Đọc dữ liệu là biết ngay nó không ra từ xưởng nội dung — không ai phải đoán.
Đây là phần đáng giữ của QG-014 kể cả sau khi cổng chặn biến mất.

**Còn nợ**: mỗi chỗ học mới có *câu hỏi*, chưa có bài để học — kể cả chỗ đã mở miễn phí. Cần
workflow sinh nội dung tầng Unit (`content_runs.unit` = 0).

## Thư viện sách cho bố mẹ (SRC-673)

Hai mươi quyển, tên do chủ dự án gửi 2026-09-03, giữ **cả hai tên**: tên tiếng Anh kiểu book
title là tên riêng nên không dịch theo công tắc VI/EN, tên tiếng Việt đứng dưới như phụ đề. Phần
còn lại (lời hứa, nỗi đau, mục lục, đoạn đọc thử) song ngữ.

**Ba trang, không gộp:**

| URL | Việc của bố mẹ ở đây |
| --- | --- |
| `/ebooks` | nhìn cả kệ, lọc theo nhóm |
| `/ebooks/{slug}` | quyết có đọc quyển này không |
| `/ebooks/{slug}/read` | đọc |

Gộp trang đọc vào trang giới thiệu thì mất đường gửi riêng cho nhau, và một màn vừa bán vừa cho
đọc thì không làm tốt việc nào. Trang đọc cố ý là cột chữ hẹp giữa màn — ngược với luật
full-width của phần còn lại — vì dòng dài quá 75 ký tự thì mắt lạc dòng; cỡ chữ đổi được và nhớ
trong `localStorage` của chính máy người đọc.

**Bìa sinh bằng thuật toán** (`EbookCover.tsx`): bìa thật chưa có, nhưng 20 ô xám giống nhau thì
không nhận ra quyển nào. Bìa dựng từ băm FNV-1a của `seq:title` rồi bơm vào mulberry32, nên cùng
một tên luôn ra cùng một bìa — dùng `Math.random` thì mỗi lần tải một bìa khác, tức là mất luôn
tác dụng nhận diện. Bảng màu bó trong họ biển sâu chứ không lấy cả dải màu: 20 bìa cạnh nhau mỗi
tấm một tông thì trang đọc như hộp kẹo. Là SVG nên phóng to vẫn nét và không thêm request nào cho
một trang có 20 tấm bìa.

**Đọc thử = chương 1, và đó là sự thật của dữ liệu**: `excerpt` là toàn bộ chữ hiện có của quyển
sách trong repo. Trang đọc nói thẳng "các chương còn lại vẫn đang được viết" thay vì hứa một thứ
chưa có đường chạy (luật rút ra ở SRC-656).

## Những câu bố mẹ hay nói: Pain → Need → Competency (SRC-704)

Chỉ đạo chủ dự án 2026-09-12: dựng một trang giới thiệu event cho Marlins **không mở bằng
"Student Portrait"**, mà mở bằng những câu phụ huynh thực sự nói hàng ngày, rồi bóc dần
Pain → Need → Job → Event.

| URL | Trả lời câu gì |
| --- | --- |
| `/marlin-pains` | "Có ai giống nhà tôi không?" — 8 câu bố mẹ hay nói, mỗi câu một thẻ |
| `/marlin-pains/{slug}` | "Vậy thật ra con đang thiếu gì?" — quote → JTBD → điểm quan trọng → need → competency |

Bốn quyết định của trang này, và lý do:

* **Đọc KHÔNG cần đăng nhập.** Người đọc chính là phụ huynh *chưa* có tài khoản; đặt trang sau cửa
  Google thì chính người cần nó nhất là người không mở được. Nhánh route vì thế nằm **trước** cửa
  đăng nhập trong `apps/marlins/src/App.tsx`, và hai endpoint `GET /v1/marlin-pains[/{slug}]` là
  public có khai báo trong `authCoverage.test.ts` — cùng lẽ với `/v1/b21/*`.
* **Nội dung nằm trong D1, không nằm trong bundle.** `marlin_pains` + `marlin_pain_quotes` +
  `marlin_pain_needs` + `marlin_pain_need_competencies` (migration 0214, seed
  `scripts/seed-marlin-pains.sql`). Sửa một câu là một lần seed, không phải một lần deploy app.
* **Tên competency lấy bằng JOIN sang `b21_competencies`, không chép lại.** Bảng nối chỉ giữ *mã*
  và câu `why` giải thích vì sao competency ấy trả lời cho need ấy. Chép tên vào bảng nối thì lần
  cập nhật khung B21 đầu tiên là lần trang này bắt đầu nói khác với `b21.nemo12.com`. Mã đã bị khung
  gỡ vẫn hiện (rơi về chính mã đó) chứ không im lặng biến mất — mất một dòng mà không ai biết là
  đúng thứ lỗi khó tìm nhất.
* **Bấm một need thì mở ra tại chỗ, không sang trang khác.** Phụ huynh đang đọc một mạch từ câu
  than phiền xuống; một lần đổi trang ở giữa mạch đó là một lần mất chỗ. Link từng competency mở
  tab mới sang `b21.nemo12.com/f/{khung}/c/{mã}` — cùng lý do, chỉ ngược chiều: rời cổng Marlins
  giữa mạch đọc thì phải quay lại được bằng cách đóng tab.

Ánh xạ 8 pain → 16 need → 42 competency của khung `nemo` (Building 21 Student Competencies) là
**ánh xạ do người chọn**, không phải suy ra từ từ khoá: một cổng keyword sẽ nối "motivation" với
mọi competency có chữ "effort" và không nối được need `relevance` với `SS.3 Make an Impact`, tức
là sai ở đúng chỗ ánh xạ này có giá trị.

Chữ trên trang là **tiếng Việt, thuật ngữ giữ nguyên tiếng Anh** (Autonomy, JTBD, Transferable
Skills) đúng như bản chủ dự án viết, và trang **không có nút EN/VI**: chỉ có một bản, nên một cái
nút hai lựa chọn mà một lựa chọn không tồn tại là một cái nút nói dối.

## Trang khoá bố mẹ: học phí đóng sẵn, badge trên nền biển, khối năng lực chỉ cho người soạn (SRC-705)

Ba chỉnh từ ảnh chụp màn hình của chủ dự án ngày 2026-09-13, đều trên `/parent-courses/{code}`.

**Học phí vào accordion đóng sẵn.** SRC-664 đã bỏ giá khỏi thẻ danh sách vì ở màn *chọn* giá
không giúp chọn; ở trang chi tiết thì giá ở lại, nhưng nó đang **phơi ra** trong một thẻ riêng ở
cột phải, tức là thứ đầu tiên mắt bắt được khi trang mở. Bố mẹ quyết bằng con số trước khi kịp
đọc khoá dạy gì. Nay nó đóng sẵn dưới tiêu đề "Học phí", một cú bấm là mở — không giấu, chỉ đổi
thứ tự đọc: nội dung trước, giá sau.

**Badge viền phải tự khai màu chữ trong theme biển sâu.** `variant="outline"` của shadcn là
`border-border text-foreground`, mà `--foreground` là màu mực sinh ra cho nền sáng; trên nền biển
nó ra chữ gần đen trên xanh đậm và chip Key Concept đọc như đang bị vô hiệu hoá. Đây là lần thứ
**ba** cùng một khuôn hỏng trong `.theme-marlins` (trước đó: `.text-muted-foreground`, và
`.n12-tab` ở SRC-580) — **bất cứ lớp nào lấy màu mực làm mặc định đều chìm ở cổng này, và không
có gì đỏ ở đâu cả.** Luật mới nhắm đúng `[data-slot="badge"].border-border` để badge đặc ("Bậc
1", "Miễn phí") giữ nguyên màu nền của chúng.

**Khối năng lực trong Unit chỉ hiện cho người soạn.** Mã `PC.1.2`, chữ "bậc", "ảnh hưởng ké" là
ngôn ngữ của người thiết kế chương trình; bố mẹ vào đây để hiểu con mình. `apps/marlins/src/authorView.ts`
gác `UnitCompetencyBlock` theo email tài khoản.

Đây là cổng **hiển thị**, không phải gác bảo mật: `GET /v1/marlin-framework` vẫn trả khung năng
lực cho mọi người và devtools vẫn đọc được. Đúng mức cần cho một thứ để soát nội dung — ngày nào
khung trở thành thứ phải giấu thật thì phải gác ở API, không phải ở màn hình. Kiểm ở **cả hai
chỗ** (`Courses.tsx` và trong chính component): để component tự trả `null` thì `div` bọc ngoài
vẫn còn và để lại một khoảng trống không ai giải thích được.

`CourseCompetencySummary` ở đầu trang vẫn hiện cho mọi người — nó cũng dùng mã `PC.x`, và có thể
sẽ theo sau, nhưng nó không nằm trong ảnh chủ dự án gửi nên chưa đổi.

## Trang chỗ học: khoảng cách là thứ chia bài, không phải trang trí (SRC-706)

Ảnh chủ dự án gửi 2026-09-13: body của `/parent-courses/{code}/u/{ui}/l/{li}` đọc như một mảng
chữ liền. Hai nguyên nhân, và chúng cộng hưởng với nhau.

**Mọi khối cách nhau đúng một khoảng.** Wrapper để `space-y-6`, tức khoảng cách giữa hai cụm lớn
bằng khoảng cách giữa hai đoạn văn bên trong một cụm. Mắt không có chỗ nào để biết một cụm đã kết
thúc, nên toàn trang phải đọc tuần tự từ trên xuống mới hiểu cấu trúc — trong khi cấu trúc ấy đã
được vẽ sẵn ở panel trái. Nay `space-y-16` ở cấp cụm, `space-y-5`/`p-8` bên trong: chênh lệch
giữa hai mức mới là thứ nói "đây là một cụm khác".

**Ba dòng đầu trang lặp lại thứ đã có ở chỗ khác.** "Unit n · tên unit" nằm ngay bên trái trong
panel mục lục; "chỗ học miễn phí" đã nói ở trang khoá lúc bố mẹ bấm vào. Cả hai bị bỏ — bố mẹ mở
một chỗ học ra là để đọc nội dung, không phải để được nhắc mình đang ở đâu ba lần. Còn lại tên
bài và Guiding Question.

Khối quiz rút từ ba dòng xuống một: bỏ "n câu, mỗi câu 4 phương án" (bấm vào là thấy, biết trước
cũng không đổi quyết định) và bỏ dòng ba con số lần-gần-nhất/tốt-nhất/số-lần. **Ba con số đứng
cạnh nhau thì không con số nào đọng lại**; giữ đúng "tốt nhất" là cái bố mẹ thật sự muốn biết.

Giới hạn đã biết: `apps/marlins` chưa có bộ e2e nên thay đổi này chỉ được kiểm tới `typecheck` +
`build`, không có cổng nào bắt lỗi runtime hay nhìn được bố cục thật.

## Tổng quan Unit: bậc thứ ba có đường đi tới (SRC-707)

Chỉ đạo chủ dự án 2026-09-13: panel trái của chế độ học phải có một link mở **Unit Overview**.

Chẩn đoán: cây khoá có bốn bậc (Course → Unit → Lesson → cụm), nhưng panel trái chỉ có đường tới
hai bậc — khoá (logo, nút ✕) và chỗ học. Bậc **Unit** giữ đúng ba thứ quyết định nghĩa của cả
unit (Big Idea · Essential Question · Key Concepts) mà không có cửa nào: bố mẹ đang đọc dở phải
thoát ra trang bán rồi mở đúng accordion mới xem lại được "unit này trả lời câu gì".

| URL | Trả lời câu gì |
| --- | --- |
| `/parent-courses/{code}/u/{n}` | "Unit này rốt cuộc đi trả lời câu gì, và bắt đầu từ đâu" |
| `/parent-courses/{code}/u/{n}/l/{m}` | "Tôi làm gì trong chỗ học này" |

Bốn quyết định:

* **Hai cửa vào, không phải một.** Một dòng `Tổng quan Unit n` đứng ngay trên mục lục (một cú bấm,
  không phải mở dropdown trước), và tên unit trong dropdown nay **là nút** chứ không phải nhãn —
  một tiêu đề đứng trước danh sách mà bấm không được là đúng chỗ người ta sẽ bấm đầu tiên.
* **Cùng khung học, không phải trang khác.** Tổng quan unit là một bậc của chính cây ấy, nên nó
  giữ panel trái; đưa ra ngoài khung học thì bố mẹ mất mục lục ngay lúc cần nó nhất.
* **Mục lục đổi theo màn đang mở.** Ở tổng quan, "Trong bài này" thành **"Trong unit này"** và
  liệt kê các chỗ học. Liệt kê các cụm của chỗ học số 1 trong lúc xem tổng quan là mời bố mẹ vào
  một chỗ chưa ai chọn.
* **Không khoá theo học thử.** Trang này là KHUNG của unit — thứ đã hiện công khai ở trang khoá để
  bố mẹ quyết có mua không. Khoá nó ở đây là giấu đúng thứ mình đang dùng để mời. Nội dung thân
  bài vẫn khoá như cũ (`isFreePreview`).

Khác trang khoá ở một chỗ có chủ đích: Key Concept ở đây **trải nghĩa và ví dụ ra thẳng trang**,
không giấu sau popover. Ở trang bán mỗi chữ thêm là một chữ đẩy khung khoá xuống dưới; ở đây bố mẹ
đã vào học và đang cần đúng phần sâu ấy.

## Quiz là một cụm, nên nó chỉ hiện ở màn của nó (SRC-708)

Thân bài vốn đã hiện **một cụm một lúc** từ 2026-09-04 — nhưng khối quiz thì được render ở **mọi**
cụm. Nên màn "Vào bài" mang theo cả thẻ quiz của cụm thứ tư, màn "Ba ví dụ" cũng vậy: mục lục bên
trái nói có bốn cụm, còn màn hình thì mỗi cụm rưỡi một lần. Đó là phần lớn cảm giác chật còn lại
sau khi đã giãn khoảng cách ở SRC-706.

Nay `here.slug === "quiz"` mới render khối đó, đúng như ba cụm kia. Kèm theo: viên nút "Đọc xong
cụm này" không hiện ở màn quiz — ở đó việc phải làm là bấm "Làm quiz", nên hai lời mời cạnh nhau
cho cùng một bước là một lời mời thừa.

Đường đi vẫn liền: đánh dấu xong cụm cuối trước quiz thì `toggle()` đưa thẳng sang trang quiz
(logic có sẵn), nên bố mẹ đọc tuần tự không bao giờ phải ghé màn quiz để bấm thêm một nút nữa.
Màn quiz là chỗ dành cho người bấm thẳng vào mục 4 trong mục lục, hoặc quay lại sau một tuần để
làm lại.

## Câu định danh của cổng: một câu, hai đầu trang (SRC-721)

Chỉ đạo chủ dự án 2026-09-15: chỗ cạnh logo đổi từ **"cổng phụ huynh"** sang

> Cộng đồng cha mẹ thay đổi bản thân, để giúp con đi được xa hơn

và câu ấy là **giá trị dùng chung** cho cả header lẫn chân trang, giữ ở
[`apps/marlins/src/brand.ts`](../../../apps/marlins/src/brand.ts).

Vì sao đổi: "cổng phụ huynh" mô tả **cái cửa** (đây là khu vực dành cho phụ huynh), còn câu mới nói
**bố mẹ vào đây để làm gì**. Trước chỉ đạo này header và chân trang còn nói hai câu khác hẳn nhau,
tức là hai đầu của cùng một trang tự giới thiệu bằng hai giọng.

Hai độ dài, không phải một câu rồi cắt bằng CSS: từ `xl` trở lên hiện câu đầy đủ, hẹp hơn thì bản
ngắn **"Cộng đồng cha mẹ thay đổi bản thân"**. Cắt bằng `truncate` sẽ ra "Cộng đồng cha mẹ thay đổi
bản…", một câu dở dang đọc như lỗi hiển thị; bản ngắn dừng ở một mệnh đề trọn nghĩa.

Câu này nằm ở **dòng thứ hai, dưới tên Marlins**, không nối tiếp cùng dòng với logo: nó dài hơn hẳn
nhãn cũ, để cùng dòng thì trên laptop nó đẩy công tắc ngôn ngữ và menu avatar sát mép. Thứ tự đọc
cũng đúng vai — tên trước, lời hứa sau.
