:root {
  color-scheme: dark;
  --ink: #fff9dc;
  --summon: #f7a8ff;
  --accent: #ffdb6b;
  --danger: #ff7182;
  --action-hint-font-size: clamp(0.56rem, 1.9vw, 0.7rem);
}

* {
  box-sizing: border-box;
  /* html/body에만 걸어두면 iOS Safari 등 일부 브라우저는 실제 터치 대상 엘리먼트 기준으로
     더블탭 확대 여부를 판단해 개별 요소(팝업, 버튼 등)에서 새어나갈 수 있다. 모든 요소에
     명시해 어디를 더블클릭/더블탭해도 확대되지 않게 한다. */
  touch-action: manipulation;
}

html,
body {
  min-height: 100%;
  margin: 0;
  touch-action: none;
}

body {
  display: grid;
  min-height: 100svh;
  place-items: center;
  overflow: hidden;
  /* 외곽은 전장 색과 경쟁하지 않도록 무채색 블랙으로 유지한다. */
  background:
    radial-gradient(circle at 50% 20%, #242424 0, #111 42%, #050505 100%);
  color: var(--ink);
  font-family: "Avenir Next", "Apple SD Gothic Neo", "Noto Sans KR", sans-serif;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

button {
  border: 0;
  color: inherit;
  cursor: pointer;
  font: inherit;
  -webkit-tap-highlight-color: transparent;
}

button:focus-visible {
  outline: 3px solid #fff5bd;
  outline-offset: 3px;
}

.ad-shell {
  width: min(500px, calc(100vw - 20px), calc((100svh - 20px) * 0.5625));
  height: auto;
  aspect-ratio: 9 / 16;
}

.playable {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  isolation: isolate;
  border: 2px solid #050505;
  border-radius: 26px;
  background: #0a4a36;
  box-shadow: 0 28px 70px rgba(0, 0, 0, 0.82), inset 0 0 0 1px rgba(255, 255, 255, 0.12);
}

/* 광고 게임 영역은 문서가 아닌 하나의 게임 화면으로 동작한다. 브라우저의 확대, 텍스트 선택,
   이미지 드래그, 길게 누르기 호출 메뉴 및 마우스 포인터 모양이 전장 위에 나타나지 않게 막는다. */
.playable,
.playable * {
  cursor: default;
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
  -webkit-user-drag: none;
  -webkit-user-select: none;
  touch-action: none;
  user-select: none;
}

/* Unity Camera orthographicSize=9.2: full background height is 30.155 world units. */
.scene-background,
.scene-shade {
  position: absolute;
  pointer-events: none;
  user-select: none;
}

/* 원본 배경 이미지(2269x3015)의 중앙 부분만 잘라 쓴다(1135x1989, tools/prepare-source-assets.mjs의 BG_CROP).
   이미지 중심 위치(top/left)는 그대로이고, 높이만 전체 이미지 기준 163.887%에 잘린 비율(1989/3015)을 곱해
   108.116%로 맞춰 보이는 부분의 위치와 배율이 원본과 같다. */
.scene-background {
  z-index: -3;
  top: 50.272%;
  left: 50.145%;
  width: auto;
  height: 108.116%;
  max-width: none;
  transform: translate(-50%, -50%);
}

.scene-shade {
  z-index: -2;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(4, 36, 27, 0.68) 0%, rgba(4, 36, 27, 0.04) 21%, rgba(4, 36, 27, 0.02) 76%, rgba(4, 36, 27, 0.62) 100%),
    linear-gradient(90deg, rgba(0, 33, 22, 0.16), transparent 28%, transparent 72%, rgba(0, 33, 22, 0.16));
}

/* 원본 Scene의 Castle SpriteRenderer. 좌표는 app.js에서 카메라 투영으로 맞춘다. */
.map-landmark {
  position: absolute;
  /* combat-layer(z-index 20)보다 위에 두면 성 이미지가 도착 지점 근처의 몬스터를 통째로
     가려버려서(체력바만 삐져나와 이상하게 보임) "몬스터가 안 보인다"는 문제로 이어진다.
     장식 요소가 전투 가시성보다 우선할 수 없으므로 board(19)보다도 낮춰 항상 뒤에 깔리게 한다. */
  z-index: 1;
  display: block;
  pointer-events: none;
  user-select: none;
  transform: translate(-50%, -50%);
}

/* 몬스터가 성벽에 도달한 순간 성을 흔든다. 애니메이션 중에도 배치 기준 transform(-50%,-50%)을
   유지해야 한다 — 새 transform 값이 이를 덮어쓰면 흔들리는 동안 성 위치 자체가 어긋나 보인다. */
.castle-landmark.is-hit {
  animation: castle-hit-shake 420ms ease-in-out;
}

@keyframes castle-hit-shake {
  0% { transform: translate(-50%, -50%) translateX(0) rotate(0deg); filter: brightness(1); }
  15% { transform: translate(-50%, -50%) translateX(-0.42rem) rotate(-2deg); filter: brightness(1.6) drop-shadow(0 0 0.7rem rgba(255, 90, 60, 0.9)); }
  35% { transform: translate(-50%, -50%) translateX(0.34rem) rotate(1.6deg); }
  55% { transform: translate(-50%, -50%) translateX(-0.22rem) rotate(-1deg); }
  75% { transform: translate(-50%, -50%) translateX(0.12rem) rotate(0.5deg); filter: brightness(1.1); }
  100% { transform: translate(-50%, -50%) translateX(0) rotate(0deg); filter: brightness(1); }
}

/* 보스 몬스터와 같은 크기로 맞춘다. .enemy.boss는 박스(16%) 안에서 스프라이트를 185%로 한 번 더
   확대해 보여주므로, 실제 눈에 보이는 크기(16%*1.85≈29.6%) 기준으로 맞춰야 체감이 맞다. */
/* 크기는 app.js의 layoutLandmarks가 원본 SpriteRenderer 실측값(m_Size)으로 계산해 인라인으로 설정한다. */

.hud {
  --hud-brand-width: clamp(4.275rem, 19vw, 5.7rem);
  --hud-status-height: 1.6875rem;
  --hud-resource-height: 1.5rem;
  position: absolute;
  z-index: 18;
  top: 2.7%;
  right: 4.5%;
  left: 4.5%;
  display: grid;
  grid-template-columns: var(--hud-brand-width) minmax(0, 1fr);
  grid-template-areas: "title controls";
  align-items: start;
  gap: 0.35rem;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.58);
}

.hud-title {
  grid-area: title;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.16rem;
  line-height: 0.92;
}

.hud-title .hud-install-button {
  margin-top: -0.75rem;
}

.hud-logo {
  display: block;
  width: var(--hud-brand-width);
  height: auto;
  transform: translateY(-0.65rem);
  filter: drop-shadow(0 0.18rem 0.28rem rgba(0, 0, 0, 0.55));
}

/* 타이틀 오른쪽에 웨이브·점수·일시정지를 같은 줄로 둔다. */
.hud-controls {
  --hud-chip-width: min(6.6rem, calc((100% - 3.06rem) / 2));
  grid-area: controls;
  display: flex;
  min-width: 0;
  align-items: flex-start;
  justify-content: flex-end;
  gap: 0.48rem;
}

.menu-button {
  display: grid;
  width: 2.1rem;
  height: var(--hud-status-height);
  flex: 0 0 2.1rem;
  padding: 0;
  border: 1px solid #aaa;
  border-radius: 5px;
  background: #222;
  opacity: 0.8;
  color: #fff5c3;
  place-items: center;
}

.menu-button svg {
  display: block;
  width: 1.1rem;
  height: 1.1rem;
  fill: currentColor;
  filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.62));
}

.menu-button:active {
  filter: brightness(1.15);
}

/* 일시정지 팝업: 배경 클릭으로도 닫히는 백드롭 + 가운데 카드. */
.menu-popup {
  position: absolute;
  z-index: 60;
  inset: 0;
  display: grid;
  padding: 8%;
  place-items: center;
}

.menu-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(6, 6, 8, 0.72);
}

.menu-panel {
  position: relative;
  z-index: 1;
  display: grid;
  gap: 0.55rem;
  width: min(84%, 20rem);
  padding: 1rem 1rem 1.1rem;
  border: 1px solid rgba(255, 239, 166, 0.64);
  border-radius: 1rem;
  background: linear-gradient(155deg, rgba(34, 34, 38, 0.97), rgba(10, 10, 12, 0.97));
  box-shadow: 0 0.9rem 1.6rem rgba(0, 0, 0, 0.45), inset 0 1px rgba(255, 255, 255, 0.12);
  text-align: center;
  animation: panel-in 180ms ease-out;
}

.menu-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.menu-panel-header strong {
  color: #fff3b5;
  font-size: 1rem;
  font-weight: 900;
}

.wave-chip,
.score-chip {
  display: flex;
  width: var(--hud-chip-width);
  height: var(--hud-status-height);
  min-width: 0;
  align-items: center;
  justify-content: center;
  gap: 0.22rem;
  padding: 0 0.35rem;
  border: 1px solid #aaa;
  border-radius: 5px;
  background: #222;
  text-align: center;
  line-height: 1;
  white-space: nowrap;
}

.wave-chip {
  width: calc(var(--hud-chip-width) * 0.8);
  border-color: rgba(255, 255, 255, 0.8);
  background: #FC3B38;
}

.score-chip {
  opacity: 0.8;
}

.wave-chip span,
.score-chip span {
  display: inline-block;
  color: #fff;
  font-size: clamp(0.58rem, 1.8vw, 0.68rem);
  font-weight: 900;
  letter-spacing: 0.02em;
}

.wave-chip strong,
.score-chip strong {
  display: inline-block;
  margin: 0;
  color: #fff;
  font-size: clamp(0.74rem, 2.1vw, 0.82rem);
  line-height: 1;
}

/* 골드와 생명력은 상단 상태 표시 바로 아래 오른쪽에 붙인다. */
.resource-row {
  position: absolute;
  top: calc(var(--hud-status-height) + 0.42rem);
  right: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
}

.resource-values {
  display: inline-flex;
  align-items: center;
  gap: 1.1rem;
}

.resource-chip {
  display: inline-flex;
  position: relative;
  height: var(--hud-resource-height);
  align-items: center;
  justify-content: center;
  gap: 0.28rem;
  padding: 0 0.95rem 0 1.05rem;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 5px;
  background: #222;
  opacity: 0.8;
  font-size: clamp(0.58rem, 1.9vw, 0.74rem);
  font-weight: 800;
}

.resource-icon {
  position: absolute;
  z-index: 2;
  top: 50%;
  left: -0.875rem;
  width: 1.75rem;
  height: 1.75rem;
  object-fit: contain;
  transform: translateY(-50%);
  pointer-events: none;
}

.resource-chip strong {
  /* 아이콘은 칩 왼쪽에 겹쳐 배치하므로, 숫자는 아이콘 영역을 뺀 공간의 가운데로 보정한다. */
  transform: translateX(0.4rem);
  color: var(--ink);
}

.hp-chip strong {
  color: var(--danger);
}

/* 골드 칩과 같은 금색 테마로 통일한다. */
.hp-chip {
  position: relative;
  border-color: rgba(255, 219, 107, 0.5);
}

/* 몬스터가 성벽에 도달해 HP가 깎이는 순간, 골드 부족 흔들림(currency-chip.is-insufficient)과
   같은 방식으로 생명력 칩을 흔들고 붉은 링을 한 번 띄운다. */
.hp-chip.is-hit {
  animation: hp-chip-hit-shake 420ms ease-out;
}

.hp-chip.is-hit::after {
  content: "";
  position: absolute;
  z-index: 0;
  inset: -0.25rem;
  border: 2px solid rgba(255, 90, 90, 0.95);
  border-radius: 999px;
  opacity: 0;
  /* forwards가 없으면 애니메이션이 끝난 뒤 opacity가 기본값(1)으로 되돌아가,
     다음 피격 전까지 붉은 띠가 계속 보이는 버그가 생긴다. */
  animation: hp-chip-hit-ring 420ms ease-out forwards;
}

@keyframes hp-chip-hit-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-0.3rem); }
  45% { transform: translateX(0.26rem); }
  70% { transform: translateX(-0.16rem); }
}

@keyframes hp-chip-hit-ring {
  0% { opacity: 0.95; transform: scale(0.9); }
  100% { opacity: 0; transform: scale(1.4); }
}

.currency-chip {
  position: relative;
  border-color: rgba(255, 219, 107, 0.5);
}

/* 영웅 소환 비용이 모이면 골드 칩도 은은하게 빛나 다음 행동을 알려준다. */
.currency-chip.is-summon-affordable {
  animation: currency-affordable-pulse 1.7s ease-in-out infinite;
}

@keyframes currency-affordable-pulse {
  0%, 100% {
    border-color: rgba(255, 219, 107, 0.5);
    box-shadow: none;
  }
  50% {
    border-color: rgba(255, 234, 145, 0.95);
    box-shadow: 0 0 0.7rem rgba(255, 207, 82, 0.62);
  }
}

/* 골드 부족 시 골드 칩에 붙는 1회성 강조 효과. 골렘 버튼의 준비 완료 링 연출과 같은 톤이되,
   "부족함/실패"를 나타내야 하므로 위험색(빨강) 링 + 짧은 흔들림으로 표현한다. */
.currency-chip.is-insufficient {
  animation: currency-insufficient-shake 420ms ease-out;
}

.currency-chip.is-insufficient::after {
  content: "";
  position: absolute;
  z-index: 0;
  inset: -0.25rem;
  border: 2px solid rgba(255, 113, 130, 0.92);
  border-radius: 999px;
  pointer-events: none;
  opacity: 0;
  animation: currency-insufficient-ring 420ms ease-out forwards;
}

@keyframes currency-insufficient-shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-0.2rem); }
  50% { transform: translateX(0.2rem); }
  75% { transform: translateX(-0.12rem); }
}

@keyframes currency-insufficient-ring {
  0% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.3); }
}

/* 골드 획득 순간(웨이브 클리어·골렘 처치) 골드 칩에 붙는 1회성 긍정 피드백.
   부족 경고(빨강)와 대비되도록 금색 확장 링 + 살짝 튀는 스케일로 표현한다. */
.currency-chip.is-gain {
  animation: currency-gain-bounce 420ms ease-out;
}

.currency-chip.is-gain::after {
  content: "";
  position: absolute;
  z-index: 0;
  inset: -0.25rem;
  border: 2px solid rgba(255, 219, 107, 0.95);
  border-radius: 999px;
  pointer-events: none;
  opacity: 0;
  animation: currency-gain-ring 480ms ease-out forwards;
}

@keyframes currency-gain-bounce {
  0% { transform: scale(1); }
  35% { transform: scale(1.16); }
  100% { transform: scale(1); }
}

@keyframes currency-gain-ring {
  0% { opacity: 0.95; transform: scale(0.85); }
  100% { opacity: 0; transform: scale(1.45); }
}

.currency-chip strong {
  color: var(--accent);
}

.hud-install-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--hud-brand-width);
  min-height: 1.8rem;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  animation: hud-install-pulse 1.7s ease-in-out infinite;
}

.hud-install-image {
  display: block;
  width: 100%;
  height: auto;
}

.hud-install-button:active {
  animation: none;
  transform: scale(0.96);
  filter: brightness(1.08);
}

@keyframes hud-install-pulse {
  0%, 100% { filter: drop-shadow(0 0.16rem 0.25rem rgba(237, 112, 41, 0.4)); transform: scale(1); }
  50% { filter: drop-shadow(0 0 0.5rem rgba(255, 194, 61, 0.8)); transform: scale(1.035); }
}

/* 상단 안내 영역은 일반 플레이 중 숨기고, 튜토리얼 단계에서만 다시 표시한다. */
.message-strip {
  display: none;
}

.playable.is-tutorial-active .message-strip {
  position: absolute;
  z-index: 30;
  top: 15.8%;
  right: 7%;
  left: 7%;
  display: flex;
  min-height: 1.8rem;
  align-items: center;
  justify-content: center;
  padding: 0.28rem 0.72rem;
  border: 1px solid rgba(255, 241, 170, 0.22);
  border-radius: 999px;
  background: rgba(18, 18, 20, 0.55);
  color: #f5f2c9;
  font-size: clamp(0.58rem, 1.95vw, 0.8rem);
  font-weight: 700;
  text-align: center;
  text-shadow: 0 2px 5px rgba(0, 0, 0, 0.6);
}

.message-dot {
  width: 0.42rem;
  height: 0.42rem;
  flex: 0 0 auto;
  margin-right: 0.4rem;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 10px var(--accent);
}

.board {
  position: absolute;
  /* 큰 영웅(특히 위쪽 줄)이 웨이브 안내 배너보다 위 z축에 그려져야 배너에 가려 잘려 보이지 않는다. */
  z-index: 19;
  inset: 0;
  pointer-events: none;
}

.board-slot {
  position: absolute;
  top: var(--slot-y);
  left: var(--slot-x);
  display: grid;
  width: clamp(2.9rem, 9.2%, 4.55rem);
  height: auto;
  aspect-ratio: 1 / 1;
  place-items: center;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 36%;
  background: transparent;
  pointer-events: auto;
  transform: translate(-50%, -50%);
  transition: filter 160ms ease, transform 160ms ease, border-color 160ms ease;
}

.board-slot:hover {
  filter: brightness(1.12);
}

.board-slot.is-selected {
  border-color: rgba(255, 220, 106, 0.95);
  background: rgba(255, 220, 106, 0.16);
  box-shadow: 0 0 0.75rem rgba(255, 219, 107, 0.62), inset 0 0 0.35rem rgba(255, 240, 167, 0.28);
  filter: brightness(1.16);
}

.board-slot.is-merge-ready::after {
  content: "";
  position: absolute;
  z-index: 2;
  top: 21%;
  left: 50%;
  width: 90%;
  height: 45%;
  border-radius: 50% 50% 40% 40% / 14% 14% 18% 18%;
  background:
    linear-gradient(to right,
      transparent 0%,
      hsla(var(--merge-pair-hue), 100%, 76%, 0.12) 15%,
      hsla(var(--merge-pair-hue), 100%, 90%, 0.38) 34%,
      hsla(var(--merge-pair-hue), 100%, 96%, 0.52) 50%,
      hsla(var(--merge-pair-hue), 100%, 90%, 0.38) 66%,
      hsla(var(--merge-pair-hue), 100%, 76%, 0.12) 85%,
      transparent 100%),
    linear-gradient(to top,
      hsla(var(--merge-pair-hue), 100%, 94%, 0.82) 0%,
      hsla(var(--merge-pair-hue), 100%, 86%, 0.64) 30%,
      hsla(var(--merge-pair-hue), 100%, 76%, 0.3) 68%,
      transparent 100%);
  -webkit-mask-image: linear-gradient(to top, rgba(0, 0, 0, 0.82) 0%, #000 12%, #000 60%, transparent 100%);
  mask-image: linear-gradient(to top, rgba(0, 0, 0, 0.82) 0%, #000 12%, #000 60%, transparent 100%);
  mix-blend-mode: screen;
  transform: translate(-50%, 0) scaleY(0.94);
  transform-origin: center bottom;
  pointer-events: none;
  animation: merge-pair-pillar 1.7s ease-in-out infinite;
}

.board-slot.is-merge-ready .hero-sprite {
  position: relative;
  z-index: 1;
  animation: merge-pair-heartbeat 1.7s ease-in-out infinite;
}

/* 합성 대상이 아닌 영웅은 웹 팝업 뒤 배경(dim backdrop)처럼 어둡게만 처리한다. brightness는 불투명한 부분에
   검은 막을 얹은 것과 같아 스프라이트 윤곽을 따라가며, 영웅을 투명하게 만들거나 블러를 주지 않는다.
   0.5는 검은 막 50%에 해당하며, 오라도 영웅과 같은 밝기로 맞춘다. */
.board-slot.is-merge-dimmed .hero-sprite {
  filter: brightness(0.5);
}

.board-slot.is-merge-dimmed .epic-aura {
  filter: brightness(0.5);
}

@keyframes merge-pair-pillar {
  0%, 100% { opacity: 0.6; transform: translate(-50%, 0) scaleY(0.94); }
  15%, 35%, 55% { opacity: 0.52; }
  24% { opacity: 0.86; }
  44% { opacity: 0.8; }
  50% { transform: translate(-50%, -3%) scaleY(1.02); }
}

@keyframes merge-pair-heartbeat {
  0%, 15%, 35%, 55%, 100% {
    transform: translate(var(--hero-offset-x, 0%), var(--hero-offset-y, -40%)) scale(var(--hero-scale, 1));
  }
  24% {
    transform: translate(var(--hero-offset-x, 0%), var(--hero-offset-y, -40%)) scale(calc(var(--hero-scale, 1) * 1.13));
  }
  44% {
    transform: translate(var(--hero-offset-x, 0%), var(--hero-offset-y, -40%)) scale(calc(var(--hero-scale, 1) * 1.08));
  }
}

/* 최초 플레이 튜토리얼이 소환/합성을 유도하는 대상에 붙이는 강조 펄스.
   보드 슬롯뿐 아니라 합성 버튼(.context-merge)에도 그대로 재사용한다. */
.board-slot.is-tutorial-target,
.context-merge.is-tutorial-target,
.action-button.is-tutorial-target {
  border-color: rgba(255, 220, 106, 0.95);
  box-shadow: 0 0 0 0.2rem rgba(255, 219, 107, 0.35), 0 0 1rem rgba(255, 219, 107, 0.65);
  animation: tutorial-target-pulse 1s ease-in-out infinite;
}

@keyframes tutorial-target-pulse {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(1.4); }
}

/* 튜토리얼 손가락 포인터: 대상 칸 위에서 위아래로 톡톡 두드리는 느낌을 준다. */
.tutorial-pointer {
  position: absolute;
  z-index: 45;
  font-size: 2.3rem;
  pointer-events: none;
  filter: grayscale(1) brightness(1.22) drop-shadow(0 4px 8px rgba(0, 0, 0, 0.55));
  animation: tutorial-point-bounce 1s ease-in-out infinite;
  opacity: 0;
  visibility: hidden;
  /* 사라질 때는 페이드가 끝난 뒤에 visibility를 hidden으로 바꿔, 클릭 통과·즉시 컷 없이 자연스럽게 사라지게 한다. */
  transition: opacity 180ms ease, visibility 0s linear 180ms;
}

.tutorial-pointer.is-visible {
  opacity: 1;
  visibility: visible;
  transition: opacity 180ms ease, visibility 0s linear 0s;
}

@keyframes tutorial-point-bounce {
  0%, 100% { transform: translate(-50%, 0.25rem) translateY(0); }
  50% { transform: translate(-50%, 0.25rem) translateY(0.35rem); }
}

.tutorial-bubble {
  position: absolute;
  z-index: 46;
  top: 20%;
  left: 50%;
  display: grid;
  gap: 0.5rem;
  pointer-events: none;
  width: min(84%, 21rem);
  padding: 0.75rem 0.85rem 0.65rem;
  border: 1px solid rgba(255, 239, 166, 0.64);
  border-radius: 0.6rem;
  background: linear-gradient(155deg, rgba(34, 34, 38, 0.97), rgba(10, 10, 12, 0.97));
  box-shadow: 0 0.65rem 1.2rem rgba(0, 0, 0, 0.42);
  text-align: center;
  /* x축 중앙 정렬(-50%)은 모든 위치 변형(is-bottom/is-above-dock/is-summon-nudge)에서 공통이므로
     여기 한 곳에만 두고, 등장 연출은 --tb-y/--tb-scale 변수로만 바꾼다. 과거에는 @keyframes
     panel-in이 transform 전체를 매 프레임 덮어써서 애니메이션 도중 translateX(-50%)가 사라져
     말풍선이 화면 우측에서 밀려들어오는 것처럼 보였다. */
  --tb-y: 0.4rem;
  --tb-scale: 0.98;
  transform: translateX(-50%) translateY(var(--tb-y)) scale(var(--tb-scale));
  opacity: 0;
  visibility: hidden;
  transition: opacity 200ms ease, transform 200ms ease, visibility 0s linear 200ms;
}

.tutorial-bubble.is-visible {
  --tb-y: 0;
  --tb-scale: 1;
  opacity: 1;
  visibility: visible;
  transition: opacity 200ms ease, transform 200ms ease, visibility 0s linear 0s;
}

/* 합성 단계는 우측 상단 영웅 정보/합성 버튼 패널(.hero-context)과 자리가 겹치므로,
   이 단계일 때만 말풍선을 화면 하단으로 내려서 그 패널이 항상 보이게 한다. */
.tutorial-bubble.is-bottom {
  top: auto;
  bottom: 14%;
}

/* 소환/합성 등 하단 action-dock 버튼을 가리키는 단계는 말풍선(건너뛰기 버튼 포함)도
   그 버튼 바로 위에 붙여, 화면 상단에 홀로 떠서 강조 대상과 멀어 보이지 않게 한다.
   정확한 위치는 대상 버튼의 실측 좌표로 app.js가 --bubble-bottom을 계산해 넣는다. */
.tutorial-bubble.is-above-dock {
  top: auto;
  bottom: var(--bubble-bottom, 18%);
}

/* 진행 중 재안내에는 튜토리얼 건너뛰기 조작을 노출하지 않는다. */
.tutorial-bubble.is-summon-nudge .tutorial-actions {
  display: none;
}

/* 멈춰 있는 플레이어의 시선이 빈 칸과 안내 문구에 함께 모이도록, 대상 칸과 인접 말풍선을 묶어 강조한다. */
.board-slot.is-summon-nudge-target {
  border-color: rgba(255, 220, 106, 0.98);
  box-shadow: 0 0 0 0.2rem rgba(255, 219, 107, 0.34), 0 0 1.1rem rgba(255, 219, 107, 0.78);
  animation: summon-nudge-focus 1s ease-in-out infinite;
}

@keyframes summon-nudge-focus {
  0%, 100% { transform: translate(-50%, -50%) scale(1); filter: brightness(1); }
  50% { transform: translate(-50%, -50%) scale(1.08); filter: brightness(1.45); }
}

.tutorial-bubble.is-summon-nudge {
  top: var(--nudge-y, 50%);
  bottom: auto;
  /* 말풍선 폭(84%)과 translate(-50%)를 고려해 양쪽이 잘리지 않는 좌우 범위로 제한한다.
     x축 중앙 정렬은 기본 .tutorial-bubble의 transform과 동일해 여기서 다시 선언하지 않는다
     (다시 선언하면 .is-visible의 transform 선언과 같은 우선순위로 충돌한다). */
  left: clamp(43%, var(--nudge-x, 50%), 57%);
}

.tutorial-bubble p {
  margin: 0;
  color: var(--ink);
  font-size: var(--action-hint-font-size);
  line-height: 1.5;
}

/* 말풍선은 기본 숨기고, 튜토리얼이나 is-visible 재안내에서만 표시한다. */
.tutorial-bubble {
  display: none;
}

.playable.is-tutorial-active .tutorial-bubble,
.tutorial-bubble.is-visible {
  display: grid;
}

.tutorial-actions {
  display: grid;
  justify-items: center;
  gap: 0.4rem;
}

/* 각진 박스형 버튼으로: 살짝 낮춘 radius + 커진 크기로 눌러볼 만한 요소임을 분명히 한다. */
.tutorial-skip {
  pointer-events: auto;
  padding: 0.5rem 1.2rem;
  border: 1px solid rgba(255, 255, 255, 0.36);
  border-radius: 0.5rem;
  background: rgba(255, 255, 255, 0.1);
  color: #f3f1e6;
  font-size: 0.86rem;
  font-weight: 800;
  text-decoration: none;
  box-shadow: 0 0.25rem 0.55rem rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.14);
  transition: filter 140ms ease, transform 140ms ease, box-shadow 140ms ease;
}

.tutorial-skip:hover {
  filter: brightness(1.2);
}

.tutorial-skip:active {
  transform: scale(0.96);
}

/* 2초간 아무 진행이 없으면(app.js updateTutorialSkipHighlight) 금색 테두리 펄스로
   시선을 끌어, 막혀 있다고 느끼지 않게 "여기 누르면 넘어간다"를 알려준다. */
.tutorial-skip.is-idle-highlight {
  animation: tutorial-skip-pulse 1s ease-in-out infinite;
}

@keyframes tutorial-skip-pulse {
  0%, 100% {
    border-color: rgba(255, 255, 255, 0.36);
    box-shadow: 0 0.25rem 0.55rem rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.14);
  }
  50% {
    border-color: rgba(255, 224, 130, 0.95);
    box-shadow: 0 0.25rem 0.55rem rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.14), 0 0 0.9rem rgba(255, 214, 107, 0.7);
  }
}

/* 공격 순간 슬롯 전체가 살짝 튀는 펀치감. transform: translate(-50%,-50%)는 슬롯 배치 기준점이라 유지해야 한다. */
.board-slot.is-punching {
  animation: slot-punch 0.26s ease-out;
}

/* 예전엔 1.32배까지 튀어서 그 아래 실제 Attack 프레임 전환(무기 각도·이펙트 변화)이
   묻혀 "그냥 커졌다 작아지기만 한다"는 인상을 줬다. 펀치감은 살리되 폭을 확 줄인다. */
@keyframes slot-punch {
  0% { transform: translate(-50%, -50%) scale(1) rotate(0deg); }
  30% { transform: translate(-50%, -50%) scale(1.08) rotate(-1.5deg); }
  60% { transform: translate(-50%, -50%) scale(0.98) rotate(0.5deg); }
  100% { transform: translate(-50%, -50%) scale(1) rotate(0deg); }
}

/* 소환/합성 결과가 화면에 통통 튀며 등장하는 연출. */
.board-slot.is-popping {
  animation: slot-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes slot-pop {
  0% { transform: translate(-50%, -50%) scale(0.2); filter: brightness(1.8); }
  55% { transform: translate(-50%, -50%) scale(1.22); filter: brightness(1.3); }
  80% { transform: translate(-50%, -50%) scale(0.94); filter: brightness(1); }
  100% { transform: translate(-50%, -50%) scale(1); filter: brightness(1); }
}

/* 에픽 등급 전용 표시: 캐릭터 뒤에서 은은하게 맥동하는 금빛 오라 + 우상단 별 배지. */
.epic-aura {
  position: relative;
  z-index: 0;
  grid-area: 1 / 1;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(circle, rgba(255, 224, 130, 0.85) 0%, rgba(255, 190, 90, 0.4) 45%, transparent 72%);
  transition: filter 180ms ease;
  animation: epic-aura-pulse 1.8s ease-in-out infinite;
}

@keyframes epic-aura-pulse {
  0%, 100% { transform: scale(0.92); opacity: 0.75; }
  50% { transform: scale(1.18); opacity: 1; }
}


.board-slot.is-occupied::before {
  content: "";
  position: absolute;
  left: 18%;
  top: 52%;
  width: 64%;
  height: 22%;
  border-radius: 50%;
  background: radial-gradient(ellipse, rgba(12, 18, 29, 0.38), transparent 72%);
  pointer-events: none;
}

.hero-sprite {
  display: block;
  /* board-slot이 grid-template 없이 display:grid만 쓰므로, 자식 여러 개(오라·배지 포함)가
     행마다 따로 배치되지 않도록 전부 같은 셀에 명시적으로 겹쳐 놓는다. */
  grid-area: 1 / 1;
  /* 등급별 상대 크기는 유지하면서 필드 영웅만 기존 대비 10% 크게 표시한다. */
  width: 135.52%;
  height: 135.52%;
  /* 이 요소는 위치·스케일·피벗만 담당하는 "창"이다. 실제 프레임 스트립은 .hero-sprite-frame이
     이 창보다 넓게 그려지고, overflow:hidden으로 한 프레임만 보이게 잘라낸다. */
  overflow: hidden;
  /* drop-shadow filter는 없앴다: 이 요소는 idle 루프/공격 애니메이션 동안 프레임이 계속
     바뀌는데, filter가 걸려 있으면 바뀔 때마다 그림자를 다시 렌더링해야 해서 필드에 있는
     모든 영웅이 항상 이 비용을 낸다. 특히 모바일에서 타격 순간(공격 애니메이션이 겹칠 때)
     버벅임의 주요 원인으로 보여 제거했다. */
  pointer-events: none;
  user-select: none;
  transition: filter 180ms ease, opacity 180ms ease;
  transform-origin: var(--hero-pivot-x, 50%) var(--hero-pivot-y, 90%);
  transform: translate(var(--hero-offset-x, 0%), var(--hero-offset-y, -40%)) scale(var(--hero-scale, 1));
}

/* 시트 스크러빙을 실제로 담당하는 자식. background-position 대신 transform(translateX)으로
   프레임을 옮긴다: background-position은 스텝마다 리페인트를 유발하지만 transform은 합성
   레이어에서만 처리되어(리페인트 없음) 모바일에서 훨씬 저렴하다. Idle은 app.js가 매 프레임
   직접 translateX를 설정하고, Attack은 아래 sprite-cycle 애니메이션이 담당한다. */
.hero-sprite-frame {
  display: block;
  width: calc(var(--idle-frames, 1) * 100%);
  height: 100%;
  background-image: var(--hero-idle-sprite);
  background-repeat: no-repeat;
  background-size: 100% 100%;
}

/* 소환한 영웅의 Idle 시트가 아직 오지 않았거나 로드에 실패한 동안에는 스프라이트와 에픽 오라를 숨겨,
   영웅보다 등장 연출이 먼저 보이지 않게 한다. 로드 실패 시에는 이름표로 대신한다. */
.board-slot.is-sprite-loading .hero-sprite,
.board-slot.is-sprite-loading .epic-aura,
.board-slot.is-sprite-fallback .hero-sprite {
  visibility: hidden;
}

.hero-fallback,
.hero-loading {
  display: none;
}

.board-slot.is-sprite-fallback .hero-fallback {
  position: relative;
  z-index: 2;
  display: grid;
  grid-area: 1 / 1;
  max-width: 150%;
  padding: 0.18rem 0.4rem;
  border: 1px solid rgba(255, 242, 179, 0.66);
  border-radius: 999px;
  background: rgba(25, 19, 38, 0.88);
  color: var(--accent);
  font-size: 0.5rem;
  font-weight: 900;
  line-height: 1;
  white-space: nowrap;
  pointer-events: none;
}

.hero-fallback[data-rank="Rare"] { border-color: rgba(143, 208, 255, 0.85); color: #bfe4ff; }
.hero-fallback[data-rank="Unique"] { border-color: rgba(247, 168, 255, 0.85); color: var(--summon); }

/* 로딩 안내는 JS가 일정 시간이 지나도 시트가 오지 않을 때만 .is-sprite-slow를 붙여 보여 준다. */
.board-slot.is-sprite-slow .hero-loading {
  position: relative;
  z-index: 2;
  display: block;
  grid-area: 1 / 1;
  width: 1.1rem;
  height: 1.1rem;
  border: 2px solid rgba(255, 244, 189, 0.3);
  border-top-color: var(--accent);
  border-radius: 50%;
  pointer-events: none;
  animation: hero-loading-spin 0.8s linear infinite;
}

@keyframes hero-loading-spin {
  to { transform: rotate(360deg); }
}

@keyframes sprite-cycle {
  from { transform: translateX(0%); }
  /* 프레임 스트립 자신의 너비(= attack-frames배) 기준으로, 마지막 프레임까지만 옮긴다. */
  to { transform: translateX(calc(-100% * (var(--attack-frames, 1) - 1) / var(--attack-frames, 1))); }
}

/* 공격 시 Idle 시트 대신 Attack 시트를 한 번만 재생한다. 재생 시간은 app.js가 --attack-duration으로 지정한다. */
.hero-sprite.is-attacking .hero-sprite-frame {
  width: calc(var(--attack-frames, 1) * 100%);
  background-image: var(--hero-attack-sprite);
  animation-name: sprite-cycle;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
  animation-duration: var(--attack-duration, 0.3s);
}

/* 히어로 Attack 시트의 실제 프레임 수에 맞춰 step 개수만 다르게 지정한다(값은 app.js의 attackFrames와 1:1). */
.hero-sprite.is-attacking[data-attack-frames="4"] .hero-sprite-frame { animation-timing-function: steps(3); }
.hero-sprite.is-attacking[data-attack-frames="5"] .hero-sprite-frame { animation-timing-function: steps(4); }
.hero-sprite.is-attacking[data-attack-frames="6"] .hero-sprite-frame { animation-timing-function: steps(5); }
.hero-sprite.is-attacking[data-attack-frames="7"] .hero-sprite-frame { animation-timing-function: steps(6); }
.hero-sprite.is-attacking[data-attack-frames="8"] .hero-sprite-frame { animation-timing-function: steps(7); }
.hero-sprite.is-attacking[data-attack-frames="9"] .hero-sprite-frame { animation-timing-function: steps(8); }
.hero-sprite.is-attacking[data-attack-frames="10"] .hero-sprite-frame { animation-timing-function: steps(9); }
.hero-sprite.is-attacking[data-attack-frames="11"] .hero-sprite-frame { animation-timing-function: steps(10); }
.hero-sprite.is-attacking[data-attack-frames="12"] .hero-sprite-frame { animation-timing-function: steps(11); }
.hero-sprite.is-attacking[data-attack-frames="13"] .hero-sprite-frame { animation-timing-function: steps(12); }
.hero-sprite.is-attacking[data-attack-frames="14"] .hero-sprite-frame { animation-timing-function: steps(13); }
.hero-sprite.is-attacking[data-attack-frames="16"] .hero-sprite-frame { animation-timing-function: steps(15); }
.hero-sprite.is-attacking[data-attack-frames="20"] .hero-sprite-frame { animation-timing-function: steps(19); }

.combat-layer {
  position: absolute;
  z-index: 20;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

/* 몬스터는 Canvas에서 한 번에 그려 DOM 노드·스타일 갱신을 줄이고, 영웅·HUD는 기존 HTML을 유지한다.
   z-index는 board(19)보다 높게, combat-layer(20)와는 DOM 순서(캔버스가 먼저 옴)로 동률을 깨서
   몬스터가 항상 board 위·투사체/이펙트 아래에 그려지도록 한다. */
.enemy-canvas {
  position: absolute;
  z-index: 20;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.projectile {
  position: absolute;
  z-index: 5;
  width: 0.72rem;
  height: 0.72rem;
  border-radius: 50%;
  background: radial-gradient(circle at 32% 30%, #ffffff 0%, #fff8c9 28%, #ffd34e 56%, #ff8b27 100%);
  box-shadow: 0 0 0.45rem rgba(255, 255, 255, 0.98), 0 0 0.95rem rgba(255, 205, 62, 0.95),
    0 0 1.45rem rgba(255, 126, 30, 0.72);
  transform: translate(-50%, -50%);
  animation: projectile-flight var(--flight-time, 180ms) cubic-bezier(0.18, 0.78, 0.24, 1) forwards;
}

.projectile::before {
  content: "";
  position: absolute;
  inset: -0.32rem;
  border: 1px solid rgba(255, 246, 191, 0.86);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 228, 113, 0.4), transparent 72%);
  box-shadow: 0 0 0.62rem rgba(255, 193, 45, 0.8);
  pointer-events: none;
}

.projectile::after {
  content: "";
  position: absolute;
  right: 38%;
  top: 50%;
  width: 3.6rem;
  height: 155%;
  border-radius: 50%;
  background: linear-gradient(90deg, transparent 0%, rgba(255, 173, 39, 0.12) 24%, rgba(255, 210, 76, 0.82) 76%, #fff9dc 100%);
  box-shadow: 0 0 0.5rem rgba(255, 191, 54, 0.76);
  filter: blur(1px);
  transform: translateY(-50%);
  pointer-events: none;
}

.projectile.bow-projectile::before {
  inset: auto;
  top: 50%;
  left: 72%;
  width: 0.68rem;
  height: 0.76rem;
  border: 0;
  border-radius: 0;
  background: linear-gradient(135deg, #fffde7 8%, #ffe087 55%, #ff9e37 100%);
  box-shadow: none;
  clip-path: polygon(0 7%, 100% 50%, 0 93%, 19% 50%);
  transform: translateY(-50%);
}

.projectile.magic-projectile::after {
  right: 34%;
  width: 4.5rem;
  height: 0.7rem;
  background: linear-gradient(90deg, transparent 0%, rgba(49, 139, 255, 0.1) 22%, rgba(93, 211, 255, 0.88) 76%, #effdff 100%);
  box-shadow: 0 0 0.7rem rgba(60, 184, 255, 0.98);
  filter: blur(2px);
}

.projectile.magic-projectile::before {
  inset: auto;
  top: 50%;
  left: -1.75rem;
  width: 3.35rem;
  height: 1.7rem;
  border: 0;
  border-radius: 0;
  background: linear-gradient(100deg, rgba(70, 155, 255, 0.45), #57cbff 40%, #f1ffff 61%, #65c9ff 100%);
  box-shadow: none;
  clip-path: polygon(0 40%, 39% 40%, 32% 0, 100% 46%, 66% 53%, 77% 100%, 17% 62%, 0 60%);
  filter: drop-shadow(0 0 0.45rem rgba(86, 220, 255, 0.98));
  transform: translateY(-50%);
}

.projectile.bow-projectile {
  width: 1.55rem;
  height: 0.24rem;
  border-radius: 999px;
  background: linear-gradient(90deg, rgba(255, 190, 65, 0.08) 0%, #ffd873 55%, #fffef0 100%);
  box-shadow: 0 0 0.26rem rgba(255, 255, 255, 0.96), 0 0 0.76rem rgba(255, 203, 84, 0.98);
}

.projectile.magic-projectile {
  width: 0.88rem;
  height: 0.88rem;
  background: radial-gradient(circle at 32% 30%, #ffffff 0%, #e4fbff 25%, #79dcff 56%, #328aff 100%);
  box-shadow: 0 0 0.5rem rgba(255, 255, 255, 0.98), 0 0 1.1rem rgba(83, 211, 255, 0.98),
    0 0 1.7rem rgba(61, 118, 255, 0.75);
}

.impact-effect {
  position: absolute;
  z-index: 6;
  display: grid;
  width: 3rem;
  height: 3rem;
  place-items: center;
  color: #fff3a5;
  pointer-events: none;
  transform: translate(-50%, -50%);
  animation: impact-flash 280ms ease-out forwards;
}

.attack-impact-pillar {
  position: absolute;
  z-index: 5;
  width: 3.8rem;
  height: 4.8rem;
  pointer-events: none;
  transform: translate(-50%, -50%);
  background: radial-gradient(ellipse at 50% 82%, #fffef0 0%, rgba(255, 223, 104, 0.98) 12%, rgba(255, 137, 34, 0.86) 34%, rgba(255, 69, 19, 0.24) 63%, transparent 78%);
  filter: drop-shadow(0 0 0.48rem rgba(255, 176, 53, 0.96)) drop-shadow(0 0 1rem rgba(255, 90, 25, 0.74));
  animation: attack-impact-pillar-rise 440ms cubic-bezier(0.15, 0.78, 0.23, 1) forwards;
}

.attack-impact-pillar::before {
  content: "";
  position: absolute;
  inset: 15% 23% 4%;
  clip-path: polygon(49% 100%, 22% 77%, 36% 54%, 24% 29%, 48% 43%, 67% 3%, 73% 43%, 96% 72%, 77% 100%);
  background: linear-gradient(to top, #fffef2 0%, #fff2a5 22%, rgba(255, 187, 48, 0.9) 54%, rgba(255, 89, 22, 0.08) 100%);
}

.attack-impact-pillar::after {
  content: "";
  position: absolute;
  top: 62%;
  left: 50%;
  width: 0.16rem;
  height: 0.16rem;
  border-radius: 50%;
  background: #fffde7;
  box-shadow: -1.3rem -1.4rem 0 rgba(255, 240, 162, 0.96), 1.2rem -1.8rem 0 rgba(255, 250, 208, 0.92),
    -1.15rem 0.1rem 0 rgba(255, 169, 47, 0.98), 1.35rem -0.2rem 0 rgba(255, 187, 53, 0.98),
    -0.65rem -2.2rem 0 rgba(255, 251, 211, 0.9), 0.55rem -2.5rem 0 rgba(255, 190, 59, 0.94);
}

.attack-impact-pillar.weapon-magic {
  background: radial-gradient(ellipse at 50% 82%, #f5ffff 0%, rgba(195, 250, 255, 0.98) 13%, rgba(71, 205, 255, 0.88) 36%, rgba(58, 104, 255, 0.3) 65%, transparent 79%);
  filter: drop-shadow(0 0 0.48rem rgba(113, 235, 255, 0.98)) drop-shadow(0 0 1rem rgba(73, 120, 255, 0.82));
}

.attack-impact-pillar.weapon-magic::before {
  background: linear-gradient(to top, #ffffff 0%, #c5faff 22%, rgba(86, 214, 255, 0.94) 55%, rgba(72, 120, 255, 0.08) 100%);
}

.attack-impact-pillar.weapon-magic::after {
  background: #edffff;
  box-shadow: -1.3rem -1.4rem 0 rgba(178, 249, 255, 0.98), 1.2rem -1.8rem 0 rgba(238, 255, 255, 0.94),
    -1.15rem 0.1rem 0 rgba(82, 193, 255, 0.98), 1.35rem -0.2rem 0 rgba(111, 166, 255, 0.98),
    -0.65rem -2.2rem 0 rgba(234, 255, 255, 0.92), 0.55rem -2.5rem 0 rgba(111, 183, 255, 0.96);
}

.attack-impact-pillar.is-area {
  width: 4.7rem;
  height: 5.3rem;
}

.impact-effect::before {
  content: "✹";
  position: absolute;
  inset: -0.45rem;
  display: grid;
  place-items: center;
  color: currentColor;
  font-size: 3.65rem;
  font-weight: 900;
  line-height: 1;
  opacity: 0;
  text-shadow: 0 0 0.55rem currentColor;
  animation: impact-star 280ms ease-out forwards;
}

.impact-ring {
  position: absolute;
  inset: 12%;
  border: 2px solid currentColor;
  border-radius: 50%;
  animation: impact-ring 320ms ease-out forwards;
}

.impact-effect.is-area .impact-ring {
  inset: -35%;
  border-width: 3px;
}

.impact-effect::after {
  content: "";
  position: absolute;
  inset: 15%;
  background: repeating-conic-gradient(from 15deg, transparent 0deg 40deg, currentColor 41deg 44deg, transparent 45deg 60deg);
  border-radius: 50%;
  animation: impact-rays 280ms ease-out forwards;
}

.impact-core {
  position: relative;
  z-index: 1;
  color: #fffbe0;
  font-size: 1.8rem;
  font-weight: 900;
  line-height: 1;
  text-shadow: 0 0 0.7rem currentColor;
}

/* Unity 스프라이트 이펙트 시트에서 뽑은 실제 타격 애니메이션(공용 hit·근접 slash·마법 magic·광역 fireball). */
.vfx-sprite {
  position: absolute;
  pointer-events: none;
  background-repeat: no-repeat;
  background-size: contain;
  transform: translate(-50%, -50%);
}

.vfx-hit {
  z-index: 6;
  width: 3rem;
  height: 3rem;
  filter: drop-shadow(0 0 0.3rem rgba(255, 231, 139, 0.84));
  animation: vfx-pop 260ms ease-out forwards;
}

.vfx-slash {
  z-index: 7;
  width: 4.3rem;
  height: 4.3rem;
  filter: drop-shadow(0 0 0.42rem rgba(255, 203, 92, 0.9));
  animation: vfx-slash-pop 240ms ease-out forwards;
}

.vfx-magic {
  z-index: 6;
  width: 3.1rem;
  height: 3.1rem;
  filter: drop-shadow(0 0 0.42rem rgba(100, 212, 255, 0.92));
  animation: vfx-pop 300ms ease-out forwards;
}

.vfx-fireball {
  z-index: 8;
  width: 4.4rem;
  height: 4.4rem;
  animation: vfx-pop 320ms ease-out forwards;
}

@keyframes vfx-pop {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.55); }
  25% { opacity: 1; transform: translate(-50%, -50%) scale(1.1); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.25); }
}

@keyframes vfx-slash-pop {
  0% { opacity: 0; transform: translate(-50%, -50%) rotate(var(--vfx-angle, 0deg)) scale(0.6); }
  25% { opacity: 1; transform: translate(-50%, -50%) rotate(var(--vfx-angle, 0deg)) scale(1.15); }
  100% { opacity: 0; transform: translate(-50%, -50%) rotate(var(--vfx-angle, 0deg)) scale(1.3); }
}

.impact-effect.bow-impact {
  color: #ffe4a0;
}

.impact-effect.magic-impact {
  color: #a7eaff;
}

.combat-text {
  position: absolute;
  z-index: 7;
  color: #fff9df;
  font-size: clamp(0.8rem, 3vw, 1.15rem);
  font-weight: 900;
  pointer-events: none;
  -webkit-text-stroke: 0.9px rgba(28, 14, 8, 0.9);
  letter-spacing: -0.03em;
  text-shadow: 0 0.14rem 0 rgba(89, 42, 14, 0.95), 0 0 0.42rem rgba(255, 214, 130, 0.55), 0 0.28rem 0.32rem rgba(0, 0, 0, 0.78);
  transform: translate(-50%, -50%);
  animation: combat-float 800ms cubic-bezier(0.22, 1.1, 0.36, 1) forwards;
}

.combat-text.merge-burst {
  color: var(--summon);
  font-size: clamp(0.58rem, 2.2vw, 0.9rem);
}

.combat-text.merge-burst {
  color: var(--accent);
  font-size: clamp(0.7rem, 2.7vw, 1.05rem);
}

/* 큰 피해 크리티컬일수록 분홍 → 하늘색 → 파란색으로 바꿔 단계가 바로 보이게 한다. */
.critical-cyan {
  --critical-number-color: #83eaff;
  --critical-label-color: #d5f8ff;
  --critical-stroke: rgba(5, 57, 83, 0.9);
  --critical-number-glow: rgba(43, 193, 255, 0.98);
  --critical-label-glow: rgba(65, 209, 255, 0.92);
  --critical-burst-core: #f2fdff;
  --critical-burst-glow: #54dcff;
  --critical-burst-wide-glow: rgba(39, 151, 255, 0.82);
  --critical-ring-inner: #e6fbff;
  --critical-ring-outer: #54dfff;
  --critical-flare-mid: rgba(112, 232, 255, 0.94);
  --critical-flare-low: rgba(42, 188, 255, 0.56);
  --critical-flare-glow: rgba(45, 171, 255, 0.82);
}

.critical-blue {
  --critical-number-color: #82aaff;
  --critical-label-color: #dce8ff;
  --critical-stroke: rgba(15, 32, 94, 0.92);
  --critical-number-glow: rgba(76, 113, 255, 0.98);
  --critical-label-glow: rgba(100, 140, 255, 0.94);
  --critical-burst-core: #f4f7ff;
  --critical-burst-glow: #718fff;
  --critical-burst-wide-glow: rgba(54, 77, 255, 0.9);
  --critical-ring-inner: #e9efff;
  --critical-ring-outer: #718fff;
  --critical-flare-mid: rgba(133, 173, 255, 0.96);
  --critical-flare-low: rgba(72, 103, 255, 0.62);
  --critical-flare-glow: rgba(65, 96, 255, 0.86);
}

.combat-text.critical-number {
  color: var(--critical-number-color, #ff79cf);
  font-size: clamp(0.95rem, 3.6vw, 1.4rem);
  -webkit-text-stroke: 0.7px var(--critical-stroke, rgba(93, 8, 63, 0.85));
  text-shadow: 0 0 0.5rem var(--critical-number-glow, rgba(255, 81, 188, 0.95)), 0 2px 4px rgba(0, 0, 0, 0.7);
}

/* "크리티컬!" 라벨은 데미지 숫자보다 한 단계 작고 옅게 둬서, 정작 봐야 할 숫자(critical-number)를
   가리지 않으면서도 크리티컬이 떴다는 사실은 함께 알려준다. */
.combat-text.critical-label {
  color: var(--critical-label-color, #ffd1f2);
  font-size: clamp(0.56rem, 2.1vw, 0.78rem);
  -webkit-text-stroke: 0.6px var(--critical-stroke, rgba(93, 8, 63, 0.8));
  text-shadow: 0 0 0.4rem var(--critical-label-glow, rgba(255, 121, 207, 0.85)), 0 2px 4px rgba(0, 0, 0, 0.7);
}

/* 크리티컬 지점의 방사형 파편과 짧은 화면 플래시로 고피해 순간을 강조한다. */
.critical-burst {
  position: absolute;
  z-index: 10;
  width: 0.8rem;
  height: 0.8rem;
  pointer-events: none;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: var(--critical-burst-core, #fff5fd);
  box-shadow: 0 0 0.6rem 0.22rem var(--critical-burst-glow, #ff75cd),
    0 0 1.6rem 0.42rem var(--critical-burst-wide-glow, rgba(255, 61, 181, 0.86));
  animation: critical-burst 520ms cubic-bezier(0.15, 0.7, 0.25, 1) forwards;
}

.critical-burst::before,
.critical-burst::after {
  content: "";
  position: absolute;
  inset: -0.18rem;
  border: 2px solid var(--critical-ring-inner, #fff0fb);
  border-radius: 50%;
}

.critical-burst::after {
  inset: -0.55rem;
  border-color: var(--critical-ring-outer, #ff73cd);
}

/* 크리티컬 타격 순간 타격 지점에서 위로 솟구치는 짧은 섬광. 동그랗게 사방으로 퍼지는 대신
   소환 빛기둥과 같은 방향성(수직)으로 짧고 굵게 튀어올라, 고피해 순간의 타격감을 준다. */
.critical-flare {
  position: absolute;
  z-index: 9;
  width: 0.85rem;
  height: 2.9rem;
  pointer-events: none;
  transform: translate(-50%, -96%);
  transform-origin: center bottom;
  background: linear-gradient(
    to top,
    rgba(255, 255, 255, 0.98) 0%,
    var(--critical-flare-mid, rgba(255, 130, 214, 0.92)) 30%,
    var(--critical-flare-low, rgba(255, 97, 199, 0.4)) 68%,
    transparent 100%
  );
  border-radius: 50%;
  box-shadow: 0 0 0.75rem var(--critical-flare-glow, rgba(255, 97, 199, 0.65));
  animation: critical-flare-rise 320ms cubic-bezier(0.15, 0.8, 0.3, 1) forwards;
}

.normal-hit-burst {
  position: absolute;
  z-index: 9;
  width: 0.56rem;
  height: 0.56rem;
  pointer-events: none;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: #fff8d0;
  box-shadow: 0 0 0.42rem 0.15rem rgba(255, 223, 108, 0.95),
    0 0 1.12rem 0.3rem rgba(255, 176, 30, 0.68);
  animation: normal-hit-burst 360ms cubic-bezier(0.15, 0.7, 0.25, 1) forwards;
}

.normal-hit-burst::before,
.normal-hit-burst::after {
  content: "";
  position: absolute;
  inset: -0.126rem;
  border: 1.5px solid rgba(255, 250, 208, 0.98);
  border-radius: 50%;
}

.normal-hit-burst::after {
  inset: -0.385rem;
  border-color: rgba(255, 187, 39, 0.95);
}

.normal-kill-flash {
  position: absolute;
  z-index: 8;
  width: 0.95rem;
  height: 1.6rem;
  pointer-events: none;
  transform: translate(-50%, -50%);
  transform-origin: center bottom;
  border-radius: 50% 50% 40% 40% / 20% 20% 12% 12%;
  background:
    radial-gradient(ellipse at 50% 78%, rgba(255, 255, 225, 0.98) 0%, rgba(255, 218, 91, 0.76) 28%, transparent 72%),
    linear-gradient(
      to top,
      rgba(255, 249, 202, 0.98) 0%,
      rgba(255, 211, 73, 0.86) 38%,
      rgba(255, 177, 31, 0.36) 76%,
      transparent 100%
    );
  box-shadow: 0 0 0.85rem 0.16rem rgba(255, 202, 56, 0.78);
  animation: normal-kill-flash-rise 300ms cubic-bezier(0.15, 0.8, 0.3, 1) forwards;
}

@keyframes normal-kill-flash-rise {
  0% { opacity: 0; transform: translate(-50%, -50%) scaleY(0.2) scaleX(0.75); }
  28% { opacity: 0.96; transform: translate(-50%, -50%) scaleY(1) scaleX(1); }
  100% { opacity: 0; transform: translate(-50%, -50%) scaleY(0.72) scaleX(0.88); }
}

.boss-death-burst {
  position: absolute;
  z-index: 9;
  width: clamp(5rem, 18vw, 8.5rem);
  aspect-ratio: 1;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(circle, rgba(255, 255, 240, 0.94), rgba(255, 175, 85, 0.54) 36%, transparent 70%);
  animation: boss-death-burst 650ms ease-out forwards;
}

.boss-death-burst::before,
.boss-death-burst::after {
  content: "";
  position: absolute;
  inset: 18%;
  border-radius: 50%;
  pointer-events: none;
}

.boss-death-burst::before {
  border: 3px solid rgba(255, 211, 122, 0.95);
  box-shadow: 0 0 1.2rem rgba(255, 134, 67, 0.8);
  animation: boss-death-ring 650ms ease-out forwards;
}

.boss-death-burst::after {
  background: repeating-conic-gradient(from 15deg, transparent 0deg 30deg, rgba(255, 223, 151, 0.94) 32deg 37deg, transparent 39deg 60deg);
  animation: boss-death-rays 650ms ease-out forwards;
}

.boss-death-burst.is-golem {
  background: radial-gradient(circle, rgba(240, 255, 255, 0.94), rgba(90, 220, 241, 0.52) 36%, transparent 70%);
}

.boss-death-burst.is-golem::before {
  border-color: rgba(141, 244, 255, 0.95);
  box-shadow: 0 0 1.2rem rgba(69, 210, 245, 0.8);
}

.boss-death-burst.is-golem::after {
  background: repeating-conic-gradient(from 15deg, transparent 0deg 30deg, rgba(177, 251, 255, 0.94) 32deg 37deg, transparent 39deg 60deg);
}

@keyframes boss-death-burst {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.45); }
  22% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.4); }
}

@keyframes boss-death-ring {
  0% { opacity: 0; transform: scale(0.35); }
  25% { opacity: 1; }
  100% { opacity: 0; transform: scale(1.8); }
}

@keyframes boss-death-rays {
  0% { opacity: 0; transform: rotate(0deg) scale(0.4); }
  22% { opacity: 1; }
  100% { opacity: 0; transform: rotate(22deg) scale(1.3); }
}

@keyframes critical-flare-rise {
  0% { opacity: 0; transform: translate(-50%, -96%) scaleY(0.2) scaleX(0.6); }
  32% { opacity: 1; transform: translate(-50%, -96%) scaleY(1.2) scaleX(1); }
  100% { opacity: 0; transform: translate(-50%, -96%) scaleY(1) scaleX(0.55); }
}

.encounter-alert {
  position: absolute;
  z-index: 41;
  top: 31%;
  left: 50%;
  width: min(88%, 25rem);
  padding: 0.7rem 1rem 0.75rem;
  overflow: hidden;
  border: 1px solid rgba(255, 239, 176, 0.92);
  border-radius: 0.85rem;
  color: #fff9df;
  text-align: center;
  pointer-events: none;
  transform: translateX(-50%);
  box-shadow: 0 0 1.5rem rgba(0, 0, 0, 0.62), inset 0 0 1.3rem rgba(255, 255, 255, 0.2);
  animation: encounter-alert 1750ms cubic-bezier(0.18, 0.86, 0.28, 1) forwards;
}

.encounter-alert::before {
  content: "";
  position: absolute;
  top: -130%;
  left: -18%;
  width: 32%;
  height: 360%;
  background: rgba(255, 255, 255, 0.58);
  transform: rotate(24deg);
  animation: encounter-sweep 940ms ease-out forwards;
}

.encounter-alert span,
.encounter-alert strong,
.encounter-alert small {
  position: relative;
  z-index: 1;
  display: block;
}

.encounter-alert span {
  color: #ffe88c;
  font-size: clamp(0.48rem, 1.8vw, 0.64rem);
  font-weight: 900;
  letter-spacing: 0.12em;
}

.encounter-alert strong {
  margin: 0.06rem 0;
  font-size: clamp(1rem, 4.5vw, 1.55rem);
  line-height: 1.12;
  letter-spacing: -0.04em;
  text-shadow: 0 0.16rem 0.22rem rgba(0, 0, 0, 0.62);
}

.encounter-alert small {
  color: rgba(255, 251, 223, 0.92);
  font-size: clamp(0.47rem, 1.7vw, 0.62rem);
  font-weight: 700;
}

.encounter-alert.is-boss {
  background: linear-gradient(105deg, rgba(67, 10, 32, 0.96), rgba(150, 32, 53, 0.91), rgba(61, 10, 39, 0.95));
}

.encounter-alert.is-golem {
  border-color: rgba(111, 240, 255, 0.92);
  background: linear-gradient(105deg, rgba(9, 58, 78, 0.97), rgba(11, 131, 147, 0.91), rgba(17, 61, 94, 0.96));
}

.encounter-alert.is-golem span {
  color: #aaf8ff;
}

.encounter-alert.is-summon {
  background: linear-gradient(105deg, rgba(20, 20, 23, 0.95), rgba(44, 44, 50, 0.92), rgba(20, 20, 23, 0.95));
}

/* 에픽 이외 등급은 간단한 이름표 수준으로 줄인다: 더 작고, 더 짧게 뜬다.
   JS에서 1100ms 만에 DOM을 제거하므로, 페이드 아웃이 그 안에 끝나도록 animation-duration도 맞춘다. */
.encounter-alert.is-compact-summon {
  top: 24%;
  width: min(70%, 16rem);
  padding: 0.38rem 0.8rem 0.42rem;
  animation-duration: 1100ms;
}

.encounter-alert.is-compact-summon strong {
  font-size: clamp(0.86rem, 3.6vw, 1.15rem);
}

/* 에픽 소환은 금색 오라 톤으로 다른 등급과 구분해 더 화려하게 보이도록 한다. */
.encounter-alert.is-epic-summon {
  border-color: rgba(255, 224, 130, 0.95);
  background: linear-gradient(105deg, rgba(78, 52, 8, 0.96), rgba(255, 191, 74, 0.4), rgba(78, 52, 8, 0.96));
  box-shadow: 0 0 2rem rgba(255, 200, 90, 0.55), inset 0 0 1.3rem rgba(255, 255, 255, 0.25);
}

.encounter-alert.is-epic-summon span {
  color: #fff3b5;
}

.encounter-alert.is-epic-summon strong {
  font-size: clamp(1.1rem, 5vw, 1.7rem);
  text-shadow: 0 0.16rem 0.3rem rgba(0, 0, 0, 0.62), 0 0 0.6rem rgba(255, 216, 117, 0.7);
}

.playable.is-encountering {
  animation: encounter-shake 440ms ease-out;
}

@keyframes critical-burst {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.2); }
  30% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(4.1); }
}

@keyframes normal-hit-burst {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.2); }
  30% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(4.1); }
}

@keyframes encounter-alert {
  0% { opacity: 0; transform: translate(-50%, -1.4rem) scale(1.2); }
  13% { opacity: 1; transform: translate(-50%, 0) scale(1); }
  73% { opacity: 1; transform: translate(-50%, 0) scale(1); }
  100% { opacity: 0; transform: translate(-50%, 0.7rem) scale(0.94); }
}

@keyframes encounter-sweep {
  0% { transform: translateX(-220%) rotate(24deg); opacity: 0; }
  20% { opacity: 0.85; }
  100% { transform: translateX(520%) rotate(24deg); opacity: 0; }
}

@keyframes encounter-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-0.28rem); }
  45% { transform: translateX(0.24rem); }
  68% { transform: translateX(-0.12rem); }
}

/* 합성 순간 크게 터지는 방사형 섬광. 텍스트(combat-text)와 별개로 더 화려한 존재감을 준다. */
.merge-source-flash {
  position: absolute;
  z-index: 7;
  width: 3.7rem;
  height: 3.7rem;
  border: 2px solid rgba(255, 237, 158, 0.96);
  border-radius: 50%;
  pointer-events: none;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle, rgba(255, 244, 181, 0.72) 0%, rgba(255, 180, 88, 0.3) 42%, transparent 72%);
  box-shadow: 0 0 1.4rem rgba(255, 213, 107, 0.9);
  animation: merge-source-collapse 380ms ease-in forwards;
}

@keyframes merge-source-collapse {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.45); }
  30% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(0.08); }
}

.merge-flash {
  position: absolute;
  z-index: 6;
  width: 6.8rem;
  height: 6.8rem;
  border-radius: 50%;
  pointer-events: none;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle, rgba(255, 255, 245, 0.98) 0%, rgba(255, 225, 140, 0.85) 28%, rgba(255, 168, 255, 0.48) 54%, transparent 76%);
  box-shadow: 0 0 2.8rem rgba(255, 216, 130, 0.95), 0 0 5.2rem rgba(247, 168, 255, 0.7);
  animation: merge-flash 640ms ease-out forwards;
}

.merge-flash::before,
.merge-flash::after {
  content: "";
  position: absolute;
  inset: -0.4rem;
  border: 3px solid #fff3c4;
  border-radius: 50%;
  opacity: 0.85;
}

.merge-flash::after {
  border-color: var(--summon);
  animation: merge-ring 560ms 90ms ease-out forwards;
}

@keyframes merge-flash {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.15) rotate(-32deg); }
  28% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(2.05) rotate(32deg); }
}

@keyframes merge-ring {
  0% { opacity: 0.95; transform: scale(0.5); }
  100% { opacity: 0; transform: scale(2.7); }
}

/* 소환 순간 바닥에서 위로 솟는 빛기둥. 합성 섬광은 방사형(전방위)이라 위치가 뭉개져 보이지만,
   이건 세로로만 솟아 소환 지점을 명확히 가리킨다. 노란빛 골드 톤으로 넓고 화려하게 표현한다. */
.summon-pillar {
  position: absolute;
  z-index: 6;
  width: 4.6rem;
  height: 9.6rem;
  pointer-events: none;
  transform: translate(-50%, -88%);
  transform-origin: center bottom;
  background: linear-gradient(
    to top,
    rgba(255, 255, 255, 0.98) 0%,
    rgba(255, 231, 140, 0.94) 14%,
    rgba(255, 196, 61, 0.62) 42%,
    rgba(255, 219, 107, 0.24) 74%,
    transparent 100%
  );
  box-shadow: 0 0 2.4rem rgba(255, 213, 107, 0.75), 0 0 4.6rem rgba(255, 180, 60, 0.4);
  border-radius: 50% 50% 50% 50% / 8% 8% 92% 92%;
  animation: summon-pillar-rise 640ms cubic-bezier(0.12, 0.9, 0.25, 1) forwards;
}

/* 폭(scaleX)은 넓게 유지한 채 세로(scaleY)만 빠르게 목표치를 넘겨 솟구쳤다가(오버슈트)
   살짝 가라앉는 whoosh 동작으로, 넓으면서도 위로 솟는 느낌을 함께 살린다. */
@keyframes summon-pillar-rise {
  0% { opacity: 0; transform: translate(-50%, -88%) scaleY(0.08) scaleX(0.75); }
  18% { opacity: 1; transform: translate(-50%, -88%) scaleY(1.2) scaleX(1); }
  42% { transform: translate(-50%, -88%) scaleY(1) scaleX(1); }
  75% { opacity: 0.82; }
  100% { opacity: 0; transform: translate(-50%, -88%) scaleY(1.08) scaleX(0.88); }
}

/* 빛기둥 바닥에 깔리는 소환진. 지면에 붙어 보이도록 세로로 눌린 타원을 사용한다. */
.summon-ring {
  position: absolute;
  z-index: 5;
  width: 5rem;
  height: 1.6rem;
  border: 2px solid rgba(255, 240, 176, 0.98);
  border-radius: 50%;
  pointer-events: none;
  transform: translate(-50%, -20%);
  background: radial-gradient(ellipse, rgba(255, 231, 140, 0.65) 0%, rgba(255, 196, 61, 0.32) 55%, transparent 78%);
  box-shadow: 0 0 1.3rem rgba(255, 213, 107, 0.85);
  animation: summon-ring-expand 660ms ease-out forwards;
}

@keyframes summon-ring-expand {
  0% { opacity: 0; transform: translate(-50%, -20%) scale(0.3); }
  25% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -20%) scale(2.5); }
}

/* 에픽 소환은 금색 대신 실제 불기둥에 가까운 빨강→주황→노랑 그라디언트로 표현해 등급의
   존재감을 강조한다. 크기·위치·애니메이션은 기본 summon-pillar/-ring을 그대로 물려받고
   색상·크기만 여기서 덮어쓴다. */
.summon-pillar.is-epic {
  width: 5.4rem;
  height: 11rem;
  background: linear-gradient(
    to top,
    rgba(255, 255, 255, 0.98) 0%,
    rgba(255, 214, 110, 0.96) 10%,
    rgba(255, 140, 40, 0.88) 30%,
    rgba(255, 68, 20, 0.68) 58%,
    rgba(150, 20, 10, 0.38) 82%,
    transparent 100%
  );
  box-shadow: 0 0 3rem rgba(255, 120, 40, 0.85), 0 0 6rem rgba(255, 60, 20, 0.55);
}

.summon-ring.is-epic {
  width: 5.8rem;
  height: 1.9rem;
  border-color: rgba(255, 200, 140, 0.98);
  background: radial-gradient(ellipse, rgba(255, 150, 60, 0.78) 0%, rgba(255, 68, 20, 0.42) 55%, transparent 78%);
  box-shadow: 0 0 1.7rem rgba(255, 100, 40, 0.9);
}

/* 성이 피격당한 순간 성 위에서 터지는 화염 섬광. 캐릭터 타격 이펙트와 구분되도록 크고
   붉게 표현해, 흔들림(castle-hit-shake)과 함께 "성이 맞았다"는 걸 명확히 알린다. */
.castle-impact-burst {
  position: absolute;
  z-index: 8;
  width: 6.5rem;
  height: 6.5rem;
  pointer-events: none;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 244, 200, 0.98) 0%, rgba(255, 140, 60, 0.78) 30%, rgba(255, 60, 40, 0.42) 58%, transparent 78%);
  box-shadow: 0 0 2.6rem rgba(255, 110, 50, 0.85), 0 0 5rem rgba(255, 60, 40, 0.5);
  animation: castle-impact-flash 460ms ease-out forwards;
}

.castle-impact-burst::after {
  content: "";
  position: absolute;
  inset: -0.5rem;
  border: 3px solid rgba(255, 200, 140, 0.9);
  border-radius: 50%;
  animation: castle-impact-ring 460ms ease-out forwards;
}

@keyframes castle-impact-flash {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.3) rotate(-15deg); }
  25% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.9) rotate(15deg); }
}

@keyframes castle-impact-ring {
  0% { opacity: 0.9; transform: scale(0.6); }
  100% { opacity: 0; transform: scale(2.6); }
}

.action-dock {
  position: absolute;
  z-index: 24;
  right: 4.5%;
  bottom: 3.2%;
  left: 4.5%;
  display: flex;
  justify-content: center;
  gap: 0.22rem;
}

.hero-context {
  position: absolute;
  z-index: 26;
  top: 19%;
  left: 4.5%;
  width: min(46%, 13rem);
  display: grid;
  gap: 0.28rem;
  padding: 0.62rem 0.62rem 0.55rem;
  border: 1px solid rgba(255, 239, 166, 0.64);
  border-radius: 0.8rem;
  background: linear-gradient(155deg, rgba(34, 34, 38, 0.97), rgba(10, 10, 12, 0.97));
  box-shadow: 0 0.65rem 1.2rem rgba(0, 0, 0, 0.38), inset 0 1px rgba(255, 255, 255, 0.12);
  animation: panel-in 180ms ease-out;
}

.context-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.35rem;
}

.context-rank {
  display: grid;
  min-width: 2.45rem;
  height: 1.45rem;
  place-items: center;
  padding: 0 0.34rem;
  border: 1px solid rgba(255, 242, 179, 0.66);
  border-radius: 999px;
  background: rgba(255, 213, 92, 0.15);
  color: var(--accent);
  font-size: 0.54rem;
  font-weight: 900;
}

.context-close {
  display: grid;
  width: 1.35rem;
  height: 1.35rem;
  place-items: center;
  padding: 0;
  border: 1px solid rgba(255, 239, 172, 0.34);
  border-radius: 0.42rem;
  background: rgba(255, 255, 255, 0.06);
  color: #fff2b7;
  font-size: 0.9rem;
  line-height: 1;
}

.context-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  color: #fff2b7;
  font-size: clamp(0.68rem, 2.35vw, 0.88rem);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.context-meta,
.context-hint {
  color: #cfcdc0;
  font-size: clamp(0.47rem, 1.55vw, 0.62rem);
  line-height: 1.25;
}

.context-hint {
  min-height: 1.55em;
  color: #ffe08b;
}

.context-merge {
  width: 100%;
  margin-top: 0.12rem;
  padding: 0.38rem 0.5rem;
  border: 1px solid rgba(255, 213, 92, 0.76);
  border-radius: 0.5rem;
  background: linear-gradient(160deg, rgba(144, 88, 48, 0.98), rgba(81, 38, 29, 0.98));
  color: #fff2b7;
  font-size: clamp(0.58rem, 2vw, 0.76rem);
  font-weight: 900;
}

/* 실제로 합성 가능한(활성화된) 상태에서는 설치 CTA 버튼과 같은 금색으로 눈에 띄게 한다. */
.context-merge:not(:disabled) {
  border-color: rgba(255, 226, 140, 0.55);
  background: linear-gradient(180deg, #ffe08a, #f6c451);
  color: #3a2411;
  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.32), inset 0 1px rgba(255, 255, 255, 0.45);
}

.context-merge:disabled {
  cursor: default;
  filter: saturate(0.55);
  opacity: 0.52;
}

.action-button {
  display: inline-flex;
  min-width: 0;
  flex: 1 1 0;
  align-items: center;
  justify-content: center;
  gap: 0.23rem;
  padding: 0.35rem 0.24rem;
  /* 테두리를 좀 더 두껍게 해 버튼 경계가 또렷하고 눌러보고 싶게 보이도록 한다. */
  border: 2px solid rgba(255, 239, 158, 0.48);
  border-radius: 0.75rem;
  background: linear-gradient(160deg, rgba(34, 34, 38, 0.98), rgba(10, 10, 12, 0.96));
  box-shadow: 0 0.28rem 0.55rem rgba(0, 0, 0, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.16);
  transition: filter 160ms ease, transform 160ms ease, box-shadow 160ms ease;
}

.action-button:hover {
  filter: brightness(1.13);
  transform: translateY(-2px);
}

.action-button:disabled,
.action-button.is-unavailable {
  cursor: default;
  filter: saturate(0.65);
  opacity: 0.62;
  transform: none;
}

.action-copy {
  display: grid;
  min-width: 0;
  position: relative;
  z-index: 1;
  justify-items: start;
  line-height: 1;
}

.action-copy strong {
  margin-top: 0.15rem;
  color: #fff2b7;
  font-size: clamp(0.58rem, 2.2vw, 0.8rem);
}

.action-button.golem-action {
  position: relative;
  isolation: isolate;
  grid-column: 1 / -1;
  grid-row: 2;
  width: min(100%, 15.8rem);
  max-width: 15.8rem;
  min-height: 2.55rem;
  margin-bottom: 0;
  justify-self: center;
  /* 채도를 낮춘 파스텔 톤으로 눈이 편안한 느낌을 준다. */
  border-color: rgba(206, 219, 226, 0.68);
  background: linear-gradient(160deg, rgba(98, 110, 118, 0.95), rgba(48, 58, 64, 0.95));
}

.action-button.golem-action .action-copy {
  width: 100%;
  justify-items: center;
  text-align: center;
}

.action-button.golem-action .action-copy strong {
  margin-top: 0;
}

.action-button.auto-action {
  position: relative;
  width: 100%;
  min-height: 3.7rem;
  padding: 0.4rem 0.4rem;
  border-radius: 0.9rem;
}

/* 버튼 위쪽에 은은한 유광 하이라이트를 얹어 밋밋한 단색 그라데이션보다 입체감을 준다.
   버튼에 overflow:hidden을 걸면 위에 떠야 하는 summon-idle-hint까지 잘리므로,
   border-radius를 pseudo-element에 그대로 물려받아 클리핑 없이 둥근 모서리를 맞춘다. */
.action-button.auto-action::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.24) 0%, rgba(255, 255, 255, 0) 48%);
  pointer-events: none;
}

.action-button.auto-action .action-copy {
  width: 100%;
  justify-items: center;
  text-align: center;
}

.action-button.auto-action .action-copy strong {
  margin-top: 0;
  font-size: clamp(0.72rem, 2.55vw, 0.94rem);
  letter-spacing: 0.02em;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.4);
}

/* 은은한 살구빛 파스텔 테두리 + 살짝 밝은 배경으로 차분한 톤을 유지한다. */
.auto-summon-action {
  border-color: rgba(255, 214, 165, 0.75);
  background: linear-gradient(160deg, rgba(140, 104, 64, 0.95), rgba(78, 54, 34, 0.95));
  box-shadow: 0 0.3rem 0.6rem rgba(0, 0, 0, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.16), 0 0 0.7rem rgba(255, 214, 165, 0.16);
}

/* 연라벤더 테두리 + 살짝 밝은 배경으로 소환 버튼과 같은 차분한 톤을 유지한다. */
.auto-merge-action {
  border-color: rgba(216, 196, 255, 0.78);
  background: linear-gradient(160deg, rgba(108, 84, 138, 0.95), rgba(58, 42, 82, 0.95));
  box-shadow: 0 0.3rem 0.6rem rgba(0, 0, 0, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.16), 0 0 0.7rem rgba(216, 196, 255, 0.16);
}

.action-button.image-action {
  display: block;
  flex: 0 0 30.4%;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  transition: transform 90ms ease, filter 90ms ease;
  transform-origin: center;
}

.action-button.image-action:active:not(:disabled):not(.is-unavailable) {
  animation: none;
  filter: brightness(0.72) drop-shadow(0 0.12rem 0.14rem rgba(0, 0, 0, 0.72));
  transform: translateY(0.12rem) scale(0.92);
}

.action-button.golem-action.image-action {
  grid-column: auto;
  grid-row: auto;
  width: 100%;
  max-width: none;
  margin-bottom: 0;
  justify-self: stretch;
}

.action-button.auto-action.image-action::before {
  display: none;
}

.action-button-image {
  position: relative;
  z-index: 1;
  display: block;
  width: 90%;
  margin-inline: auto;
  height: auto;
  pointer-events: none;
}

.action-button.image-action .action-copy {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* 영웅소환 버튼 위에 짧게 뜨는 재안내 문구. 버튼 자체의 overflow와 무관하게 항상 위에 뜨도록
   버튼(position:relative)을 기준으로 absolute 배치한다. */
.summon-idle-hint,
.golem-status-hint {
  position: absolute;
  z-index: 2;
  bottom: calc(100% + 0.4rem);
  left: 50%;
  padding: 0.26rem 0.62rem;
  border: 1px solid rgba(255, 219, 107, 0.65);
  border-radius: 999px;
  background: rgba(20, 20, 23, 0.94);
  color: #fff2b7;
  font-size: var(--action-hint-font-size);
  font-weight: 800;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, 0.15rem);
  transition: opacity 220ms ease, transform 220ms ease, visibility 0s linear 220ms;
}

.summon-idle-hint.is-visible,
.golem-status-hint.is-visible {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
  transition: opacity 220ms ease, transform 220ms ease, visibility 0s linear 0s;
}

.action-button.auto-action.is-tutorial-target:not(.image-action) {
  animation: tutorial-target-pulse 1s ease-in-out infinite;
}

.action-feedback {
  position: absolute;
  z-index: 30;
  bottom: calc(100% + 0.08rem);
  left: 50%;
  padding: 0.26rem 0.42rem;
  border: 1px solid rgba(255, 239, 158, 0.78);
  border-radius: 999px;
  background: rgba(25, 19, 38, 0.94);
  box-shadow: 0 0.32rem 0.62rem rgba(0, 0, 0, 0.32);
  color: #fff4bd;
  font-size: var(--action-hint-font-size);
  font-weight: 900;
  line-height: 1;
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, 0.2rem) scale(0.92);
  white-space: nowrap;
}

.action-feedback.is-visible {
  animation: action-feedback-pop 1.15s ease-out both;
}

@keyframes action-feedback-pop {
  0% { opacity: 0; transform: translate(-50%, 0.35rem) scale(0.88); }
  18%, 70% { opacity: 1; transform: translate(-50%, 0) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -0.38rem) scale(0.96); }
}

/* 소환 가능할 때는 버튼 프레임 대신 투명 PNG 자체를 계속 점멸시킨다. */
.action-button.golem-action.image-action.is-summon-ready {
  animation: none;
  box-shadow: none;
}

.action-button.golem-action.image-action.is-summon-ready::before,
.action-button.golem-action.image-action.is-summon-ready::after {
  display: none;
}

.action-button.golem-action.image-action.is-summon-ready .action-button-image {
  animation: tutorial-target-pulse 1s ease-in-out infinite;
}

.action-button.golem-action.image-action.is-summon-ready:active .action-button-image {
  animation: none;
  filter: brightness(0.72);
}

.action-button.auto-action.image-action.is-tutorial-target {
  animation: none;
  box-shadow: none;
}

.action-button.auto-action.image-action.is-action-ready .action-button-image {
  animation: tutorial-target-pulse 1s ease-in-out infinite;
}

.action-button.auto-action.image-action.is-action-ready:active .action-button-image {
  animation: none;
  filter: brightness(0.72);
}

.is-hidden {
  display: none !important;
}

.end-card {
  position: absolute;
  z-index: 55;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 12% 9%;
  background: linear-gradient(180deg, rgba(20, 20, 23, 0.7), rgba(8, 8, 9, 0.96));
  text-align: center;
  animation: end-in 360ms ease-out;
}

.end-game-icon {
  width: 8rem;
  height: auto;
  filter: drop-shadow(0 0 1rem rgba(255, 221, 89, 0.32));
}

.start-icon {
  width: 9rem;
  height: auto;
  filter: drop-shadow(0 0 1.2rem rgba(255, 221, 89, 0.38));
}

.end-kicker {
  margin-top: 0.9rem;
  color: #c9c6ba;
  font-size: clamp(0.5rem, 1.6vw, 0.65rem);
  font-weight: 900;
  letter-spacing: 0.18em;
}

.end-card h1 {
  margin: 0.42rem 0 0;
  color: #fff3b5;
  font-size: clamp(1.35rem, 6vw, 2.1rem);
  line-height: 1.15;
}

.end-card p {
  margin: 0.75rem 0 1rem;
  color: #d8d5c8;
  font-size: clamp(0.68rem, 2.2vw, 0.85rem);
  line-height: 1.5;
}

.store-buttons {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.6rem;
  width: 100%;
}

.start-card .store-buttons {
  margin-top: 1.25rem;
}

.menu-panel .store-buttons {
  margin-top: 0.7rem;
}

.end-card .store-buttons {
  width: min(94%, 18rem);
  margin-inline: auto;
}

.card-play-button {
  margin-top: 0.75rem;
}

.start-card .start-play-button {
  width: min(100%, 14.625rem);
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}

.start-play-image {
  display: block;
  width: 100%;
  height: auto;
}

.start-card .start-play-button.is-pulsing {
  animation: start-play-image-pulse 1.6s ease-in-out infinite;
}

@keyframes start-play-image-pulse {
  0%, 100% { transform: scale(1); filter: drop-shadow(0 0.5rem 0.65rem rgba(0, 0, 0, 0.42)); }
  50% { transform: scale(1.045); filter: drop-shadow(0 0.5rem 1rem rgba(255, 216, 117, 0.55)); }
}

.start-play-button:active {
  animation: none;
  transform: scale(0.96);
  filter: brightness(1.08);
}

.store-button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 3.4rem;
  padding: 0.55rem 0.45rem;
  border: 1px solid #8b8b8b;
  border-radius: 0.6rem;
  background: #080808;
  color: #fff;
  cursor: pointer;
}

.store-button svg { width: 1.65rem; height: 1.85rem; flex-shrink: 0; }
.store-button span { display: grid; gap: 0.15rem; text-align: left; }
.store-button small { font-size: 0.6rem; }
.store-button strong { font-size: clamp(0.7rem, 2.7vw, 1rem); white-space: nowrap; }
.store-button:hover { background: #222; }
.store-button:focus-visible { outline: 2px solid #ffe08a; outline-offset: 3px; }

.cta-button,
.continue-button {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.68rem 0.8rem;
  border-radius: 0.72rem;
  font-size: clamp(0.72rem, 2.5vw, 0.92rem);
  font-weight: 900;
}

/* 예전엔 금색→주황 그라데이션 + 아래쪽 진갈색 단색 띠(box-shadow)가 겹쳐 색이 세 갈래로
   보이는 "투톤" 느낌이었다. 같은 금색 계열 안에서만 그라데이션하고, 띠 대신 은은한 그림자와
   위쪽 하이라이트만 남겨 하나의 버튼처럼 보이게 정리했다. */
.cta-button {
  border: 2px solid rgba(255, 226, 140, 0.6);
  background: linear-gradient(180deg, #ffe08a, #f6c451);
  color: #3a2411;
  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.32), inset 0 1px rgba(255, 255, 255, 0.45);
}

.cta-button.card-play-button {
  border: 1px solid rgba(218, 190, 119, 0.55);
  background: linear-gradient(180deg, #c9b47c, #b49b62);
  color: #342b19;
  box-shadow: 0 0.2rem 0.45rem rgba(0, 0, 0, 0.2);
}

/* 시작 화면의 CTA는 가만히 있으면 눈에 덜 띄므로 은은한 확대/발광 펄스로 시선을 끈다. */
.cta-button.is-pulsing {
  animation: cta-pulse 1.6s ease-in-out infinite;
}

.cta-button.image-cta-button {
  width: min(100%, 14.625rem);
  aspect-ratio: 13 / 4;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}

.menu-panel .image-cta-button {
  justify-self: center;
}

.play-cta-image {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 13 / 4;
  object-fit: contain;
}

.milestone-card .image-cta-button {
  margin-top: 1rem;
}

.end-card .retry-button.image-cta-button {
  margin-top: 1rem;
  transform: translateY(0.2rem);
}

@keyframes cta-pulse {
  0%, 100% { transform: scale(1); box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.32), inset 0 1px rgba(255, 255, 255, 0.45); }
  50% { transform: scale(1.045); box-shadow: 0 0.5rem 1.5rem rgba(255, 216, 117, 0.55), inset 0 1px rgba(255, 255, 255, 0.45); }
}

.continue-button {
  margin-top: 0.62rem;
  border: 1px solid rgba(255, 239, 172, 0.32);
  background: rgba(255, 255, 255, 0.06);
  color: #d8d5c8;
}

/* 웨이브 전환/보스 등장 시 잠깐 뜨는 화면 중앙 배너. 기본 상태는 완전히 숨겨져 있다가
   is-showing이 붙는 순간에만 애니메이션으로 재생되고 스스로 사라진다. */
.wave-banner {
  position: absolute;
  z-index: 40;
  inset: 0;
  display: grid;
  place-items: center;
  gap: 0.3rem;
  text-align: center;
  pointer-events: none;
  opacity: 0;
}

.wave-banner strong {
  color: #fff3b5;
  font-size: clamp(1.6rem, 8vw, 2.6rem);
  font-weight: 900;
  letter-spacing: 0.06em;
  text-shadow: 0 0 0.9rem rgba(255, 219, 107, 0.85), 0 0.3rem 0.4rem rgba(0, 0, 0, 0.6);
}

.wave-banner span {
  color: #ffe08b;
  font-size: clamp(0.75rem, 3.2vw, 1.05rem);
  font-weight: 800;
  letter-spacing: 0.1em;
  text-shadow: 0 0.2rem 0.3rem rgba(0, 0, 0, 0.6);
}

.wave-banner.is-showing {
  animation: wave-banner-pop 1.7s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

.wave-banner.is-boss strong {
  color: #ffb199;
  font-size: clamp(2rem, 10vw, 3.1rem);
  text-shadow: 0 0 1.3rem rgba(255, 90, 60, 0.9), 0 0.3rem 0.4rem rgba(0, 0, 0, 0.65);
}

.wave-banner.is-boss span {
  color: #ffd4c4;
}

.wave-banner.is-boss.is-showing {
  animation-name: wave-banner-pop-boss;
}

@keyframes wave-banner-pop {
  0% { opacity: 0; transform: scale(0.7); }
  14% { opacity: 1; transform: scale(1.06); }
  22% { transform: scale(1); }
  78% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.05); }
}

@keyframes wave-banner-pop-boss {
  0% { opacity: 0; transform: scale(0.5) rotate(-4deg); }
  12% { opacity: 1; transform: scale(1.18) rotate(2deg); }
  20% { transform: scale(1) rotate(0); }
  30%, 34% { transform: scale(1.04) rotate(0); }
  82% { opacity: 1; transform: scale(1) rotate(0); }
  100% { opacity: 0; transform: scale(1.05) rotate(0); }
}

/* 보스 등장 순간 화면 전체가 살짝 붉게 번쩍이는 플래시. */
.wave-banner.is-boss.is-showing::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle, rgba(255, 120, 90, 0.35), transparent 70%);
  animation: boss-screen-flash 700ms ease-out forwards;
}

@keyframes boss-screen-flash {
  0% { opacity: 0; }
  15% { opacity: 1; }
  100% { opacity: 0; }
}


.toast-region {
  position: absolute;
  z-index: 70;
  top: 21%;
  right: 7%;
  left: 7%;
  display: grid;
  justify-items: center;
  gap: 0.32rem;
  pointer-events: none;
}

.toast {
  padding: 0.42rem 0.68rem;
  border: 1px solid rgba(255, 239, 169, 0.56);
  border-radius: 999px;
  background: rgba(20, 20, 23, 0.94);
  color: #fff2b7;
  font-size: var(--action-hint-font-size);
  font-weight: 800;
  animation: toast-in 180ms ease-out, toast-out 300ms ease-in 1.95s forwards;
}

@keyframes impact-flash {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.4) rotate(-14deg); }
  26% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.55) rotate(14deg); }
}

@keyframes impact-ring {
  0% { opacity: 0.9; transform: scale(0.48, 0.24); }
  100% { opacity: 0; transform: scale(1.6, 0.8); }
}

@keyframes impact-star {
  0% { opacity: 0; transform: scale(0.28) rotate(-26deg); }
  24% { opacity: 1; transform: scale(1.16) rotate(8deg); }
  100% { opacity: 0; transform: scale(1.68) rotate(28deg); }
}

@keyframes impact-rays {
  0% { opacity: 0; transform: scale(0.55); }
  22% { opacity: 1; }
  100% { opacity: 0; transform: scale(1.28); }
}

@keyframes attack-impact-pillar-rise {
  0% { opacity: 0; transform: translate(-50%, -42%) scale(0.24, 0.22); }
  18% { opacity: 1; transform: translate(-50%, -50%) scale(1.05, 0.92); }
  56% { opacity: 0.94; transform: translate(-50%, -59%) scale(0.94, 1.08); }
  100% { opacity: 0; transform: translate(-50%, -74%) scale(0.62, 1.16); }
}

@keyframes projectile-flight {
  from { transform: translate(-50%, -50%) rotate(var(--flight-angle, 0rad)); }
  to { left: var(--target-x); top: var(--target-y); opacity: 0.35; transform: translate(-50%, -50%) rotate(var(--flight-angle, 0rad)) scale(0.7); }
}

@keyframes combat-float {
  0% { opacity: 0; transform: translate(-50%, -35%) scale(0.5); }
  16% { opacity: 1; transform: translate(-50%, -55%) scale(1.35); }
  30% { transform: translate(-50%, -70%) scale(1.05); }
  100% { opacity: 0; transform: translate(-50%, -160%) scale(1.1); }
}

@keyframes panel-in {
  from { opacity: 0; transform: translateY(0.4rem) scale(0.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes toast-in {
  from { opacity: 0; transform: translateY(-0.35rem); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes toast-out {
  to { opacity: 0; transform: translateY(-0.35rem); }
}

@keyframes end-in {
  from { opacity: 0; transform: scale(1.04); }
  to { opacity: 1; transform: scale(1); }
}

@media (max-width: 360px) {
  .resource-chip {
    min-width: 2.45rem;
    padding-right: 0.85rem;
    padding-left: 1.05rem;
  }
}
