/*
 * muzig 인터랙션 팩 — 전역 스타일
 *
 * 시안 보드(claude.ai/artifact/7CYPVDGVxPZ2bFrdgh8NVm)에서 살아남은 28종 중
 * **컴포넌트를 안 고치고 붙일 수 있는 것**들입니다. 나머지는 화면별 패치로 갑니다.
 *
 * 규칙 — v1·v2 모션 팩과 같이 돌아가야 합니다.
 *  · 클래스·키프레임 이름은 전부 `mzx-` 접두. v1 의 `mz-`, v2 의 `mz-v2-` 와 안 겹칩니다.
 *  · `--mz-*` 커스텀 프로퍼티는 건드리지 않습니다(v1 이 @property 로 등록해 둔 게 있습니다).
 *  · 색은 기존 토큰만 씁니다. 새 색을 만들지 않습니다.
 *  · 움직임을 끈 사용자에게는 전부 정지합니다.
 */

:root {
  --mzx-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  --mzx-fast: 0.18s;
  --mzx-mid: 0.34s;
  --mzx-slow: 0.55s;
}

/* ───────────────────────── 13 · 커서 광원 (홈 히어로)
   아주 옅게만. 과하면 싸구려 배너가 됩니다.
   좌표는 ix.js 가 --mzx-gx/--mzx-gy 로 넣습니다. */
.mzx-glow {
  position: relative;
  isolation: isolate;          /* 뒤에 깔린 v1 오로라와 섞이지 않게 */
}
.mzx-glow::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s ease;
  background: radial-gradient(
    220px circle at var(--mzx-gx, 50%) var(--mzx-gy, 50%),
    rgba(255, 255, 255, 0.34),
    transparent 70%
  );
}
.mzx-glow[data-mzx-on="1"]::after { opacity: 1; }

/* ───────────────────────── 24 · 카드 미세 기울임
   최대 4도. 그 이상은 장난감처럼 보입니다. */
.mzx-tilt {
  transition: transform var(--mzx-fast) ease;
  will-change: transform;
}
.mzx-tilt[data-mzx-on="1"] {
  transform: perspective(620px)
    rotateY(calc(var(--mzx-tx, 0) * 4deg))
    rotateX(calc(var(--mzx-ty, 0) * -4deg));
}

/* ───────────────────────── 25 · 커버 블러업
   저해상이 먼저 흐리게 깔리고 원본이 오면 또렷해집니다.
   회색 네모 대신 색이 먼저 보입니다. */
img.mzx-blurup {
  filter: blur(12px);
  transform: scale(1.04);
  transition: filter var(--mzx-slow) ease, transform var(--mzx-slow) ease;
}
img.mzx-blurup[data-mzx-loaded="1"] {
  filter: none;
  transform: none;
}

/* ───────────────────────── 08 · 미끄러지는 메뉴 표시 (사이드바)
   표시줄 하나가 항목 사이를 옮겨 다닙니다. */
.mzx-navwrap { position: relative; }
.mzx-navbar {
  position: absolute;
  /* 사이드바가 화면 왼쪽 끝에 붙어 있어서 left:0 이면 막대가 화면 가장자리에
     반쯤 잘려 보입니다. 메뉴 글자 왼쪽 여백 안으로 들여 놓습니다. */
  left: 8px;
  /* top:0 이 없으면 절대위치 요소가 **자기 흐름 위치**(사이드바 패딩 아래 24px)에서
     시작해서, translateY 가 거기에 더해집니다. 한 번 물렸습니다. */
  top: 0;
  width: 3px;
  height: 22px;
  border-radius: 2px;
  background: var(--color-accent, #fe7b85);
  /* 위치는 top 으로 옮깁니다. transform 으로 하면 **인라인 값까지 무시**됩니다 —
     inline style 에 translateY(189px) 가 분명히 들어가 있는데 computed 가
     matrix(1,0,0,1,0,0) 로 나왔습니다. 어딘가에서 !important 로 덮고 있습니다.
     top 은 그 영향을 안 받고, 전환도 똑같이 부드럽습니다. */
  opacity: 0;
  transition: top var(--mzx-mid) var(--mzx-ease), opacity var(--mzx-fast);
  pointer-events: none;
}
.mzx-navwrap[data-mzx-ready="1"] .mzx-navbar { opacity: 1; }
[dir="rtl"] .mzx-navbar { left: auto; right: 0; }

/* ───────────────────────── 22 · 재생 중 행 게이지
   목록을 보면서도 어디까지 들었는지 압니다.
   --mzx-played 는 0~1. ix.js 가 <audio> 에서 읽어 넣습니다. */
.mzx-playing { position: relative; }
.mzx-playing::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 0;
  width: 3px;
  height: 100%;
  border-radius: 2px;
  background: var(--color-accent, #fe7b85);
  transform: scaleY(var(--mzx-played, 0));
  transform-origin: top;
  transition: transform 0.25s linear;
}

/* ───────────────────────── 43 · 토스트가 쌓임
   겹칠 때 서로 밀어내지 않고 카드 덱처럼. 최근 것이 위. */
.mzx-toaststack .Toastify__toast {
  transition: transform var(--mzx-mid) var(--mzx-ease), opacity var(--mzx-mid);
}
.mzx-toaststack .Toastify__toast[data-mzx-depth="1"] { transform: translateY(-7px) scale(0.96); opacity: 0.8; }
.mzx-toaststack .Toastify__toast[data-mzx-depth="2"] { transform: translateY(-13px) scale(0.92); opacity: 0.55; }
.mzx-toaststack .Toastify__toast[data-mzx-depth="3"] { transform: translateY(-18px) scale(0.89); opacity: 0.3; }

/* ───────────────────────── 44 · 모달 뒤가 물러남
   앞뒤가 생기면 모달이 떠 보입니다. 0.98 이상은 멀미가 납니다. */
body.mzx-modal-open .mzx-behind {
  transform: scale(0.985);
  filter: blur(0.6px);
  transition: transform var(--mzx-mid) var(--mzx-ease), filter var(--mzx-mid);
}
.mzx-behind { transition: transform var(--mzx-mid) var(--mzx-ease), filter var(--mzx-mid); }

/* ───────────────────────── 45 · 오류 필드가 숨을 쉼
   빨갛게 떨면 혼내는 느낌이 납니다. 한 번 부풀었다 가라앉습니다. */
@keyframes mzx-breathe {
  0%   { box-shadow: 0 0 0 0 rgba(254, 123, 133, 0); }
  40%  { box-shadow: 0 0 0 5px rgba(254, 123, 133, 0.3); }
  100% { box-shadow: 0 0 0 0 rgba(254, 123, 133, 0); }
}
.mzx-err { animation: mzx-breathe 0.78s ease-out 1; }

/* ───────────────────────── 28 · 빈 화면이 반응
   빈 상태가 죽은 화면처럼 보이지 않게, 아주 조금만. */
.mzx-emptyart {
  transition: transform 0.22s ease;
  will-change: transform;
}
.mzx-emptyart[data-mzx-on="1"] {
  transform: translate(calc(var(--mzx-tx, 0) * 9px), calc(var(--mzx-ty, 0) * 7px));
}

/* ───────────────────────── 39 · 다른 카드가 물러남 (요금제)
   보고 있는 것만 남고 나머지가 뒤로. 비교가 쉬워집니다. */
.mzx-compare > * {
  transition: transform var(--mzx-mid) var(--mzx-ease), opacity var(--mzx-mid);
}
.mzx-compare[data-mzx-hover="1"] > *:not([data-mzx-front="1"]) {
  transform: scale(0.965);
  opacity: 0.62;
}
.mzx-compare[data-mzx-hover="1"] > [data-mzx-front="1"] {
  transform: translateY(-4px);
}

/* ───────────────────────── 42 · 언어 교차 페이드
   글자가 사라졌다 나타나면 레이아웃이 한 번 무너집니다. */
.mzx-langswap { transition: opacity 0.28s ease; }
body.mzx-langswapping .mzx-langswap { opacity: 0.25; }

/* ───────────────────────── 34 · 다운로드 → 체크 모프
   버튼이 사라지고 새 아이콘이 뜨는 게 아니라 같은 자리에서 바뀝니다. */
.mzx-morph { transition: background-color var(--mzx-mid), color var(--mzx-mid); position: relative; }
.mzx-morph[data-mzx-state="busy"] { opacity: 0.75; }
.mzx-morph[data-mzx-state="done"]::after {
  content: "✓";
  margin-inline-start: 0.35em;
  display: inline-block;
  animation: mzx-pop 0.4s var(--mzx-ease);
}
@keyframes mzx-pop {
  from { transform: scale(0.4); opacity: 0; }
  to   { transform: scale(1); opacity: 1; }
}

/* ───────────────────────── 32 · 장바구니로 날아가기
   담겼는지 몰라 두 번 누르는 일이 없어집니다. */
.mzx-fly {
  position: fixed;
  z-index: 2147483000;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--color-accent, #fe7b85);
  pointer-events: none;
}

/* ───────────────────────── 49 · 사진 제자리 확대
   라이트박스를 띄우지 않고 그 자리에서. 뒤로가기가 필요 없습니다. */
.mzx-zoomable { cursor: zoom-in; transition: transform var(--mzx-mid) var(--mzx-ease); }
.mzx-zoomable[data-mzx-zoom="1"] { cursor: zoom-out; transform: scale(1.6); z-index: 5; position: relative; }

/* ───────────────────────── 02 · 대기의 호흡 (생성 중)
   스피너는 멈춘 것처럼 보입니다. 숨 쉬는 파형과 "지금 뭘 하는 중"이라는
   한 줄이 같이 있으면 같은 시간도 짧게 느껴집니다. */
.mzx-breath {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  height: 34px;
  margin: 10px auto 6px;
}
.mzx-breath i {
  display: block;
  width: 3px;
  height: 8px;
  border-radius: 2px;
  background: var(--color-accent, #fe7b85);
  opacity: 0.55;
  animation: mzx-bar 1.25s var(--mzx-ease) infinite;
}
@keyframes mzx-bar {
  0%, 100% { height: 8px;  opacity: 0.4; }
  50%      { height: 26px; opacity: 1; }
}
.mzx-breath-cap {
  display: block;
  text-align: center;
  font-size: 0.82rem;
  opacity: 0.65;
  /* 문구가 바뀔 때 글자가 튀지 않도록 자리를 잡아 둡니다 */
  min-height: 1.2em;
  animation: mzx-capfade 2.6s ease-in-out infinite;
}
@keyframes mzx-capfade {
  0%, 12%, 88%, 100% { opacity: 0.25; }
  30%, 70%           { opacity: 0.7; }
}

/* ───────────────────────── 03 · 파형 위 위치 표시 (스크럽)
   파형이 <canvas> 라 막대를 하나씩 못 건드립니다. 선을 얹습니다. */
.mzx-scrub { position: relative; }
.mzx-scrubline {
  position: absolute;
  top: 0;
  left: 0;
  width: 1px;
  height: 100%;
  background: var(--color-accent, #fe7b85);
  opacity: 0;
  transition: opacity var(--mzx-fast);
  pointer-events: none;
}
.mzx-scrub[data-mzx-on="1"] .mzx-scrubline { opacity: 0.85; }

/* ───────────────────────── 16 · 효과음 파형이 그려짐
   같은 이유로 마스크를 왼쪽에서 오른쪽으로 엽니다. 펜으로 긋는 것처럼. */
canvas.mzx-wavedraw {
  -webkit-mask-image: linear-gradient(to right, #000 0 var(--mzx-draw, 100%), transparent var(--mzx-draw, 100%));
          mask-image: linear-gradient(to right, #000 0 var(--mzx-draw, 100%), transparent var(--mzx-draw, 100%));
  transition: --mzx-draw 0s;
}
canvas.mzx-wavedraw[data-mzx-draw="1"] { animation: mzx-wipe 0.62s var(--mzx-ease) 1; }
@keyframes mzx-wipe {
  from { -webkit-mask-image: linear-gradient(to right, #000 0 0%, transparent 0%);
                 mask-image: linear-gradient(to right, #000 0 0%, transparent 0%); }
  to   { -webkit-mask-image: linear-gradient(to right, #000 0 100%, transparent 100%);
                 mask-image: linear-gradient(to right, #000 0 100%, transparent 100%); }
}

/* ───────────────────────── 07 · 크레딧이 굴러 내려감 · 38 · 부족할 때
   숫자가 그냥 바뀌면 얼마가 빠졌는지 모릅니다. */
.mzx-odo { font-variant-numeric: tabular-nums; transition: color var(--mzx-mid); }
.mzx-odo[data-mzx-dir="down"] { color: var(--color-accent, #fe7b85); }
.mzx-odo[data-mzx-dir="up"]   { color: #1a9c6b; }

/* ───────────────────────── 36 · 확률 바가 경계에서 멈칫
   20% 아래만 '미검출'입니다. 그 선에서 한 박자 쉬면 구간이 눈에 남습니다.
   실제 멈춤은 ix.js 의 키프레임이 만듭니다. 여기선 경계 눈금만. */
.mzx-probbar { position: relative; }
.mzx-probbar::after {
  content: "";
  position: absolute;
  left: 20%;
  top: -3px;
  bottom: -3px;
  width: 1px;
  background: currentColor;
  opacity: 0.28;
}

/* ───────────────────────── 37 · 리포트가 넘어감 */
.mzx-page { animation: mzx-turn 0.55s var(--mzx-ease) 1; transform-origin: left center; }
@keyframes mzx-turn {
  0%   { transform: perspective(600px) rotateY(0); }
  45%  { transform: perspective(600px) rotateY(-24deg); }
  100% { transform: perspective(600px) rotateY(0); }
}

/* ───────────────────────── 40 · 체크가 그려짐 (결제 완료) */
.mzx-check {
  display: flex;
  justify-content: center;
  color: var(--color-accent, #fe7b85);
  margin-bottom: 8px;
}

/* ───────────────────────── 11 · 첫 곡 축하
   평생 한 번입니다. 매번 터지면 금방 귀찮아집니다. */
.mzx-ribbon {
  position: fixed;
  top: -20px;
  z-index: 2147483000;
  width: 7px;
  height: 14px;
  border-radius: 2px;
  background: var(--color-accent, #fe7b85);
  pointer-events: none;
}
.mzx-ribbon:nth-child(3n)   { background: #ffd166; }
.mzx-ribbon:nth-child(3n+1) { background: #7cc6ff; }

/* ───────────────────────── 12 · 플레이어 펼치기 */
.mzx-player { transition: height var(--mzx-slow) var(--mzx-ease); position: relative; }
.mzx-player[data-mzx-open="1"] { height: 46vh; }
.mzx-playerhandle {
  position: absolute;
  top: -14px;
  left: 50%;
  margin-left: -18px;
  width: 36px;
  height: 18px;
  border: 0;
  border-radius: 9px 9px 0 0;
  background: var(--color-bg-secondary, #f4f4f5);
  color: var(--color-text-secondary, #555);
  font-size: 11px;
  line-height: 18px;
  cursor: pointer;
}

/* ───────────────────────── 29 · 검색어가 칠해짐 */
.mzx-mark {
  border-radius: 2px;
  padding: 0 1px;
  background: rgba(254, 123, 133, 0.22);
  animation: mzx-markin 0.3s ease 1;
}
@keyframes mzx-markin {
  from { background: transparent; }
  to   { background: rgba(254, 123, 133, 0.22); }
}

/* ───────────────────────── 움직임을 끈 사용자
   전부 멈춥니다. 색·굵기로 전달되는 것은 남습니다. */
@media (prefers-reduced-motion: reduce) {
  .mzx-breath i,
  .mzx-breath-cap,
  canvas.mzx-wavedraw[data-mzx-draw="1"],
  .mzx-page,
  .mzx-mark { animation: none !important; }
  .mzx-breath i { height: 14px; opacity: 0.7; }
  .mzx-player,
  .mzx-odo { transition: none !important; }
  canvas.mzx-wavedraw { -webkit-mask-image: none !important; mask-image: none !important; }
  .mzx-glow::after,
  .mzx-tilt,
  .mzx-emptyart,
  .mzx-compare > *,
  img.mzx-blurup,
  .mzx-navbar,
  .mzx-playing::before,
  .mzx-zoomable,
  .mzx-behind { transition: none !important; }
  .mzx-tilt[data-mzx-on="1"],
  .mzx-emptyart[data-mzx-on="1"],
  .mzx-zoomable[data-mzx-zoom="1"] { transform: none !important; }
  .mzx-glow::after { opacity: 0 !important; }
  img.mzx-blurup { filter: none !important; transform: none !important; }
  .mzx-err { animation: none !important; }
  .mzx-morph[data-mzx-state="done"]::after { animation: none !important; }
}
