/* ============================================================
   CÂY TIẾN ĐỘ — thanh chỉ vị trí ở mép phải
   Gốc dưới, ngọn trên, mọc ngược lên theo scroll. Lá hai bên,
   hai bông hoa là hai phần thưởng.
   Dựng bởi assets/js/sections/progress-tree.js
   ============================================================ */

.tree {
  position: fixed;
  right: clamp(0.75rem, 1.6vw, 1.6rem);
  top: 50%;
  z-index: 90; /* dưới nav (100+) và các popup (120) */
  height: min(64svh, 34rem);
  width: 15rem;
  transform: translateY(-50%);
  pointer-events: none; /* chỉ nhãn mới bắt chuột */
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-out-expo);

  /* Một nốt ấm duy nhất trên cả trang, dành riêng cho nhuỵ hoa. Bảng màu
     chung không có tông này — và đó chính là lý do nó đọc ra là phần thưởng
     chứ không phải một chi tiết trang trí nữa. */
  --bloom-heart: #C8A96A;
  --petal-out: var(--glow);
  --petal-out-line: var(--accent);
  --petal-in: #FAF8F2;
  --petal-in-line: var(--accent-bright);
  --pollen: #FDFCF8;
}
.tree.is-on { opacity: 1; }

/* Absolute chứ không grid: nhãn định vị bằng `top: %`, mà phần trăm chỉ có
   nghĩa khi khối chứa nó CÓ chiều cao xác định. Một hàng grid auto cao đúng
   bằng nội dung — mà nội dung lại toàn absolute nên cao 0, và cả sáu nhãn
   dồn về cùng một điểm. */
.tree__svg {
  position: absolute; top: 0; right: 0;
  width: 4.5rem; height: 100%;
  overflow: visible;
}

/* ---- Gốc và thân -------------------------------------------- */

.tree__groove,
.tree__trunk,
.tree__twig,
.tree__root path {
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.tree__groove {
  stroke: var(--ink);
  stroke-width: 2.1;
  opacity: 0.12;
}
.tree__trunk {
  stroke: var(--accent);
  opacity: 0.85;
  /* KHÔNG transition dashoffset: JS ghi giá trị này mỗi frame, thêm transition
     là nó luôn chạy đuổi giá trị cũ — thân bị trễ và nhoè theo scroll. */
}

/* Ngoại lệ duy nhất: lúc chốt bông bật, thân phải vươn một quãng dài trong
   một nhịp. Khoảnh khắc đó đích đứng yên nên transition chạy sạch một lần;
   JS gỡ class ngay sau 1s để trả thân về chế độ bám scroll. */
.tree.is-surging .tree__trunk {
  transition: stroke-dashoffset 900ms var(--ease-out-expo);
}

/* Rễ có sẵn từ đầu: cây phải có gốc trước khi có thân. */
.tree__root path {
  stroke: var(--accent);
  stroke-width: 2.7;
  opacity: 0.4;
}

/* ---- Nhánh --------------------------------------------------- */

/* Nhánh nhô LÊN đúng chiều cây mọc, không trượt ngang như bản cũ. */
.tree__branch {
  opacity: 0;
  transform: translateY(9px);
  transition: opacity var(--dur-slow) var(--ease-out-expo),
              transform var(--dur-slow) var(--ease-spring);
}
.tree__branch.is-grown { opacity: 1; transform: none; }

.tree__twig {
  stroke: var(--accent);
  stroke-width: 2;
  opacity: 0.8;
}
.tree__bud {
  fill: var(--accent);
  opacity: 0.5;
}

/* ---- Lá hai bên ---------------------------------------------- */
/* Lá bung ra từ cuống, nên gốc toạ độ phải nằm ở phía dính vào twig:
   lá trái mọc sang trái → cuống ở mép phải, lá phải thì ngược lại. */

.tree__leaf {
  fill: var(--soft);
  stroke: var(--accent);
  stroke-width: 0.8;
  opacity: 0.92;
  transform: scale(0.08);
  transform-box: fill-box;
  transition: transform var(--dur-slow) var(--ease-spring);
}
.tree__leaf--l { transform-origin: right bottom; }
.tree__leaf--r { transform-origin: left bottom; transition-delay: 90ms; }
.tree__branch.is-grown .tree__leaf { transform: scale(1); }

/* ---- Hai bông hoa -------------------------------------------- */
/* Bông chỉ nở khi nhánh đã mọc VÀ chốt đã bật (`is-lit`) — nên nó không bao
   giờ lơ lửng ngoài thân, kể cả lúc cây vừa dựng lại. */

/* `view-box` là BẮT BUỘC, không phải mặc định cho vui: JS ghi
   `transform-origin` bằng toạ độ TÂM BÔNG trong hệ viewBox. Đổi sang
   `fill-box` là mỗi cánh xoay quanh chính nó — bông vỡ ra rồi ghép lại chứ
   không nở. */
.tree__bloom,
.tree__petal,
.tree__bloom-eye { transform-box: view-box; }

.tree__petal--out {
  fill: var(--petal-out);
  stroke: var(--petal-out-line);
  stroke-width: 0.8;
  stroke-linejoin: round;
}
.tree__petal--in {
  fill: var(--petal-in);
  stroke: var(--petal-in-line);
  stroke-width: 0.55;
  stroke-linejoin: round;
}
.tree__bloom-disc { fill: var(--bloom-heart); }
.tree__pollen { fill: var(--pollen); opacity: 0.92; }

/* Nở theo ba nhịp: cánh ngoài bung trước, cánh trong len vào giữa, nhuỵ nảy
   lên sau cùng. Một cú `scale` chung cho cả bông thì chỉ ra hiệu ứng phóng to,
   không ra hiệu ứng nở. */
.tree__petal,
.tree__bloom-eye {
  transform: scale(0);
  transition: transform 640ms var(--ease-spring);
}
.tree__branch.is-grown .tree__bloom.is-lit .tree__petal {
  transform: scale(1);
  transition-delay: calc(var(--i, 0) * 48ms);
}
.tree__branch.is-grown .tree__bloom.is-lit .tree__bloom-eye {
  transform: scale(1);
  transition-delay: 330ms;
}

/* Nhịp thở vào muộn để không cướp mất cú bung ban đầu. */
.tree__branch.is-grown .tree__bloom.is-lit {
  animation: treeBloomBreathe 6s var(--ease-in-out-soft) 1100ms infinite;
}

@keyframes treeBloomBreathe {
  0%, 100% { transform: scale(1) rotate(0deg); }
  50% { transform: scale(1.05) rotate(4deg); }
}

/* ---- Tung hoa ------------------------------------------------ */
/* JS gắn nhóm này CẠNH bông rồi xoá sau ~2.6s. Mỗi mảnh mang sẵn hướng bay,
   quãng rơi, góc xoay và độ trễ riêng, nên cả chùm dùng chung một keyframe. */

.tree__sparks { pointer-events: none; }

.tree__spark {
  fill: var(--accent-bright);
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: treeSpark 1800ms var(--ease-out-expo) var(--delay, 0ms) both;
}
.tree__spark:nth-child(3n) { fill: var(--petal-in); }
.tree__spark:nth-child(4n) { fill: var(--bloom-heart); }

/* `--dx`/`--dy` là số trần; nhân với 1px để ra đơn vị user của SVG. */
@keyframes treeSpark {
  0% { transform: translate(0px, 0px) rotate(0deg) scale(0.3); opacity: 0; }
  10% { opacity: 1; }
  60% { opacity: 0.95; }
  100% {
    transform: translate(calc(var(--dx, 0) * 1px), calc(var(--dy, 0) * 1px))
               rotate(calc(var(--rot, 0) * 1deg)) scale(1);
    opacity: 0;
  }
}

/* ---- Nhãn ---------------------------------------------------- */

.tree__labels {
  position: absolute;
  inset: 0 4.5rem 0 0;
  margin: 0; padding: 0;
  list-style: none;
}
.tree__item {
  position: absolute;
  right: 0.5rem;
  transform: translateY(-50%);
  text-align: right;
  max-width: 100%;
}
.tree__label {
  pointer-events: auto;
  display: inline-block;
  font-size: var(--text-micro);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink-faint);
  opacity: 0.55;
  padding: 0.2rem 0;
  transition: color var(--dur-normal) linear,
              opacity var(--dur-normal) linear;
}
.tree__label:hover { color: var(--accent); opacity: 1; }
.tree__item.is-current .tree__label {
  color: var(--accent);
  opacity: 1;
  font-weight: 500;
}

/* ---- Trên nền tối (MEMORY UNIVERSE) -------------------------- */
/* Cây là `fixed` nên không hưởng được `.on-night` của section. */

.tree.is-night {
  --bloom-heart: #D9BE84;
  --petal-out: #E8A48C;
  --petal-out-line: #F6D5CF;
  --petal-in: #FFF1E0;
  --petal-in-line: #F2C14E;
  --pollen: #FFFFFF;
}
.tree.is-night .tree__groove { stroke: #FFF3E2; opacity: 0.16; }
.tree.is-night .tree__trunk,
.tree.is-night .tree__twig,
.tree.is-night .tree__root path { stroke: #E8B04B; }
.tree.is-night .tree__bud { fill: #E8B04B; }
/* Tông trung gian, không phải tô sẫm: cây là `fixed` nên lúc nào cũng có một
   khúc nằm vắt sang section sáng bên cạnh. Lá sẫm chìm vào nền tối, lá sáng
   thì biến mất trên giấy — chỉ tông giữa mới đọc được ở cả hai bên ranh giới. */
.tree.is-night .tree__leaf { fill: #B8860B; stroke: #F3D9A4; }
.tree.is-night .tree__spark { fill: #F2C14E; }
.tree.is-night .tree__label { color: #F0D3C4; }
.tree.is-night .tree__item.is-current .tree__label { color: #F2C14E; }

/* ---- Dưới 1600px: thu cây, chỉ giữ nhãn của nhánh đang đứng ---
   Nội dung trang bó trong `--measure-wide: 68rem` và căn giữa, nên từ 1600px
   trở xuống mép phải của nó thò vào đúng chỗ cột nhãn — đo được nhãn đè lên
   nội dung 86px ở 1440 và 216px ở 1024.

   Hai việc cùng lúc, không phải một: ẩn bớt nhãn cho đỡ rối, VÀ thu panel từ
   15rem xuống 11rem. Chỉ ẩn chữ thì panel vẫn chiếm đúng chỗ cũ; thu panel mới
   thật sự kéo mép trái cây lùi ~64px.

   Đánh đổi: từ 1024–1599px không đọc được cả danh sách chương nữa. Và nhãn của
   nhánh ĐANG ĐỨNG thì vẫn còn đè — cách này giảm từ sáu nhãn xuống một, chứ
   không dọn sạch. Muốn sạch hẳn thì phải chừa lề phải cho cây ở mọi section. */

@media (max-width: 1599px) {
  .tree {
    width: 11rem;
    /* Chiều cao phải hạ theo bề ngang, không được tách rời: viewBox 80×600 vẽ
       với `preserveAspectRatio: none`, nên svg 3.4rem mà vẫn cao 34rem là trục
       x bị nén 25% — lá và hoa dẹp lại. Cặp 3.4rem × 26rem cho tỉ lệ 0.98. */
    height: min(52svh, 26rem);
    right: 0.4rem;
  }
  .tree__svg { width: 3.4rem; }
  .tree__labels { inset: 0 3.4rem 0 0; }
  .tree__label { opacity: 0; }
  .tree__item.is-current .tree__label { opacity: 1; }
}

/* Lá và cánh hoa bên phải cần thêm vài mm để không chạm mép cửa sổ. */
@media (max-width: 560px) {
  .tree { width: 3.2rem; right: 0.5rem; }
  .tree__svg { width: 2.9rem; }
  .tree__labels { display: none; }
}

/* ---- Giảm chuyển động ---------------------------------------- */
/* Cây vẽ đủ ngay từ đầu, vẫn đánh dấu chỗ đang đứng — không mất thông tin.
   Bông vẫn nở đủ hình, chỉ bỏ phần chuyển động; tung hoa thì JS không sinh. */

.tree.is-static .tree__trunk,
.tree.is-static .tree__branch,
.tree.is-static .tree__leaf,
.tree.is-static .tree__petal,
.tree.is-static .tree__bloom-eye,
.tree.is-static .tree__label { transition: none; }
.tree.is-static .tree__branch { opacity: 1; transform: none; }
.tree.is-static .tree__leaf { transform: scale(1); }
.tree.is-static .tree__bloom { animation: none; }

@media (prefers-reduced-motion: reduce) {
  .tree { transition: none; }
  .tree__bloom,
  .tree__spark { animation: none !important; }
}

