/* ============================================================
   VAULT — phần bên trong của plonemraz.github.io
   Token màu lấy từ theme.css. File này lo bố cục.

   Khung: thanh ngang cố định trên đỉnh, thanh dọc cố định bên
   trái, orb cố định chính giữa màn hình nằm dưới cùng, nút đổi
   ngôn ngữ cố định góc dưới phải.
   ============================================================ */

:root {
  --topbar-h: 64px;
  --sidebar-w: 288px;
}

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  font: 400 16px/1.65 ui-sans-serif, system-ui, -apple-system,
        "Segoe UI", Roboto, sans-serif;
  background: var(--background);
  color: var(--foreground);
}

/* --- Song ngữ giao diện -------------------------------------
   Cả hai bản đều nằm trong DOM; nút chỉ đổi bản nào hiện. Nhờ
   vậy khi tắt JS trang vẫn đọc được bằng ngôn ngữ mặc định. */
html[data-ui-lang="en"] [data-l="vi"],
html[data-ui-lang="vi"] [data-l="en"] { display: none; }

/* --- Orb: cố định chính giữa, dưới mọi thứ ------------------ */
#orb-layer {
  position: fixed;
  left: 50%;
  top: 50%;
  width: min(120vh, 1000px);
  height: min(120vh, 1000px);
  transform: translate(-50%, -50%);
  opacity: .45;          /* mặt tiền dùng .85; trong này nhạt hơn vì
                            có chữ đè lên, nhưng .22 thì mờ quá */
  pointer-events: none;
  z-index: 0;
}
#orb-layer svg { width: 100%; height: 100%; display: block; }
#orb-layer::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 50%,
              transparent 66%, var(--background) 100%);
}

/* --- Thanh ngang -------------------------------------------- */
.topbar {
  position: fixed;
  inset: 0 0 auto 0;
  height: var(--topbar-h);
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  padding: 0 clamp(1rem, 3vw, 2rem);
  border-bottom: 1px solid var(--border);
  background: color-mix(in oklab, var(--background) 88%, transparent);
  backdrop-filter: blur(10px);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  color: var(--foreground);
  text-decoration: none;
  font-weight: 600;
  letter-spacing: -.01em;
  white-space: nowrap;
}
.brand img { border-radius: 50%; }
/* Tên viết hoa và cao bằng logo bên cạnh. Chữ hoa không có phần
   nhô lên hay thòng xuống, nên chiều cao nhìn thấy của nó ĐÚNG BẰNG
   cap height — với font hệ thống là khoảng 0.7em. Muốn cap height
   bằng 28px của logo thì font-size phải là 28 / 0.7 = 40px; đặt
   thẳng 28px sẽ ra chữ chỉ cao chừng 20px, thấp hơn logo rõ rệt.
   line-height 1 để hộp dòng không đội thanh 64px lên. */
.brand span {
  text-transform: uppercase;
  /* 37px, không phải 40px. Chữ hoa cao 0.7em, nên 40px cho đúng 28px
     — bằng hộp ảnh logo. Nhưng đĩa logo chỉ chiếm 26px trong hộp đó,
     hai mép còn lại là khoảng trong suốt, nên 40px làm tên nhô cao
     hơn logo thấy rõ. 26 / 0.7 = 37. Số này đo trên ảnh chụp thật,
     không suy ra từ khai báo width/height của thẻ img. */
  font-size: 37px;
  line-height: 1;
  /* Cắt hộp dòng về đúng chữ hoa. Không có dòng này thì hộp vẫn chừa
     chỗ cho phần thòng xuống, mà align-items căn theo HỘP chứ không
     theo chữ, nên tên bị đẩy xuống 2px so với tâm logo. Trình duyệt
     nào chưa hỗ trợ thì bỏ qua và về đúng trạng thái lệch 2px đó —
     xấu hơn một chút, không vỡ. */
  text-box: trim-both cap alphabetic;
}

.topnav { display: flex; gap: clamp(.75rem, 2vw, 1.75rem); }
.topnav a {
  color: var(--muted-foreground);
  text-decoration: none;
  font-size: .9375rem;
  padding: .35rem 0;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
  transition: color .15s ease, border-color .15s ease;
}
.topnav a:hover { color: var(--foreground); }
.topnav a.on { color: var(--foreground); border-bottom-color: var(--link); }

/* --- Nút và bảng điều hướng của màn hẹp ----------------------
   Màn rộng không dùng tới: nút ẩn, bảng ẩn, tấm che ẩn. */
:root { --np-w: min(88vw, 340px); }

/* Bảng ở trạng thái đóng nằm ngoài mép phải. Không cắt thì trang bị
   tràn ngang và cuộn được sang phải — chữ trong bài bị đẩy khỏi màn.
   Dùng clip chứ không hidden: hidden sẽ biến <html> thành khối cuộn
   và phá position:fixed của thanh trên. */
html { overflow-x: clip; }

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 40px;
  height: 40px;
  padding: 0 8px;
  border: 1px solid var(--border);
  border-radius: calc(var(--radius) - 2px);
  background: transparent;
  cursor: pointer;
}
.nav-toggle span {
  display: block;
  height: 1.5px;
  background: var(--foreground);
  border-radius: 2px;
}

.navpanel {
  display: none;
  position: fixed;
  top: var(--topbar-h);
  right: 0;
  bottom: 0;
  width: var(--np-w);
  z-index: 28;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* chừa đỉnh cho ô tìm kiếm ghim ở đó */
  padding: 5rem .85rem 1.5rem;
  background: var(--background);
  border-left: 1px solid var(--border);
  transform: translateX(100%);
  transition: transform .25s ease;
}
html[data-nav="open"] .navpanel { transform: none; }

/* Tấm che: làm mờ bài đang đọc và bắt cú chạm ra ngoài để đóng. */
.np-scrim {
  display: none;
  position: fixed;
  inset: var(--topbar-h) 0 0 0;
  z-index: 27;
  background: color-mix(in oklab, var(--background) 55%, transparent);
  opacity: 0;
  pointer-events: none;
  transition: opacity .25s ease;
}
html[data-nav="open"] .np-scrim { opacity: 1; pointer-events: auto; }

.np-tabs { list-style: none; margin: 0; padding: 0; }
.np-tabs > li {
  border-bottom: 1px solid var(--border);
  border-left: 2px solid transparent;
}
.np-tabs > li:last-child { border-bottom: 0; }
.np-tabs > li.on { border-left-color: var(--link); }

/* Tên tab và nút xổ nằm cùng một hàng: bấm tên thì chuyển trang,
   bấm mũi tên thì chỉ xổ danh sách ra tại chỗ. */
.np-row { display: flex; align-items: center; }

.np-link {
  flex: 1;
  display: block;
  padding: .8rem .6rem;
  color: var(--foreground);
  text-decoration: none;
  font-size: 1rem;
  transition: color .15s ease;
}
.np-tabs > li.on .np-link { color: var(--link); font-weight: 600; }
.np-link:hover { color: var(--link); }

.np-expand {
  flex: none;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 0;
  background: transparent;
  color: var(--muted-foreground);
  cursor: pointer;
  transition: color .15s ease;
}
.np-expand:hover { color: var(--foreground); }
.np-expand svg { transition: transform .2s ease; }
.np-expand[aria-expanded="true"] svg { transform: rotate(180deg); }
.np-expand[aria-expanded="true"] { color: var(--foreground); }

/* Danh sách của một tab, xổ ngay bên trong tab đó. Dùng lại nguyên
   bộ .side-h / .side-list / .side-sub nên trông giống cột dọc. */
.np-list { padding: 0 0 .85rem; }
.np-list .side-h:first-child { margin-top: .25rem; }
.np-list .side-list a { padding-left: .65rem; }

/* Mục nào chỉ có MỘT nhóm thì tiêu đề nhóm là thừa trong bảng: tên
   tab ngay phía trên đã nói đúng chừng ấy. Cột dọc màn rộng thì vẫn
   giữ, vì ở đó không có tên tab nào đứng trên nó. */
.np-list .side-h--solo { display: none; }

/* --- Thanh dọc ---------------------------------------------- */
.sidebar {
  position: fixed;
  top: var(--topbar-h);
  left: 0;
  bottom: 0;
  width: var(--sidebar-w);
  z-index: 20;
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--border);
  background: color-mix(in oklab, var(--background) 82%, transparent);
  backdrop-filter: blur(8px);
}

/* Tiêu đề nhóm. Sáng hơn các mục bên dưới — trước đây cả tiêu đề
   lẫn mục đều dùng --muted-foreground nên cột dọc chỉ còn một sắc
   xám. Phân tầng ở đây bằng ĐỘ SÁNG và CỠ CHỮ, không thêm màu mới. */
.side-h {
  margin: 1.5rem .5rem .5rem;
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--foreground);
  opacity: .72;
}
.side-h:first-child { margin-top: 0; }

.side-list { list-style: none; margin: 0; padding: 0; }
.side-list a {
  display: block;
  padding: .55rem .65rem;
  border-radius: calc(var(--radius) - 2px);
  color: var(--muted-foreground);
  text-decoration: none;
  border-left: 2px solid transparent;
  transition: background .15s ease, color .15s ease;
}
.side-list a:hover { background: var(--muted); color: var(--foreground); }
.side-list a.on {
  background: var(--muted);
  color: var(--foreground);
  border-left-color: var(--link);
}
.side-list .code {
  display: inline-block;
  min-width: 3.25rem;
  font: 600 .75rem/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--link);
}
.side-list .name { font-size: .9375rem; }
.side-list .meta {
  display: block;
  margin-top: .1rem;
  font-size: .75rem;
  color: var(--muted-foreground);
  opacity: .8;
}

/* --- Khung nội dung ----------------------------------------- */
.content {
  position: relative;
  z-index: 10;
  margin-left: var(--sidebar-w);
  padding: calc(var(--topbar-h) + clamp(2rem, 5vh, 3.5rem))
           clamp(1.25rem, 5vw, 4rem) 6rem;
  min-height: 100vh;
  transition: margin-left .25s ease;
}
body.no-sidebar .content,

/* Cột đọc: hẹp vừa tầm mắt, và căn GIỮA vùng còn lại thay vì dán
   sát mép trái. Khi thanh dọc mở, vùng còn lại là phần bên phải
   nó; khi đóng thì là cả bề ngang — cột tự dịch theo. */
.prose {
  max-width: 38rem;
  margin-inline: auto;
}

/* Justify cho văn xuôi dài. Dòng cuối của mỗi đoạn vẫn căn trái
   theo mặc định, nên đoạn một dòng không bị kéo giãn. hyphens
   giúp tiếng Anh bớt "sông" trắng giữa dòng; tiếng Việt không có
   từ điển ngắt âm tiết nên thuộc tính này lặng lẽ vô hiệu. */
.prose p,
.prose li {
  text-align: justify;
  text-justify: inter-word;
  hyphens: auto;
}

/* Những dòng ngắn KHÔNG được justify: nhãn, meta, chú thích,
   danh sách nghề nghiệp. Kéo giãn mấy dòng này trông rất gượng. */
.prose .kicker,
.prose .lede,
.prose .tr-note,
.prose .keywords,
.prose .note,
.paper-actions,
.about-links,
.doi-line,
.cv-roles li,
.cv-edu li {
  text-align: left;
  hyphens: manual;
}
.prose h1 {
  margin: 0 0 .35rem;
  font-size: clamp(1.75rem, 4vw, 2.5rem);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -.02em;
}
.prose .kicker {
  margin: 0 0 2rem;
  font-size: .875rem;
  color: var(--muted-foreground);
}
.prose h2 { margin: 2.5rem 0 .75rem; font-size: 1.375rem; font-weight: 600; }
.prose h3 { margin: 2rem 0 .5rem; font-size: 1.125rem; font-weight: 600; }
.prose p, .prose li { color: var(--foreground); }
.prose blockquote {
  margin: 1.5rem 0;
  padding-left: 1.25rem;
  border-left: 2px solid var(--border);
  color: var(--muted-foreground);
}
.prose hr { border: 0; border-top: 1px solid var(--border); margin: 2.5rem 0; }
.prose code {
  font: .875em/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--muted);
  padding: .1em .35em;
  border-radius: 4px;
}
.prose img { max-width: 100%; height: auto; }

/* --- Trang tác giả ------------------------------------------ */
/* Không có thanh dọc nên cột đọc nới rộng hơn bài viết thường */
.about { max-width: 44rem; margin-inline: auto; }

.cv-roles {
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
}
.cv-roles li {
  display: grid;
  grid-template-columns: minmax(9rem, max-content) 1fr max-content;
  gap: .25rem 1.5rem;
  align-items: baseline;
  padding: .6rem 0;
  border-bottom: 1px solid var(--border);
}
.cv-roles li:last-child { border-bottom: 0; }
.cv-roles .org { font-weight: 600; }
.cv-roles .role { color: var(--muted-foreground); }
.cv-roles .when {
  font: .8125rem/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--muted-foreground);
  white-space: nowrap;
}

.cv-edu { margin: 0; padding-left: 1.1rem; }
.cv-edu li { margin: .5rem 0; }

.about .note {
  color: var(--muted-foreground);
  font-size: .9375rem;
}

.about-links {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  margin-top: 3rem;
  padding-top: 2rem;
  border-top: 1px solid var(--border);
}

@media (max-width: 34rem) {
  .cv-roles li { grid-template-columns: 1fr max-content; }
  .cv-roles .role { grid-column: 1 / -1; }
}

/* --- Trang tài liệu ----------------------------------------- */
.paper .pcode {
  font: 600 .8125rem/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--link);
}

/* Phụ đề: tóm tắt một câu đứng ngay dưới tiêu đề. Trước đây chỉ
   trang tài liệu có, nay bài blog và fiction cũng dùng — nên định
   nghĩa một lần cho cả trang thay vì mỗi chỗ một kiểu. */
.prose .lede {
  margin: 0 0 1.75rem;
  font-size: 1.0625rem;
  color: var(--muted-foreground);
}

.paper-actions {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  margin: 0 0 2.5rem;
}
/* Nền xám xanh thay vì --foreground trắng: trắng cho 18.99:1 so
   với nền, quá chói cho một mảng đặc. --muted-foreground (#9CA8AB)
   hạ xuống 8.08:1 mà nút vẫn nổi rõ, chữ trên nút vẫn 8.08:1.
   Các token xám xanh tối hơn (--muted, --card, --primary) đều chỉ
   đạt 1.1-2.6:1 so với nền, tức nút sẽ tan vào trang. */
.btn {
  display: inline-block;
  padding: .55rem 1.1rem;
  border-radius: 999px;
  background: var(--muted-foreground);
  color: var(--background);
  font-size: .9375rem;
  font-weight: 600;
  text-decoration: none;
  transition: opacity .15s ease;
}
.btn:hover { opacity: .85; }

/* Abstract dài, nên nới dòng ra một chút cho dễ đọc */
.paper .abstract { line-height: 1.8; }

.paper .tr-note {
  margin: .75rem 0 0;
  font-size: .8125rem;
  color: var(--muted-foreground);
  opacity: .85;
  font-style: italic;
}

.paper .keywords {
  margin-top: 2rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
  font-size: .9375rem;
  color: var(--muted-foreground);
}
.paper .k-label {
  display: block;
  margin-bottom: .3rem;
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
}

.paper .doi-line {
  margin-top: 1.5rem;
  font: .8125rem/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
}
.paper .doi-line a { color: var(--muted-foreground); }
.paper .doi-line a:hover { color: var(--link); }

/* Danh sach cuon rieng, de o tim kiem ghim duoi day thanh doc */
.sidebar-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 1.5rem 1rem;
}
/* Chừa chỗ cho ô tìm kiếm ghim đỉnh, nếu không mục đầu bị nó che. */
.sidebar .sidebar-scroll { padding-top: 5rem; }

/* --- Ô tìm kiếm ---------------------------------------------
   Không còn nằm trong cột dọc: nó tự ghim ĐỈNH cột dọc bằng
   position:fixed, và trên màn hẹp thì ghim đỉnh bảng điều hướng.
   Đỉnh chứ không phải đáy: người cố ý đi tìm một bài thì không nên
   phải cuộn hết danh sách mới tới được ô tìm. Một phần tử duy nhất
   nên không trùng id, không phải đồng bộ hai ô. */
.search {
  position: fixed;
  left: 0;
  top: var(--topbar-h);
  width: var(--sidebar-w);
  z-index: 21;
  padding: .75rem;
  border-bottom: 1px solid var(--border);
  background: var(--background);
}
body.no-sidebar .search { display: none; }

.search-box {
  display: flex;
  align-items: center;
  gap: .55rem;
  padding: .6rem .9rem;
  border: 1px solid var(--border);
  border-radius: calc(var(--radius) - 2px);
  background: color-mix(in oklab, var(--card) 90%, transparent);
  backdrop-filter: blur(8px);
  color: var(--muted-foreground);
  transition: border-color .15s ease;
}
.search-box:focus-within { border-color: var(--link); color: var(--foreground); }

.search-box input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  color: var(--foreground);
  font: 400 .9375rem/1.4 inherit;
  outline: none;
}
.search-box input::placeholder { color: var(--muted-foreground); }
.search-box input::-webkit-search-cancel-button { filter: invert(.6); }

/* Bảng kết quả mở LÊN TRÊN vì ô nằm sát đáy màn hình */
.search-panel {
  position: absolute;
  left: .75rem;
  right: .75rem;
  /* Đổ xuống. Trước đây ô tìm nằm đáy cột nên bảng bung lên; giờ ô
     nằm đỉnh, bung lên là chui ra sau thanh ngang. */
  top: 100%;
  max-height: min(64vh, 30rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: .35rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: color-mix(in oklab, var(--card) 96%, transparent);
  backdrop-filter: blur(12px);
}
.search-panel a {
  display: block;
  padding: .5rem .6rem;
  border-radius: calc(var(--radius) - 4px);
  text-decoration: none;
  color: var(--foreground);
}
.search-panel a:hover { background: var(--muted); }
.search-panel .r-title { display: block; font-size: .9375rem; }

/* Tóm tắt trong kết quả: ô tìm kiếm vốn đã dò khớp cả trong summary,
   nên hiện nó ra để người dùng thấy VÌ SAO một mục khớp. */
.search-panel .r-sum {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-top: .15rem;
  font-size: .8125rem;
  line-height: 1.45;
  color: var(--muted-foreground);
}
.search-panel .r-meta {
  display: block;
  margin-top: .15rem;
  font-size: .75rem;
  color: var(--muted-foreground);
}
.search-panel .t { color: var(--link); margin-left: .35rem; }
.search-empty {
  margin: 0;
  padding: .6rem;
  font-size: .875rem;
  color: var(--muted-foreground);
}

/* --- Nút đổi ngôn ngữ --------------------------------------- */
/* Nút đổi ngôn ngữ nằm trong thanh ngang, ngay sau thương hiệu.
   Không làm mờ nữa: ở đây nó không đè lên nội dung nào. */
.langtoggle {
  /* Hút toàn bộ khoảng trống của thanh vào khe NGAY SAU nút này.
     Kết quả: thương hiệu và nút dính nhau bên trái, menu bị đẩy hẳn
     sang phải. Chỉ đúng khi nút đứng trước .topnav trong DOM — đứng
     sau thì khoảng trống rơi ra ngoài cùng và cả ba khối dồn sang
     trái. Màn hẹp .topnav ẩn đi, khi đó nó đẩy nút hamburger. */
  margin-right: auto;
  flex: none;
  padding: .3rem .55rem;
  border-radius: calc(var(--radius) - 5px);
  border: 1px solid var(--border);
  background: transparent;
  font: 700 .75rem/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .04em;
  cursor: pointer;
  transition: border-color .15s ease;
}
/* EN xanh azure, VIE đỏ tươi — nhìn là biết đang ở ngôn ngữ nào.
   Viền đổi màu theo chữ, nếu không thì chữ đỏ nằm trong viền xanh. */
.langtoggle [data-l="en"] { color: var(--link); }
.langtoggle [data-l="vi"] { color: var(--destructive); }

html[data-ui-lang="en"] .langtoggle {
  border-color: color-mix(in oklab, var(--link) 55%, transparent);
}
html[data-ui-lang="vi"] .langtoggle {
  border-color: color-mix(in oklab, var(--destructive) 55%, transparent);
}
html[data-ui-lang="en"] .langtoggle:hover { border-color: var(--link); }
html[data-ui-lang="vi"] .langtoggle:hover { border-color: var(--destructive); }

/* --- Màn hẹp ------------------------------------------------ */
/* Ngưỡng nâng từ 60rem lên 68rem: nav có 8 mục nên ở khoảng
   960-1080px nó chật và tràn. Cho hamburger xuất hiện sớm hơn. */
@media (max-width: 68rem) {
  /* Màn hẹp: thanh ngang và cột dọc biến mất, tất cả dồn vào bảng
     điều hướng mở bằng MỘT nút ở góc trên bên phải. */
  .topnav { display: none; }
  .sidebar { display: none; }
  .content { margin-left: 0; }

  .nav-toggle { display: inline-flex; }
  .navpanel   { display: block; }

  /* Ô tìm kiếm vẫn ghim đỉnh như ở màn rộng, chỉ đổi từ đỉnh cột dọc
     sang đỉnh bảng điều hướng, và trượt cùng bảng. */
  .search {
    left: auto;
    right: 0;
    width: var(--np-w);
    z-index: 29;
    border-left: 1px solid var(--border);
    background: var(--background);
    transform: translateX(100%);
    transition: transform .25s ease;
  }
  html[data-nav="open"] .search { transform: none; }
  body.no-sidebar .search { display: block; }
}

@media (prefers-reduced-motion: reduce) {
  .sidebar, .content, .navpanel, .np-scrim { transition: none; }
}

/* ============================================================
   AI GEN — lưới ảnh và lightbox
   ============================================================ */

.album h1 { margin: 0 0 .35rem; font-size: clamp(1.5rem, 3.5vw, 2.25rem); font-weight: 600; }
.album .kicker { margin: 0 0 2rem; font-size: .875rem; color: var(--muted-foreground); }

/* Lưới tự co: mỗi ô tối thiểu 160px, lấp đầy bề ngang có được */
.shots {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: .6rem;
}

.shot {
  display: block;
  position: relative;
  aspect-ratio: 1;
  overflow: hidden;
  border-radius: calc(var(--radius) - 2px);
  border: 1px solid var(--border);
  background: var(--muted);
  transition: border-color .15s ease, transform .15s ease;
}
.shot:hover { border-color: var(--link); transform: translateY(-2px); }
.shot img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* --- Lightbox --- */
.lbox {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: none;
  place-items: center;
  padding: clamp(.75rem, 3vw, 2.5rem);
  background: color-mix(in oklab, var(--background) 92%, transparent);
  backdrop-filter: blur(6px);
}
.lbox[open], .lbox.on { display: grid; }
.lbox img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: var(--radius);
}
.lbox-close, .lbox-prev, .lbox-next {
  position: fixed;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: color-mix(in oklab, var(--card) 88%, transparent);
  color: var(--foreground);
  font: 400 1.25rem/1 ui-sans-serif, system-ui, sans-serif;
  cursor: pointer;
}
.lbox-close { top: clamp(.75rem, 3vw, 1.5rem); right: clamp(.75rem, 3vw, 1.5rem); }
.lbox-prev  { left: clamp(.5rem, 2vw, 1.5rem); top: 50%; transform: translateY(-50%); }
.lbox-next  { right: clamp(.5rem, 2vw, 1.5rem); top: 50%; transform: translateY(-50%); }
.lbox-count {
  position: fixed;
  bottom: clamp(.75rem, 3vh, 1.5rem);
  left: 50%;
  transform: translateX(-50%);
  font: .8125rem/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--muted-foreground);
}
body.lbox-open { overflow: hidden; }

/* --- Trạng thái rỗng của thanh dọc --- */
.side-empty {
  margin: .5rem .65rem;
  font-size: .875rem;
  color: var(--muted-foreground);
}

/* --- Trang repo --- */
.project .lang-chip {
  display: inline-block;
  padding: .1rem .5rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  font: 600 .75rem/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--link);
}
.project .snapshot-note {
  margin: -1.5rem 0 2.5rem;
  font-size: .8125rem;
  font-style: italic;
  color: var(--muted-foreground);
  text-align: left;
}

/* --- P's Gems: trang giới thiệu --- */
.gems-intro { max-width: 42rem; margin-inline: auto; }
.gems-intro .roster-lead { margin: 0 0 1rem; }

.roster { list-style: none; margin: 0 0 1rem; padding: 0; }
.roster li {
  padding: .7rem 0;
  border-bottom: 1px solid var(--border);
  text-align: left;
}
.roster li:last-child { border-bottom: 0; }
.roster a { text-decoration: none; }
.roster .who {
  display: inline-block;
  min-width: 4.5rem;
  font-weight: 600;
  color: var(--link);
}
.roster .role { color: var(--foreground); font-weight: 600; }
.roster li > span { color: var(--muted-foreground); }

/* Tiêu đề nhóm trong một album: Anime, Realistic… */
.group-h {
  margin: 2.25rem 0 .75rem;
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.group-h:first-of-type { margin-top: 1rem; }

/* Ảnh minh hoạ trên trang giới thiệu P's Gems — căn giữa */
.gem-figure { margin: 2rem 0 2.5rem; text-align: center; }
.gem-visual {
  display: inline-block;
  max-width: min(22rem, 100%);
  border-radius: var(--radius);
  border: 1px solid var(--border);
  overflow: hidden;
  line-height: 0;
}
.gem-visual img { width: 100%; height: auto; display: block; }

/* ============================================================
   Trang cửa ngõ mỗi mục: giới thiệu + danh mục
   ============================================================ */
.section-intro { max-width: 42rem; margin-inline: auto; }
.section-intro h2 {
  margin: 2.75rem 0 .75rem;
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}

.entry-list { list-style: none; margin: 0; padding: 0; }
.entry-list li {
  padding: .85rem 0;
  border-bottom: 1px solid var(--border);
  text-align: left;
}
.entry-list li:last-child { border-bottom: 0; }

.entry-list .e-title {
  display: block;
  font-weight: 600;
  color: var(--foreground);
  text-decoration: none;
  line-height: 1.35;
}
.entry-list .e-title:hover { color: var(--link); }
.entry-list .e-code {
  font: 600 .8125rem/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--link);
}
.entry-list .e-meta {
  display: block;
  margin-top: .2rem;
  font-size: .8125rem;
  color: var(--muted-foreground);
}
.entry-list .e-tag { color: var(--link); }
.entry-list .e-doi {
  font: .75rem/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--muted-foreground);
  text-decoration: none;
}
.entry-list .e-doi:hover { color: var(--link); }
.entry-list .e-sum {
  display: block;
  margin-top: .3rem;
  font-size: .9375rem;
  color: var(--muted-foreground);
}

/* Mục liên hệ ở cuối trang Tác giả (trang Contact riêng đã bỏ) */
.contact-list { list-style: none; margin: 0; padding: 0; }
.contact-list li { padding: .3rem 0; text-align: left; }

/* ============================================================
   Nút phản hồi ở đầu và cuối mỗi bài
   ============================================================ */
.vote {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .6rem .9rem;
  margin: 1.75rem 0;
  padding: .7rem 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  text-align: left;
}
.vote-label { font-size: .875rem; color: var(--muted-foreground); }
.vote-buttons { display: flex; gap: .45rem; margin-left: auto; }

.vote-btn {
  display: grid;
  place-items: center;
  width: 34px;
  height: 30px;
  border-radius: calc(var(--radius) - 5px);
  border: 1px solid var(--border);
  background: transparent;
  color: var(--muted-foreground);
  cursor: pointer;
  transition: color .15s ease, border-color .15s ease, background .15s ease;
}
.vote-btn:hover { color: var(--foreground); border-color: var(--muted-foreground); }

/* Đã chọn: xanh cho đồng ý, đỏ cho không — cùng bảng màu với nút
   ngôn ngữ, nên người đọc nhận ra ngay ý nghĩa hai màu. */
.vote-btn.up[aria-pressed="true"] {
  color: var(--link);
  border-color: var(--link);
  background: color-mix(in oklab, var(--link) 12%, transparent);
}
.vote-btn.down[aria-pressed="true"] {
  color: var(--destructive);
  border-color: var(--destructive);
  background: color-mix(in oklab, var(--destructive) 12%, transparent);
}

/* --- Gem's Space -----------------------------------------------
   Chip tên và dòng khai tư cách người viết. Đặt ngay dưới kicker
   nên kéo lên 1.5rem, ăn vào khoảng trống 2rem của kicker. */
.gem-chip {
  display: inline-block;
  margin-right: .4rem;
  padding: .1rem .5rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  font: 600 .75rem/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--link);
}
/* Khoảng âm ở trên là để dòng này ÔM lấy dòng kicker (kicker chừa
   2rem ở dưới). Khi bài có phụ đề thì phụ đề đứng giữa, và phụ đề
   chỉ chừa 1.75rem — kéo lên nữa là hai dòng dính vào nhau. */
.lede + .gem-note { margin-top: 0; }
.gem-note {
  margin: -1.5rem 0 2.5rem;
  font-size: .8125rem;
  font-style: italic;
  color: var(--muted-foreground);
  text-align: left;
  hyphens: manual;
}

/* Ảnh minh hoạ trong bài. Ảnh dọc cao gấp đôi cột chữ nên nếu chỉ
   chặn bề ngang thì nó chiếm trọn màn hình; chặn thêm chiều cao. */
.gem-fig {
  display: block;
  margin: 1.75rem auto;
  max-width: 100%;
  max-height: 32rem;
  width: auto;
  border-radius: calc(var(--radius) - 2px);
}

/* --- GEMs: tài liệu kỹ thuật ---------------------------------- */

/* Nhãn trạng thái của chương: đã đóng / bảng sơ bộ / đang viết. */
.gems-status {
  display: inline-block;
  padding: .1rem .5rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  font: 600 .75rem/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--link);
}

/* Bảng — quy tắc bảng ĐẦU TIÊN của trang. Trước GEMs không bài nào
   dùng bảng, mà đặc tả thì sống bằng bảng.
   display:block để bảng tự cuộn ngang trong cột chữ: bảng bốn cột
   trên điện thoại mà không có khoản này là vỡ cả trang. */
.prose table {
  display: block;
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  border-collapse: collapse;
  margin: 1.5rem 0;
  font-size: .9375rem;
  line-height: 1.5;
}
.prose th,
.prose td {
  padding: .5rem .75rem;
  border: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
  hyphens: manual;
}
.prose th {
  font-weight: 600;
  background: color-mix(in oklab, var(--muted) 60%, transparent);
  white-space: nowrap;
}
.prose tbody tr:nth-child(even) td {
  background: color-mix(in oklab, var(--muted) 25%, transparent);
}

/* Chuyển chương. Hai ô cạnh nhau, ô sau đẩy sang phải; màn hẹp thì
   xếp dọc. */
.doc-nav {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
  margin: 3rem 0 0;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
}
.doc-nav a {
  flex: 1 1 12rem;
  padding: .7rem .9rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  text-decoration: none;
  transition: border-color .15s ease;
}
.doc-nav a:hover { border-color: var(--link); }
.doc-next { text-align: right; margin-left: auto; }
.doc-nav .doc-dir {
  display: block;
  font-size: .75rem;
  color: var(--muted-foreground);
}
.doc-nav .doc-name {
  display: block;
  margin-top: .15rem;
  font-weight: 600;
  color: var(--foreground);
}

/* Mục lục GEMs, tầng con. Gạch dọc bên trái để mắt bám được tầng —
   đệ quy nên tầng ba, tầng bốn tự thụt tiếp theo cùng quy tắc. */
.side-sub {
  list-style: none;
  margin: .15rem 0 .35rem;
  padding: 0 0 0 .75rem;
  border-left: 1px solid var(--border);
}
.side-sub a {
  display: block;
  padding: .3rem .5rem;
  border-radius: calc(var(--radius) - 6px);
  text-decoration: none;
  font-size: .9375rem;
}
.side-sub a:hover { background: color-mix(in oklab, var(--muted) 45%, transparent); }
.side-sub a.on { color: var(--link); }
.side-sub .name { display: block; color: inherit; }
.side-sub .meta {
  display: block;
  font-size: .75rem;
  color: var(--muted-foreground);
}

/* Cũng tầng con, nhưng trên trang mục của GEMs. */
.doc-kids {
  list-style: none;
  margin: .4rem 0 0;
  padding: 0 0 0 .9rem;
  border-left: 1px solid var(--border);
}
.doc-kids li { padding: .25rem 0; }
.doc-kids a { text-decoration: none; }
.doc-kids .name { color: var(--foreground); }
.doc-kids a:hover .name { color: var(--link); }
.doc-kids .meta {
  margin-left: .5rem;
  font-size: .75rem;
  color: var(--muted-foreground);
}

/* Dòng chỉ chỗ ở cuối trang giới thiệu mỗi mục. Là chú thích, không
   phải văn xuôi của bài, nên hạ xuống một bậc. */
.section-intro .note {
  margin-top: 2rem;
  font-size: .9375rem;
  color: var(--muted-foreground);
}

/* --- Dòng meta dưới tiêu đề bài ------------------------------
   Mang những thứ trước đây nằm ở trang giới thiệu của mục: thẻ với
   bài viết, DOI với tài liệu. */
.prose .kicker .tag {
  color: var(--link);
  white-space: nowrap;
}
.prose .kicker .e-doi {
  font: inherit;
  color: var(--muted-foreground);
  text-decoration: none;
  border-bottom: 1px solid var(--border);
}
.prose .kicker .e-doi:hover { color: var(--link); border-bottom-color: var(--link); }

/* --- Khối mã -------------------------------------------------
   Trang chưa từng có quy tắc nào cho <pre>, nên khối mã trong các
   bản chụp README cứ giãn hết cỡ theo dòng dài nhất — có khối rộng
   1092px. Trên điện thoại, một phần tử rộng hơn màn hình kéo giãn
   luôn khung nhìn, khung nhìn vượt ngưỡng 68rem, và trang chuyển
   sang bố cục màn rộng: nút điều hướng văng ra ngoài mép phải.
   Cho khối mã tự cuộn ngang bên trong là hết. */
.prose pre {
  max-width: 100%;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  margin: 1.5rem 0;
  padding: .9rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: color-mix(in oklab, var(--muted) 70%, transparent);
  font: .8125rem/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
  text-align: left;
  hyphens: manual;
}
/* Trong khối mã thì thẻ code không cần nền và bo góc riêng nữa. */
.prose pre code {
  padding: 0;
  background: none;
  font: inherit;
}

/* Chuỗi dài không có chỗ ngắt — URL, định danh — cũng kéo giãn được
   cả trang y như vậy. Cho phép bẻ chúng. */
.prose { overflow-wrap: break-word; }
