/* ==========================================================================
   Design tokens
   ========================================================================== */
:root {
  /* Surfaces */
  --void: #04060b;
  --bg-base: #070b14;
  --bg-elev: #0a0f1c;
  --panel: rgba(15, 22, 38, 0.62);
  --panel-solid: #0c1220;
  --panel-strong: #101a2e;
  --border-subtle: rgba(148, 163, 184, 0.14);
  --border-blue: rgba(59, 130, 246, 0.38);

  /* Accents (Blue = chủ đạo, Violet = điểm nhấn signature) */
  --blue-700: #1d4ed8;
  --blue-600: #2563eb;
  --blue-500: #3b82f6;
  --blue-400: #60a5fa;
  --blue-300: #93c5fd;
  --cyan-400: #38bdf8;
  --violet-400: #a78bfa;

  /* Text */
  --text-100: #eef2fb;
  --text-300: #c2cbdc;
  --text-500: #8793ab;
  --text-600: #5b6680;
  --text-700: #3d4760;

  /* Type */
  --font-display: "Space Grotesk", "Inter", ui-sans-serif, system-ui, sans-serif;
  --font-sans: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
    Roboto, Helvetica, Arial, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo,
    Consolas, "Liberation Mono", monospace;

  /* Layout */
  --content-width: 74rem;
  --radius-lg: 22px;
  --radius-md: 16px;
  --radius-sm: 10px;
  --ease: cubic-bezier(0.16, 0.8, 0.24, 1);
  --nav-h: 4.5rem;
  --player-h: 4.75rem;
}

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

html {
  color-scheme: dark;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + 0.75rem);
}

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg-base);
  color: var(--text-100);
  font-family: var(--font-sans);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  /* Chừa chỗ cho Music Player cố định phía dưới (chiều cao thực tế do
     JS đo và ghi vào biến --player-h, tránh che nội dung footer). */
  padding-bottom: var(--player-h);
}

a {
  color: inherit;
}

ol,
ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

h1,
h2,
h3,
p {
  margin: 0;
}

img {
  max-width: 100%;
  display: block;
}

:focus-visible {
  outline: 2px solid var(--blue-400);
  outline-offset: 3px;
  border-radius: 4px;
}

.skip-link {
  position: absolute;
  left: 1rem;
  top: -3rem;
  background: var(--blue-500);
  color: #04060b;
  padding: 0.6rem 1rem;
  border-radius: var(--radius-sm);
  font-weight: 600;
  z-index: 999;
  transition: top 0.2s var(--ease);
}

.skip-link:focus {
  top: 1rem;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   Ambient background — chỉ dùng CSS gradient/transform, không JS/particles
   để giữ FPS ổn định trên mobile.
   ========================================================================== */
.bg-decor {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}

.glow {
  position: absolute;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.55;
  will-change: transform;
}

.glow-a {
  width: 38rem;
  height: 38rem;
  top: -14rem;
  left: -10rem;
  background: radial-gradient(circle, rgba(37, 99, 235, 0.55), transparent 70%);
  animation: drift-a 26s ease-in-out infinite;
}

.glow-b {
  width: 32rem;
  height: 32rem;
  top: 32rem;
  right: -12rem;
  background: radial-gradient(circle, rgba(56, 189, 248, 0.35), transparent 70%);
  animation: drift-b 30s ease-in-out infinite;
}

@keyframes drift-a {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(3rem, 2rem); }
}

@keyframes drift-b {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(-2.5rem, -1.5rem); }
}

.grid-overlay {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(148, 163, 184, 0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(148, 163, 184, 0.05) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(ellipse 80% 60% at 50% 0%, black 40%, transparent 90%);
}

/* Canvas particle nền — hạt trắng nhỏ + đường nối rất mờ, vẽ bằng JS
   (public/js/particles.js). Luôn đứng sau nội dung và không chặn tương tác. */
.particles-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  /* Che bớt phần particle sát mép trên/dưới để không "đấu" với nav/footer,
     giữ nội dung luôn dễ đọc. */
  mask-image: linear-gradient(
    180deg,
    transparent 0,
    black 6rem,
    black calc(100% - 6rem),
    transparent 100%
  );
  -webkit-mask-image: linear-gradient(
    180deg,
    transparent 0,
    black 6rem,
    black calc(100% - 6rem),
    transparent 100%
  );
}

@media (max-width: 48rem) {
  .glow-a, .glow-b {
    animation: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .particles-canvas {
    /* particles.js tự vẽ đúng 1 khung tĩnh khi reduced-motion bật */
  }
}

/* ==========================================================================
   Navbar
   ========================================================================== */
.navbar {
  position: sticky;
  top: 0;
  z-index: 100;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  border-bottom: 1px solid transparent;
  transition: background-color 0.3s var(--ease), border-color 0.3s var(--ease),
    box-shadow 0.3s var(--ease);
}

.navbar.is-scrolled {
  background: rgba(7, 11, 20, 0.72);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  border-bottom-color: var(--border-subtle);
  box-shadow: 0 10px 40px -20px rgba(0, 0, 0, 0.6);
}

.navbar-inner {
  width: 100%;
  max-width: var(--content-width);
  margin: 0 auto;
  padding: 0 clamp(1.25rem, 4vw, 2.5rem);
  display: flex;
  align-items: center;
  gap: 1.5rem;
}

.mark {
  font-family: var(--font-mono);
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--text-100);
  text-decoration: none;
  letter-spacing: 0.01em;
  white-space: nowrap;
}

.mark-accent {
  color: var(--blue-400);
}

.mark-bracket {
  color: var(--text-600);
}

.mark-text {
  display: inline-block;
}

.mark-cursor {
  display: inline-block;
  width: 0.11em;
  height: 0.95em;
  margin-left: 0.05em;
  vertical-align: -0.1em;
  background: var(--blue-400);
  opacity: 0;
  border-radius: 1px;
}

.mark-cursor.is-active {
  opacity: 1;
  animation: blink 0.9s step-end infinite;
}

@media (prefers-reduced-motion: reduce) {
  .mark-cursor { display: none; }
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  margin-left: auto;
}

.nav-link {
  position: relative;
  padding: 0.5rem 0.85rem;
  font-size: 0.88rem;
  font-weight: 500;
  color: var(--text-500);
  text-decoration: none;
  border-radius: 999px;
  transition: color 0.2s var(--ease), background-color 0.2s var(--ease);
}

.nav-link::after {
  content: "";
  position: absolute;
  left: 0.85rem;
  right: 0.85rem;
  bottom: 0.32rem;
  height: 2px;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--blue-400), var(--cyan-400));
  transform: scaleX(0);
  transform-origin: center;
  transition: transform 0.25s var(--ease);
}

.nav-link:hover::after,
.nav-link.is-active::after {
  transform: scaleX(1);
}

.nav-link:hover {
  color: var(--text-100);
  background: rgba(59, 130, 246, 0.1);
}

.nav-link.is-active {
  color: var(--blue-300);
  background: rgba(59, 130, 246, 0.14);
}

.nav-cta {
  margin-left: 0.5rem;
  flex-shrink: 0;
}

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 2.4rem;
  height: 2.4rem;
  margin-left: auto;
  background: transparent;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.nav-toggle span {
  display: block;
  width: 1.2rem;
  height: 2px;
  margin: 0 auto;
  background: var(--text-100);
  border-radius: 2px;
  transition: transform 0.25s var(--ease), opacity 0.25s var(--ease);
}

.nav-toggle[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}
.nav-toggle[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
}
.nav-toggle[aria-expanded="true"] span:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

.mobile-menu {
  display: none;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.5rem clamp(1.25rem, 4vw, 2.5rem) 1.25rem;
  background: rgba(7, 11, 20, 0.96);
  backdrop-filter: blur(16px);
  border-bottom: 1px solid var(--border-subtle);
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  transition: max-height 0.3s var(--ease), opacity 0.25s var(--ease);
}

.mobile-menu.is-open {
  max-height: 24rem;
  opacity: 1;
}

.mobile-link {
  padding: 0.75rem 0.5rem;
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--text-300);
  text-decoration: none;
  border-bottom: 1px solid var(--border-subtle);
}

.mobile-link:last-child {
  border-bottom: none;
}

/* ==========================================================================
   Buttons
   ========================================================================== */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.75rem 1.3rem;
  font-family: var(--font-sans);
  font-size: 0.9rem;
  font-weight: 600;
  text-decoration: none;
  border-radius: 999px;
  border: 1px solid transparent;
  cursor: pointer;
  overflow: hidden;
  transition: transform 0.2s var(--ease), box-shadow 0.25s var(--ease),
    background-color 0.2s var(--ease), border-color 0.2s var(--ease),
    color 0.2s var(--ease);
  will-change: transform;
}

/* Hiệu ứng "quét sáng" chạy ngang nút khi hover — dải sáng chéo, mượt,
   chỉ dùng transform (GPU) nên không ảnh hưởng hiệu năng cuộn trang. */
.btn::after {
  content: "";
  position: absolute;
  top: 0;
  left: -75%;
  width: 45%;
  height: 100%;
  background: linear-gradient(120deg, transparent, rgba(255, 255, 255, 0.4), transparent);
  transform: skewX(-20deg);
  transition: left 0.7s var(--ease);
  pointer-events: none;
}

.btn:hover::after {
  left: 130%;
}

.btn:active {
  transform: translateY(1px) scale(0.98);
}

.btn-icon {
  width: 1rem;
  height: 1rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 0.25s var(--ease);
}

.btn-primary {
  background: linear-gradient(135deg, var(--blue-600), var(--blue-400));
  color: #04060b;
  box-shadow: 0 8px 24px -8px rgba(59, 130, 246, 0.55);
}

.btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 14px 32px -8px rgba(59, 130, 246, 0.75);
}

.btn-primary:hover .btn-icon {
  transform: translateX(3px);
}

.btn-outline {
  background: rgba(59, 130, 246, 0.06);
  border-color: var(--border-blue);
  color: var(--text-100);
}

.btn-outline:hover {
  background: rgba(59, 130, 246, 0.14);
  border-color: var(--blue-400);
  transform: translateY(-2px);
}

.btn-ghost {
  padding: 0.55rem 1.1rem;
  background: transparent;
  border-color: var(--border-subtle);
  color: var(--text-300);
  font-size: 0.85rem;
}

.btn-ghost:hover {
  border-color: var(--blue-400);
  color: var(--blue-300);
}

/* ==========================================================================
   Hero
   ========================================================================== */
.hero {
  position: relative;
  z-index: 1;
  padding-bottom: clamp(3rem, 6vw, 5rem);
}

.hero-banner {
  position: relative;
  width: 100%;
  height: clamp(9rem, 26vw, 15rem);
  overflow: hidden;
  background: linear-gradient(160deg, #0b1424 0%, #060a13 100%);
  background-size: cover;
  background-position: center;
}

.hero-banner::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(4, 6, 11, 0.1) 0%, var(--bg-base) 100%);
}

.hero-content {
  max-width: var(--content-width);
  margin: 0 auto;
  padding: 0 clamp(1.25rem, 4vw, 2.5rem);
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: end;
  margin-top: clamp(-4.5rem, -8vw, -3.5rem);
  position: relative;
  z-index: 2;
}

.hero-identity {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.avatar-frame {
  width: clamp(6rem, 12vw, 8rem);
  height: clamp(6rem, 12vw, 8rem);
  border-radius: 28%;
  overflow: hidden;
  background: var(--panel-solid);
  border: 3px solid var(--bg-base);
  box-shadow: 0 0 0 1px var(--border-blue), 0 12px 32px -10px rgba(59, 130, 246, 0.5);
  background-size: cover;
  background-position: center;
  /* Nhịp sáng chậm quanh khung avatar — box-shadow của chính element không
     bị overflow:hidden của nó cắt mất (overflow chỉ cắt nội dung bên trong),
     nên tạo được viền "thở" ra ngoài mà không cần thêm phần tử/HTML mới. */
  animation: avatar-glow-pulse 4s ease-in-out infinite;
}

@keyframes avatar-glow-pulse {
  0%, 100% {
    box-shadow: 0 0 0 1px var(--border-blue), 0 12px 32px -10px rgba(59, 130, 246, 0.5),
      0 0 0 0 rgba(56, 189, 248, 0.25);
  }
  50% {
    box-shadow: 0 0 0 1px var(--blue-400), 0 12px 34px -10px rgba(59, 130, 246, 0.68),
      0 0 26px 7px rgba(56, 189, 248, 0.3);
  }
}

.placeholder-box {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  text-align: center;
  border: 1.5px dashed var(--border-blue);
  background: repeating-linear-gradient(
    135deg,
    rgba(59, 130, 246, 0.06),
    rgba(59, 130, 246, 0.06) 10px,
    rgba(59, 130, 246, 0.02) 10px,
    rgba(59, 130, 246, 0.02) 20px
  );
  color: var(--text-500);
}

.avatar-placeholder {
  border-radius: 28%;
  padding: 0.5rem;
}

.ph-icon {
  width: 1.6rem;
  height: 1.6rem;
  fill: var(--blue-400);
  opacity: 0.85;
}

.ph-icon-sm {
  width: 1.25rem;
  height: 1.25rem;
}

.ph-title {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--text-300);
  letter-spacing: 0.02em;
}

.ph-title-sm {
  font-size: 0.62rem;
}

.ph-hint {
  display: none;
}

.banner-placeholder {
  flex-direction: row;
  gap: 0.75rem;
  border-width: 0 0 1.5px 0;
  border-radius: 0;
}

.banner-placeholder .ph-icon {
  width: 1.9rem;
  height: 1.9rem;
}

.banner-placeholder .ph-hint {
  display: block;
  font-size: 0.72rem;
  color: var(--text-600);
  font-family: var(--font-mono);
}

.banner-placeholder .ph-hint code {
  color: var(--blue-300);
}

.hero-has-image .placeholder-box {
  display: none;
}

.hero-text {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.eyebrow,
.section-eyebrow {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--blue-300);
  margin-bottom: 0.35rem;
}

.prompt {
  color: var(--cyan-400);
  margin-right: 0.35em;
}

.hero-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2rem, 5vw, 3.1rem);
  line-height: 1.08;
  letter-spacing: -0.01em;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.6rem;
}

.hero-nick {
  font-size: 0.42em;
  font-family: var(--font-mono);
  font-weight: 600;
  /* Điểm nhấn "chữ ký" của trang — gradient 3 tông chạy chậm, làm biệt danh
     nổi bật mà không cần thêm màu rời rạc ở nơi khác. */
  background: linear-gradient(90deg, var(--blue-400), var(--cyan-400), var(--violet-400), var(--blue-400));
  background-size: 300% auto;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: var(--blue-400); /* fallback nếu trình duyệt không hỗ trợ background-clip:text */
  animation: nick-gradient-flow 6s linear infinite;
}

@keyframes nick-gradient-flow {
  to { background-position: 300% center; }
}

.hero-role {
  font-size: clamp(1.05rem, 2vw, 1.25rem);
  font-weight: 600;
  color: var(--text-100);
  margin-top: 0.3rem;
}

.hero-meta {
  font-size: 0.9rem;
  color: var(--text-500);
  margin-top: 0.2rem;
}

.dot-sep {
  margin: 0 0.5rem;
  color: var(--text-700);
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem;
  margin-top: 1.35rem;
}

.hero-socials {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 1.25rem;
}

/* ==========================================================================
   Editor / terminal card (dùng lại cho hero + contact)
   ========================================================================== */
.hero-terminal,
.contact-panel {
  position: relative;
  width: 100%;
  background: var(--panel);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: 0 24px 60px -24px rgba(0, 0, 0, 0.7);
  transition: border-color 0.3s var(--ease), box-shadow 0.3s var(--ease);
}

.hero-terminal::before,
.contact-panel::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(360px circle at var(--mx, 50%) var(--my, 0%), rgba(59, 130, 246, 0.14), transparent 70%);
  opacity: 0;
  transition: opacity 0.35s var(--ease);
  pointer-events: none;
}

.hero-terminal:hover::before,
.contact-panel:hover::before {
  opacity: 1;
}

.hero-terminal:hover,
.contact-panel:hover {
  border-color: var(--border-blue);
  box-shadow: 0 24px 60px -20px rgba(37, 99, 235, 0.35);
}

.editor-titlebar {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.75rem 1rem;
  background: rgba(16, 26, 46, 0.7);
  border-bottom: 1px solid var(--border-subtle);
}

.dot {
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
  background: var(--text-700);
}

.dot-a { background: #ef6b6b; }
.dot-b { background: #ecb75f; }
.dot-c { background: #58c78a; }

.tab {
  margin-left: 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--text-500);
}

.code {
  counter-reset: line;
  padding: 1.1rem 1.15rem;
  font-family: var(--font-mono);
  font-size: clamp(0.78rem, 1.4vw, 0.86rem);
  overflow-x: auto;
}

.code-row {
  counter-increment: line;
  display: grid;
  grid-template-columns: 1.5rem 1fr;
  column-gap: 0.9rem;
  min-height: 1.55rem;
  align-items: baseline;
  white-space: pre;
  /* Hiệu ứng "gõ code" khi tải trang — từng dòng hiện nối tiếp nhau,
     tạo cảm giác terminal đang gõ trực tiếp thay vì bung hết cùng lúc. */
  opacity: 0;
  animation: code-line-in 0.5s var(--ease) forwards;
}

.code-row:nth-child(1) { animation-delay: 0.45s; }
.code-row:nth-child(2) { animation-delay: 0.58s; }
.code-row:nth-child(3) { animation-delay: 0.71s; }
.code-row:nth-child(4) { animation-delay: 0.84s; }
.code-row:nth-child(5) { animation-delay: 0.97s; }
.code-row:nth-child(6) { animation-delay: 1.10s; }
.code-row:nth-child(7) { animation-delay: 1.23s; }

@keyframes code-line-in {
  from { opacity: 0; transform: translateX(-10px); }
  to { opacity: 1; transform: translateX(0); }
}

.code-row::before {
  content: counter(line);
  color: var(--text-700);
  text-align: right;
  user-select: none;
}

.code-line--indent {
  padding-left: 1.1rem;
}

.kw { color: #e3b34d; }
.var { color: var(--text-100); }
.key { color: var(--blue-300); }
.str { color: #5fd9a4; }
.punc { color: var(--text-600); }

.caret {
  display: inline-block;
  width: 0.5em;
  height: 1em;
  margin-left: 0.2em;
  vertical-align: text-bottom;
  background: var(--blue-400);
  animation: blink 1.1s step-end infinite;
}

@keyframes blink {
  0%, 49% { opacity: 1; }
  50%, 100% { opacity: 0; }
}

.scroll-cue {
  display: flex;
  justify-content: center;
  margin-top: clamp(2.5rem, 5vw, 4rem);
}

.scroll-cue span {
  width: 1.4rem;
  height: 2.3rem;
  border: 1.5px solid var(--border-blue);
  border-radius: 999px;
  position: relative;
}

.scroll-cue span::before {
  content: "";
  position: absolute;
  top: 6px;
  left: 50%;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--blue-400);
  transform: translateX(-50%);
  animation: scroll-dot 1.8s ease-in-out infinite;
}

@keyframes scroll-dot {
  0% { top: 6px; opacity: 1; }
  70% { top: 15px; opacity: 0; }
  100% { top: 15px; opacity: 0; }
}

/* ==========================================================================
   Generic section layout
   ========================================================================== */
.section {
  position: relative;
  z-index: 1;
  padding: clamp(4rem, 8vw, 6.5rem) 0;
}

.section::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--border-subtle) 15%, var(--border-subtle) 85%, transparent);
}

.section-inner {
  max-width: var(--content-width);
  margin: 0 auto;
  padding: 0 clamp(1.25rem, 4vw, 2.5rem);
}

.section-title {
  position: relative;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.7rem, 3.4vw, 2.4rem);
  letter-spacing: -0.01em;
  padding-bottom: 0.65rem;
  margin-bottom: clamp(1.75rem, 4vw, 2.75rem);
}

.section-title::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  height: 3px;
  width: 3.25rem;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--blue-400), var(--cyan-400), var(--violet-400));
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.7s var(--ease) 0.1s;
}

.section-title.is-visible::after {
  transform: scaleX(1);
}

/* ==========================================================================
   About
   ========================================================================== */
.about-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: clamp(1.75rem, 4vw, 3rem);
  align-items: start;
}

.about-bio {
  font-size: clamp(1rem, 1.6vw, 1.12rem);
  line-height: 1.85;
  color: var(--text-300);
  max-width: 42rem;
}

.stat-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.9rem;
}

.stat-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 1.1rem 1.15rem;
  background: var(--panel);
  backdrop-filter: blur(14px);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  overflow: hidden;
  transition: transform 0.25s var(--ease), border-color 0.25s var(--ease),
    box-shadow 0.25s var(--ease);
}

.stat-card::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(200px circle at var(--mx, 50%) var(--my, 0%), rgba(59, 130, 246, 0.22), transparent 70%);
  opacity: 0;
  transition: opacity 0.3s var(--ease);
}

.stat-card:hover::before {
  opacity: 1;
}

.stat-card:hover {
  transform: translateY(-4px);
  border-color: var(--border-blue);
  box-shadow: 0 16px 36px -18px rgba(37, 99, 235, 0.55);
}

.stat-label {
  position: relative;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-600);
}

.stat-value {
  position: relative;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text-100);
}

/* ==========================================================================
   Skills
   ========================================================================== */
.skills-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.9rem;
}

.skill-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 1.15rem 1.2rem;
  background: var(--panel);
  backdrop-filter: blur(14px);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  overflow: hidden;
  transition: transform 0.25s var(--ease), border-color 0.25s var(--ease),
    box-shadow 0.25s var(--ease);
}

.skill-card::before {
  content: "";
  position: absolute;
  inset: 0;
  /* Spotlight bám theo vị trí con trỏ (--mx/--my do JS cập nhật khi
     hover) thay vì điểm cố định — cảm giác thẻ "phản hồi" theo chuột. */
  background: radial-gradient(220px circle at var(--mx, 50%) var(--my, 0%), rgba(59, 130, 246, 0.28), transparent 70%);
  opacity: 0;
  transition: opacity 0.3s var(--ease);
}

.skill-card:hover {
  transform: translateY(-5px);
  border-color: var(--border-blue);
  box-shadow: 0 18px 40px -20px rgba(37, 99, 235, 0.6);
}

.skill-card:hover::before {
  opacity: 1;
}

.skill-icon {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.6rem;
  height: 2.6rem;
  flex-shrink: 0;
  border-radius: 12px;
  color: var(--blue-300);
  background: rgba(59, 130, 246, 0.12);
  border: 1px solid var(--border-blue);
  transition: color 0.25s var(--ease), background-color 0.25s var(--ease),
    border-color 0.25s var(--ease);
}

.skill-icon svg {
  width: 1.4rem;
  height: 1.4rem;
}

.skill-card:hover .skill-icon {
  color: var(--text-100);
  background: rgba(59, 130, 246, 0.22);
  border-color: var(--blue-400);
}

.skill-name {
  position: relative;
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--text-100);
}

.skill-category {
  position: relative;
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  color: var(--text-600);
}

/* ==========================================================================
   Hobbies
   ========================================================================== */
.hobbies-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.9rem;
}

.hobby-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  padding: 1.4rem 1rem;
  text-align: center;
  background: var(--panel);
  backdrop-filter: blur(14px);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  overflow: hidden;
  transition: transform 0.25s var(--ease), border-color 0.25s var(--ease),
    box-shadow 0.25s var(--ease);
}

.hobby-card::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(220px circle at var(--mx, 50%) var(--my, 0%), rgba(59, 130, 246, 0.28), transparent 70%);
  opacity: 0;
  transition: opacity 0.3s var(--ease);
}

.hobby-card:hover {
  transform: translateY(-5px);
  border-color: var(--border-blue);
  box-shadow: 0 18px 40px -20px rgba(37, 99, 235, 0.6);
}

.hobby-card:hover::before {
  opacity: 1;
}

.hobby-icon {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.8rem;
  height: 2.8rem;
  flex-shrink: 0;
  font-size: 1.45rem;
  line-height: 1;
  border-radius: 12px;
  background: rgba(59, 130, 246, 0.12);
  border: 1px solid var(--border-blue);
  transition: background-color 0.25s var(--ease), border-color 0.25s var(--ease),
    transform 0.25s var(--ease);
}

.hobby-card:hover .hobby-icon {
  background: rgba(59, 130, 246, 0.22);
  border-color: var(--blue-400);
  transform: scale(1.08);
}

.hobby-name {
  position: relative;
  font-weight: 600;
  font-size: 0.88rem;
  color: var(--text-100);
}

/* ==========================================================================
   Contact
   ========================================================================== */
.contact-sub {
  color: var(--text-500);
  margin-top: -1.25rem;
  margin-bottom: 2rem;
  max-width: 32rem;
}

.contact-panel {
  max-width: 40rem;
}

.console-body {
  padding: 1.25rem 1.3rem 1.5rem;
}

.console-line {
  margin: 0 0 0.9rem;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--text-500);
}

.console-output {
  padding-left: 0.1rem;
}

.console-hint {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--text-600);
}

.social-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.65rem;
}

.social-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  flex-shrink: 0;
  border: 1px solid var(--border-blue);
  border-radius: 999px;
  color: var(--blue-300);
  text-decoration: none;
  background: rgba(59, 130, 246, 0.08);
  transition: border-color 0.25s var(--ease), color 0.25s var(--ease),
    background-color 0.25s var(--ease), box-shadow 0.25s var(--ease),
    transform 0.25s var(--ease);
}

.social-icon {
  display: inline-flex;
  width: 1.3rem;
  height: 1.3rem;
}

.social-icon svg {
  width: 100%;
  height: 100%;
}

.social-link:hover,
.social-link:focus-visible {
  color: var(--text-100);
  border-color: var(--blue-400);
  background: rgba(59, 130, 246, 0.2);
  box-shadow: 0 10px 26px -12px rgba(59, 130, 246, 0.75);
  transform: translateY(-3px);
}

.social-link:focus-visible {
  outline: 2px solid var(--blue-400);
  outline-offset: 2px;
}

/* Tooltip hiện đại — CSS thuần, không thêm thư viện ngoài */
.social-link::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 0.5rem);
  left: 50%;
  transform: translate(-50%, 4px);
  padding: 0.3rem 0.6rem;
  border-radius: 8px;
  background: var(--panel-solid);
  border: 1px solid var(--border-blue);
  color: var(--text-100);
  font-family: var(--font-mono);
  font-size: 0.7rem;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease);
  z-index: 5;
}

.social-link:hover::after,
.social-link:focus-visible::after {
  opacity: 1;
  transform: translate(-50%, 0);
}

@media (max-width: 30rem) {
  .social-link {
    width: 2.5rem;
    height: 2.5rem;
  }
}

/* ==========================================================================
   Footer
   ========================================================================== */
.site-footer {
  position: relative;
  z-index: 1;
  border-top: 1px solid var(--border-subtle);
  padding: 2rem clamp(1.25rem, 4vw, 2.5rem);
}

.footer-inner {
  max-width: var(--content-width);
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}

.mark-sm {
  font-size: 0.85rem;
}

.site-footer p {
  margin: 0;
  font-size: 0.78rem;
  color: var(--text-600);
}

/* ==========================================================================
   Scroll reveal
   ========================================================================== */
.reveal {
  opacity: 0;
  transform: translateY(16px);
  /* transition-delay dùng biến --reveal-delay (set qua JS cho grid,
     hoặc nth-child bên dưới cho card tĩnh) để tạo hiệu ứng "cascade"
     thay vì tất cả bung ra cùng lúc — cảm giác mượt & có chủ đích hơn. */
  transition: opacity 0.55s var(--ease), transform 0.55s var(--ease);
  transition-delay: var(--reveal-delay, 0s);
}

/* will-change chỉ bật đúng lúc phần tử sắp/đang animate (class do JS thêm
   ngay trước khi gắn is-visible), rồi gỡ ngay sau khi transition kết thúc.
   Tránh việc hàng chục .reveal (skill/hobby cards…) cùng giữ layer GPU
   suốt vòng đời trang — nguyên nhân chính gây giật khi có nhiều thẻ. */
.reveal.will-animate {
  will-change: opacity, transform;
}

.reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* Stagger cho 4 stat-card tĩnh trong phần Giới thiệu. */
.stat-grid .stat-card:nth-child(1) { --reveal-delay: 0s; }
.stat-grid .stat-card:nth-child(2) { --reveal-delay: 0.06s; }
.stat-grid .stat-card:nth-child(3) { --reveal-delay: 0.12s; }
.stat-grid .stat-card:nth-child(4) { --reveal-delay: 0.18s; }

@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
    transition-delay: 0s;
  }
}

/* ==========================================================================
   Responsive — Laptop / small desktop
   ========================================================================== */
@media (max-width: 74rem) {
  .skills-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* Tablet */
@media (max-width: 61rem) {
  .nav-links,
  .nav-cta {
    display: none;
  }

  .nav-toggle {
    display: flex;
    margin-right: 0;
    border-radius: var(--radius-sm) 0 0 var(--radius-sm);
  }

  /* Dấu 3 gạch (nav-toggle) dịch sát ra hẳn mép phải màn hình thay vì bị
     padding của .navbar-inner đẩy vào trong. */
  .navbar-inner {
    padding-right: -1;
  }

  /* Menu xổ xuống khi bấm nút -> hiện dạng thẻ nổi (dropdown) ngay dưới nút
     3 gạch, đổ xuống thấp hơn thay vì chiếm hết chiều ngang header. Neo vào
     đúng góc phải (right: 0) để nằm ngay dưới nút 3 gạch (đã sát mép phải).
     Nội dung bên trong (các liên kết) canh trái như bình thường. */
  .mobile-menu {
    display: flex;
    position: absolute;
    top: 100%;
    right: 0;
    left: auto;
    z-index: 150;
    width: max-content;
    min-width: 13rem;
    max-width: min(18rem, calc(100vw - 1.5rem));
    margin: 0.6rem 0.75rem 0 0;
    padding: 0.5rem 1rem 0.6rem;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    box-shadow: 0 18px 40px -18px rgba(2, 6, 16, 0.75);
  }

  .mobile-link {
    text-align: left;
  }

  .hero-content {
    grid-template-columns: 1fr;
  }

  .about-grid {
    grid-template-columns: 1fr;
  }

  .stat-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  .hobbies-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* Small tablet / large phone */
@media (max-width: 48rem) {
  .hero-content {
    margin-top: -2.75rem;
  }

  .skills-grid,
  .hobbies-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .stat-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Mobile */
@media (max-width: 30rem) {
  :root {
    --nav-h: 4rem;
  }

  .hero-banner {
    height: 8rem;
  }

  .hero-content {
    margin-top: -2.25rem;
  }

  .hero-identity {
    gap: 1.1rem;
  }

  .avatar-frame {
    width: 5.25rem;
    height: 5.25rem;
  }

  .hero-name {
    gap: 0.4rem;
  }

  .hero-actions {
    flex-direction: column;
    align-items: stretch;
  }

  .btn {
    justify-content: center;
  }

  .hobbies-grid,
  .skills-grid {
    grid-template-columns: 1fr;
  }

  .stat-grid {
    grid-template-columns: 1fr;
  }

  .code-row {
    grid-template-columns: 1.15rem 1fr;
    column-gap: 0.55rem;
  }

  .footer-inner {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* ==========================================================================
   MUSIC PLAYER — thanh phát nhạc cố định dưới cùng trang
   Dữ liệu: public/config/playlist.json | File nhạc: public/audio/
   ========================================================================== */
.music-player {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 400;
  background: linear-gradient(180deg, rgba(10, 15, 28, 0.88), var(--panel-solid) 40%);
  border-top: 1px solid var(--border-blue);
  box-shadow: 0 -12px 32px -14px rgba(2, 6, 16, 0.65);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
}

.music-player audio {
  display: none;
}

.mp-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1.25rem;
  max-width: 90rem;
  margin: 0 auto;
  padding: 0.65rem clamp(1rem, 3vw, 1.75rem);
  /* QUAN TRỌNG: KHÔNG dùng var(--player-h) ở đây.
     --player-h được JS (mpObservePlayerHeight) đo TỪ chính #music-player
     (phần tử cha bọc ngoài .mp-inner) rồi ghi ngược lại. Nếu min-height của
     .mp-inner lại lấy đúng biến đó thì tạo thành vòng phản hồi: đo cha ->
     ghi biến -> biến làm con to ra -> cha to theo (do border-top 1px cộng
     dồn) -> ResizeObserver đo lại thấy cha to hơn -> ghi biến lớn hơn nữa...
     lặp vô hạn mỗi khung hình, khiến thanh nhạc phình to nuốt cả màn hình.
     Dùng số cố định để chiều cao tối thiểu không phụ thuộc ngược vào phép đo. */
  min-height: 4.75rem;
}

/* ---------- Track info ---------- */
.mp-track {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex: 1 1 12rem;
  min-width: 0;
}

.mp-cover {
  position: relative;
  flex: none;
  width: 3rem;
  height: 3rem;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: linear-gradient(150deg, var(--panel-strong), var(--bg-elev));
  border: 1px solid var(--border-subtle);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-500);
  transition: box-shadow 0.3s var(--ease), border-color 0.3s var(--ease);
}

.mp-cover img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.25s var(--ease);
}

.mp-cover.has-image img {
  opacity: 1;
}

.mp-cover-icon {
  width: 1.3rem;
  height: 1.3rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.music-player.is-playing .mp-cover {
  border-color: var(--border-blue);
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.16), 0 0 18px rgba(59, 130, 246, 0.35);
  animation: mp-cover-pulse 2.4s ease-in-out infinite;
}

@keyframes mp-cover-pulse {
  0%, 100% { box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.14), 0 0 14px rgba(59, 130, 246, 0.28); }
  50% { box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.22), 0 0 22px rgba(59, 130, 246, 0.48); }
}

.mp-meta {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.mp-title-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}

.mp-title {
  font-family: var(--font-display);
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--text-100);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mp-artist {
  font-size: 0.76rem;
  color: var(--text-500);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Mini equalizer — chỉ chạy khi đang phát */
.mp-eq {
  display: none;
  align-items: flex-end;
  gap: 2px;
  flex: none;
  height: 0.7rem;
}

.music-player.is-playing .mp-eq {
  display: flex;
}

.mp-eq span {
  width: 2.5px;
  border-radius: 2px;
  background: var(--blue-400);
  height: 4px;
  animation: mp-eq-bounce 1s ease-in-out infinite;
}

.mp-eq span:nth-child(2) { animation-delay: 0.18s; }
.mp-eq span:nth-child(3) { animation-delay: 0.34s; }

@keyframes mp-eq-bounce {
  0%, 100% { height: 3px; }
  50% { height: 11px; }
}

/* ---------- Controls (prev / play / next / seek) ---------- */
.mp-controls {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  flex: 2 1 20rem;
  min-width: 0;
}

.mp-buttons {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex: none;
}

.mp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-300);
  cursor: pointer;
  border-radius: 999px;
  transition: transform 0.18s var(--ease), background-color 0.2s var(--ease),
    color 0.2s var(--ease), border-color 0.2s var(--ease);
}

.mp-btn:hover:not(:disabled) {
  color: var(--text-100);
  background: rgba(148, 163, 184, 0.1);
}

.mp-btn:active:not(:disabled) {
  transform: scale(0.9);
}

.mp-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.mp-btn svg {
  width: 1.1rem;
  height: 1.1rem;
}

.mp-btn-prev,
.mp-btn-next {
  width: 2.15rem;
  height: 2.15rem;
}

.mp-btn-play {
  width: 2.6rem;
  height: 2.6rem;
  background: linear-gradient(145deg, var(--blue-500), var(--blue-700));
  color: #f5f9ff;
  box-shadow: 0 6px 18px -6px rgba(37, 99, 235, 0.65);
}

.mp-btn-play:hover:not(:disabled) {
  background: linear-gradient(145deg, var(--blue-400), var(--blue-600));
  color: #f5f9ff;
  transform: translateY(-1px);
}

.mp-btn-play svg {
  width: 1.2rem;
  height: 1.2rem;
}

.mp-progress {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  flex: 1 1 auto;
  min-width: 0;
}

.mp-time {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-500);
  flex: none;
  min-width: 2.4em;
  text-align: center;
}

/* ---------- Range inputs (seek + volume) — style dùng chung ---------- */
.mp-seek,
.mp-vol-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 4px;
  border-radius: 999px;
  background: linear-gradient(
      to right,
      var(--blue-500) var(--progress, 0%),
      rgba(148, 163, 184, 0.2) var(--progress, 0%)
    );
  cursor: pointer;
  outline-offset: 4px;
}

.mp-seek {
  flex: 1 1 auto;
  min-width: 4rem;
  --progress: 0%;
}

.mp-vol-slider {
  width: 5.5rem;
  --progress: 80%;
}

.mp-seek::-webkit-slider-thumb,
.mp-vol-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--blue-300);
  border: 2px solid var(--bg-base);
  box-shadow: 0 0 0 1px var(--border-blue);
  transition: transform 0.15s var(--ease);
}

.mp-seek::-webkit-slider-thumb:hover,
.mp-vol-slider::-webkit-slider-thumb:hover {
  transform: scale(1.2);
}

.mp-seek::-moz-range-thumb,
.mp-vol-slider::-moz-range-thumb {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--blue-300);
  border: 2px solid var(--bg-base);
  box-shadow: 0 0 0 1px var(--border-blue);
}

.mp-seek::-moz-range-track,
.mp-vol-slider::-moz-range-track {
  height: 4px;
  border-radius: 999px;
  background: transparent;
}

/* ---------- Volume + playlist toggle ---------- */
.mp-extra {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex: none;
}

.mp-volume {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.mp-btn-list.is-active {
  color: var(--blue-300);
  background: rgba(59, 130, 246, 0.14);
  border-color: var(--border-blue);
}

/* ---------- Playlist panel ---------- */
.mp-playlist-panel {
  position: fixed;
  right: clamp(1rem, 3vw, 1.75rem);
  bottom: var(--player-h);
  width: 22rem;
  max-width: calc(100vw - 2rem);
  max-height: min(28rem, 60vh);
  display: flex;
  flex-direction: column;
  background: var(--panel-solid);
  border: 1px solid var(--border-subtle);
  border-bottom: none;
  border-radius: var(--radius-md) var(--radius-md) 0 0;
  box-shadow: 0 -18px 40px -18px rgba(2, 6, 16, 0.7);
  opacity: 0;
  transform: translateY(10px);
  pointer-events: none;
  transition: opacity 0.22s var(--ease), transform 0.28s var(--ease);
  z-index: 399;
}

.mp-playlist-panel.is-open {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

.mp-playlist-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--border-subtle);
  flex: none;
}

.mp-playlist-header p {
  font-family: var(--font-display);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-100);
}

.mp-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 999px;
  border: none;
  background: transparent;
  color: var(--text-500);
  cursor: pointer;
  transition: background-color 0.2s var(--ease), color 0.2s var(--ease);
}

.mp-close:hover {
  color: var(--text-100);
  background: rgba(148, 163, 184, 0.12);
}

.mp-close svg {
  width: 1rem;
  height: 1rem;
}

.mp-playlist {
  overflow-y: auto;
  padding: 0.4rem;
}

.mp-playlist-item {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  width: 100%;
  padding: 0.5rem 0.6rem;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  background: transparent;
  cursor: pointer;
  text-align: left;
  transition: background-color 0.18s var(--ease), border-color 0.18s var(--ease);
}

.mp-playlist-item:hover {
  background: rgba(148, 163, 184, 0.08);
}

.mp-playlist-item.is-active {
  background: rgba(59, 130, 246, 0.12);
  border-color: var(--border-blue);
}

.mp-pl-thumb {
  flex: none;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 8px;
  overflow: hidden;
  background: var(--panel-strong);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-600);
}

.mp-pl-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.mp-pl-thumb svg {
  width: 1rem;
  height: 1rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.mp-pl-meta {
  min-width: 0;
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.mp-pl-title {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-100);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mp-pl-artist {
  font-size: 0.72rem;
  color: var(--text-500);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mp-pl-indicator {
  display: none;
  flex: none;
  align-items: flex-end;
  gap: 2px;
  height: 0.65rem;
  color: var(--blue-400);
}

.mp-playlist-item.is-active .mp-pl-indicator {
  display: flex;
}

.mp-pl-indicator span {
  width: 2.5px;
  border-radius: 2px;
  background: currentColor;
  height: 4px;
}

.music-player.is-playing .mp-pl-indicator span {
  animation: mp-eq-bounce 1s ease-in-out infinite;
}

.mp-pl-indicator span:nth-child(2) { animation-delay: 0.18s; }
.mp-pl-indicator span:nth-child(3) { animation-delay: 0.34s; }

.mp-playlist-empty,
.mp-playlist-error {
  padding: 1.25rem 0.75rem;
  text-align: center;
  font-size: 0.8rem;
  color: var(--text-500);
}

/* ---------- Autoplay-blocked toast ---------- */
.mp-autoplay-hint {
  position: fixed;
  right: clamp(1rem, 3vw, 1.75rem);
  bottom: calc(var(--player-h) + 0.75rem);
  max-width: min(20rem, calc(100vw - 2rem));
  padding: 0.65rem 0.9rem;
  border-radius: var(--radius-sm);
  background: var(--panel-strong);
  border: 1px solid var(--border-blue);
  color: var(--text-300);
  font-size: 0.78rem;
  box-shadow: 0 12px 28px -12px rgba(2, 6, 16, 0.6);
  opacity: 0;
  transform: translateY(8px);
  pointer-events: none;
  transition: opacity 0.25s var(--ease), transform 0.25s var(--ease);
  z-index: 401;
}

.mp-autoplay-hint.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.mp-autoplay-hint[hidden] {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  .music-player.is-playing .mp-cover,
  .mp-eq span,
  .mp-pl-indicator span {
    animation: none !important;
  }
}

/* ---------- Responsive ---------- */
@media (max-width: 60rem) {
  .mp-volume {
    display: none;
  }
}

@media (max-width: 48rem) {
  .mp-inner {
    align-items: center;
  }

  .mp-track {
    order: 1;
    flex: 1 1 auto;
  }

  .mp-extra {
    order: 2;
  }

  .mp-controls {
    order: 3;
    flex: 1 1 100%;
  }

  .mp-time {
    display: none;
  }

  .mp-playlist-panel {
    left: 0;
    right: 0;
    width: auto;
    max-width: none;
    max-height: 65vh;
    border-radius: var(--radius-md) var(--radius-md) 0 0;
  }
}

@media (max-width: 30rem) {
  .mp-cover {
    width: 2.4rem;
    height: 2.4rem;
  }

  .mp-artist {
    display: none;
  }

  .mp-inner {
    padding-top: 0.55rem;
    padding-bottom: 0.65rem;
    gap: 0.4rem 0.75rem;
  }

  .mp-btn-prev,
  .mp-btn-next {
    width: 1.95rem;
    height: 1.95rem;
  }

  .mp-btn-play {
    width: 2.35rem;
    height: 2.35rem;
  }

  .mp-autoplay-hint {
    left: 1rem;
    right: 1rem;
    max-width: none;
  }
}

/* ==========================================================================
   PRELOADER — màn hình chờ ngắn khi tải trang lần đầu, tạo cảm giác
   chuyên nghiệp thay vì hiện nội dung "giật cục" ngay lập tức.
   JS (initPreloader trong main.js) thêm class .is-hidden để ẩn đi sau khi
   nội dung chính đã render, rồi gỡ hẳn phần tử khỏi DOM.
   ========================================================================== */
.preloader {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--void);
  transition: opacity 0.5s var(--ease), visibility 0.5s var(--ease);
}

.preloader-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.35rem;
  width: min(21rem, 84vw);
}

.preloader-mark {
  font-family: var(--font-mono);
  font-size: 1.1rem;
  font-weight: 500;
  color: var(--text-100);
  letter-spacing: 0.01em;
  opacity: 0;
  animation: preloader-fade-in 0.6s var(--ease) 0.15s forwards;
}

.preloader-status {
  list-style: none;
  margin: 0;
  padding: 0.9rem 1rem;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  border: 1px solid var(--border-subtle, rgba(148, 163, 184, 0.14));
  border-radius: 10px;
  background: rgba(148, 163, 184, 0.05);
  font-family: var(--font-mono);
  font-size: 0.78rem;
}

.preloader-status-line {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease);
}

.preloader-status-line.is-visible {
  opacity: 1;
  transform: none;
}

.preloader-status-line::before {
  content: "\203A";
  color: var(--blue-400);
  margin-right: 0.15rem;
}

.preloader-status-text {
  flex: 1;
  color: var(--text-500);
}

.preloader-status-ok {
  color: #5fd9a4;
  font-weight: 600;
  opacity: 0;
  transform: translateX(-2px);
  transition: opacity 0.25s var(--ease), transform 0.25s var(--ease);
}

.preloader-status-line.is-done .preloader-status-ok {
  opacity: 1;
  transform: none;
}

.preloader-status-line.is-done .preloader-status-text {
  color: var(--text-100);
}

.preloader-bar {
  width: 100%;
  height: 2px;
  border-radius: 999px;
  background: rgba(148, 163, 184, 0.14);
  overflow: hidden;
}

.preloader-bar-fill {
  display: block;
  height: 100%;
  width: 8%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--blue-500), var(--cyan-400), var(--violet-400));
  transition: width 0.4s var(--ease);
}

.preloader-bar-fill.is-indeterminate {
  width: 40%;
  animation: preloader-slide 1.1s ease-in-out infinite;
}

.preloader-enter-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.65rem 1.4rem;
  border-radius: 999px;
  border: 1px solid var(--border-blue, rgba(96, 165, 250, 0.35));
  background: rgba(96, 165, 250, 0.08);
  color: var(--text-300);
  font-family: inherit;
  font-size: 0.88rem;
  font-weight: 600;
  cursor: not-allowed;
  opacity: 0.4;
  pointer-events: none;
  transform: translateY(4px);
  transition: opacity 0.4s var(--ease), transform 0.4s var(--ease),
    background-color 0.2s var(--ease), border-color 0.2s var(--ease),
    box-shadow 0.2s var(--ease), color 0.2s var(--ease);
}

.preloader-enter-btn .btn-icon {
  width: 1rem;
  height: 1rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 0.2s var(--ease);
}

.preloader-enter-btn.is-ready {
  opacity: 1;
  transform: none;
  cursor: pointer;
  pointer-events: auto;
  color: var(--text-100);
  border-color: var(--blue-400);
  background: rgba(96, 165, 250, 0.14);
  animation: preloader-btn-pulse 1.8s ease-in-out infinite;
}

.preloader-enter-btn.is-ready:hover,
.preloader-enter-btn.is-ready:focus-visible {
  background: rgba(96, 165, 250, 0.22);
  animation-play-state: paused;
}

.preloader-enter-btn.is-ready:hover .btn-icon,
.preloader-enter-btn.is-ready:focus-visible .btn-icon {
  transform: translateX(3px);
}

@keyframes preloader-btn-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(96, 165, 250, 0.35); }
  50% { box-shadow: 0 0 0 7px rgba(96, 165, 250, 0); }
}

@keyframes preloader-fade-in {
  to { opacity: 1; }
}

@keyframes preloader-slide {
  0% { transform: translateX(-120%); }
  100% { transform: translateX(340%); }
}

.preloader.is-hidden {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

html.is-preloading,
html.is-preloading body {
  overflow: hidden;
}

@media (prefers-reduced-motion: reduce) {
  .preloader-mark { opacity: 1; animation: none; }
  .preloader-bar-fill.is-indeterminate { animation: none; width: 100%; }
  .preloader-status-line { transition: none; }
  .preloader-status-ok { transition: none; }
  .preloader-enter-btn.is-ready { animation: none; }
}

/* ==========================================================================
   SCROLL PROGRESS — dải mỏng trên cùng thể hiện % đã cuộn trang.
   Cập nhật bằng transform: scaleX() (GPU) qua rAF trong main.js, không
   dùng width để tránh reflow liên tục khi cuộn.
   ========================================================================== */
.scroll-progress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  z-index: 200;
  transform-origin: left center;
  transform: scaleX(0);
  background: linear-gradient(90deg, var(--blue-500), var(--cyan-400), var(--violet-400));
  box-shadow: 0 0 12px rgba(59, 130, 246, 0.6);
  will-change: transform;
}

/* ==========================================================================
   BACK TO TOP — nút nổi góc dưới phải, vòng tròn hiển thị % đã cuộn,
   chỉ hiện khi đã cuộn xuống một đoạn. Nằm ngay trên Music Player.
   ========================================================================== */
.back-to-top {
  position: fixed;
  right: clamp(1rem, 3vw, 1.75rem);
  bottom: calc(var(--player-h) + 1.25rem);
  z-index: 398;
  width: 2.85rem;
  height: 2.85rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--panel-solid);
  color: var(--blue-300);
  cursor: pointer;
  opacity: 0;
  transform: translateY(12px) scale(0.9);
  pointer-events: none;
  box-shadow: 0 14px 30px -14px rgba(2, 6, 16, 0.7);
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease),
    color 0.25s var(--ease), box-shadow 0.25s var(--ease);
}

.back-to-top.is-visible {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}

.back-to-top:hover {
  color: var(--text-100);
  box-shadow: 0 16px 34px -12px rgba(59, 130, 246, 0.55);
}

.back-to-top:active {
  transform: scale(0.92);
}

.btt-ring {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
}

.btt-ring-track {
  fill: none;
  stroke: rgba(148, 163, 184, 0.18);
  stroke-width: 2;
}

.btt-ring-progress {
  fill: none;
  stroke: var(--blue-400);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-dasharray: 119.4;
  stroke-dashoffset: 119.4;
  transition: stroke-dashoffset 0.15s linear;
}

.btt-arrow {
  position: relative;
  width: 1.1rem;
  height: 1.1rem;
}

@media (max-width: 30rem) {
  .back-to-top {
    right: 0.85rem;
    bottom: calc(var(--player-h) + 0.85rem);
    width: 2.5rem;
    height: 2.5rem;
  }
}

/* ==========================================================================
   GLOW BORDER — viền gradient chạy quanh card khi hover, dùng conic-gradient
   xoay qua @property (nội suy góc mượt, được trình duyệt hiện đại hỗ trợ).
   Chỉ bật animation khi thật sự hover (animation-play-state) để không tốn
   CPU/GPU cho hàng chục card cùng lúc khi không tương tác.
   ========================================================================== */
@property --border-angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

.glow-border {
  position: relative;
}

.glow-border::after {
  content: "";
  position: absolute;
  inset: -1px;
  z-index: 3;
  border-radius: inherit;
  padding: 1px;
  background: conic-gradient(
    from var(--border-angle),
    transparent 0%,
    var(--blue-400) 18%,
    var(--violet-400) 38%,
    var(--cyan-400) 58%,
    transparent 76%,
    transparent 100%
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s var(--ease);
  animation: border-angle-spin 3.2s linear infinite;
  animation-play-state: paused;
}

.glow-border:hover::after,
.glow-border:focus-within::after {
  opacity: 1;
  animation-play-state: running;
}

/* Biến thể luôn bật — dùng cho khối profile.js để có viền chạy quanh
   liên tục thay vì chỉ khi hover. */
.glow-border--active::after {
  opacity: 0.85;
  animation-play-state: running;
}

.glow-border--active:hover::after,
.glow-border--active:focus-within::after {
  opacity: 1;
}

@keyframes border-angle-spin {
  to { --border-angle: 360deg; }
}

@media (prefers-reduced-motion: reduce) {
  .glow-border::after { animation: none; }
  .glow-border--active::after { animation: none; opacity: 0.6; }
}

/* ==========================================================================
   NAV PILL — nền "viên thuốc" trượt mượt theo mục menu đang active/hover,
   thay vì bừng sáng tức thì. Vị trí/độ rộng do JS đo & ghi qua
   transform/width (main.js -> initNavPill), CSS chỉ lo phần chuyển động.
   ========================================================================== */
.nav-links {
  position: relative;
}

.nav-pill {
  position: absolute;
  top: 50%;
  left: 0;
  height: 2.3rem;
  width: 0;
  border-radius: 999px;
  background: rgba(59, 130, 246, 0.14);
  border: 1px solid var(--border-blue);
  transform: translate(0, -50%);
  transition: transform 0.35s var(--ease), width 0.35s var(--ease),
    height 0.35s var(--ease), opacity 0.25s var(--ease);
  opacity: 0;
  pointer-events: none;
  z-index: 0;
}

.nav-pill.is-ready {
  opacity: 1;
}

.nav-link {
  z-index: 1;
}

/* ==========================================================================
   MAGNETIC BUTTONS — nút hero hơi "hút" theo con trỏ khi rê gần, snap lại
   vị trí gốc khi rời chuột. JS chỉ set 2 biến --magnet-x/--magnet-y (cùng
   pattern với --mx/--my của spotlight), CSS lo phần chuyển động qua
   transition transform sẵn có của .btn.
   ========================================================================== */
.btn {
  --magnet-x: 0px;
  --magnet-y: 0px;
  transform: translate(var(--magnet-x), var(--magnet-y));
}

.btn-primary:hover,
.btn-outline:hover {
  transform: translate(var(--magnet-x), calc(var(--magnet-y) - 2px));
}

@media (hover: none), (pointer: coarse) {
  .btn { transform: none !important; }
}

/* ==========================================================================
   HERO — hiệu ứng vào trang "điện ảnh" hơn: khối hero mờ dần kèm blur nhẹ,
   các phần tử con (avatar, từng dòng chữ, khung terminal) bung ra theo thứ
   tự. Tận dụng lại đúng cơ chế .reveal/.is-visible sẵn có (IntersectionObserver
   trong main.js) — chỉ override riêng cho .hero-content nên không ảnh hưởng
   các card reveal khác trên trang.
   ========================================================================== */
.hero-content.reveal {
  filter: blur(10px);
  transition: opacity 0.9s var(--ease), transform 0.9s var(--ease), filter 0.9s var(--ease);
}

.hero-content.reveal.is-visible {
  filter: blur(0);
}

.hero-content .avatar-frame,
.hero-content .hero-text > *,
.hero-content .hero-terminal {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.65s var(--ease), transform 0.65s var(--ease);
}

.hero-content.is-visible .avatar-frame { opacity: 1; transform: none; transition-delay: 0.08s; }
.hero-content.is-visible .hero-text > *:nth-child(1) { opacity: 1; transform: none; transition-delay: 0.16s; }
.hero-content.is-visible .hero-text > *:nth-child(2) { opacity: 1; transform: none; transition-delay: 0.22s; }
.hero-content.is-visible .hero-text > *:nth-child(3) { opacity: 1; transform: none; transition-delay: 0.28s; }
.hero-content.is-visible .hero-text > *:nth-child(4) { opacity: 1; transform: none; transition-delay: 0.34s; }
.hero-content.is-visible .hero-text > *:nth-child(5) { opacity: 1; transform: none; transition-delay: 0.4s; }
.hero-content.is-visible .hero-text > *:nth-child(6) { opacity: 1; transform: none; transition-delay: 0.46s; }
.hero-content.is-visible .hero-terminal { opacity: 1; transform: none; transition-delay: 0.5s; }

@media (prefers-reduced-motion: reduce) {
  .hero-content.reveal { filter: none; }
  .hero-content .avatar-frame,
  .hero-content .hero-text > *,
  .hero-content .hero-terminal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ==========================================================================
   Scrollbar & selection — chi tiết nhỏ nhưng tăng cảm giác hoàn thiện.
   ========================================================================== */
::selection {
  background: rgba(59, 130, 246, 0.35);
  color: #f5f9ff;
}

html {
  scrollbar-color: var(--blue-600) var(--bg-base);
  scrollbar-width: thin;
}

::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

::-webkit-scrollbar-track {
  background: var(--bg-base);
}

::-webkit-scrollbar-thumb {
  background: linear-gradient(var(--blue-600), var(--violet-400));
  border-radius: 999px;
  border: 2px solid var(--bg-base);
}

::-webkit-scrollbar-thumb:hover {
  background: linear-gradient(var(--blue-400), var(--violet-400));
}
