/* ============ Hiệu ứng (CSS thuần, chỉ dùng transform/opacity để giữ 60fps) ============ */

/* Chuyển trang: mờ dần + trượt nhẹ lên. Mỗi trang là phần tử mới nên tự chạy khi điều hướng. */
@keyframes trang-vao { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
.trang { animation: trang-vao .34s cubic-bezier(.2, .8, .2, 1) both; }

/* Thẻ và danh sách trượt vào lần lượt (staggered): đặt style="--i:N" */
@keyframes truot-vao { from { opacity: 0; transform: translateY(18px) scale(.98); } to { opacity: 1; transform: none; } }
.lan-luot { animation: truot-vao .45s cubic-bezier(.2, .8, .2, 1) both; animation-delay: calc(var(--i, 0) * 45ms); }

/* Xóa: trượt sang trái rồi biến mất */
@keyframes truot-ra { to { opacity: 0; transform: translateX(-60%); max-height: 0; margin: 0; } }
.dang-xoa { animation: truot-ra .26s ease-in forwards; overflow: hidden; max-height: 100px; }

/* Thanh ngân sách chạy đầy từ 0 (màu đổi theo mức nhờ class vang/do) */
@keyframes chay-day { from { width: 0; } to { width: var(--w); } }
.thanh-ns > i { animation: chay-day .9s cubic-bezier(.2, .8, .2, 1) both; animation-delay: calc(var(--i, 0) * 60ms + 120ms); transition: background .4s; }

/* Biểu đồ donut & vòng tiến độ vẽ dần từ 0 */
@keyframes ve-vong { from { stroke-dasharray: 0 var(--c); } }
.mieng { fill: none; stroke-width: 15; stroke-dasharray: var(--len) calc(var(--c) - var(--len)); stroke-dashoffset: calc(-1 * var(--off, 0));
  animation: ve-vong 1s cubic-bezier(.2, .8, .2, 1) both; animation-delay: calc(var(--i, 0) * 90ms); }
.vong-nen { fill: none; stroke: var(--the-2); }
.vong-tien { fill: none; stroke-linecap: round; stroke-dasharray: var(--len) calc(var(--c) - var(--len));
  animation: ve-vong 1.1s cubic-bezier(.2, .8, .2, 1) both; animation-delay: .15s; }

/* Cột biểu đồ mọc lên */
@keyframes moc-len { from { transform: scaleY(0); } to { transform: scaleY(1); } }
.cot > i { animation: moc-len .6s cubic-bezier(.2, .8, .2, 1) both; animation-delay: calc(var(--i, 0) * 14ms); }

/* Nút: hiệu ứng gợn sóng (ripple) lan từ chỗ chạm. Phần tử .gon được thêm bởi JS khi bấm. */
.nut .gon, .nut-them .gon {
  position: absolute; border-radius: 50%; pointer-events: none; background: currentColor; opacity: .22; transform: scale(0);
  animation: gon .55s ease-out forwards;
}
@keyframes gon { to { transform: scale(4); opacity: 0; } }

/* Bottom sheet kéo lên mượt; nền mờ dần */
@keyframes sheet-len { from { transform: translateY(100%); } to { transform: none; } }
@keyframes nen-hien { from { opacity: 0; } to { opacity: 1; } }
.sheet { animation: sheet-len .38s cubic-bezier(.2, .9, .25, 1) both; }
.sheet.dang-dong { animation: sheet-xuong .26s ease-in forwards; }
@keyframes sheet-xuong { to { transform: translateY(100%); } }
.sheet-nen { animation: nen-hien .3s ease both; }
.sheet-nen.dang-dong { animation: nen-an .26s ease forwards; }
@keyframes nen-an { to { opacity: 0; } }

/* Cảnh báo vượt ngân sách: rung nhẹ một lần rồi nhấp nháy dịu 3 lần (không gây khó chịu) */
@keyframes rung-nhe { 0%, 100% { transform: translateX(0); } 20% { transform: translateX(-5px); } 40% { transform: translateX(5px); } 60% { transform: translateX(-3px); } 80% { transform: translateX(3px); } }
@keyframes nhip { 0%, 100% { box-shadow: 0 0 0 0 rgba(208, 49, 43, .0); } 50% { box-shadow: 0 0 0 8px rgba(208, 49, 43, .16); } }
.canh-bao.do { animation: rung-nhe .5s ease .2s 1, nhip 1.6s ease-in-out .7s 3; }
.canh-bao.vang { animation: nhip-vang 1.8s ease-in-out .5s 2; }
@keyframes nhip-vang { 0%, 100% { box-shadow: 0 0 0 0 rgba(242, 176, 30, 0); } 50% { box-shadow: 0 0 0 8px rgba(242, 176, 30, .22); } }

/* Skeleton khi đang tải dữ liệu */
@keyframes lap-lanh { to { transform: translateX(100%); } }
.skeleton { position: relative; overflow: hidden; background: var(--the-2); border-radius: 16px; }
.skeleton::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .45), transparent); animation: lap-lanh 1.3s infinite; }
:root[data-theme="dark"] .skeleton::after { background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .08), transparent); }
.sk-the { height: 150px; border-radius: var(--r); }
.sk-dong { height: 72px; }

/* Biểu tượng thành công nảy lên */
@keyframes nay { 0% { transform: scale(.4); opacity: 0; } 60% { transform: scale(1.15); opacity: 1; } 100% { transform: scale(1); } }
.nay { animation: nay .5s cubic-bezier(.3, 1.5, .5, 1) both; }

/* Confetti khi đạt mục tiêu: các mảnh giấy rơi xuống, sinh từ Razor */
@keyframes roi { 0% { transform: translate3d(0, -12vh, 0) rotate(0deg); opacity: 1; } 100% { transform: translate3d(var(--dx), 108vh, 0) rotate(var(--rot)); opacity: .9; } }
.confetti { position: fixed; inset: 0; z-index: 300; pointer-events: none; overflow: hidden; }
.confetti i { position: absolute; top: 0; left: var(--x); width: var(--w); height: calc(var(--w) * 1.6); background: var(--c); border-radius: 2px;
  animation: roi var(--t) cubic-bezier(.3, .1, .6, 1) forwards; animation-delay: var(--d); opacity: 0; }

/* Tôn trọng người dùng tắt hiệu ứng chuyển động trong cài đặt hệ thống */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-delay: 0ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
  .confetti { display: none; }
}
