/* Lớp component kiểu Gameboy phiêu lưu (người dùng 21/10/2026: "UI phong cách claude tối giản như hiện tại, component phong cách
   gameboy phiêu lưu"; khởi đầu từ nút "Đã đọc"). Khung trang (thanh bên, thanh trên, chữ bài) vẫn là site-kit + theme.css; tệp này
   chỉ đổi component: khung Mục tiêu, mục Khi đi thi / Tóm tắt, câu hỏi, đáp án, trang trước/sau, khối code, nút.
   Nạp SAU theme.css ở mọi wiki, và trong ứng dụng /app/ (scripts/app.mjs chèn link) nên chỉ dựa vào biến của site-kit.
   Ô pixel --P = 2px. Khung: lớp ::before ghép nhiều dải màu (linear-gradient) thành hình chữ nhật bo bậc thang (lớn 4-2-1-1 ô,
   nhỏ 2-1 ô như nút "Đã đọc"), viền --px-b, nền --px-f; bóng pixel lệch --px-d bằng drop-shadow nên ôm đúng bậc thang.
   Nút nhỏ: viền 4 box-shadow (góc khía một ô). Chuyển động dùng steps() cho nhịp máy chơi game; --motion 0 thì đứng yên. */
:root {
  --P: 2px;
  --gb-bg: oklch(.89 .085 124); --gb-ink: oklch(.36 .075 140); --gb-hi: oklch(.78 .14 82);
  --gb-gold: oklch(.80 .135 84); --gb-gold-hi: oklch(.95 .07 96); --gb-gold-lo: oklch(.60 .12 68);
  --bad: oklch(.62 .135 33); --bad-ink: oklch(.53 .14 33);
  --px-ink: var(--text);
  --px-soft: color-mix(in oklab, var(--text) 28%, var(--bg));
  --px-shade: color-mix(in oklab, var(--text) 18%, var(--bg));
  /* Sprite pixel (sách phép, rương Mimic, cảnh Frieren bị ngậm; 04. Workspace/frieren-style-20261023/sprites.py, mimic_vec.py).
     Rương theo ảnh phim: ván gỗ đỏ sẫm, đai và vành đồng nâu, miệng tối, nanh trắng; áo choàng trắng, tóc bạc, tất xanh đen, ủng nâu.
     Vàng dùng chung --gb-gold như cúp; bìa sách theo màu nhấn (.px-book.v0…v3). */
  --fx-k: oklch(.24 .03 20); --fx-R: oklch(.47 .135 27); --fx-r: oklch(.55 .13 30); --fx-D: oklch(.38 .11 25);
  --fx-b: oklch(.50 .07 60); --fx-B: oklch(.62 .075 70); --fx-m: oklch(.17 .03 15); --fx-t: oklch(.985 .008 95); --fx-T: oklch(.84 .012 80);
  --fx-q: oklch(.66 .15 12); --fx-W: oklch(.985 .006 95); --fx-V: oklch(.88 .012 90); --fx-a: oklch(.79 .06 290);
  --fx-n: oklch(.30 .06 275); --fx-N: oklch(.42 .06 275); --fx-o: oklch(.48 .07 55); --fx-O: oklch(.60 .08 62); --fx-so: oklch(.35 .05 50);
  --fx-p: oklch(.96 .025 90); --fx-l: oklch(.76 .045 85); --fx-gem: oklch(.56 .19 27);
  --fx-petal: oklch(.74 .10 240); --fx-petal-2: oklch(.78 .10 350);
}
:root[data-theme="dark"] {
  --gb-bg: oklch(.40 .065 138); --gb-ink: oklch(.90 .09 122); --gb-hi: oklch(.84 .13 85);
  --bad: oklch(.70 .11 35); --bad-ink: oklch(.77 .10 38);
  --px-ink: color-mix(in oklab, var(--text) 82%, var(--bg));
  --px-soft: color-mix(in oklab, var(--text) 30%, var(--bg));
  --px-shade: color-mix(in oklab, #000 42%, var(--bg));
  --fx-k: oklch(.15 .02 20);
}
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) {
  --gb-bg: oklch(.40 .065 138); --gb-ink: oklch(.90 .09 122); --gb-hi: oklch(.84 .13 85);
  --bad: oklch(.70 .11 35); --bad-ink: oklch(.77 .10 38);
  --px-ink: color-mix(in oklab, var(--text) 82%, var(--bg));
  --px-soft: color-mix(in oklab, var(--text) 30%, var(--bg));
  --px-shade: color-mix(in oklab, #000 42%, var(--bg));
    --fx-k: oklch(.15 .02 20);
} }
@media (prefers-reduced-motion: reduce) { :root { --motion: 0 !important; } }

/* ---------- hình minh hoạ: một họ chữ ---------- */
/* Trong hình chỉ dùng Inter cho mọi chữ và số, số đều cột (người dùng 21/10/2026: "nhiều font chữ trong hình minh hoạ đang hơi lạc quẻ").
   Trước đó một hình trộn ba họ: nhãn Inter, số JetBrains Mono, gốc svg Lora. Chú thích dưới hình là lời bài nên vẫn Lora như chữ chính.
   Ghi đè thuộc tính font-family của từng <text> nên cần !important; hình động (anim) và hình tương tác CP cũng là svg trong .fig. */
.fig svg, .fig svg :is(text, tspan) { font-family: var(--sans) !important; font-variant-numeric: tabular-nums; }

/* ---------- khung pixel ---------- */
/* :where() giữ độ ưu tiên 0 để luật riêng của từng component (màu viền, nền, bóng) luôn thắng phần chung này. */
:where(.callout, .quiz, .theory .t-sec.is-example, .theory .t-sec.is-exam, .theory .t-sec.is-summary, .theory .hook, .pager-link, .qz-sub, .quiz-opt) {
  --px-b: var(--px-ink); --px-f: var(--surface); --px-s: var(--px-shade); --px-d: var(--P);
  position: relative; isolation: isolate; border-color: transparent !important; border-radius: 0 !important; background: none !important; box-shadow: none !important;
}
:where(.callout, .quiz, .theory .t-sec.is-example, .theory .t-sec.is-exam, .theory .t-sec.is-summary, .theory .hook, .pager-link, .qz-sub)::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    linear-gradient(var(--px-f) 0 0) calc(var(--P) * 4) var(--P) / calc(100% - var(--P) * 8) calc(100% - var(--P) * 2),
    linear-gradient(var(--px-f) 0 0) calc(var(--P) * 2) calc(var(--P) * 2) / calc(100% - var(--P) * 4) calc(100% - var(--P) * 4),
    linear-gradient(var(--px-f) 0 0) var(--P) calc(var(--P) * 4) / calc(100% - var(--P) * 2) calc(100% - var(--P) * 8),
    linear-gradient(var(--px-b) 0 0) calc(var(--P) * 4) 0 / calc(100% - var(--P) * 8) 100%,
    linear-gradient(var(--px-b) 0 0) calc(var(--P) * 2) var(--P) / calc(100% - var(--P) * 4) calc(100% - var(--P) * 2),
    linear-gradient(var(--px-b) 0 0) var(--P) calc(var(--P) * 2) / calc(100% - var(--P) * 2) calc(100% - var(--P) * 4),
    linear-gradient(var(--px-b) 0 0) 0 calc(var(--P) * 4) / 100% calc(100% - var(--P) * 8);
  background-repeat: no-repeat;
  filter: drop-shadow(var(--px-d) var(--px-d) 0 var(--px-s));
  transition: filter calc(90ms * var(--motion, 1)) steps(2);
}
.quiz-opt::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    linear-gradient(var(--px-f) 0 0) calc(var(--P) * 2) var(--P) / calc(100% - var(--P) * 4) calc(100% - var(--P) * 2),
    linear-gradient(var(--px-f) 0 0) var(--P) calc(var(--P) * 2) / calc(100% - var(--P) * 2) calc(100% - var(--P) * 4),
    linear-gradient(var(--px-b) 0 0) calc(var(--P) * 2) 0 / calc(100% - var(--P) * 4) 100%,
    linear-gradient(var(--px-b) 0 0) var(--P) var(--P) / calc(100% - var(--P) * 2) calc(100% - var(--P) * 2),
    linear-gradient(var(--px-b) 0 0) 0 calc(var(--P) * 2) / 100% calc(100% - var(--P) * 4);
  background-repeat: no-repeat;
  filter: drop-shadow(var(--px-d) var(--px-d) 0 var(--px-s));
  transition: filter calc(90ms * var(--motion, 1)) steps(2);
}

/* Nhãn kiểu hộp thoại game: chữ mono in hoa thưa, mũi tên pixel ▶ phía trước. */
.callout-title, .quiz-label {
  display: flex; align-items: center; gap: 9px;
  font: 700 11.5px/1.3 var(--mono) !important; letter-spacing: .12em !important; text-transform: uppercase;
}
.callout-title::before, .quiz-label::before {
  content: ""; flex: none; width: 6px; height: 10px; background: currentColor;
  clip-path: polygon(0 0, 2px 0, 2px 2px, 4px 2px, 4px 4px, 6px 4px, 6px 6px, 4px 6px, 4px 8px, 2px 8px, 2px 10px, 0 10px);
}
/* Đường kẻ trong khung: chấm pixel 2px. */
.quiz-why, .quiz-src {
  border-top: 0 !important;
  background: repeating-linear-gradient(90deg, var(--px-soft) 0 var(--P), transparent var(--P) calc(var(--P) * 3)) 0 0 / 100% var(--P) no-repeat;
}

/* ---------- khung Mục tiêu, ghi chú ---------- */
.callout { padding: 18px 22px 8px; }
.callout-goal { --px-f: color-mix(in oklab, var(--accent) 7%, var(--surface)); --px-s: var(--accent); }
.callout-goal .callout-title { color: var(--accent-ink); }
.callout-trap { --px-f: color-mix(in oklab, var(--accent) 12%, var(--surface)); --px-b: var(--accent-ink); }
.callout-tip, .callout-note, .callout-proof { --px-b: var(--px-soft); --px-d: 0px; }
/* Khối thu gọn (người dùng 23/10/2026: chứng minh và bài nâng cao mặc định ẩn, bấm mới hiện; "2 style khác nhau cho 2 loại… thể hiện
   được là một nút bấm khó khăn", "phong cách gameboy fantasy", âm hưởng Frieren nhưng "vẫn tuân thủ tý màu + style gốc của trang",
   "mimic, sách cũng phải giống giống trong frieren"). Sprite nhiều màu do render.py chèn (FX_BOOK, FX_CHEST), mỗi ô 2px:
   - Chứng minh: nút pixel như nút "Đã đọc" (viền mực, bóng xám), hình sách phép bìa da, góc và khoá vàng, vòng phép đá đỏ; rê chuột
     thì sách bay lên, vòng phép lấp lánh; mở ra thì sách mở trang, vòng phép bay lên, khung giãn hết bề ngang như khung ghi chú.
   - Bài nâng cao: nút mạ vàng với rương báu. Thỉnh thoảng nắp hé lộ răng (rương có thể là Mimic), rê chuột thì Mimic đớp liên hồi,
     thè lưỡi; bấm mở thì rương sập, rung, Frieren bị ngậm chỉ còn hai chân đạp loạn xạ, sau đó thỉnh thoảng đạp, rê chuột thì giãy
     (người dùng 23/10/2026: "mở ra phải ra frieren bị sập chứ haha"). Cảnh này cùng hình trang 404, vẽ ô 2px.
   Lề hai bên của nút cân nhau (người dùng 23/10/2026: sách và chữ "ko canh giữa đều… so với khung"): lề phải bớt 1px vì chữ giãn
   .12em để thừa một khoảng sau chữ cuối. */
.fx-ic { flex: none; display: inline-grid; line-height: 0; }
.fx-ic svg { display: block; overflow: visible; shape-rendering: crispEdges; }
/* Màu từng ô theo chữ cái trong sprites.py; 'r' và 'S' khác nghĩa giữa sách (đá đỏ, gáy sách) và rương (vệt sáng nắp, đế ủng). */
.fx-ic .c-k { fill: var(--fx-k); } .fx-ic .c-g { fill: var(--gb-gold); } .fx-ic .c-h { fill: var(--gb-gold-hi); }
.fx-ic .c-R { fill: var(--fx-R); } .fx-ic .c-D { fill: var(--fx-D); } .fx-ic .c-b { fill: var(--fx-b); } .fx-ic .c-B { fill: var(--fx-B); }
.fx-ic .c-m { fill: var(--fx-m); } .fx-ic .c-t { fill: var(--fx-t); } .fx-ic .c-T { fill: var(--fx-T); } .fx-ic .c-q { fill: var(--fx-q); }
.fx-ic .c-W { fill: var(--fx-W); } .fx-ic .c-V { fill: var(--fx-V); } .fx-ic .c-a { fill: var(--fx-a); }
.fx-ic .c-n { fill: var(--fx-n); } .fx-ic .c-N { fill: var(--fx-N); } .fx-ic .c-o { fill: var(--fx-o); } .fx-ic .c-O { fill: var(--fx-O); }
.fx-ic .c-L { fill: var(--fx-L); } .fx-ic .c-p { fill: var(--fx-p); } .fx-ic .c-l { fill: var(--fx-l); }
.fx-ic :is(.px-book, .px-book-open) .c-S { fill: var(--fx-S); } .fx-ic :is(.px-book, .px-book-open) .c-r { fill: var(--fx-gem); }
.fx-ic :is(.px-chest, .px-mimic) .c-r { fill: var(--fx-r); } .fx-ic .px-mimic .c-S { fill: var(--fx-so); }
details.callout-proof > summary, .adv > summary { cursor: pointer; list-style: none; -webkit-tap-highlight-color: transparent; }
details.callout-proof > summary::-webkit-details-marker, .adv > summary::-webkit-details-marker { display: none; }
:is(details.callout-proof, .prose details.callout-proof) > summary::before, .prose .adv > summary::before { content: none; }
details.callout-proof > summary:focus-visible, .adv > summary:focus-visible { outline: 2px dashed var(--accent); outline-offset: 6px; }

/* Chứng minh: đóng là nút nhỏ (khung bậc 2-1 ô như nút "Đã đọc"), mở là khung ghi chú; viền, bóng, chữ theo màu nhấn của trang
   (người dùng 23/10/2026: "style của trang (CP / AI) màu nào thì cũng phải theo màu đó"). */
details.callout-proof[open] { --px-b: color-mix(in oklab, var(--accent) 38%, var(--bg)); }
details.callout-proof > summary { color: var(--accent-ink); }
/* summary phải là khối flex: để inline-flex thì nó nằm trên một dòng chữ cao 26,4px của khung, chừa chỗ chân chữ, nút dư 7px ở đáy
   nên sách và chữ lệch lên (người dùng 23/10/2026 thấy "ko canh giữa đều"). */
details.callout-proof > summary { display: flex; gap: 11px; }
/* Bốn kiểu sách phép, bìa lấy sắc màu nhấn của trang (--hue, --chroma của theme.css): vừa, sẫm, lệch sắc hai phía. */
:is(.px-book, .px-book-open).v0 { --fx-L: oklch(.50 calc(.12 * var(--chroma, 1)) var(--hue)); --fx-S: oklch(.37 calc(.10 * var(--chroma, 1)) var(--hue)); }
:is(.px-book, .px-book-open).v1 { --fx-L: oklch(.40 calc(.10 * var(--chroma, 1)) var(--hue)); --fx-S: oklch(.29 calc(.08 * var(--chroma, 1)) var(--hue)); }
:is(.px-book, .px-book-open).v2 { --fx-L: oklch(.56 calc(.12 * var(--chroma, 1)) calc(var(--hue) + 30)); --fx-S: oklch(.42 calc(.10 * var(--chroma, 1)) calc(var(--hue) + 30)); }
:is(.px-book, .px-book-open).v3 { --fx-L: oklch(.47 calc(.11 * var(--chroma, 1)) calc(var(--hue) - 30)); --fx-S: oklch(.35 calc(.09 * var(--chroma, 1)) calc(var(--hue) - 30)); }
details.callout-proof .px-book-open, details.callout-proof[open] .px-book { display: none; }
details.callout-proof[open] .px-book-open { display: block; }
details.callout-proof:not([open]) { width: fit-content; max-width: 100%; padding: 9px 13px 9px 14px; margin-left: var(--P);
  --px-b: var(--accent-ink); --px-f: color-mix(in oklab, var(--accent) 5%, var(--surface)); --px-s: color-mix(in oklab, var(--accent) 40%, var(--bg)); --px-d: var(--P);
  transition: transform calc(90ms * var(--motion, 1)) steps(2); }
details.callout-proof:not([open])::before {
  background:
    linear-gradient(var(--px-f) 0 0) calc(var(--P) * 2) var(--P) / calc(100% - var(--P) * 4) calc(100% - var(--P) * 2),
    linear-gradient(var(--px-f) 0 0) var(--P) calc(var(--P) * 2) / calc(100% - var(--P) * 2) calc(100% - var(--P) * 4),
    linear-gradient(var(--px-b) 0 0) calc(var(--P) * 2) 0 / calc(100% - var(--P) * 4) 100%,
    linear-gradient(var(--px-b) 0 0) var(--P) var(--P) / calc(100% - var(--P) * 2) calc(100% - var(--P) * 2),
    linear-gradient(var(--px-b) 0 0) 0 calc(var(--P) * 2) / 100% calc(100% - var(--P) * 4);
  background-repeat: no-repeat; }
details.callout-proof:not([open]) > summary { margin: 0 !important; }
@media (hover: hover) {
  details.callout-proof:not([open]):hover { --px-d: calc(var(--P) * 2); transform: translateY(calc(-1 * var(--P))); }
  details.callout-proof:not([open]):hover .px-book { animation: fx-float calc(900ms * var(--motion, 1)) steps(1) infinite; }
  details.callout-proof:not([open]):hover .px-book :is(.c-r, .c-h) { animation: fx-twinkle calc(450ms * var(--motion, 1)) steps(1) infinite; }
}
details.callout-proof:not([open]):active { --px-d: 0px; transform: translate(var(--P), var(--P)); }
details.callout-proof[open] > summary { margin-bottom: .9em; }
details.callout-proof[open] .px-book-open { animation: fx-open calc(240ms * var(--motion, 1)) steps(3); }
@keyframes fx-float { 0%, 100% { transform: translateY(-2px); } 50% { transform: translateY(-4px); } }
@keyframes fx-twinkle { 50% { fill: var(--gb-gold-hi); } }
@keyframes fx-open { 0% { transform: translateY(4px); opacity: .4; } 60% { transform: translateY(-2px); opacity: 1; } 100% { transform: none; } }

/* Bài nâng cao: nút viền đôi (viền màu nhấn, chỉ nhạt bên trong, bóng màu nhấn đậm), rương giữ màu gỗ. */
.adv > summary { position: relative; display: inline-flex; align-items: center; gap: 12px; padding: 9px 13px 9px 14px;
  font: 700 11.5px/1.3 var(--mono); letter-spacing: .12em; text-transform: uppercase; color: var(--accent-ink);
  transition: transform calc(90ms * var(--motion, 1)) steps(2); }
.adv[open] > summary { margin-bottom: 1.2em; }
:root:is(:lang(ar), :lang(fa), :lang(th), :lang(ja), :lang(ko), :lang(zh-Hans), :lang(zh-Hant)) .adv > summary { font-family: var(--sans); letter-spacing: 0; }
.prose .adv > summary { margin: var(--P) var(--P) calc(var(--P) * 3); background: color-mix(in oklab, var(--accent) 7%, var(--surface));
  box-shadow: inset 0 0 0 var(--P) color-mix(in oklab, var(--accent) 28%, var(--surface)),
    0 calc(-1 * var(--P)) 0 0 var(--accent-ink), 0 var(--P) 0 0 var(--accent-ink), calc(-1 * var(--P)) 0 0 0 var(--accent-ink), var(--P) 0 0 0 var(--accent-ink),
    calc(var(--P) * 2) calc(var(--P) * 2) 0 0 color-mix(in oklab, var(--accent) 60%, var(--bg)); }
@media (hover: hover) { .prose .adv > summary:hover { transform: translateY(calc(-1 * var(--P))); } }
.prose .adv > summary:active { transform: translate(var(--P), var(--P)); }
.px-chest .ch-teeth { opacity: 0; }
.adv:not([open]) > summary .ch-lid { animation: fx-peek calc(7s * var(--motion, 1)) steps(1) infinite; }
.adv:not([open]) > summary .ch-teeth { animation: fx-peek-t calc(7s * var(--motion, 1)) steps(1) infinite; }
@media (hover: hover) {
  .adv:not([open]) > summary:hover .ch-teeth { opacity: 1; animation: none; }
  .adv:not([open]) > summary:hover .ch-lid { transform: translateY(-2px); animation: fx-chomp calc(560ms * var(--motion, 1)) steps(1) infinite; }
}
.adv .px-eaten, .adv[open] .px-chest { display: none; }
/* Cảnh 40 × 48px (lưới 26 ô 2px, viewBox ôm sát hình) rộng hơn ô 26px của rương: tràn sang trái 6px (vẫn cách viền khung 6px),
   phải 2px để khoảng hở tới chữ còn 10px; chữ xô 6px. Nút cao lên theo cảnh. */
.adv[open] .px-eaten { display: block; margin: 0 -2px 0 -6px; animation: fx-snap calc(320ms * var(--motion, 1)) steps(4); }
.adv[open] .px-eaten .mm-a { animation: fx-kick-a calc(260ms * var(--motion, 1)) steps(1) 6, fx-kick-idle-a calc(6s * var(--motion, 1)) steps(1) calc(1.56s * var(--motion, 1)) infinite; }
.adv[open] .px-eaten .mm-b { animation: fx-kick-b calc(260ms * var(--motion, 1)) steps(1) 6, fx-kick-idle-b calc(6s * var(--motion, 1)) steps(1) calc(1.56s * var(--motion, 1)) infinite; }
@media (hover: hover) {
  .adv[open] > summary:hover .px-eaten .mm-a { animation: fx-kick-a calc(220ms * var(--motion, 1)) steps(1) infinite; }
  .adv[open] > summary:hover .px-eaten .mm-b { animation: fx-kick-b calc(220ms * var(--motion, 1)) steps(1) infinite; }
}
@keyframes fx-peek { 0%, 86%, 92%, 100% { transform: none; } 88%, 94% { transform: translateY(-1px); } }
@keyframes fx-peek-t { 0%, 86%, 92%, 100% { opacity: 0; } 88%, 94% { opacity: 1; } }
@keyframes fx-chomp { 0%, 100% { transform: translateY(-2px); } 50% { transform: none; } }
/* Rương sập xuống rồi rung qua lại. */
@keyframes fx-snap { 0% { transform: translateY(-4px); } 25% { transform: translateX(-2px); } 50% { transform: translateX(2px); } 75% { transform: translateX(-2px); } 100% { transform: none; } }
@keyframes fx-kick-idle-a { 0%, 84%, 88%, 92%, 100% { opacity: 1; } 86%, 90% { opacity: 0; } }
@keyframes fx-kick-idle-b { 0%, 84%, 88%, 92%, 100% { opacity: 0; } 86%, 90% { opacity: 1; } }

/* Trang 404 (CP, AI): rương hoá ra là Mimic đang ngậm người, chỉ còn hai chân đạp (hai khung luân phiên), kèm câu than quen thuộc
   trong hộp thoại pixel. Lưới 21 ô vẽ ô 4px. Giảm chuyển động thì đứng ở khung đầu. */
.fx-mimic { justify-items: center; row-gap: 10px; margin-bottom: 14px; }
.fx-mimic svg { width: 96px; height: 114px; }
/* Trang 404 nằm giữa màn hình theo chiều dọc (người dùng 23/10/2026: "trang 404 sao ko nằm ở chính giữa màn hình"). */
.empty:has(.fx-mimic) { min-height: calc(100svh - 190px); display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 24px 0; }
.empty:has(.fx-mimic) > p { margin: 0 0 16px; }
.px-mimic .mm-a { animation: fx-kick-a calc(380ms * var(--motion, 1)) steps(1) infinite; }
.px-mimic .mm-b { opacity: 0; animation: fx-kick-b calc(380ms * var(--motion, 1)) steps(1) infinite; }
@keyframes fx-kick-a { 50% { opacity: 0; } }
@keyframes fx-kick-b { 50% { opacity: 1; } }
.fx-say { position: relative; padding: 7px 12px; font: 700 11.5px/1.3 var(--mono); letter-spacing: .06em; color: var(--text); background: var(--surface); white-space: nowrap;
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-ink), 0 var(--P) 0 0 var(--px-ink), calc(-1 * var(--P)) 0 0 0 var(--px-ink), var(--P) 0 0 0 var(--px-ink), var(--P) calc(var(--P) * 2) 0 0 var(--px-shade); }
.fx-say::after { content: ""; position: absolute; left: 50%; top: calc(100% + var(--P)); width: 8px; height: 6px; margin-left: -4px; background: var(--px-ink);
  clip-path: polygon(0 0, 8px 0, 8px 2px, 6px 2px, 6px 4px, 4px 4px, 4px 6px, 2px 6px, 2px 4px, 0 4px); }
:root[data-theme="dark"] .callout-goal { --px-s: color-mix(in oklab, var(--accent) 55%, #000); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .callout-goal { --px-s: color-mix(in oklab, var(--accent) 55%, #000); } }

/* ---------- đoạn mở đầu bài: hộp thoại game thay cho vạch trái (người dùng 21/10/2026, làm trước ở CP), ▼ nhấp nháy như chờ bấm A ---------- */
.theory .hook { margin: 0 var(--P) 1.8em 0; padding: 18px 24px 22px !important; border: 0 !important; font-style: normal; color: var(--text); }
.theory .hook::after { content: ""; position: absolute; right: 16px; bottom: 10px; width: 10px; height: 6px; background: var(--accent-ink);
  clip-path: polygon(0 0, 10px 0, 10px 2px, 8px 2px, 8px 4px, 6px 4px, 6px 6px, 4px 6px, 4px 4px, 2px 4px, 2px 2px, 0 2px);
  animation: px-blink calc(1.2s * var(--motion, 1)) steps(1) infinite; }
@media (max-width: 640px) { .theory .hook { padding: 16px 18px 20px !important; } }

/* ---------- mục Ví dụ, Khi đi thi, Tóm tắt: bảng nền ngả, viền nhạt, không bóng ---------- */
.theory .t-sec:is(.is-example, .is-exam, .is-summary) { --px-b: var(--px-soft); --px-f: var(--bg-side); --px-d: 0px; padding: 6px 26px 4px; }

/* ---------- câu hỏi ---------- */
.quiz { padding: 20px 22px; }
.quiz-label { color: var(--accent-ink); }
/* Đáp án: khung nhỏ nét nhạt; rê chuột: nét đậm, nhấc lên, hiện bóng; bấm: lún xuống như phím. */
.quiz-opt { --px-b: var(--px-soft); --px-f: var(--bg); --px-d: 0px; padding: 10px 14px; align-items: center !important; transition: transform calc(90ms * var(--motion, 1)) steps(2), opacity calc(150ms * var(--motion, 1)); }
@media (hover: hover) { .quiz:not(.is-answered) .quiz-opt:hover { --px-b: var(--px-ink); --px-d: var(--P); transform: translateY(calc(-1 * var(--P))); } }
.quiz:not(.is-answered) .quiz-opt:active { --px-b: var(--px-ink); --px-d: 0px; transform: translate(var(--P), var(--P)); }
.quiz-opt:focus-visible { outline: 2px dashed var(--accent); outline-offset: 4px; }
/* Phím chữ cái: ô vuông khía góc như nút máy chơi game. */
.quiz-key {
  display: inline-grid; place-items: center; align-self: center; flex: none; width: 22px; height: 22px; min-width: 0; margin: 0 2px; position: relative;
  font: 700 11px/1 var(--mono) !important; color: var(--text-2) !important; background: var(--surface);
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-soft), 0 var(--P) 0 0 var(--px-soft), calc(-1 * var(--P)) 0 0 0 var(--px-soft), var(--P) 0 0 0 var(--px-soft);
}
/* Đúng: màn xanh Gameboy, nảy lên; sai: cam đỏ dịu, rung hai nhịp. */
.quiz-opt.is-right { --px-b: var(--gb-ink); --px-f: var(--gb-bg); --px-s: color-mix(in oklab, var(--gb-ink) 40%, var(--bg)); --px-d: var(--P); color: var(--gb-ink); opacity: 1 !important; }
.quiz-opt.is-right .quiz-key { background: var(--gb-ink); color: var(--gb-bg) !important; box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--gb-ink), 0 var(--P) 0 0 var(--gb-ink), calc(-1 * var(--P)) 0 0 0 var(--gb-ink), var(--P) 0 0 0 var(--gb-ink); }
.quiz-opt.is-wrong { --px-b: var(--bad-ink); --px-f: color-mix(in oklab, var(--bad) 13%, var(--bg)); color: var(--text); opacity: 1 !important; }
.quiz-opt.is-wrong .quiz-key { background: var(--bad-ink); color: var(--bg) !important; box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--bad-ink), 0 var(--P) 0 0 var(--bad-ink), calc(-1 * var(--P)) 0 0 0 var(--bad-ink), var(--P) 0 0 0 var(--bad-ink); }
.quiz-opt.is-right.is-chosen { animation: px-pop calc(320ms * var(--motion, 1)) steps(4); }
.quiz-opt.is-wrong { animation: px-shake calc(300ms * var(--motion, 1)) steps(6); }
@keyframes px-pop { 0%, 100% { transform: none; } 40% { transform: translateY(-4px); } 70% { transform: translateY(1px); } }
@keyframes px-shake { 0%, 100% { transform: none; } 20%, 60% { transform: translateX(-4px); } 40%, 80% { transform: translateX(4px); } }
/* Trả lời đúng: một hàng hoa pixel mọc lên trên mép trên của đáp án như phép cánh đồng hoa, đứng một lúc rồi nhấp nháy tắt
   (23/10/2026, thay cho tung hạt vuông và ngôi sao nhảy; người dùng 21/10: "nhanh chóng nhẹ nhàng thôi, nhưng cũng là động lực").
   Ảnh hoa vẽ bằng sprites.py (ô 2px, màu cố định hồng, xanh, tím, nhuỵ vàng). Trả lời sai: rung như cũ và một làn khói nhỏ bốc lên
   từ phím chữ cái như phép bị hỏng. --motion 0 (giảm chuyển động) thì không có. */
.quiz-opt.is-right.is-chosen::after { content: ""; position: absolute; left: 10px; right: 10px; bottom: calc(100% - var(--P)); z-index: 3; height: 14px; pointer-events: none;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 7' shape-rendering='crispEdges'%3E%3Cpath fill='%23e9a3bd' d='M2 0h1v1H2zM1 1h1v1H1zM3 1h1v1H3zM20 1h1v1H20zM2 2h1v1H2zM19 2h1v1H19zM21 2h1v1H21zM20 3h1v1H20z'/%3E%3Cpath fill='%23c9aef0' d='M12 0h1v1H12zM11 1h1v1H11zM13 1h1v1H13zM12 2h1v1H12z'/%3E%3Cpath fill='%23efc65a' d='M2 1h1v1H2zM12 1h1v1H12zM20 2h1v1H20zM7 3h1v1H7zM16 4h1v1H16z'/%3E%3Cpath fill='%239db8ee' d='M6 1h1v1H6zM8 1h1v1H8zM6 2h3v1H6zM5 3h2v1H5zM8 3h2v1H8zM16 3h1v1H16zM6 4h3v1H6zM15 4h1v1H15zM17 4h1v1H17zM16 5h1v1H16z'/%3E%3Cpath fill='%236f9f55' d='M2 3h1v1H2zM12 3h1v1H12zM2 4h2v1H2zM12 4h2v1H12zM20 4h1v1H20zM1 5h2v1H1zM7 5h1v1H7zM11 5h2v1H11zM19 5h2v1H19zM2 6h1v1H2zM7 6h2v1H7zM12 6h1v1H12zM16 6h1v1H16zM20 6h1v1H20z'/%3E%3C/svg%3E") 0 100% / 48px 14px repeat-x; opacity: 0;
  animation: fx-bloom calc(1.8s * var(--motion, 1)) forwards, fx-bloom-blink calc(1.8s * var(--motion, 1)) step-end forwards; }
@keyframes fx-bloom { 0% { clip-path: inset(100% 0 0 0); animation-timing-function: steps(7); } 22%, 100% { clip-path: inset(0); } }
@keyframes fx-bloom-blink { 0%, 76% { opacity: 1; } 80%, 88%, 96%, 100% { opacity: 0; } 84%, 92% { opacity: 1; } }
.quiz-opt.is-wrong.is-chosen .quiz-key::after { content: ""; position: absolute; left: 50%; bottom: calc(100% + 2px); width: 4px; height: 4px; margin-left: -2px; pointer-events: none; opacity: 0;
  background: var(--px-soft); box-shadow: -4px -4px 0 0 var(--px-soft), 4px -8px 0 0 var(--px-soft);
  animation: fx-fizzle calc(700ms * var(--motion, 1)) steps(5) forwards; }
@keyframes fx-fizzle { 0% { opacity: 1; transform: translateY(6px); } 100% { opacity: 0; transform: translateY(-8px); } }
.quiz-why, .quiz-src { padding-top: 14px; }
.quiz:has(.quiz-opt.is-chosen.is-right) .quiz-verdict { color: var(--gb-ink); }
.quiz:has(.quiz-opt.is-chosen.is-wrong) .quiz-verdict { color: var(--bad-ink); }

/* ---------- trang trước / sau ---------- */
.pager-link { --px-b: var(--px-soft); --px-d: 0px; padding: 16px 18px; transition: transform calc(90ms * var(--motion, 1)) steps(2); }
@media (hover: hover) { .pager-link:hover { --px-b: var(--px-ink); --px-d: var(--P); transform: translateY(calc(-1 * var(--P))); } }
.pager-link:active { --px-b: var(--px-ink); --px-d: 0px; transform: translate(var(--P), var(--P)); }

/* ---------- khối code: viền pixel nhạt (góc khía), nút vuông ---------- */
.code, .theory .t-code { border: 0 !important; border-radius: 0 !important; margin-left: var(--P); margin-right: var(--P);
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-soft), 0 var(--P) 0 0 var(--px-soft), calc(-1 * var(--P)) 0 0 0 var(--px-soft), var(--P) 0 0 0 var(--px-soft); }
.theory pre.out { border: 0 !important; border-radius: 0 !important; margin-left: var(--P); margin-right: var(--P);
  box-shadow: 0 var(--P) 0 0 var(--px-soft), calc(-1 * var(--P)) 0 0 0 var(--px-soft), var(--P) 0 0 0 var(--px-soft); }
.theory .t-code.has-out { margin-bottom: var(--P); }
.tool, .run-btn { border-radius: 0 !important; }
/* Nút Chạy luôn ở bên phải nút sao chép, ngắt dòng (người dùng 21/10/2026); order cho cả khối code CP tự dựng. */
:is(.code-tools, .io-tools) > .run-btn { order: 9; margin-inline-start: 6px; }
.run-btn { height: 26px; margin: 0 var(--P); padding: 0 10px 0 8px; font: 700 12px/1 var(--mono); letter-spacing: .04em;
  box-shadow: 0 calc(-1 * var(--P)) 0 0 currentColor, 0 var(--P) 0 0 currentColor, calc(-1 * var(--P)) 0 0 0 currentColor, var(--P) 0 0 0 currentColor;
  transition: transform calc(90ms * var(--motion, 1)) steps(2); }
@media (hover: hover) { .run-btn:hover { background: color-mix(in oklab, currentColor 12%, transparent); } }
.run-btn:active { transform: translate(var(--P), var(--P)); }

/* ---------- nút chung (ứng dụng, thanh trên) ---------- */
.btn, .anim-play { margin: var(--P); }
.btn, .acct, .anim-play {
  border: 0 !important; border-radius: 0 !important;
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-b, var(--px-ink)), 0 var(--P) 0 0 var(--px-b, var(--px-ink)), calc(-1 * var(--P)) 0 0 0 var(--px-b, var(--px-ink)), var(--P) 0 0 0 var(--px-b, var(--px-ink)),
    var(--P) calc(var(--P) * 2) 0 0 var(--px-shade);
  transition: transform calc(90ms * var(--motion, 1)) steps(2);
}
/* Ảnh đại diện trên nút tài khoản: ô vuông khía góc như logo nhà tài trợ, không tròn. */
.acct-av { border-radius: 0 !important; clip-path: var(--px-notch); }
.acct { --px-b: var(--px-soft); box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-b), 0 var(--P) 0 0 var(--px-b), calc(-1 * var(--P)) 0 0 0 var(--px-b), var(--P) 0 0 0 var(--px-b); }
@media (hover: hover) { .btn:not(:disabled):hover, .anim-play:hover { transform: translateY(calc(-1 * var(--P))); } .acct:hover { --px-b: var(--px-ink); } }
.btn:not(:disabled):active, .anim-play:active { transform: translate(var(--P), var(--P)); box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-ink), 0 var(--P) 0 0 var(--px-ink), calc(-1 * var(--P)) 0 0 0 var(--px-ink), var(--P) 0 0 0 var(--px-ink); }
.btn.is-primary { --px-b: var(--px-ink); }
.search-box { border: 0 !important; border-radius: 0 !important;
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-soft), 0 var(--P) 0 0 var(--px-soft), calc(-1 * var(--P)) 0 0 0 var(--px-soft), var(--P) 0 0 0 var(--px-soft) !important; }
.search-box:focus-within { box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-ink), 0 var(--P) 0 0 var(--px-ink), calc(-1 * var(--P)) 0 0 0 var(--px-ink), var(--P) 0 0 0 var(--px-ink) !important; }
.tab.is-active::after { height: 4px; border-radius: 0; bottom: 0; }

/* ---------- biểu tượng pixel (shared/lib/px-icons.mjs, 8 × 8 ô): luôn 16px để mỗi ô đúng 2px, nét căng ---------- */
.i { width: 16px !important; height: 16px !important; }
.run-btn .i { width: 8px !important; height: 8px !important; }
/* Chữ số pixel Silkscreen (chỉ có 0 tới 9, # % + , . nên chữ cái rơi về phông mono); chỉ nét ở cỡ bội 8px.
   ?v= đổi khi sửa glyph: tệp giữ tên mà trình duyệt được giữ /ui/* 4 giờ (21/10/2026: vẽ lại số 4, ở cả theme.css). */
@font-face { font-family: "Silkscreen"; src: url("fonts/silkscreen-digits.woff2?v=2") format("woff2"); font-weight: 700; unicode-range: U+23, U+25, U+2B-2C, U+2E, U+30-39, U+A0; font-display: swap; }
/* Góc khía một ô cho nền của dòng, nút biểu tượng (cắt góc nền, chữ không bị ảnh hưởng). */
:root { --px-notch: polygon(var(--P) 0, calc(100% - var(--P)) 0, calc(100% - var(--P)) var(--P), 100% var(--P), 100% calc(100% - var(--P)), calc(100% - var(--P)) calc(100% - var(--P)), calc(100% - var(--P)) 100%, var(--P) 100%, var(--P) calc(100% - var(--P)), 0 calc(100% - var(--P)), 0 var(--P), var(--P) var(--P)); }
.side-link, .side-phase, .side-lesson, .hit, .icon-btn, .tool, .lang-pop a { border-radius: 0 !important; clip-path: var(--px-notch); }
.icon-btn:active, .tool:active { transform: translate(1px, 1px); }

/* ---------- thanh bên kiểu menu game: đường bài là chấm pixel, bài đang đọc có con trỏ ▶ trên đường đó ---------- */
.side-lessons-inner { border-left: 0 !important; background: repeating-linear-gradient(var(--px-soft) 0 var(--P), transparent var(--P) calc(var(--P) * 2)) 0 0 / var(--P) 100% no-repeat; }
.side-lesson { position: relative; clip-path: none; }
.side-lesson:hover:not(.is-active) { clip-path: var(--px-notch); }
.side-lesson.is-active::before { content: ""; position: absolute; top: 50%; left: -9px; width: 6px; height: 10px; margin-top: -5px; background: var(--accent);
  clip-path: polygon(0 0, 2px 0, 2px 2px, 4px 2px, 4px 4px, 6px 4px, 6px 6px, 4px 6px, 4px 8px, 2px 8px, 2px 10px, 0 10px); }
.side-lesson.is-active { overflow: visible; }
.side-lesson-done { color: var(--gb-ink); }

/* ---------- tab bài học: tab đang mở có con trỏ ▶ như ô chọn của menu game ---------- */
.tab.is-active::before { content: ""; position: absolute; top: 50%; left: 2px; width: 6px; height: 10px; margin-top: -6px; background: var(--accent);
  clip-path: polygon(0 0, 2px 0, 2px 2px, 4px 2px, 4px 4px, 6px 4px, 6px 6px, 4px 6px, 4px 8px, 2px 8px, 2px 10px, 0 10px); }
.tab.is-active::after { left: 12px; right: 12px; }

/* ---------- mục lục: bỏ đường kẻ, mục đang đọc có con trỏ ▶ ---------- */
.toc a { position: relative; border-left: 0 !important; padding-left: 14px !important; }
.toc a.is-active::before { content: ""; position: absolute; top: 50%; left: 0; width: 6px; height: 10px; margin-top: -5px; background: var(--accent);
  clip-path: polygon(0 0, 2px 0, 2px 2px, 4px 2px, 4px 4px, 6px 4px, 6px 6px, 4px 6px, 4px 8px, 2px 8px, 2px 10px, 0 10px); }
.toc-box > summary { border-left: 0 !important; padding-left: 0 !important; font: 700 11.5px/1.3 var(--mono) !important; letter-spacing: .12em; text-transform: uppercase; }

/* ---------- khung kết quả tìm: bảng pixel viền mực, bóng lệch ---------- */
.search-pop { border: 0 !important; border-radius: 0 !important;
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-ink), 0 var(--P) 0 0 var(--px-ink), calc(-1 * var(--P)) 0 0 0 var(--px-ink), var(--P) 0 0 0 var(--px-ink), calc(var(--P) * 2) calc(var(--P) * 3) 0 0 var(--px-shade) !important; }
.hit-group { font: 700 11px/1.3 var(--mono) !important; letter-spacing: .12em; text-transform: uppercase; }
.hit-group:not(:first-child) { border-top: 0 !important; background: repeating-linear-gradient(90deg, var(--px-soft) 0 var(--P), transparent var(--P) calc(var(--P) * 3)) 0 0 / 100% var(--P) no-repeat; }

/* ---------- menu ngôn ngữ (trang chính): bảng pixel như khung kết quả tìm, thứ tiếng đang dùng có ▶ ---------- */
.lang-pop { border: 0 !important; border-radius: 0 !important;
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-ink), 0 var(--P) 0 0 var(--px-ink), calc(-1 * var(--P)) 0 0 0 var(--px-ink), var(--P) 0 0 0 var(--px-ink), calc(var(--P) * 2) calc(var(--P) * 3) 0 0 var(--px-shade) !important; }
.lang-pop a { position: relative; padding-inline-start: 22px !important; }
.lang-pop a[aria-current]::before { content: ""; position: absolute; top: 50%; inset-inline-start: 8px; width: 6px; height: 10px; margin-top: -5px; background: var(--accent);
  clip-path: polygon(0 0, 2px 0, 2px 2px, 4px 2px, 4px 4px, 6px 4px, 6px 6px, 4px 6px, 4px 8px, 2px 8px, 2px 10px, 0 10px); }
[dir="rtl"] .lang-pop a[aria-current]::before { transform: scaleX(-1); }

/* ---------- thanh tiến độ kiểu thanh máu: ô đặc chia đốt, viền pixel ---------- */
.meter { height: 6px !important; border-radius: 0 !important; margin: var(--P); overflow: visible !important; background: color-mix(in oklab, var(--accent) 10%, var(--surface)) !important;
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-soft), 0 var(--P) 0 0 var(--px-soft), calc(-1 * var(--P)) 0 0 0 var(--px-soft), var(--P) 0 0 0 var(--px-soft); }
.meter i { border-radius: 0 !important; background: repeating-linear-gradient(90deg, var(--accent) 0 4px, color-mix(in oklab, var(--accent) 65%, #000) 4px 6px) !important; }

/* ---------- thông báo: hộp thoại Gameboy, viền mực, mũi tên ▼ nhấp nháy ở góc ---------- */
.toast { border-radius: 0 !important; background: var(--surface) !important; color: var(--text) !important; padding: 11px 30px 11px 16px !important; font-weight: 600;
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-ink), 0 var(--P) 0 0 var(--px-ink), calc(-1 * var(--P)) 0 0 0 var(--px-ink), var(--P) 0 0 0 var(--px-ink), calc(var(--P) * 2) calc(var(--P) * 3) 0 0 var(--px-shade) !important; }
.toast::after { content: ""; position: absolute; right: 12px; bottom: 10px; width: 6px; height: 4px; background: var(--accent);
  clip-path: polygon(0 0, 6px 0, 6px 2px, 4px 2px, 4px 4px, 2px 4px, 2px 2px, 0 2px); animation: px-blink 1s steps(2) infinite; }
@keyframes px-blink { 50% { opacity: 0; } }

/* ---------- liên kết phụ của bài (Trắc nghiệm, Thực hành): nút pixel nhỏ ---------- */
.lesson-links { gap: 10px 14px !important; }
.lesson-links a { padding: 5px 10px 5px 8px; margin: var(--P); background: var(--surface);
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-soft), 0 var(--P) 0 0 var(--px-soft), calc(-1 * var(--P)) 0 0 0 var(--px-soft), var(--P) 0 0 0 var(--px-soft);
  transition: transform calc(90ms * var(--motion, 1)) steps(2); }
@media (hover: hover) { .lesson-links a:hover { transform: translateY(calc(-1 * var(--P)));
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-ink), 0 var(--P) 0 0 var(--px-ink), calc(-1 * var(--P)) 0 0 0 var(--px-ink), var(--P) 0 0 0 var(--px-ink), var(--P) calc(var(--P) * 2) 0 0 var(--px-shade); } }
.lesson-links b { font: 700 16px/1 "Silkscreen", var(--mono) !important; color: var(--accent-ink) !important; }
@media (max-width: 360px) { .lesson-links { column-gap: 8px !important; } .lesson-links a { gap: 5px; padding: 5px 7px 5px 6px; } }

/* ---------- số thứ tự trong danh sách (Lộ trình, chương, mục trắc nghiệm): chữ số pixel ---------- */
.phases .phase-num { font: 700 24px/1 "Silkscreen", var(--mono) !important; letter-spacing: 0; }
.page-home .phases .phase-num, .row-id { font: 700 16px/1 "Silkscreen", var(--mono) !important; letter-spacing: 0; }
.page-home .phases .phase-num { color: var(--accent-ink); }
.row-id { color: var(--faint); padding-top: 3px; }
/* Cột số bài đủ cho "3.10" bằng chữ số pixel (đo 48px ở 16px); điện thoại trước chỉ 40px nên số dính vào tên (người dùng 21/10/2026). */
:root .row { grid-template-columns: 52px minmax(0, 1fr) auto; column-gap: 14px; }
/* Tên Việt và tên Anh của bài (wiki CP: .row-vi rồi .row-title): mỗi tên một dòng, cùng lề trái; trước để một trái một phải trông lệch. */
:root .row-body:has(.row-vi) { display: grid; justify-content: start; row-gap: 3px; }
:root .row-body:has(.row-vi) .row-vi { font-size: 16px; font-weight: 500; color: var(--text); }
:root .row-body:has(.row-vi) .row-title { margin-inline-start: 0; text-align: start; font-size: 13.5px; font-weight: 400; color: var(--muted); }
@media (hover: hover) { .row:hover .row-id { color: var(--accent-ink); } }

/* ---------- danh sách kiểu menu game: rê chuột vào dòng thì hiện con trỏ pixel ▶ bên trái ---------- */
:is(.phases .phase, .qz-level-head, .row) { position: relative; border-radius: 0 !important; }
:is(.phases .phase, .qz-level-head, .row)::before {
  content: ""; position: absolute; top: 50%; inset-inline-start: -16px; width: 6px; height: 10px; margin-top: -5px; background: var(--accent); opacity: 0;
  clip-path: polygon(0 0, 2px 0, 2px 2px, 4px 2px, 4px 4px, 6px 4px, 6px 6px, 4px 6px, 4px 8px, 2px 8px, 2px 10px, 0 10px);
}
@media (hover: hover) { :is(.phases .phase, .qz-level-head, .row):hover::before { opacity: 1; animation: px-nudge calc(600ms * var(--motion, 1)) steps(1) infinite; } }
:is(.phases .phase, .qz-level-head, .row):focus-visible::before { opacity: 1; }
@keyframes px-nudge { 50% { transform: translateX(2px); } }
[dir="rtl"] :is(.phases .phase, .qz-level-head, .row)::before { transform: scaleX(-1); animation: none; }

/* ---------- bài tập CP: ô đánh dấu pixel (vuông rỗng / vuông có dấu tích) ---------- */
.pr-check > svg { display: none; }
.pr-check::before { content: ""; width: 18px; height: 18px; background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 9'%3E%3Cpath d='M0 0h9v1H0zM0 8h9v1H0zM0 1h1v7H0zM8 1h1v7H8z'/%3E%3C/svg%3E") 0 0 / 100% 100%;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 9'%3E%3Cpath d='M0 0h9v1H0zM0 8h9v1H0zM0 1h1v7H0zM8 1h1v7H8z'/%3E%3C/svg%3E") 0 0 / 100% 100%; }
.pr.is-done .pr-check { color: var(--gb-ink); }
.pr.is-done .pr-check::before {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 9'%3E%3Cpath d='M0 0h9v1H0zM0 8h9v1H0zM0 1h1v7H0zM8 1h1v7H8zM1 4h1v1H1zM2 5h1v1H2zM3 6h1v1H3zM4 5h1v1H4zM5 4h1v1H5zM6 3h1v1H6zM7 2h1v1H7z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 9'%3E%3Cpath d='M0 0h9v1H0zM0 8h9v1H0zM0 1h1v7H0zM8 1h1v7H8zM1 4h1v1H1zM2 5h1v1H2zM3 6h1v1H3zM4 5h1v1H4zM5 4h1v1H5zM6 3h1v1H6zM7 2h1v1H7z'/%3E%3C/svg%3E"); }

/* ---------- hình mô phỏng CP: nút, ô nhập, nhãn nguồn vuông viền pixel ---------- */
.vz-btn, .vz-f input[type=text], .vz-f input[type=number], .theory .ref-src {
  border: 0 !important; border-radius: 0 !important; margin: var(--P);
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-soft), 0 var(--P) 0 0 var(--px-soft), calc(-1 * var(--P)) 0 0 0 var(--px-soft), var(--P) 0 0 0 var(--px-soft); }
.vz-btn { font: 700 12px/1 var(--mono); transition: transform calc(90ms * var(--motion, 1)) steps(2); }
@media (hover: hover) { .vz-btn:hover { box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-ink), 0 var(--P) 0 0 var(--px-ink), calc(-1 * var(--P)) 0 0 0 var(--px-ink), var(--P) 0 0 0 var(--px-ink); transform: translateY(calc(-1 * var(--P))); } }
.vz-btn:active { transform: translate(var(--P), var(--P)); }
.vz-btn.is-on { box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--accent), 0 var(--P) 0 0 var(--accent), calc(-1 * var(--P)) 0 0 0 var(--accent), var(--P) 0 0 0 var(--accent); }
.vz-f input:focus-visible { outline: 0; box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--accent), 0 var(--P) 0 0 var(--accent), calc(-1 * var(--P)) 0 0 0 var(--accent), var(--P) 0 0 0 var(--accent); }

/* ---------- trắc nghiệm trong ứng dụng (/app/#/quiz) ---------- */
.qz-sub { --px-b: var(--px-soft); --px-d: 0px; transition: transform calc(90ms * var(--motion, 1)) steps(2); }
@media (hover: hover) { .qz-sub:hover { --px-b: var(--px-ink); --px-d: var(--P); transform: translateY(calc(-1 * var(--P))); } }
.qz-bar { height: 10px; border-radius: 0; margin-left: var(--P); margin-right: var(--P); background: color-mix(in oklab, var(--accent) 10%, var(--surface));
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-ink), 0 var(--P) 0 0 var(--px-ink), calc(-1 * var(--P)) 0 0 0 var(--px-ink), var(--P) 0 0 0 var(--px-ink); }
.qz-bar i { border-radius: 0; background: repeating-linear-gradient(90deg, var(--accent) 0 6px, color-mix(in oklab, var(--accent) 65%, #000) 6px 8px); transition: width calc(200ms * var(--motion, 1)) steps(4); }
.qz-cell { border: 0 !important; border-radius: 0 !important; margin: var(--P); background: var(--surface);
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-soft), 0 var(--P) 0 0 var(--px-soft), calc(-1 * var(--P)) 0 0 0 var(--px-soft), var(--P) 0 0 0 var(--px-soft); font-family: var(--mono); }
.qz-cell.is-done { background: var(--active); }
.qz-cell.is-right { background: var(--gb-bg) !important; color: var(--gb-ink) !important;
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--gb-ink), 0 var(--P) 0 0 var(--gb-ink), calc(-1 * var(--P)) 0 0 0 var(--gb-ink), var(--P) 0 0 0 var(--gb-ink); }
.qz-cell.is-wrong { background: color-mix(in oklab, var(--bad) 13%, var(--bg)) !important; color: var(--text) !important;
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--bad-ink), 0 var(--P) 0 0 var(--bad-ink), calc(-1 * var(--P)) 0 0 0 var(--bad-ink), var(--P) 0 0 0 var(--bad-ink); }
.qz-cell.is-current { outline: 0 !important; color: var(--text);
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-ink), 0 var(--P) 0 0 var(--px-ink), calc(-1 * var(--P)) 0 0 0 var(--px-ink), var(--P) 0 0 0 var(--px-ink), var(--P) calc(var(--P) * 2) 0 0 var(--accent); }
.qz-code { border-radius: 0; }
.quiz-learn-kind { border: 0 !important; border-radius: 0 !important; margin: var(--P); font-family: var(--mono) !important; letter-spacing: .1em !important;
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-soft), 0 var(--P) 0 0 var(--px-soft), calc(-1 * var(--P)) 0 0 0 var(--px-soft), var(--P) 0 0 0 var(--px-soft); }

/* ---------- đầu bài: độ khó (đầu lâu) và độ hữu dụng (sao), shared/lib/meters.mjs ---------- */
/* Hai thanh 5 ô xếp chồng, cách nút "Đã đọc" một khoảng rõ (người dùng 21/10/2026: "cách nhau ra một chút, trông dính dính");
   rê chuột hoặc chạm vào thanh thì hiện hộp thoại nhỏ "Độ khó 3/5" (thanh sao: hộp ở dưới để khỏi đè thanh đầu lâu).
   :root đứng trước để thắng luật cũ cùng tên trong sites/cp/styles/code.css. */
:root .lesson-head .lesson-act { grid-column: 2; justify-self: end; align-self: start; display: flex; align-items: center; gap: 32px; margin-top: 4px; }
:root .lesson-head .lesson-act .mark { margin: 0; }
:root .lesson-meters { display: grid; gap: 8px; }
:root .lm { position: relative; display: flex; gap: 3px; line-height: 0; cursor: help; outline: 0; }
:root .lm i { display: block; width: 14px; height: 14px; color: var(--px-soft); opacity: .55; }
:root .lm svg { display: block; width: 14px; height: 14px; fill: currentColor; shape-rendering: crispEdges; }
:root .lm i.on { opacity: 1; }
/* Nửa ô: hình tắt bên dưới, hình sáng chồng lên cắt còn 4 trong 7 cột pixel bên trái (8px của 14px). */
:root .lm i.half { position: relative; opacity: 1; }
:root .lm i.half svg { opacity: .55; }
:root .lm i.half svg.h { position: absolute; inset: 0; opacity: 1; clip-path: inset(0 6px 0 0); }
:root .lm-hard i.on, :root .lm-hard i.half svg.h { color: var(--bad-ink); }
:root .lm-use i.on, :root .lm-use i.half svg.h { color: var(--gb-gold-lo); }
:root[data-theme="dark"] .lm-use :is(i.on, i.half svg.h) { color: var(--gb-gold); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .lm-use :is(i.on, i.half svg.h) { color: var(--gb-gold); } }
.lm[data-tip]::after {
  content: attr(data-tip); position: absolute; right: -6px; bottom: calc(100% + 10px); z-index: 25; padding: 6px 10px; white-space: nowrap; pointer-events: none;
  font: 700 11px/1.2 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--text); background: var(--surface);
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-ink), 0 var(--P) 0 0 var(--px-ink), calc(-1 * var(--P)) 0 0 0 var(--px-ink), var(--P) 0 0 0 var(--px-ink), var(--P) calc(var(--P) * 2) 0 0 var(--px-shade);
  opacity: 0; visibility: hidden; transform: translateY(4px); transition: transform calc(90ms * var(--motion, 1)) steps(2), visibility 0s calc(90ms * var(--motion, 1)), opacity 0s calc(90ms * var(--motion, 1));
}
.lm-use[data-tip]::after { bottom: auto; top: calc(100% + 10px); transform: translateY(-4px); }
.lm[data-tip]:is(:hover, :focus)::after { opacity: 1; visibility: visible; transform: none; transition: transform calc(90ms * var(--motion, 1)) steps(2); }
.lm:focus-visible { outline: 2px dashed var(--accent); outline-offset: 4px; }
@media (max-width: 640px) {
  :root .lesson-head .lesson-act { grid-column: 1; grid-row: 4; justify-self: start; margin-top: 18px; gap: 24px; }
  :root .lesson-head .lesson-act .mark { order: -1; }
  .lm[data-tip]::after { right: auto; left: -6px; }
}

/* ---------- thanh tải (shared/components/Loader.astro) ---------- */
/* Vạch sát mép trên trang, chạy từng nấc tới 90% trong lúc chờ trang mới, có trang mới thì tắt (người dùng 21/10/2026: bỏ
   màn hình chờ Gameboy vì nặng, "để chớp cái thôi"). 23/10/2026: thành sao băng, đầu là ngôi sao pixel hình chữ thập, đuôi nhạt dần
   về bên trái. Không ảnh, không che nội dung, không nhận chuột. */
.cnk-loader { position: fixed; top: 0; left: 0; right: 0; z-index: 300; height: 6px; pointer-events: none; visibility: hidden; }
.cnk-loader i { position: relative; display: block; width: 0; height: var(--P); margin-top: var(--P);
  background: linear-gradient(90deg, transparent, color-mix(in oklab, var(--accent) 55%, transparent) 55%, var(--accent)); }
.cnk-loader i::after { content: ""; position: absolute; right: calc(-1 * var(--P) * 2); top: calc(-1 * var(--P)); width: 6px; height: 6px; background: var(--gb-gold);
  clip-path: polygon(2px 0, 4px 0, 4px 2px, 6px 2px, 6px 4px, 4px 4px, 4px 6px, 2px 6px, 2px 4px, 0 4px, 0 2px, 2px 2px); }
.is-loading .cnk-loader { visibility: visible; }
.is-loading .cnk-loader i { animation: ld-go calc(1.6s * var(--motion, 1)) steps(16) forwards; }
@keyframes ld-go { 30% { width: 50%; } 100% { width: 90%; } }
/* Chữ không phải Latin (Ả Rập, Ba Tư viết liền nét; Thái, CJK không có trong phông mono): mọi nhãn mono in hoa giãn chữ của lớp này
   (nhãn khung, nhãn câu hỏi, mục lục, nhóm kết quả tìm, nút…) về phông thường, không giãn chữ. */
:root:is(:lang(ar), :lang(fa), :lang(th), :lang(ja), :lang(ko), :lang(zh-Hans), :lang(zh-Hant)) :is(.callout-title, .quiz-label, .toc-box > summary, .hit-group, .quiz-learn-kind, .run-btn, .vz-btn, .lm[data-tip])
  { font-family: var(--sans) !important; letter-spacing: 0 !important; }
:root:is(:lang(ar), :lang(fa), :lang(th), :lang(ja), :lang(ko), :lang(zh-Hans), :lang(zh-Hant)) .lm[data-tip]::after { font-family: var(--sans); letter-spacing: 0; }

/* ---------- nút "Đã đọc" ---------- */
/* Khung pixel bo bậc thang 2-1 ô (viền vẽ bằng drop-shadow trên lớp .mark-frame, nền cắt theo --mk-clip), bấm thì lún như phím
   máy chơi game; đã đọc thì nền xanh màn Gameboy, dấu tích pixel. Lúc đánh dấu (achieve() trong site.js): nút nảy, cúp vàng pixel
   bật lên thẳng cột giữa phía trên nút, hạt pixel bung từ cúp (người dùng 21/10/2026: chỉ cần cúp, không chữ, đặt giữa trên nút). */
.mark {
  --mk-b: var(--px-ink); --mk-bg: var(--surface); --mk-ink: var(--text-2); --mk-s: var(--px-shade);
  --mk-clip: polygon(0 calc(var(--P) * 2), var(--P) calc(var(--P) * 2), var(--P) var(--P), calc(var(--P) * 2) var(--P), calc(var(--P) * 2) 0,
    calc(100% - var(--P) * 2) 0, calc(100% - var(--P) * 2) var(--P), calc(100% - var(--P)) var(--P), calc(100% - var(--P)) calc(var(--P) * 2),
    100% calc(var(--P) * 2), 100% calc(100% - var(--P) * 2), calc(100% - var(--P)) calc(100% - var(--P) * 2), calc(100% - var(--P)) calc(100% - var(--P)),
    calc(100% - var(--P) * 2) calc(100% - var(--P)), calc(100% - var(--P) * 2) 100%, calc(var(--P) * 2) 100%, calc(var(--P) * 2) calc(100% - var(--P)),
    var(--P) calc(100% - var(--P)), var(--P) calc(100% - var(--P) * 2), 0 calc(100% - var(--P) * 2));
  position: relative; isolation: isolate; gap: 8px; height: 32px; padding: 0 14px 0 12px; border: 0; border-radius: 0; background: none;
  color: var(--mk-ink); font-weight: 600; letter-spacing: .01em;
  transition: transform calc(90ms * var(--motion, 1)) steps(2), color calc(90ms * var(--motion, 1)) steps(2);
}
.mark .mark-frame { position: absolute; inset: 0; z-index: -1; pointer-events: none;
  filter: drop-shadow(var(--P) 0 0 var(--mk-b)) drop-shadow(calc(-1 * var(--P)) 0 0 var(--mk-b)) drop-shadow(0 var(--P) 0 var(--mk-b))
    drop-shadow(0 calc(-1 * var(--P)) 0 var(--mk-b)) drop-shadow(var(--P) var(--P) 0 var(--mk-s)); }
.mark .mark-frame::before { content: ''; position: absolute; inset: 0; background: var(--mk-bg); clip-path: var(--mk-clip); }
.mark .px { width: 14px; height: 14px; flex: none; fill: currentColor; shape-rendering: crispEdges; }
.mark:hover { background: none; transform: translateY(calc(-1 * var(--P))); }
.mark:active { transform: translate(var(--P), var(--P)); }
.mark:active .mark-frame { filter: drop-shadow(var(--P) 0 0 var(--mk-b)) drop-shadow(calc(-1 * var(--P)) 0 0 var(--mk-b))
  drop-shadow(0 var(--P) 0 var(--mk-b)) drop-shadow(0 calc(-1 * var(--P)) 0 var(--mk-b)); }
.mark.is-done { --mk-b: var(--gb-ink); --mk-bg: var(--gb-bg); --mk-ink: var(--gb-ink); --mk-s: color-mix(in oklab, var(--gb-ink) 40%, var(--bg));
  color: var(--gb-ink); border-color: transparent; background: none; }
.mark.is-pop { animation: mk-pop calc(360ms * var(--motion, 1)) steps(4); }
@keyframes mk-pop { 0% { transform: scale(1); } 35% { transform: scale(1.12); } 70% { transform: scale(.97); } 100% { transform: scale(1); } }
/* Cúp 16 × 13 ô, mỗi ô 2px: tâm cúp và tâm nút nằm trên cùng một cột dọc. Cúp nằm sau mặt nút (z-index -2 trong nút) nên trồi lên
   từ sau nút như vật phẩm bật ra khỏi khối "?", nảy, lơ lửng, rồi nhấp nháy tắt như vật phẩm trong game. */
.mark-cup { position: absolute; left: 50%; bottom: calc(100% + 8px); z-index: -2; width: 32px; height: 26px; margin-left: -16px; pointer-events: none;
  animation: mk-cup calc(1.9s * var(--motion, 1)) forwards, mk-blink calc(1.9s * var(--motion, 1)) step-end forwards; }
.mark-cup svg { display: block; width: 32px; height: 26px; shape-rendering: crispEdges; }
.mark-cup .c-i { fill: var(--gb-ink); }
.mark-cup .c-y { fill: var(--gb-gold); }
.mark-cup .c-h { fill: var(--gb-gold-hi); }
.mark-cup .c-s { fill: var(--gb-gold-lo); }
@keyframes mk-cup {
  0% { transform: translateY(34px); animation-timing-function: steps(4); }
  14% { transform: translateY(-6px); animation-timing-function: steps(1); }
  20% { transform: translateY(0); animation-timing-function: steps(1); }
  38% { transform: translateY(-2px); animation-timing-function: steps(1); }
  54% { transform: translateY(0); animation-timing-function: steps(1); }
  70% { transform: translateY(-2px); animation-timing-function: steps(1); }
  100% { transform: translateY(-2px); }
}
@keyframes mk-blink { 0%, 74% { opacity: 1; } 76%, 84%, 92%, 100% { opacity: 0; } 80%, 88% { opacity: 1; } }
.mark-burst { position: absolute; left: 50%; top: 50%; width: 0; height: 0; pointer-events: none; }
/* Hạt bung là hoa pixel chữ thập (nhuỵ vàng, bốn cánh), xen hai màu cánh, như cánh đồng hoa nở quanh cúp (23/10/2026). */
.mark-burst i { position: absolute; width: 2px; height: 2px; margin: -1px; background: var(--gb-gold); opacity: 0; --pt: var(--fx-petal);
  box-shadow: 0 -2px 0 0 var(--pt), 2px 0 0 0 var(--pt), 0 2px 0 0 var(--pt), -2px 0 0 0 var(--pt);
  animation: mk-burst calc(560ms * var(--motion, 1)) calc(270ms * var(--motion, 1)) steps(6) forwards; }
.mark-burst i:nth-child(2n) { --pt: var(--fx-petal-2); }
@keyframes mk-burst { from { transform: rotate(var(--a)) translateX(16px); opacity: 1; } to { transform: rotate(var(--a)) translateX(40px); opacity: 0; } }
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* ========== đợt 35 việc (người dùng 23/10/2026), 04. Workspace/polish-35-20261023 ========== */
/* (1) Thông báo pixel dài hơn: "chiều dài đang hơi nhỏ". */
.toast { min-width: min(320px, calc(100vw - 32px)); text-align: center; padding: 12px 34px 12px 22px !important; }
/* (14) Hộp chữ "Độ khó 3/5" nằm giữa thanh, không lệch phải. Điện thoại: thanh sát lề trái nên hộp vẫn canh trái thanh. */
.lm[data-tip]::after { right: auto; left: 50%; transform: translate(-50%, 4px); }
.lm-use[data-tip]::after { transform: translate(-50%, -4px); }
.lm[data-tip]:is(:hover, :focus)::after { transform: translate(-50%, 0); }
@media (max-width: 640px) {
  .lm[data-tip]::after, .lm-use[data-tip]::after { left: -6px; transform: none; }
  .lm[data-tip]:is(:hover, :focus)::after { transform: none; }
}
/* (24) Chú thích từng bước của hình động, hình tương tác: căn giữa, nằm trên hàng nút ▶ (người dùng: "nên align giữa và đặt lên
   phía trên của hàng nút play"); giữ chỗ hai dòng để hàng nút không nhảy khi chú thích dài ngắn khác nhau. */
.anim-bar { flex-wrap: wrap; justify-content: center; row-gap: 8px; }
.anim-bar .anim-label { order: -1; flex: 0 0 100%; min-height: 2.8em; display: flex; align-items: center; justify-content: center; text-align: center; text-wrap: balance; }
/* (21) Ô nhập trong hình tương tác: chữ căn giữa. */
.vz-f input[type=text], .vz-f input[type=number] { text-align: center; }
/* (10) Trang đề bài: nút "Nộp bài trên LQDOJ" và "Đã làm" thẳng hàng giữa theo chiều dọc. */
.problem-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 16px; margin: 14px 0 8px; }
.problem-actions .btn, .problem-actions .mark { margin: var(--P); }
/* (22) Dấu câu ngay sau công thức không rớt xuống dòng một mình (render.py bọc công thức và dấu câu liền sau trong .nobr). */
.nobr { white-space: nowrap; }
/* (26) Logo cá nục Gameboy trong ứng dụng /app/ (app.mjs thay MARK cũ); ứng dụng không nạp theme.css nên đặt cỡ ở đây. */
.wordmark .brand-mark { flex: none; align-self: center; width: 33px; height: 28.5px; margin: -4px 0; }
/* (7, 8, 27) Gói Pro (shared/lib/lock.mjs, site.js paintPro): data-pro-only chỉ hiện với tài khoản Pro (bài tập LQDOJ: không Pro thì
   ẩn hẳn, không khóa); data-pro-else chỉ hiện khi chưa Pro. Ô khóa .pro-lock: khung pixel viền mực, ổ khóa màu nhấn, nút mở khóa. */
html:not(.is-pro) [data-pro-only] { display: none !important; }
html.is-pro [data-pro-else] { display: none !important; }
.pro-lock { position: relative; isolation: isolate; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 10px 16px;
  margin: 1.4em var(--P); padding: 14px 18px 14px 16px; color: var(--text); }
.pro-lock-frame { position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    linear-gradient(color-mix(in oklab, var(--accent) 6%, var(--surface)) 0 0) calc(var(--P) * 2) var(--P) / calc(100% - var(--P) * 4) calc(100% - var(--P) * 2),
    linear-gradient(color-mix(in oklab, var(--accent) 6%, var(--surface)) 0 0) var(--P) calc(var(--P) * 2) / calc(100% - var(--P) * 2) calc(100% - var(--P) * 4),
    linear-gradient(var(--px-ink) 0 0) calc(var(--P) * 2) 0 / calc(100% - var(--P) * 4) 100%,
    linear-gradient(var(--px-ink) 0 0) var(--P) var(--P) / calc(100% - var(--P) * 2) calc(100% - var(--P) * 2),
    linear-gradient(var(--px-ink) 0 0) 0 calc(var(--P) * 2) / 100% calc(100% - var(--P) * 4);
  background-repeat: no-repeat; filter: drop-shadow(var(--P) var(--P) 0 var(--px-shade)); }
.pro-lock-ico { width: 18px; height: 20px; display: block; fill: var(--accent-ink); shape-rendering: crispEdges; }
.pro-lock-ico .pro-lock-hole { fill: var(--surface); }
.pro-lock-title { margin: 0 !important; font: 700 11.5px/1.4 var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--text-2); }
.pro-lock-cta { justify-self: end; display: inline-flex; align-items: center; height: 30px; padding: 0 12px; margin: var(--P); white-space: nowrap;
  font: 700 12px/1 var(--mono); letter-spacing: .04em; color: var(--accent-ink) !important; text-decoration: none !important; background: var(--surface);
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--accent-ink), 0 var(--P) 0 0 var(--accent-ink), calc(-1 * var(--P)) 0 0 0 var(--accent-ink), var(--P) 0 0 0 var(--accent-ink);
  transition: transform calc(90ms * var(--motion, 1)) steps(2); }
@media (hover: hover) { .pro-lock-cta:hover { transform: translateY(calc(-1 * var(--P))); } }
.pro-lock-cta:active { transform: translate(var(--P), var(--P)); }
@media (max-width: 640px) {
  .pro-lock { grid-template-columns: auto minmax(0, 1fr); }
  .pro-lock-cta { grid-column: 1 / -1; justify-self: stretch; justify-content: center; }
}
/* Trang đề bài: hai phần Gợi ý, Lời giải. */
.pro-part { margin: 1.6em 0 0; }
.pro-part-head { display: flex; align-items: center; gap: 8px; margin: 0 0 .4em; font: 700 11.5px/1.3 var(--mono); letter-spacing: .12em; text-transform: uppercase; color: var(--accent-ink); }
.pro-part-head .i { width: 16px; height: 16px; }
/* (29) Mục giảng đánh số La Mã (sites/cp/lib/external.mjs): số ở đầu tiêu đề và trong mục lục; bài tập, bài tập ngoài mang nhãn số
   mục nó phục vụ. Kiểu Gameboy gọn: chữ mono đậm màu nhấn, nhãn khung pixel nhạt. (18, 28) Mục "Bài tập ngoài": nhóm, dòng nhãn · tên ·
   trang, mở ở thẻ mới. */
.theory h2 .sec-no { display: inline-block; min-width: 1.9em; margin: 0 .8em 0 var(--P); padding: .12em .4em; vertical-align: .22em; text-align: center;
  font: 700 .5em/1.4 var(--mono); letter-spacing: .06em; color: var(--accent-ink);
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--accent-ink), 0 var(--P) 0 0 var(--accent-ink), calc(-1 * var(--P)) 0 0 0 var(--accent-ink), var(--P) 0 0 0 var(--accent-ink); }
.toc a .toc-no { display: inline-block; min-width: 2.4em; margin-right: .4em; font: 700 11px/1 var(--mono); letter-spacing: .04em; color: var(--accent-ink); }
.sec-tag { flex: none; display: inline-grid; place-items: center; min-width: 38px; height: 20px; margin: 0 10px 0 var(--P); padding: 0 4px;
  font: 700 11px/1 var(--mono); letter-spacing: .04em; color: var(--accent-ink); background: var(--surface);
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--px-soft), 0 var(--P) 0 0 var(--px-soft), calc(-1 * var(--P)) 0 0 0 var(--px-soft), var(--P) 0 0 0 var(--px-soft); }
.sec-tag.is-none { visibility: hidden; }
.pr-main .sec-tag { margin-right: 10px; }
.theory .is-external .ext-group { margin: 0 0 1.1em; }
.theory .is-external .ext-group-name { margin: 0 0 .3em; font: 700 11px/1.3 var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.theory .is-external .ext-row { display: flex; align-items: center; gap: 0; padding: 9px 4px; color: var(--text); text-decoration: none;
  background: repeating-linear-gradient(90deg, var(--px-soft) 0 var(--P), transparent var(--P) calc(var(--P) * 3)) 0 100% / 100% var(--P) no-repeat; }
.theory .is-external .ext-name { flex: 1 1 auto; min-width: 0; font-size: 15.5px; }
.theory .is-external .ext-site { flex: none; margin-left: 12px; font: 600 12px/1 var(--mono); color: var(--muted); }
@media (hover: hover) { .theory .is-external .ext-row:hover .ext-name { color: var(--accent-ink); } }
/* (34) Màn chờ (người dùng 23/10/2026; Loader.astro, shared/lib/loading.mjs). .cnk-wait: lớp phủ trời đêm, sao nhấp nháy, sao băng rơi
   chéo, bốn người (Eisen, Heiter, Himmel, Frieren) ngồi trên đồi nhìn từ sau lưng; bật, tắt từng nấc (steps) như Gameboy. Cảnh rộng 200 ô
   (--wc mỗi ô, tối đa 5px, màn hẹp co lại để bốn người vừa khung), đất bằng nối dài hai bên và xuống đáy màn hình. .cnk-fishing: Frieren
   câu cá, phao nhấp nhô, cá nục bơi vòng dưới nước, cho phần trang đang tải. --motion 0 (giảm chuyển động): đứng yên. */
.cnk-wait { --wc: min(5px, calc(100vw / 140)); position: fixed; inset: 0; z-index: 400; display: flex; flex-direction: column; justify-content: flex-end;
  align-items: center; overflow: hidden; background: linear-gradient(#060a17 0%, #0d1730 42%, #1c2b52 74%, #30457a 100%);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity calc(320ms * var(--motion, 1)) steps(4), visibility 0s linear calc(320ms * var(--motion, 1)); }
.cnk-wait.is-on { opacity: 1; visibility: visible; pointer-events: auto; transition: opacity calc(240ms * var(--motion, 1)) steps(4); }
.cnk-wait::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 9vh; background: #070b16; }
.cnk-wait-scene { position: relative; z-index: 2; margin-bottom: 9vh; line-height: 0; }
.cnk-wait-scene::before { content: ""; position: absolute; z-index: -1; left: -100vw; right: -100vw; bottom: 0; height: calc(3 * var(--wc)); background: #070b16; }
.cnk-wait-scene svg { display: block; width: calc(200 * var(--wc)); max-width: none; height: auto; shape-rendering: crispEdges; }
.cnk-wait .w-x { fill: #04060c; } .cnk-wait .w-y { fill: #8a9cd0; } .cnk-wait .w-a { fill: #b2badc; } .cnk-wait .w-b { fill: #e8ecfa; }
.cnk-wait .w-g { fill: #96793f; } .cnk-wait .w-h { fill: #141c32; } .cnk-wait .w-H { fill: #070b16; }
/* Sao: một chấm 2px nhân bản bằng box-shadow theo vw, vh; nhấp nháy hai nhịp. */
.cnk-wait-sky { position: absolute; inset: 0; }
.cnk-wait-sky::before, .cnk-wait-sky::after { content: ""; position: absolute; left: 0; top: 0; width: 2px; height: 2px; background: transparent;
  box-shadow: 8vw 9vh #fff, 17vw 22vh #c8d4ff, 26vw 6vh #fff, 34vw 31vh #fff, 43vw 14vh #c8d4ff, 52vw 4vh #fff, 61vw 25vh #fff, 69vw 11vh #c8d4ff,
    78vw 19vh #fff, 87vw 7vh #fff, 93vw 33vh #c8d4ff, 4vw 38vh #fff, 22vw 44vh #c8d4ff, 47vw 41vh #fff, 73vw 46vh #fff, 96vw 15vh #fff; }
.cnk-wait-sky::after { box-shadow: 12vw 16vh #fff, 29vw 27vh #fff, 39vw 3vh #c8d4ff, 57vw 35vh #fff, 66vw 17vh #fff, 82vw 28vh #c8d4ff, 90vw 42vh #fff,
    14vw 33vh #fff, 59vw 9vh #fff, 75vw 37vh #c8d4ff; animation: cnk-twinkle calc(1.6s * var(--motion, 1)) steps(1) infinite; }
@keyframes cnk-twinkle { 50% { opacity: .3; } }
/* Sao băng: vệt pixel chéo từ trên phải xuống dưới trái, đầu sáng, đuôi nhạt; tám vệt lệch giờ, lệch chỗ (mưa sao băng). */
.cnk-wait-sky i { position: absolute; top: -4vh; width: 64px; height: 2px; opacity: 0; transform: rotate(-28deg);
  background: linear-gradient(90deg, #fff 0 4px, rgba(200, 212, 255, .9) 4px 16px, rgba(200, 212, 255, .35) 16px 40px, transparent 40px);
  animation: cnk-meteor calc(1.3s * var(--motion, 1)) steps(10) infinite; }
.cnk-wait-sky i:nth-child(1) { left: 70vw; animation-delay: 0s; }
.cnk-wait-sky i:nth-child(2) { left: 88vw; animation-delay: .35s; }
.cnk-wait-sky i:nth-child(3) { left: 52vw; animation-delay: .7s; }
.cnk-wait-sky i:nth-child(4) { left: 96vw; animation-delay: .95s; }
.cnk-wait-sky i:nth-child(5) { left: 63vw; animation-delay: 1.15s; }
.cnk-wait-sky i:nth-child(6) { left: 80vw; animation-delay: .55s; }
.cnk-wait-sky i:nth-child(7) { left: 44vw; top: 6vh; animation-delay: 1.05s; }
.cnk-wait-sky i:nth-child(8) { left: 104vw; top: 12vh; animation-delay: .2s; }
@keyframes cnk-meteor { 0% { opacity: 0; translate: 0 0; } 10% { opacity: 1; } 70% { opacity: 1; } 100% { opacity: 0; translate: -38vw 22vh; } }
/* Frieren câu cá: ô 4px. */
.cnk-fishing { display: grid; place-items: center; padding: 48px 0; line-height: 0; }
.cnk-fishing svg { display: block; shape-rendering: crispEdges; overflow: visible; }
.cnk-fishing .w-k { fill: #241e28; } .cnk-fishing .w-W { fill: #f8f6ee; } .cnk-fishing .w-g { fill: var(--gb-gold); } .cnk-fishing .w-a { fill: #e2deee; }
.cnk-fishing .w-s { fill: #f4ceb6; } .cnk-fishing .w-e { fill: #3c8c5a; } .cnk-fishing .w-n { fill: #2e304c; } .cnk-fishing .w-o { fill: #784e2e; }
.cnk-fishing .w-r { fill: #96683e; } .cnk-fishing .w-l { fill: var(--muted); } .cnk-fishing .w-b { fill: #3c78c8; } .cnk-fishing .w-B { fill: #285496; }
.cnk-fishing .w-f { fill: #ced6de; } .cnk-fishing .w-c { fill: #dc463c; } .cnk-fishing .w-R { fill: #827870; } .cnk-fishing .w-D { fill: #605852; }
.cnk-fishing .w-water { fill: color-mix(in oklab, #4a8fd0 35%, var(--bg)); } .cnk-fishing .w-wave { fill: color-mix(in oklab, #4a8fd0 18%, var(--bg)); }
.cnk-fishing .w-bob { animation: cnk-bob calc(900ms * var(--motion, 1)) steps(1) infinite; }
@keyframes cnk-bob { 50% { transform: translateY(1px); } }
/* Cá bơi vòng: sang phải ở dưới, quay đầu, sang trái ở trên (lật hình), lặp lại. */
.cnk-fishing .w-fish { transform-box: fill-box; transform-origin: center; animation: cnk-swim calc(2.4s * var(--motion, 1)) steps(16) infinite; }
@keyframes cnk-swim {
  0% { transform: translate(-7px, 1px) scaleX(1); }
  45% { transform: translate(7px, 1px) scaleX(1); }
  50% { transform: translate(8px, -1px) scaleX(-1); }
  95% { transform: translate(-6px, -1px) scaleX(-1); }
  100% { transform: translate(-7px, 1px) scaleX(1); }
}
/* (6, 7, 17) Wiki AI: phần ứng dụng cũ /app/ thành trang tĩnh (shared/components/QuizSet.astro, sites/ai/pages/…), nhãn Pro.
   Bộ trắc nghiệm: hàng tiến độ và nút Kiểm tra, các nhóm, ô số câu (câu còn khóa mờ), mọi câu như câu hỏi nhanh trong bài.
   Câu khôi phục từ kết quả đã lưu (.is-quiet) không chạy hiệu ứng. Đang kiểm tra (.page.is-exam): chỉ còn khung bài kiểm tra. */
.pro-tag { display: inline-block; flex: none; padding: 0 6px; vertical-align: 1px; font: 700 10.5px/1.7 var(--mono); letter-spacing: .08em;
  color: var(--gb-gold-lo); background: color-mix(in oklab, var(--gb-gold) 14%, var(--bg));
  box-shadow: 0 calc(-1 * var(--P)) 0 0 var(--gb-gold-lo), 0 var(--P) 0 0 var(--gb-gold-lo), calc(-1 * var(--P)) 0 0 0 var(--gb-gold-lo), var(--P) 0 0 0 var(--gb-gold-lo); }
.kicker .pro-tag { margin-left: 12px; }
.row-end .pro-tag { margin-right: 2px; }
.qz-set-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px 20px; margin: 0 0 20px; }
.qz-set-prog { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 18px; margin: 0; font-size: 14px; color: var(--muted); font-variant-numeric: tabular-nums; }
.qz-set-prog .qz-reset { margin-left: 0; font-size: 14px; }
.qz-set-bar .actions { margin: 0; }
.qz-topics { margin: 0 0 18px; }
.qz-jump { display: grid; grid-template-columns: repeat(auto-fill, minmax(38px, 1fr)); gap: 2px; margin: 0 0 6px; }
.qz-jump .qz-cell { height: 32px; margin: var(--P); font-size: 13px; }
.qz-jump .qz-cell.is-locked { opacity: .38; cursor: default; }
.qz-list > .qz-group { margin-top: 1.6em; }
.qz-list .qz-item { scroll-margin-top: 90px; }
.qz-list .pro-lock { margin-top: 1.6em; }
.quiz.is-quiet .quiz-opt, .quiz.is-quiet .quiz-opt::after, .quiz.is-quiet .quiz-key::after { animation: none !important; }
.quiz.is-quiet .quiz-opt.is-right.is-chosen::after, .quiz.is-quiet .quiz-opt.is-wrong.is-chosen .quiz-key::after { content: none !important; }
.page.is-exam > :not(.qz-set):not(.tab-body), .page.is-exam .tab-body > :not(.qz-set), .page.is-exam .qz-set > :not(.ex-root) { display: none !important; }
.ex-root { max-width: 760px; margin-inline: auto; }
.ex-root .qz-q > p { margin: 0 0 .5em; font: inherit; color: inherit; }
.ex-result-title { margin: 0; font: 500 30px/1.25 var(--serif); }
/* Bài tập: mọi bài trên một trang, mỗi bài một mục. */
.hw-list .hw + .hw { margin-top: 56px; padding-top: 44px; border-top: 1px dashed var(--line-strong); }
.hw-num { display: block; margin: 0 0 8px; font: 600 12px/1.4 var(--sans); letter-spacing: .06em; text-transform: uppercase; color: var(--accent-ink); }
.hw-list .hw { scroll-margin-top: 80px; }
.lesson-links a.is-active { color: var(--accent-ink); }
/* Ô số câu gập trong "Chọn câu" (bộ 100 câu chiếm cả màn hình điện thoại). */
.qz-map { margin: 0 0 6px; }
.qz-map > summary { display: inline-flex; align-items: center; gap: 8px; padding: 6px 0; list-style: none; cursor: pointer; font: 600 13px/1 var(--sans); color: var(--muted); }
.qz-map > summary::-webkit-details-marker { display: none; }
.qz-map > summary::before { content: ""; border-left: 5px solid currentColor; border-top: 4px solid transparent; border-bottom: 4px solid transparent; transition: transform calc(.15s * var(--motion, 1)) steps(2); }
.qz-map[open] > summary::before { transform: rotate(90deg); }
.qz-map > summary:hover { color: var(--text); }
.qz-map .qz-jump { margin-top: 8px; }
/* Khung Gói Pro ở trang Lộ trình (shared/components/ProCard.astro, người dùng 23/10/2026: "kêu gọi mua gói"): khung pixel như ô khóa,
   nhãn GÓI PRO có ổ khóa, tiêu đề serif, các dòng có dấu tích pixel màu nhấn, nút nâng cấp. Thành quả học viên (wins.mjs): danh sách
   gọn, kết quả đậm; chưa có thì không hiện. */
.pro-card { position: relative; isolation: isolate; display: grid; gap: 12px; justify-items: start; margin: 40px var(--P) 8px; padding: 22px 24px 24px; }
.pro-card-kicker { display: flex; align-items: center; gap: 10px; margin: 0; font: 700 11.5px/1.3 var(--mono); letter-spacing: .12em; text-transform: uppercase; color: var(--accent-ink); }
.pro-card-ico { display: block; width: 18px; height: 20px; fill: var(--accent-ink); shape-rendering: crispEdges; }
.pro-card-ico .pro-lock-hole { fill: var(--surface); }
.pro-card-title { margin: 0; font: 500 24px/1.3 var(--serif); color: var(--text); }
.pro-card-list { display: grid; gap: 8px; margin: 2px 0 6px; padding: 0; list-style: none; }
.pro-card-list li { display: flex; align-items: baseline; gap: 10px; font-size: 15px; line-height: 1.5; color: var(--text-2); }
.pro-card-list svg { flex: none; width: 12px; height: 12px; fill: var(--accent-ink); shape-rendering: crispEdges; transform: translateY(1px); }
.pro-card .pro-card-cta { justify-self: start; height: 38px; margin: var(--P); padding: 0 16px; font-size: 13px; }
@media (max-width: 640px) {
  .pro-card { padding: 18px 16px 20px; }
  .pro-card-title { font-size: 21px; }
  .pro-card .pro-card-cta { justify-self: stretch; justify-content: center; }
}
.wins { margin: 40px 0 0; }
.wins-head { margin: 0 0 12px; font: 700 12px/1.3 var(--mono); letter-spacing: .12em; text-transform: uppercase; color: var(--text-2); }
.wins-list { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }
.wins-list li { display: flex; flex-wrap: wrap; gap: 4px 12px; font-size: 15px; }
.wins-list b { color: var(--text); }
.wins-list span { color: var(--muted); }
