/* HIỆU ỨNG DÙNG CHUNG — nguồn transitions.dev (skill `transitions-dev`).
 *
 * Vì sao tách khỏi <style> trong index.html: khối ấy đã dài, mà đây là một lớp
 * riêng có thể bật tắt cả cụm. Mọi đoạn giữ nguyên tên lớp `t-*` của thư viện
 * để sau này đối chiếu với tài liệu gốc trong `.agents/skills/transitions-dev/`.
 *
 * MỌI hiệu ứng đều có chốt `prefers-reduced-motion` — ai bật "giảm chuyển động"
 * trong hệ điều hành thì thấy giao diện đứng yên, không mất chức năng nào.
 */

/* ---------------------------------------------------------------- MOTION TOKEN
 * Bảng nhịp dùng chung. Trước đây thời lượng gõ tay rải rác trong index.html
 * (.15s, .16s, .18s, 1.2s), mỗi chỗ một kiểu; nay quy về đây để cả app cùng nhịp.
 * Chọn token theo VIỆC nó làm, không theo con số gần nhất.
 */
:root {
  --duration-stagger:   40ms;   /* lệch pha giữa các phần tử trong một dãy */
  --duration-micro:     80ms;   /* trễ tooltip, một nhịp rung */
  --duration-quick:    150ms;   /* ĐÓNG hộp thoại/menu, đổi chữ tại chỗ */
  --duration-fast:     250ms;   /* MỞ hộp thoại/menu, tabs trượt, đổi icon */
  --duration-medium:   350ms;   /* đóng tấm trượt, đóng toast */
  --duration-slow:     400ms;   /* mở tấm trượt, lộ nội dung sau khung chờ */
  --duration-very-slow: 500ms;  /* khoảnh khắc nhấn mạnh: dấu tích, huy hiệu */

  --ease-smooth-out: cubic-bezier(0.22, 1, 0.36, 1);   /* mở/đóng, trượt, đổi cỡ */
  --ease-in-out: ease-in-out;                          /* đổi icon, đổi chữ, lộ nội dung */
  --ease-out: ease-out;                                /* tooltip */
  --ease-linear: linear;                               /* vệt sáng, nhịp thở khung chờ */
  --ease-bounce: cubic-bezier(0.34, 1.36, 0.64, 1);    /* huy hiệu nảy ra */

  --distance-micro:  4px;
  --distance-small:  6px;
  --distance-base:   8px;
  --distance-medium: 12px;

  --scale-large:  0.96;
  --scale-medium: 0.97;
  --scale-small:  0.98;

  --blur-small:  2px;
  --blur-medium: 3px;
  --blur-large:  8px;
}

/* ---------------------------------------------------------------- 1. KHUNG CHỜ
 * Thay dòng chữ "Đang tải…" trần trụi bằng mấy thanh xám thở nhẹ, rồi nội dung
 * thật hiện lên qua một lớp mờ tan dần. Mắt có chỗ bám trong lúc chờ máy chủ.
 */
/* Màu lấy từ bộ token của ILMS (`tokens.js` ghi ra `--c-*` dạng bộ ba RGB), nên
   khung chờ tự đúng tông ở cả bộ sáng lẫn bộ "Đêm dịu". Chiều cao đặt ngay đây
   chứ KHÔNG mượn lớp `h-4` của Tailwind: tệp này phải tự đứng được. */
.t-skel-bar {
  height: 14px;
  background: rgb(var(--c-line, 226 232 240));
  border-radius: 4px;
  animation: t-skel-tho 1000ms var(--ease-linear) infinite;
}
.t-skel-bar + .t-skel-bar { margin-top: 8px; }
@keyframes t-skel-tho { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }

/* Nội dung thật vừa được gắn vào DOM: hiện lên qua lớp mờ thay vì nhảy phắt. */
.t-reveal {
  animation: t-reveal-in var(--duration-slow) var(--ease-in-out) both;
}
@keyframes t-reveal-in {
  from { opacity: 0; filter: blur(var(--blur-small)); }
  to   { opacity: 1; filter: blur(0); }
}

/* Chữ trạng thái "Đang tải…" có vệt sáng quét qua — báo máy còn đang chạy. */
.t-shimmer {
  position: relative;
  display: inline-block;
  color: rgb(var(--c-ink-faint, 91 107 132));
}
.t-shimmer::before {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: linear-gradient(90deg,
    transparent 0%, transparent 40%,
    rgb(var(--c-ink, 15 23 42)) 50%,
    transparent 60%, transparent 100%);
  background-size: 400% 100%;
  background-repeat: no-repeat;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  animation: t-shimmer 2000ms var(--ease-linear) infinite;
}
@keyframes t-shimmer {
  0%   { background-position: 100% 0; }
  100% { background-position: 0% 0; }
}

/* ---------------------------------------------------------------- 2. TABS TRƯỢT
 * Viên bi trắng trượt giữa các thẻ. Bề ngang và vị trí do JS ghi thẳng vào
 * `style` sau khi ĐO thẻ đang chọn, nên CSS chỉ lo phần nội suy.
 */
.t-tabs { position: relative; }
.t-tabs-pill {
  position: absolute;
  z-index: 0;
  border-radius: 6px;
  background: rgb(var(--c-surface, 255 255 255));
  /* Vành kẻ mảnh, KHÔNG phải `border`: viên bi được JS đặt bề rộng đúng bằng
     `offsetWidth` của thẻ, thêm border là lệch 2px. Vành này để viên bi còn
     thấy được ở bộ "Trắng tinh" — bộ DUY NHẤT có --c-canvas TRÙNG --c-surface
     (cùng 255 255 255), nên nếu chỉ dựa vào nền thì viên bi trắng nằm trên nền
     trắng, biến mất hẳn. Đo ngày 14-09 trên màn Quyết toán và Mail Center:
     chênh sáng 0,0. Tám bộ còn lại canvas tối hơn surface 7–17 nên không lộ.
     Đổi màu preset thì nhớ: viên bi KHÔNG được tự dựa vào khoảng chênh ấy. */
  box-shadow: 0 1px 2px rgba(23, 37, 84, .08),
              inset 0 0 0 1px rgb(var(--c-line, 226 232 240));
  transform: translateX(0);
  transition:
    transform var(--duration-fast) var(--ease-smooth-out),
    width     var(--duration-fast) var(--ease-smooth-out);
  will-change: transform, width;
  pointer-events: none;
}
/* Lượt vẽ ĐẦU TIÊN (và lúc đổi cỡ màn) phải ghi vị trí mà KHÔNG chạy hiệu ứng,
   nếu không viên bi bay từ mép trái vào — trông như lỗi. */
.t-tabs-pill.t-khong-chay { transition: none !important; }
.t-tabs > [role="tab"] { position: relative; z-index: 1; }
/* Thẻ đang chọn NHƯỜNG nền lại cho viên bi. Không có dòng này thì thấy hai nền
   chồng nhau: nền trắng của thẻ nhảy ngay, viên bi bò theo sau. */
.t-tabs > [role="tab"][aria-selected="true"] {
  background: transparent !important;
  box-shadow: none !important;
}

/* ---------------------------------------------------------------- 3. SỐ NHẢY
 * Số đổi thì từng chữ số trôi lên qua một lớp mờ, hai chữ số cuối vào sau một
 * nhịp. Dùng cho ô KPI: mắt bắt được "số này vừa đổi" mà không cần chớp màu.
 */
.t-digit-group { display: inline-flex; align-items: baseline; }
.t-digit { display: inline-block; will-change: transform, opacity, filter; }
.t-digit-group.is-animating .t-digit {
  animation: t-digit-pop-in var(--duration-very-slow) cubic-bezier(0.34, 1.45, 0.64, 1) both;
}
.t-digit-group.is-animating .t-digit[data-stagger="1"] { animation-delay: 70ms; }
.t-digit-group.is-animating .t-digit[data-stagger="2"] { animation-delay: 140ms; }
@keyframes t-digit-pop-in {
  0%   { transform: translateY(var(--distance-base)); opacity: 0; filter: blur(var(--blur-small)); }
  100% { transform: translateY(0); opacity: 1; filter: blur(0); }
}

/* ---------------------------------------------------------------- 4. DẤU TÍCH
 * Khoảnh khắc "xong": tích xoay vào, nảy nhẹ, rồi nét được VẼ ra. Dành cho việc
 * có hệ quả thật (phát hành giấy báo nợ, phát hành hóa đơn), không dùng cho
 * mọi thao tác lặt vặt — dùng bừa thì nó hết nghĩa.
 */
.t-success-check { display: inline-block; transform-origin: center; opacity: 0; }
.t-success-check svg { display: block; overflow: visible; }
.t-success-check svg path { stroke-dasharray: 26; stroke-dashoffset: 26; }
.t-success-check[data-state="in"] {
  animation:
    t-check-fade   var(--duration-very-slow) var(--ease-smooth-out) forwards,
    t-check-rotate var(--duration-very-slow) var(--ease-smooth-out) forwards,
    t-check-blur   var(--duration-very-slow) var(--ease-smooth-out) forwards,
    t-check-bob    var(--duration-very-slow) cubic-bezier(0.34, 1.35, 0.64, 1) forwards;
}
.t-success-check[data-state="in"] svg path {
  animation: t-check-draw var(--duration-very-slow) var(--ease-smooth-out) var(--duration-micro) forwards;
}
@keyframes t-check-fade   { from { opacity: 0; } to { opacity: 1; } }
@keyframes t-check-rotate { from { transform: rotate(80deg); } to { transform: rotate(0deg); } }
@keyframes t-check-blur   { from { filter: blur(var(--blur-large)); } to { filter: blur(0); } }
@keyframes t-check-bob    { from { translate: 0 40px; } to { translate: 0 0; } }
@keyframes t-check-draw   { to { stroke-dashoffset: 0; } }

/* ---------------------------------------------------------------- 5. KHỐI CO GIÃN
 * grid-template-rows 0fr -> 1fr cho chiều cao chạy mượt mà KHÔNG phải đo bằng JS.
 * Đệm phải nằm ở lớp trong; đặt ở lớp ngoài thì khối không bao giờ đóng hẳn.
 */
.t-acc-panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--duration-fast) var(--ease-smooth-out);
}
.t-acc[data-open="true"] .t-acc-panel { grid-template-rows: 1fr; }
.t-acc-panel-inner {
  overflow: hidden;
  opacity: 0;
  filter: blur(var(--blur-small));
  transition:
    opacity var(--duration-fast) var(--ease-smooth-out),
    filter  var(--duration-fast) var(--ease-smooth-out);
}
.t-acc[data-open="true"] .t-acc-panel-inner { opacity: 1; filter: blur(0); }
/* Mũi chevron LẬT DỌC chứ không morph đường `d:` — morph chỉ chạy trên Chromium,
   Safari trên điện thoại đứng im. Lật dọc đi qua nét phẳng ở giữa, trông y hệt. */
.t-acc-chevron {
  display: inline-flex;
  transform-origin: center;
  transition: transform var(--duration-fast) var(--ease-smooth-out);
}
.t-acc-chevron path { vector-effect: non-scaling-stroke; }
.t-acc[data-open="true"] .t-acc-chevron { transform: scaleY(-1); }

/* ---------------------------------------------------------------- 6. XÓA Ô TÌM
 * Chữ bay lên và tan thay vì biến mất trong một khung hình, để người gõ thấy
 * rõ "ô vừa bị xóa" chứ không tưởng mình bấm hụt.
 */
.t-clear-ghost {
  position: absolute;
  pointer-events: none;
  white-space: pre;
  animation: t-clear-bay var(--duration-slow) var(--ease-smooth-out) forwards;
}
@keyframes t-clear-bay {
  from { transform: translateY(0);    opacity: 1; filter: blur(0); }
  to   { transform: translateY(-10px); opacity: 0; filter: blur(var(--blur-medium)); }
}

/* ---------------------------------------------------------------- 7. HUY HIỆU
 * Chấm đếm cạnh chuông: trượt chéo vào rồi nảy. Về 0 thì co lại và mờ đi nhanh
 * hơn lúc vào — ra thì dứt khoát, vào thì có nhịp.
 */
.t-badge { will-change: transform; }
.t-badge[data-open="true"] {
  animation: t-badge-vao 260ms var(--ease-smooth-out);
}
@keyframes t-badge-vao {
  from { transform: translate(-8px, 12px); }
  to   { transform: translate(0, 0); }
}
.t-badge-dot {
  display: block;
  transform-origin: center;
  transform: scale(1);
  opacity: 1;
  filter: blur(0);
  transition:
    transform var(--duration-very-slow) var(--ease-bounce),
    opacity   var(--duration-slow)      var(--ease-bounce),
    filter    var(--duration-very-slow) var(--ease-bounce);
}
.t-badge[data-open="false"] .t-badge-dot {
  transform: scale(0);
  opacity: 0;
  filter: blur(var(--blur-small));
  transition:
    transform 180ms cubic-bezier(0.4, 0, 0.2, 1),
    opacity   180ms cubic-bezier(0.4, 0, 0.2, 1),
    filter    180ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* ---------------------------------------------------------------- 8. RUNG BÁO SAI
 * Ô nhập sai tự chỉ ra mình: rung ngang bốn nhịp, viền đỏ rồi tự nhạt về bình
 * thường. Giữ `.is-error` và `.is-shaking` TÁCH NHAU thì rung mới phát lại được
 * nhiều lần mà viền đỏ không chớp theo.
 */
.t-input { transition: border-color var(--duration-quick) var(--ease-out); }
.t-input.is-error {
  border-color: #E11D48 !important;
  transition: border-color 280ms var(--ease-out);
}
.t-input.is-shaking { animation: t-input-rung 280ms linear; }
@keyframes t-input-rung {
  0%     { transform: translateX(0);     animation-timing-function: var(--ease-smooth-out); }
  28.57% { transform: translateX(var(--distance-small));  animation-timing-function: var(--ease-smooth-out); }
  57.14% { transform: translateX(calc(var(--distance-small) * -1)); animation-timing-function: var(--ease-smooth-out); }
  78.57% { transform: translateX(var(--distance-micro));  animation-timing-function: var(--ease-smooth-out); }
  100%   { transform: translateX(0); }
}

/* ---------------------------------------------------------------- CHỐT AN TOÀN
 * Một khối duy nhất tắt TẤT CẢ ở trên cho người bật "giảm chuyển động". Không
 * tắt lẻ từng hiệu ứng: thêm hiệu ứng mới mà quên chốt là lọt lưới ngay.
 */
@media (prefers-reduced-motion: reduce) {
  .t-skel-bar, .t-reveal, .t-shimmer::before,
  .t-digit-group .t-digit, .t-success-check, .t-success-check svg path,
  .t-clear-ghost, .t-badge, .t-input.is-shaking {
    animation: none !important;
  }
  .t-tabs-pill, .t-acc-panel, .t-acc-panel-inner, .t-acc-chevron,
  .t-badge-dot, .t-input {
    transition: none !important;
  }
  .t-success-check { opacity: 1; }
  .t-success-check svg path { stroke-dashoffset: 0 !important; }
  .t-clear-ghost { display: none; }
}
