/* Adam Solutions — trang chủ (10 section) */

/* ============ SÂN KHẤU SCROLLYTELLING (chương 0–5) ============ */

.stage { position: relative; }

/* --- Viewport ghăm --- */
/* QUAN TRỌNG: không ancestor nào của khối này được có transform/filter/will-change,
   nếu không position:sticky sẽ chết im lặng. */
.stage__viewport {
	position: sticky; top: 0; z-index: 0;
	height: 100vh; height: 100svh;
	overflow: hidden; background: var(--a-bg);
}

.stage__posters { position: absolute; inset: 0; z-index: 0; }
/* Ảnh là nhánh CHÍNH (không còn lớp 3D phủ lên). `will-change: transform` để
   Ken Burns chạy trên compositor; chỉ đặt cho ảnh đang hiện, tránh giữ layer thừa. */
.stage__poster {
	position: absolute; inset: 0;
	width: 100%; height: 100%; object-fit: cover;
	opacity: 0; transform: scale(1.04);
	/* `opacity: 0` VẪN được vẽ. Với 6 ảnh full-screen chồng nhau, ở tải pixel Retina
	   đó là 6 lần tô toàn màn hình mỗi frame. `visibility: hidden` loại hẳn khỏi pha vẽ.
	   Delay đúng bằng thời gian fade để ảnh đi ra không bị cắt đột ngột (BUG-017). */
	visibility: hidden;
	transition: opacity 1.1s var(--ease), visibility 0s linear 1.1s;
}
.stage__poster.is-active {
	opacity: 1; visibility: visible;
	transition: opacity 1.1s var(--ease), visibility 0s linear 0s;
	will-change: transform, opacity;
}
/* Poster của 2 chương kề: hiện ở opacity 0 trên lớp riêng. Compositor không vẽ lớp
   opacity 0 (không tốn lượt tô), nhưng vẫn giải mã + raster sẵn vì opacity có thể
   animate. Thiếu bước này, ảnh `visibility: hidden` chỉ được giải mã đúng frame
   chuyển chương — đo trên GPU tích hợp: tới 300ms/ảnh, frame chờ commit 50–160ms.
   (img.decode() không giúp được: nó nạp cache giải mã phần mềm, raster GPU dùng cache riêng.)
   will-change trùng với .is-active để lúc lên active không phải dựng lại lớp. */
.stage__poster.is-near {
	visibility: visible;
	transition: opacity 1.1s var(--ease), visibility 0s linear 0s;
	will-change: transform, opacity;
}
/* Không JS: chỉ giữ ảnh đầu tiên, tránh chồng 6 ảnh mờ lên nhau */
html:not(.js) .stage__poster { opacity: 0; }
html:not(.js) .stage__poster[data-chapter="0"] { opacity: 1; }
/* Không JS: bỏ ghăm, các lớp chữ xếp dọc và hiện hết. Thiếu khối này thì 6 lớp
   `position: absolute` sẽ chồng đè lên nhau. */
html:not(.js) .stage__viewport { position: static; height: auto; }
html:not(.js) .stage__panels { position: static; }
html:not(.js) .stage__chapter-panel {
	position: static; opacity: 1; visibility: visible;
	padding-block: var(--sp-8); min-height: 0;
}
html:not(.js) .stage__panel { transform: none; }
html:not(.js) .stage__aside { position: static; opacity: 1; transform: none; margin-top: var(--sp-5); }
html:not(.js) .stage__chapters { display: none; }

/* Scrim phải nằm TRÊN canvas — canvas được chèn vào cuối DOM nên nếu không đặt
   z-index, nó vẽ đè lên scrim và chữ mất nền tối, không đọc được ở vùng sân khấu sáng. */
.stage__scrim {
	position: absolute; inset: 0; pointer-events: none; z-index: 2;
	background:
		/* ENH-069: ảnh phải RÕ. Chỉ tối cục bộ sau khối chữ (nửa trái), nửa phải gần như
		   không phủ (.14). Bỏ lớp gradient thứ hai vốn phủ toàn bộ ảnh từ dưới lên. */
		linear-gradient(to right, rgba(11,11,13,.92) 0%, rgba(11,11,13,.72) 32%, rgba(11,11,13,.28) 56%, rgba(11,11,13,.14) 100%),
		linear-gradient(to top, var(--a-bg) 0%, transparent 26%);
}

/* Tia sáng sân khấu — nền cho nhánh không có WebGL */
.stage__beams { position: absolute; inset: 0; z-index: 3; pointer-events: none; overflow: hidden; }
.stage__beams span {
	position: absolute; top: -30%; width: 230px; height: 160%;
	/* Mép mềm vẽ sẵn bằng gradient elip, thay cho `filter: blur(28px)`: blur là hiệu ứng
	   GPU tính lại trên lớp lớn mỗi frame khi tia xoay — đo được là phần GPU nặng nhất
	   của sân khấu. Gradient chỉ raster một lần. */
	background: radial-gradient(50% 65% at 50% 0%, var(--a-red-glow), transparent);
	opacity: .45; transform-origin: top center;
	animation: beam-sway 9s ease-in-out infinite;
	will-change: transform;
}
.stage__beams span:nth-child(1) { left: 14%; animation-delay: 0s; }
.stage__beams span:nth-child(2) { left: 48%; animation-delay: -3s; opacity: .28; }
/* Chỉ 2 tia: mỗi tia là một lớp composite toàn chiều cao, tia thứ 3 gần như không thấy thêm */
.stage__beams span:nth-child(3) { display: none; }
/* Chỉ xoay, KHÔNG scale: scale buộc raster lại lớp đã blur mỗi frame — rotate thì không. */
@keyframes beam-sway {
	0%, 100% { transform: rotate(-9deg); }
	50%      { transform: rotate(9deg); }
}
/* --- Chương --- */
.stage__chapters { position: relative; z-index: 1; margin-top: -100vh; margin-top: -100svh; }
/* 150svh × 6 chương = 48,5% toàn trang chủ (ENH-071). Hạ xuống 110svh cắt ~2.160px
   mà không bỏ chương nào. Driver tính chương bằng getBoundingClientRect() từng khối nên
   KHÔNG phụ thuộc con số này — đổi chiều cao không làm sai phép chia chương. */
/* Chiều cao chương KHÔNG đồng loạt (ENH-071 / Phase 84).
   Khối sticky bên trong chỉ có `chiều-cao-chương − 100svh` để chạy; panel càng cao thì
   càng cần nhiều khoảng chạy đó, nếu không nó bị đẩy lên khỏi mép trên và CẮT MẤT tiêu đề.
   Đo thực tế ở viewport 700px và 900px:
     chương 1-4 (panel thường)          → 110svh: cắt 0px  ✅
     chương 0 (h1 lớn + lead + 2 nút)   → 110svh: cắt 113px ❌ · 120svh: cắt 78px ❌ · 150svh: 0 ✅
     chương 5 (4 con số + text)         → 110svh: cắt 31px  ❌ · 150svh: 0 ✅
   Nên chỉ hạ 4 chương giữa; chương đầu và cuối giữ 150svh. */
.stage__chapter { min-height: 110vh; min-height: 110svh; }
.stage__chapter:first-child,
.stage__chapter:last-child { min-height: 150vh; min-height: 150svh; }
/* Panel ghăm theo VIEWPORT (không canh giữa khối 150svh) — nếu không, khi cuộn
   tiêu đề sẽ trôi lên và bị header che. `padding-top` chừa đúng chiều cao header. */
/* BUG-019 — lớp chữ chuyển vào `.stage__viewport` (khối sticky DUY NHẤT).
   Trước đây mỗi chương có khối sticky riêng nên nó chỉ ghăm được `caoChương − caoViewport`
   rồi trôi lên; đo được tiêu đề bị header cắt tới 636px ở 36 điểm dọc sân khấu, trong khi
   chữ vẫn hiện rõ (opacity 1). Lớp ảnh poster vốn đã đúng mẫu này. */
.stage__panels { position: absolute; inset: 0; z-index: 4; pointer-events: none; }
.stage__chapter-panel {
	position: absolute; inset: 0;
	display: flex; align-items: center;
	padding-top: var(--header-h);
	opacity: 0; visibility: hidden;
	transition: opacity .5s var(--ease), visibility 0s linear .5s;
}
.stage__chapter-panel.is-active {
	opacity: 1; visibility: visible; pointer-events: auto;
	transition: opacity .5s var(--ease), visibility 0s linear 0s;
}
.stage__chapter-inner { position: relative; display: flex; align-items: center; width: 100%; }
.stage__panel {
	max-width: 640px;
	opacity: 0; transform: translateY(28px);
	transition: opacity .6s var(--ease), transform .6s var(--ease);
}
.stage__chapter-panel.is-active .stage__panel { opacity: 1; transform: none; }
/* Không JS: hiện hết, không chương nào bị giấu */
html:not(.js) .stage__panel { opacity: 1; transform: none; }

.stage__title { margin-bottom: var(--sp-5); }
.stage__chapter:first-child .stage__title { font-size: clamp(2.2rem, 1.3rem + 4vw, 4rem); line-height: 1.06; }
.stage__title em { font-style: normal; color: var(--a-red); }
.stage__text { font-size: clamp(1rem, .95rem + .3vw, 1.14rem); color: var(--a-text-muted); margin-bottom: var(--sp-6); }
.stage__cta { display: flex; flex-wrap: wrap; gap: var(--sp-4); }

.stage__scroll {
	position: absolute; left: 50%; bottom: 28px; transform: translateX(-50%);
	color: var(--a-text-dim); font-size: .72rem; letter-spacing: .18em; text-transform: uppercase;
	display: grid; justify-items: center; gap: 8px;
}
.stage__scroll::after {
	content: ""; width: 1px; height: 34px;
	background: linear-gradient(var(--a-red), transparent);
	animation: scroll-pulse 2s ease-in-out infinite;
}
@keyframes scroll-pulse { 0%,100% { opacity: .3; transform: scaleY(.6); } 50% { opacity: 1; transform: scaleY(1); } }


/* ---- Ngôn ngữ đồ hoạ áp vào sân khấu (ENH-067) ---- */
.stage__chapter { position: relative; }
/* `right: -.04em` (chỉnh quang học cho nét viền) đẩy chữ bóng ra ngoài 15px ở
   1440px ⇒ trang chủ có thanh cuộn ngang. Không thể kẹp bằng `overflow: hidden`
   ở tổ tiên vì sẽ giết `position: sticky` của `.stage__viewport`. Bù .04em không
   nhìn ra được, nên bỏ hẳn. */
.stage__ghost { z-index: 0; right: 0; }
/* Chữ bóng trôi ngang theo cuộn (main.js #9) sẽ tràn phải. Cắt ở chính chapter:
   nó không phải tổ tiên của `.stage__viewport` nên không ảnh hưởng sticky, và
   `clip` (khác `hidden`) không tạo scroll container, trục dọc vẫn visible. */
.stage__chapter { overflow-x: clip; }

.stage__panel-head {
	display: flex; align-items: baseline; gap: var(--sp-4);
	margin-bottom: var(--sp-4);
}

/* HUD + chip KHÔNG nằm trong luồng panel (panel sẽ cao quá 1 màn hình và đẩy chúng ra
   ngoài tầm nhìn), nhưng PHẢI nằm trong khối sticky của chính chương mình — nếu để ở
   ngoài, HUD của chương này sẽ hiện cùng tiêu đề của chương kế, gây sai thông tin. */
.stage__aside {
	position: absolute; bottom: 14vh; z-index: 3;
	right: max(var(--gutter), calc((100% - var(--container)) / 2));
	display: grid; gap: var(--sp-4); justify-items: start;
	max-width: 330px; text-align: left;
	opacity: 0; transform: translateX(14px);
	transition: opacity .5s var(--ease), transform .5s var(--ease);
}
/* Chỉ hiện khi đúng chương của nó đang hoạt động */
.stage__chapter-panel.is-active .stage__aside { opacity: 1; transform: none; }
html:not(.js) .stage__aside { opacity: 1; transform: none; }
.stage__aside .hud-row { flex-direction: column; gap: var(--sp-3); }
/* Nền ảnh có thể sáng bất kỳ chỗ nào — cho khối HUD một nền mờ để luôn đọc được */
/* Nền đục thay backdrop-filter — blur 3px gần như không thấy nhưng tốn một lớp mỗi khối */
.stage__aside .hud-block { padding: 6px 10px 6px 12px; background: rgba(11,11,13,.78); }
.stage__aside .spec-chip { background: rgba(11,11,13,.78); }

.stage__services { list-style: none; padding: 0; margin: var(--sp-5) 0 0; display: grid; gap: 0; grid-template-columns: 1fr 1fr; }
.stage__services li { border-top: 1px solid var(--a-border-soft); }
.stage__services li:nth-child(-n+2) { border-top: 0; }
.stage__services a {
	display: flex; align-items: baseline; gap: var(--sp-3);
	padding: var(--sp-3) var(--sp-4) var(--sp-3) 0;
	color: var(--a-text); font-size: .95rem; font-weight: 500;
	transition: .22s var(--ease);
}
.stage__services a:hover { color: var(--a-red-hover); text-decoration: none; padding-left: var(--sp-3); }
.stage__services-idx { font-family: var(--font-mono); font-size: 10px; color: var(--a-text-dim); letter-spacing: .08em; }

.stage__stats { display: grid; gap: var(--sp-6) var(--sp-5); grid-template-columns: repeat(4, 1fr); margin-top: var(--sp-6); }
.stage__stats .stat { text-align: left; padding: 0; border: 0; border-top: 1px solid var(--a-border-soft); padding-top: var(--sp-3); }
.stage__stats .stat__label { font-size: var(--fs-micro); letter-spacing: var(--tr-micro); text-transform: uppercase; color: var(--a-text-dim); margin-top: var(--sp-2); }

/* `.dash-link--cta` đã chuyển sang shared.css: nó xuất hiện ở /tin-tuc/, /san-pham/,
   4 trang dịch vụ và /du-an/ — để ở đây thì ngoài trang chủ mất sạch style (BUG-018). */

.stage__title { font-size: var(--fs-d2); }
.stage__chapter:first-child .stage__title { font-size: var(--fs-d1); line-height: 1.02; }
.stage__panel { max-width: 620px; }

/* --- Chỉ báo tiến trình (dot dọc phải) --- */
.stage__progress {
	position: fixed; right: 22px; top: 50%; transform: translateY(-50%); z-index: 40;
	opacity: 0; pointer-events: none; transition: opacity .3s var(--ease);
}
/* Chỉ hiện khi sân khấu đang trong tầm nhìn (driver 83.2 gắn .is-in-view) */
.stage.is-in-view .stage__progress { opacity: 1; pointer-events: auto; }
.stage__progress ol { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-3); }
.stage__dot {
	position: relative; display: block; width: 10px; height: 10px; padding: 0;
	border: 1px solid var(--a-text-dim); border-radius: 50%;
	background: transparent; cursor: pointer; transition: .25s var(--ease);
}
.stage__dot:hover { border-color: var(--a-red); }
.stage__dot.is-active { background: var(--a-red); border-color: var(--a-red); transform: scale(1.35); }
.stage__dot-label {
	position: absolute; right: 20px; top: 50%; transform: translateY(-50%);
	white-space: nowrap; font-size: .72rem; color: var(--a-text-muted);
	background: var(--a-surface); border: 1px solid var(--a-border);
	padding: 4px 10px; border-radius: 6px;
	opacity: 0; pointer-events: none; transition: opacity .2s var(--ease);
}
.stage__dot:hover .stage__dot-label,
.stage__dot:focus-visible .stage__dot-label { opacity: 1; }
/* Không JS thì dot vô nghĩa — ẩn đi */
html:not(.js) .stage__progress { display: none; }

/* ============ 2. DỊCH VỤ ============ */
.services__grid { display: grid; gap: var(--sp-5); grid-template-columns: repeat(4, 1fr); }
.service-card {
	position: relative; display: block; padding: var(--sp-6);
	background: var(--a-surface); border: 1px solid var(--a-border); border-radius: var(--radius);
	overflow: hidden; transition: .3s var(--ease); color: inherit;
}
.service-card::before {
	content: ""; position: absolute; inset: 0; opacity: 0; transition: opacity .3s var(--ease);
	background: linear-gradient(160deg, var(--a-red-dim), transparent 55%);
}
.service-card:hover { border-color: var(--a-red); transform: translateY(-6px); box-shadow: 0 20px 44px rgba(0,0,0,.5); color: inherit; text-decoration: none; }
.service-card:hover::before { opacity: 1; }
.service-card > * { position: relative; z-index: 1; }
.service-card__icon {
	width: 54px; height: 54px; display: grid; place-items: center; margin-bottom: var(--sp-5);
	border-radius: 14px; background: var(--a-red-dim); color: var(--a-red); border: 1px solid rgba(225,29,42,.28);
	transition: .3s var(--ease);
}
.service-card:hover .service-card__icon { background: var(--a-red); color: #fff; }
.service-card__icon svg { width: 26px; height: 26px; }
.service-card__title { font-size: 1.15rem; margin-bottom: var(--sp-3); }
.service-card__text { color: var(--a-text-muted); font-size: .92rem; margin-bottom: var(--sp-4); }
.service-card__more { font-size: .85rem; font-weight: 700; color: var(--a-red-hover); display: inline-flex; align-items: center; gap: 7px; }
.service-card__more svg { width: 15px; height: 15px; transition: transform .25s var(--ease); }
.service-card:hover .service-card__more svg { transform: translateX(5px); }
.service-card__num {
	position: absolute; right: var(--sp-4); top: var(--sp-3); z-index: 0;
	font-size: 3.6rem; font-weight: 800; line-height: 1; color: rgba(255,255,255,.035);
}

/* ============ 3. CON SỐ ============ */
.stats { background: var(--a-bg-alt); position: relative; overflow: hidden; }
.stats::before {
	content: ""; position: absolute; inset: 0;
	background: radial-gradient(760px 300px at 50% 0%, var(--a-red-dim), transparent 72%);
}
.stats > .container { position: relative; }
.stats__grid { display: grid; gap: var(--sp-6); grid-template-columns: repeat(4, 1fr); }
.stat { text-align: center; padding: var(--sp-5) var(--sp-3); border-right: 1px solid var(--a-border-soft); }
.stat:last-child { border-right: 0; }
.stat__num { font-size: clamp(2.4rem, 1.6rem + 2.6vw, 3.6rem); font-weight: 800; line-height: 1; color: var(--a-red); letter-spacing: -.03em; }
.stat__num sup { font-size: .48em; vertical-align: super; margin-left: 2px; }
.stat__label { margin: var(--sp-3) 0 0; color: var(--a-text-muted); font-size: .93rem; }



/* ---- Responsive sân khấu ----
   PHẢI nằm ở home.css, KHÔNG để trong shared.css: home.css nạp SAU shared.css,
   nên rule gốc `.stage__aside { position: absolute }` sẽ đè ngược media query
   nếu media query nằm ở file nạp trước. Đây chính là lỗi tràn ngang 14px ở 390px
   sinh ra khi tách file ở BUG-018. */
@media (max-width: 1200px) {
	.stage__progress { display: none; }
}
@media (max-width: 1150px) {
	.stage__aside { position: static; max-width: none; margin-top: var(--sp-5); }
	.stage__aside .hud-row { flex-direction: row; flex-wrap: wrap; }
}
@media (max-width: 1080px) {
	.stage__stats { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 900px) {
	/* home.css nạp SAU editorial.css nên `.stage__ghost { right: -.04em }` đè mất
	   luật mobile ở đó — phải lặp lại tại đây (nguồn của 5px cuộn ngang còn sót). */
	.stage__ghost { right: 0; }

	/* `.stage__chapter-inner` là flex ⇒ panel và khối HUD nằm cạnh nhau, trên mobile
	   HUD bị ép thành cột hẹp chồng lên tiêu đề. Xếp dọc lại. */
	.stage__chapter-inner { flex-direction: column; align-items: flex-start; justify-content: center; }
	.stage__panel { max-width: none; width: 100%; }
	.stage__aside { width: 100%; margin-top: var(--sp-5); }
	.stage__aside .hud-row { flex-direction: row; flex-wrap: wrap; gap: var(--sp-3) var(--sp-5); }
}
@media (max-width: 640px) {
	.stage__services { grid-template-columns: 1fr; }
	.stage__chapter { min-height: 100svh; }
	.stage__chapter:first-child,
	.stage__chapter:last-child { min-height: 130svh; }
	.stage__cta .btn { width: 100%; }
	.stage__scroll { display: none; }
	.stage__stats { grid-template-columns: 1fr 1fr; }
}


/* ═══════════ ENH-073 — mỗi chương vào một kiểu khác nhau ═══════════
   User: "các story telling khi scroll mỗi lần sẽ là 1 kiểu khác nhau, thay vì đồng nhất".

   Trước đây cả 6 chương dùng CHUNG `translateY(28px)` cho chữ và `translateX(14px)` cho HUD.
   Kiểu khai trong DỮ LIỆU (`adam_chapters()` → `motion`) rồi đọc qua `[data-motion]`,
   nên đổi kiểu của một chương không phải sửa CSS.

   Nguyên tắc: phân biệt bằng KIỂU chuyển động, không bằng BIÊN ĐỘ — giữ mức tiết chế.
   Mọi hiệu ứng chỉ dùng `transform` + `opacity`, chạy MỘT LẦN khi đổi chương. */

/* 0 · không gian trống — chỉ hiện dần, không trôi. Tĩnh, chờ đợi. */
.stage__chapter-panel[data-motion="settle"] .stage__panel { transform: none; }
.stage__chapter-panel[data-motion="settle"] .stage__panel > * { opacity: 0; transition: opacity .7s var(--ease); }
.stage__chapter-panel[data-motion="settle"].is-active .stage__panel > * { opacity: 1; }

/* 1 · khảo sát — từng dòng trượt lên lệch nhịp, như số đo hiện dần. */
.stage__chapter-panel[data-motion="stagger"] .stage__panel { transform: none; }
.stage__chapter-panel[data-motion="stagger"] .stage__panel > * {
	opacity: 0; transform: translateY(18px);
	transition: opacity .5s var(--ease), transform .5s var(--ease);
}
.stage__chapter-panel[data-motion="stagger"].is-active .stage__panel > * { opacity: 1; transform: none; }
.stage__chapter-panel[data-motion="stagger"].is-active .stage__panel > :nth-child(2) { transition-delay: .09s; }
.stage__chapter-panel[data-motion="stagger"].is-active .stage__panel > :nth-child(3) { transition-delay: .18s; }
.stage__chapter-panel[data-motion="stagger"].is-active .stage__panel > :nth-child(4) { transition-delay: .27s; }

/* 2 · truss — chữ HẠ XUỐNG từ trên, khớp nghĩa đen "giàn treo hạ xuống". */
.stage__chapter-panel[data-motion="drop"] .stage__panel { transform: translateY(-26px); }

/* 3 · âm thanh — nở ra từ giữa, như sóng lan. */
.stage__chapter-panel[data-motion="spread"] .stage__panel { transform: scale(.965); transform-origin: 0 50%; }
.stage__chapter-panel[data-motion="spread"] .stage__aside { transform: translateX(26px); }

/* 4 · ánh sáng — sáng lên tại chỗ, gần như không dịch chuyển. */
.stage__chapter-panel[data-motion="ignite"] .stage__panel { transform: none; opacity: 0; }
.stage__chapter-panel[data-motion="ignite"] .stage__title { opacity: .25; transition: opacity .8s var(--ease); }
.stage__chapter-panel[data-motion="ignite"].is-active .stage__title { opacity: 1; }

/* 5 · sân khấu — mọi thứ về đúng chỗ từ hai phía: kết thúc, hoàn chỉnh. */
.stage__chapter-panel[data-motion="assemble"] .stage__panel { transform: translateY(20px) scale(.985); }
.stage__chapter-panel[data-motion="assemble"] .stage__aside { transform: translateX(-22px); }

@media (prefers-reduced-motion: reduce) {
	.stage__chapter-panel[data-motion] .stage__panel,
	.stage__chapter-panel[data-motion] .stage__panel > *,
	.stage__chapter-panel[data-motion] .stage__aside,
	.stage__chapter-panel[data-motion] .stage__title { transform: none !important; opacity: 1 !important; transition: none !important; }
}

/* ── ENH-073 tầng 2: phá thế trùng cấu trúc của chương 2·3·4 ──
   Ba chương giữa trước đây giống HỆT nhau (cùng ghost + hud + chips + cta, chỉ khác ảnh và chữ),
   nên dù chuyển động đã khác nhau thì vẫn đọc như một chương lặp ba lần.
   Mỗi chương giờ bày khối dữ liệu một kiểu — dùng lại thành phần ENH-067, KHÔNG thêm cái mới. */

/* 3 · âm thanh — HUD + chip trải NGANG ngay dưới phần chữ, đọc như một dải tần.
   Nằm trong luồng (không absolute): trước đây bám `bottom: 9vh` của màn hình trong khi
   panel căn giữa dọc ⇒ dải đè lên đoạn mô tả ở mọi độ rộng đã đo (1280/1440/1920). */
.stage__chapter-inner:has(> .stage__aside--row) { flex-direction: column; align-items: flex-start; }
.stage__aside--row {
	position: static; margin-top: var(--sp-6); max-width: min(660px, 70vw);
	grid-auto-flow: column; grid-auto-columns: max-content;
	align-items: end; gap: var(--sp-5);
}
.stage__aside--row .hud-row { flex-direction: row; gap: var(--sp-5); }
.stage__aside--row .spec-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); }

/* 4 · ánh sáng — chip rải thành chùm, mỗi chip một chấm sáng như đèn vừa bật. */
.stage__aside--scatter { bottom: 16vh; gap: var(--sp-3); }
.stage__aside--scatter .spec-chips { display: grid; gap: var(--sp-2); justify-items: start; }
.stage__aside--scatter .spec-chip { position: relative; padding-left: 22px; }
.stage__aside--scatter .spec-chip::before {
	content: ""; position: absolute; left: 9px; top: 50%; transform: translateY(-50%);
	width: 6px; height: 6px; border-radius: 50%;
	background: var(--a-red); box-shadow: 0 0 9px var(--a-red);
}
/* Lệch nhau để thành "chùm" chứ không phải cột thẳng */
.stage__aside--scatter .spec-chip:nth-child(2) { margin-left: var(--sp-5); }
.stage__aside--scatter .spec-chip:nth-child(3) { margin-left: var(--sp-3); }

@media (max-width: 900px) {
	/* Mobile: mọi kiểu bày đều quay về hàng ngang dưới chữ — chỗ hẹp, không phô diễn được. */
}

/* ⚠️ BẮT BUỘC: các rule `[data-motion]` ở trên có CÙNG specificity với
   `.stage__chapter-panel.is-active .stage__panel` (đều 0,3,0) nhưng nằm SAU nên đè mất trạng thái
   hiện — chương `ignite` mất hẳn tiêu đề, các chương `drop`/`spread`/`assemble` không bao giờ
   về đúng chỗ. Thêm `.is-active` để nâng specificity lên 0,4,0. */
.stage__chapter-panel[data-motion].is-active .stage__panel { opacity: 1; transform: none; }
.stage__chapter-panel[data-motion].is-active .stage__aside { opacity: 1; transform: none; }
.stage__chapter-panel[data-motion="ignite"].is-active .stage__title { opacity: 1; }
.stage__chapter-panel[data-motion="settle"].is-active .stage__panel > *,
.stage__chapter-panel[data-motion="stagger"].is-active .stage__panel > * { opacity: 1; }

/* ⚠️ Khối responsive này PHẢI nằm CUỐI FILE, sau mọi rule desktop của ENH-073.
   Lần đầu tôi đặt nó vào media query có sẵn ở phía trên, rồi thêm rule desktop bên dưới —
   rule sau đè ngược, mobile tràn ngang 6px. Đúng bài học BUG-018. */
@media (max-width: 900px) {
	.stage__aside--row, .stage__aside--scatter {
		position: static; left: auto; right: auto; bottom: auto;
		grid-auto-flow: row;
		/* `grid-auto-columns: max-content` còn sót khi đã xếp hàng dọc sẽ giãn cột theo nội
		   dung rộng nhất ⇒ vượt khung. Trả về auto. */
		grid-auto-columns: auto;
		width: 100%; max-width: none; margin-left: 0;
	}
	.stage__aside--row .hud-row { flex-wrap: wrap; gap: var(--sp-3); }
	.stage__aside--row .spec-chips { flex-wrap: wrap; }
	.stage__aside--scatter .spec-chip:nth-child(2),
	.stage__aside--scatter .spec-chip:nth-child(3) { margin-left: 0; }

	/* Ở mobile khối HUD đã rộng hết khung, nên hiệu ứng vào theo CHIỀU NGANG vừa vô nghĩa vừa
	   đẩy nội dung vượt mép: `translateX(26px)` của chương "âm thanh" làm tràn đúng 6px (đo được).
	   Đổi sang chiều dọc. */
	.stage__chapter-panel[data-motion] .stage__aside { transform: translateY(14px); }
	.stage__chapter-panel[data-motion].is-active .stage__aside { transform: none; }
}
