/* =====================================================================
   모션 — kiosk-page-motion 계약 (계획서 §4)

   등장 : 알파 0 + y 오프셋(+10~20px) → expo-out(.16,1,.3,1) 1.4~1.6s
          오브젝트 간격 0.2s · 배경판은 페이드만(딜레이 0)
          순서 = 배경 → 읽기 순서(top, left)
   퇴장 : 0.3s ease · 진행 방향 반대로 쓸려나감(-y) + 페이드. 뒤로가기는 +y.

   ⚠ 등장 애니와 유휴/루핑 애니를 같은 요소에 걸지 않는다.
     나중 선언이 등장을 덮어써 opacity 0 에 갇힌다 (한화 실사고 2026-08-17).
     → 등장은 래퍼에, 루핑·교체는 자식(img · .pro__swap 등)에.
   ===================================================================== */

:root {
  --ease-expo: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-exit: cubic-bezier(0.45, 0, 0.75, 0.3);
  --dur-exit:  300ms;
  --dur-chrome-in:  1100ms;   /* 크롬 등장 (계획서 §3.1) */
  --dur-chrome-out: 2200ms;   /* 크롬 퇴장 */
  --dur-paper-in:   900ms;    /* 종이는 화면 전환과 겹치므로 크롬보다 짧게 */
  --dur-paper-out:  500ms;
}

/* ── 등장 잠금 ─────────────────────────────────────────────────────
   DOM 에 꽂히는 순간부터 알파 0 이어야 한다. 클래스가 붙기 전 한 프레임이라도
   원래 자리에 그려지면 화면이 번쩍인다(첫 페인트 전 잠금 규약). */
[data-fx] { opacity: 0; }

.fx-in {
  animation: fxIn var(--fx-dur, 1500ms) var(--ease-expo) var(--fx-delay, 0ms) both;
  will-change: translate, opacity;
}
@keyframes fxIn {
  0%   { opacity: 0; translate: 0 var(--fx-y, 16px); }
  55%  { opacity: 1; }
  100% { opacity: 1; translate: 0 0; }
}

/* 손님이 등장 도중 손을 대면 남은 연출을 접는다 (motion.js finishEnter) — 애니를 끊고 제자리에 */
.fx-done { animation: none !important; opacity: 1 !important; translate: none !important; }

/* 배경판(큰 면)은 움직이지 않고 페이드만 */
.fx-in-fade {
  animation: fxFade var(--fx-dur, 1200ms) var(--ease-expo) var(--fx-delay, 0ms) both;
  will-change: opacity;
}
@keyframes fxFade { from { opacity: 0; } to { opacity: 1; } }

/* ── 퇴장 ─────────────────────────────────────────────────────────
   transition 이 아니라 animation 으로 뺀다. 등장 애니(fill:both)가 물고 있는
   transform 을 transition 은 시작값으로 못 잡아 한 프레임 튄다. */
.fx-out, .fx-out-back { pointer-events: none; }
.fx-out      { animation: fxOutUp   var(--dur-exit) var(--ease-exit) both; }
.fx-out-back { animation: fxOutDown var(--dur-exit) var(--ease-exit) both; }
@keyframes fxOutUp {
  0%   { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(-30px); }
}
@keyframes fxOutDown {
  0%   { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(14px); }
}

/* ── 전역 크롬 (하단 고정) ──────────────────────────────────────────
   페이지와 별개 타임라인. 켜고 끌 때 살짝 아래로 가라앉으며 사라진다.
   로고는 관리자 화면 말고는 끄지 않는다 — 화면이 바뀌어도 늘 그 자리. */
.chrome-item {
  transition:
    opacity var(--dur-chrome-in) var(--ease-expo),
    translate var(--dur-chrome-in) var(--ease-expo);
}
.chrome-item.is-off {
  opacity: 0;
  translate: 0 12px;
  pointer-events: none;
  transition:
    opacity var(--dur-chrome-out) var(--ease-expo),
    translate var(--dur-chrome-out) var(--ease-expo);
}

/* 종이 — 페이드만. 대기 → 프롤로그처럼 둘 다 종이가 있는 전환에서는
   아무 일도 일어나지 않는다(공통 UI 유지, 글만 교체). */
#paper { transition: opacity var(--dur-paper-in) var(--ease-expo); }
#paper.is-off { opacity: 0; transition: opacity var(--dur-paper-out) ease; }
/* 화면·글이 바뀔 때 종이가 살짝 커졌다 돌아온다 (chrome.js pulsePaper) — 그림자가 구워진 PNG 라 scale 만으로 자연스럽다 */
#paper.is-pulse { animation: paperPulse 1200ms var(--ease-expo) both; }
@keyframes paperPulse {
  0%   { scale: 1; }
  35%  { scale: 1.04; }
  100% { scale: 1; }
}

/* 진행바 폭 트윈 — 화면이 바뀌어도 이어서 늘어난다 */
.progress__fill {
  transition: width 900ms var(--ease-expo);
}

/* ── 프롤로그 글 교체 (한 장 위에서 3번) ───────────────────────────
   등장(fx-in)은 래퍼(.pro__label · .pro__body)가, 교체는 자식(.pro__swap)이 맡는다. */
.pro__swap { display: block; }
.pro__swap.pro-out { animation: proOut 260ms ease both; }
.pro__swap.pro-in  { animation: proIn 700ms var(--ease-expo) both; }
@keyframes proOut { to   { opacity: 0; translate: 0 -10px; } }
@keyframes proIn  { from { opacity: 0; translate: 0 14px; } }

/* ── 선택카드 탭 피드백 ───────────────────────────────────────────
   탭 → 안쪽 3px 테두리 + 10px 딱 떨어지는 그림자 + 번호 알약 반전 (33:893) → 자동 진행 */
.choice {
  transition: box-shadow 160ms ease, filter 160ms ease;
}
.choice.is-press { filter: brightness(0.985); }
.choice.is-picked {
  box-shadow: inset 0 0 0 3px var(--ink), var(--shadow-hard);
}
.choice.is-picked .choice__no {
  background: var(--ink);
  color: #fff;
}

/* ── 입력칸 흔들림 (최대 글자수 초과) ─────────────────────────────── */
.shake { animation: shake 420ms cubic-bezier(0.36, 0.07, 0.19, 0.97) both; }
@keyframes shake {
  10%, 90% { transform: translateX(-3px); }
  20%, 80% { transform: translateX(6px); }
  30%, 50%, 70% { transform: translateX(-10px); }
  40%, 60% { transform: translateX(10px); }
}

/* ── 대기 화면 유휴 모션 (사장님 2026-08-29 "너무 멈춰 있어 심심하다") ─────────
   등장은 래퍼(data-fx)가, 루핑은 자식·의사요소가 맡는다 (위 ⚠ 규약). 전부 transform 계열이라 레이아웃을 안 건드린다. */
.attract__title img { animation: attractFloat 6s ease-in-out 2.4s infinite; }
@keyframes attractFloat {
  0%, 100% { translate: 0 0; }
  50%      { translate: 0 -8px; }
}
.attract__dots .dot { animation: dotBob 3.4s ease-in-out infinite; }
.attract__dots .dot:nth-child(2) { animation-delay: 0.45s; }
.attract__dots .dot:nth-child(3) { animation-delay: 0.9s; }
.attract__dots .dot:nth-child(4) { animation-delay: 1.35s; }
@keyframes dotBob {
  0%, 100% { translate: 0 0; }
  50%      { translate: 0 -10px; }
}
/* 시작 버튼 — 테두리 링이 퍼져 나가며 사라진다 (2.8s 마다). 버튼 자체는 등장 애니가 물고 있으니 의사요소로 */
#attract-cta::after {
  content: ""; position: absolute; inset: -8px; border-radius: calc(var(--btn-radius) + 8px);
  border: 2px solid var(--btn); opacity: 0; pointer-events: none;
  animation: ctaRing 2.8s ease-out 2.4s infinite;
}
@keyframes ctaRing {
  0%   { opacity: 0.55; scale: 0.96; }
  70%  { opacity: 0;    scale: 1.08; }
  100% { opacity: 0;    scale: 1.08; }
}
/* 종이 — 아주 천천히 숨 쉰다 (대기·프롤로그·결과 공통). 화면 전환 펄스는 부모(#paper), 숨은 자식(img) */
#paper img { animation: paperBreathe 7s ease-in-out infinite; }
@keyframes paperBreathe {
  0%, 100% { scale: 1; }
  50%      { scale: 1.02; }
}

/* ── 결과 화면 픽토그램 맥동 (등장 완료 후, 자식에만) ─────────────── */
.picto__img { animation: pictoPulse 5.5s ease-in-out 1.8s infinite; }
@keyframes pictoPulse {
  0%, 100% { scale: 1; }
  50%      { scale: 1.035; }
}

/* ── QA: 연출 끔 (?fx=off — 피그마 정지화면 대조용) ────────────────
   등장 잠금까지 풀어야 한다. 안 그러면 opacity 0 인 채로 남는다. */
html.fx-off [data-fx],
html.fx-off .fx-in,
html.fx-off .fx-in-fade { animation: none !important; opacity: 1 !important; translate: none !important; }
html.fx-off .fx-out, html.fx-off .fx-out-back { animation: none !important; opacity: 0 !important; }
html.fx-off .chrome-item, html.fx-off #paper { transition: none !important; animation: none !important; }
html.fx-off .progress__fill { transition: none !important; }
html.fx-off .picto__img { animation: none !important; }
html.fx-off .attract__title img, html.fx-off .attract__dots .dot, html.fx-off #attract-cta::after, html.fx-off #paper img { animation: none !important; }
html.fx-off .pro__swap { animation: none !important; }

/* ⚠ prefers-reduced-motion 을 따르지 않는다. 키오스크 PC 는 Windows "애니메이션 표시" 가 꺼진 경우가 흔해
   미디어쿼리로 묶은 모션(종이 펄스·숨쉬기·대기 유휴·픽토 맥동)이 전부 죽는다 — 개발 PC 에서도 그랬다 (실사고 2026-08-30).
   이 앱의 모션은 접근성 옵션이 아니라 콘텐츠다. 연출을 끄는 길은 ?fx=off (QA) 하나뿐이다. */
