/* =====================================================================
    NUMBER RANGE — 명중 이펙트 (HIT FX)

    ★ 설계 기준이 오발 이펙트와 정반대입니다.
      오발은 '가끔' 나니까 화려해도 됩니다.
      명중은 **초당 3~4번** 납니다 — 화려하면 시야를 가려서 기록에 해롭습니다.
      그래서 전부 **0.5초 안에 끝나고, 타일을 덮지 않는** 것들로만 만들었습니다.

    --g : 이펙트 색 (고른 격자 테마를 따라갑니다)
    --k : 콤보 배수 (1.0 ~ 1.4). 안 틀리고 이어갈수록 조금 커집니다.
   ===================================================================== */

/* ---------- 공통 ---------- */
.fx-flash, .fx-ring, .fx-implode, .fx-nova, .fx-glitch, .fx-digit {
  position: absolute; pointer-events: none; z-index: 5;
}

/* ---------- PULSE (기본) : 가운데가 한 번 확 밝아짐 ---------- */
.fx-flash {
  width: 34px; height: 34px; margin: -17px 0 0 -17px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--g) 0%, transparent 68%);
  animation: fxFlash .34s ease-out forwards;
}
@keyframes fxFlash {
  0%   { opacity: .95; transform: scale(calc(.3 * var(--k))); }
  100% { opacity: 0;   transform: scale(calc(1.5 * var(--k))); }
}
.fx-flash.miss-flash { animation-duration: .38s; }

/* ---------- RIPPLE : 링이 퍼져나감. 가운데가 비어서 시야를 거의 안 가림 ---------- */
.fx-ring {
  width: 20px; height: 20px; margin: -10px 0 0 -10px;
  border: 2px solid var(--g); border-radius: 50%;
  box-shadow: 0 0 12px var(--g);
  animation: fxRing .42s cubic-bezier(.2,.7,.3,1) forwards;
}
.fx-ring.d2 { animation-delay: .1s; animation-duration: .46s; }
@keyframes fxRing {
  0%   { opacity: .9; transform: scale(calc(.3 * var(--k))); border-width: 3px; }
  100% { opacity: 0;  transform: scale(calc(2.6 * var(--k))); border-width: 1px; }
}

/* ---------- SHATTER : 조각이 튀어나감 ---------- */
.fx-shard {
  position: absolute; pointer-events: none; z-index: 5;
  margin: -2px 0 0 -2px; border-radius: 1px;
  /*  ★ SPARKSTORM(.38s) 보다 **느립니다.** 속도 차이도 구분에 한몫합니다. */
  animation: fxShard .62s cubic-bezier(.15,.75,.4,1) forwards;
}
@keyframes fxShard {
  0%   { opacity: 1; transform: translate(0,0) rotate(0deg) scale(1); }
  100% { opacity: 0; transform: translate(var(--dx), var(--dy)) rotate(var(--rot)) scale(.3); }
}

/* ---------- IMPLODE : 바깥에서 안으로 빨려들어감 ---------- */
.fx-implode {
  width: 20px; height: 20px; margin: -10px 0 0 -10px;
  border: 2px solid var(--g); border-radius: 50%;
  box-shadow: 0 0 14px var(--g);
  animation: fxImplode .36s cubic-bezier(.5,0,.8,.4) forwards;
}
@keyframes fxImplode {
  0%   { opacity: 0;   transform: scale(calc(3 * var(--k))); }
  30%  { opacity: .95; }
  100% { opacity: 0;   transform: scale(.1); }
}

/* ---------- GLITCH : RGB 가 어긋나며 지직 ---------- */
.fx-glitch {
  margin-left: calc(var(--gw, 0px) * -0.5);
  transform: translate(-50%, -50%);
  animation: fxGlitch .28s steps(4) forwards;
  background: var(--g); mix-blend-mode: screen; opacity: .5;
}
.fx-glitch::before, .fx-glitch::after {
  content: ''; position: absolute; inset: 0; background: inherit;
}
.fx-glitch::before { transform: translate(-4px, 2px); filter: hue-rotate(90deg); opacity: .6; }
.fx-glitch::after  { transform: translate(4px, -2px); filter: hue-rotate(-90deg); opacity: .6; }
@keyframes fxGlitch {
  0%   { opacity: .55; clip-path: inset(0 0 60% 0); }
  25%  { opacity: .45; clip-path: inset(50% 0 20% 0); }
  50%  { opacity: .35; clip-path: inset(20% 0 50% 0); }
  75%  { opacity: .2;  clip-path: inset(70% 0 0 0); }
  100% { opacity: 0;   clip-path: inset(0 0 0 0); }
}

/* ---------- SPARKSTORM : 파편을 잔뜩 ---------- */
/* (spark 는 기본 스타일을 그대로 쓰고 개수만 늘립니다) */

/* ---------- DIGIT BURST : 없앤 숫자가 위로 떠오름 ---------- */
.fx-digit {
  transform: translate(-50%, -50%);
  font-family: var(--disp); font-weight: 900; font-size: 22px;
  text-shadow: 0 0 12px currentColor;
  animation: fxDigit .6s ease-out forwards;
}
@keyframes fxDigit {
  0%   { opacity: .95; transform: translate(-50%, -50%) scale(calc(1 * var(--k))); }
  100% { opacity: 0;   transform: translate(-50%, -170%) scale(calc(1.3 * var(--k))); }
}

/* ---------- NOVA : 밝게 터지고 링이 같이 퍼짐 ---------- */
.fx-nova {
  width: 26px; height: 26px; margin: -13px 0 0 -13px;
  border-radius: 50%;
  background: radial-gradient(circle, #fff 0%, var(--g) 40%, transparent 72%);
  animation: fxNova .44s ease-out forwards;
}
@keyframes fxNova {
  0%   { opacity: 1; transform: scale(calc(.2 * var(--k))); }
  40%  { opacity: .9; }
  100% { opacity: 0; transform: scale(calc(2.2 * var(--k))); }
}

/* =====================================================================
    콤보 · 피니시
   ===================================================================== */
/*  ★ 콤보는 HUD '안' 한가운데에 둡니다.
    전에는 bottom:-14px 로 HUD 밖(격자 쪽)에 걸쳐 있었는데,
    격자가 문서에서 뒤에 있어 **콤보를 덮어버렸습니다.** (폰에서 확인)
    페널티(+1초)와 자리가 같지만 겹치지 않습니다 —
    틀리는 순간 콤보가 0 이 되어 먼저 사라지기 때문입니다. */
.combo {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  font-family: var(--disp); font-size: 12px; font-weight: 700; letter-spacing: 2px;
  color: var(--hot); text-shadow: 0 0 12px var(--hot); pointer-events: none;
  white-space: nowrap;
}
.combo.bump { animation: comboBump .26s ease-out; }
@keyframes comboBump {
  0%   { transform: translate(-50%, -50%) scale(1.35); }
  100% { transform: translate(-50%, -50%) scale(1); }
}

/* 마지막 숫자를 없앤 순간 — 화면이 한 번 터집니다 */
#screen-game.finish::after {
  content: ''; position: fixed; inset: 0; z-index: 45; pointer-events: none;
  background: radial-gradient(circle at 50% 45%, var(--neon) 0%, transparent 55%);
  animation: finishFlash .65s ease-out forwards;
}
@keyframes finishFlash {
  0%   { opacity: .55; transform: scale(.6); }
  100% { opacity: 0;   transform: scale(1.6); }
}

/* =====================================================================
    상점 미리보기 — 실제 이펙트가 계속 재생됩니다
   ===================================================================== */
.fx-prev {
  display: flex; align-items: center; justify-content: center;
  --g: var(--neon); --k: 1;
}
.fxp-tile {
  width: 34px; height: 34px; border-radius: 6px;
  border: 1px solid var(--tile-edge); background: var(--tile-bg);
  color: var(--neon-soft); font-family: var(--disp); font-size: 15px;
  display: flex; align-items: center; justify-content: center;
}
.fxp-anim {
  position: absolute; left: 50%; top: 50%;
  width: 26px; height: 26px; margin: -13px 0 0 -13px; border-radius: 50%;
}
/*  ★ 2026-08-27 — 카드 그림이 **실제보다 두 배 컸습니다.**
    카드에서 게임용 키프레임(fxRing·fxFlash…)을 그대로 썼는데,
    그것들은 2.6배까지 커지도록 만들어져 있습니다.
      실제 : 고리 52px / 칸 70px  → 0.74배 (칸 안에 얌전히)
      카드 : 고리 68px / 칸 34px  → 2.0배  (칸을 삼킴)
    그래서 카드와 해보기가 딴판으로 보였습니다.
    카드 전용 키프레임을 따로 두어 **칸을 넘지 않게** 합니다.
    ★ 게임용 키프레임은 --k(콤보 배율)를 쓰는데, 카드에는 그 값이 없습니다.
      섞어 쓰면 안 되는 또 하나의 이유입니다. */
@keyframes fxpFlash   { 0% { opacity: 1;  transform: scale(.3) }  100% { opacity: 0; transform: scale(.95) } }
@keyframes fxpRing    { 0% { opacity: .9; transform: scale(.25); border-width: 3px }
                        100% { opacity: 0; transform: scale(.95); border-width: 1px } }
@keyframes fxpImplode { 0% { opacity: 0;  transform: scale(1.05) } 25% { opacity: 1 }
                        100% { opacity: 0; transform: scale(.1) } }
@keyframes fxpNova    { 0% { opacity: 1;  transform: scale(.2) }  100% { opacity: 0; transform: scale(.95) } }
@keyframes fxpHole    { 0% { opacity: 0;  transform: scale(.9) }  25% { opacity: 1 }
                        100% { opacity: 0; transform: scale(.08) } }

.fx-prev[data-fx="pulse"]   .fxp-anim { background: radial-gradient(circle, var(--g), transparent 68%);
                                        animation: fxpFlash 1.4s ease-out infinite; }
.fx-prev[data-fx="ripple"]  .fxp-anim { border: 2px solid var(--g); box-shadow: 0 0 10px var(--g);
                                        animation: fxpRing 1.4s ease-out infinite; }
.fx-prev[data-fx="shatter"] .fxp-anim { background: var(--g); border-radius: 2px;
                                        animation: fxpShatter 1.4s ease-out infinite; }
@keyframes fxpShatter {
  0%   { opacity: 1; transform: scale(.5) rotate(0deg); }
  100% { opacity: 0; transform: scale(1.6) rotate(140deg); }
}
.fx-prev[data-fx="implode"] .fxp-anim { border: 2px solid var(--g); box-shadow: 0 0 12px var(--g);
                                        animation: fxpImplode 1.4s ease-out infinite; }
.fx-prev[data-fx="glitch"]  .fxp-anim { background: var(--g); border-radius: 3px; opacity: .5;
                                        animation: fxGlitch 1.2s steps(4) infinite; }
.fx-prev[data-fx="spark"]   .fxp-anim { background: radial-gradient(circle, var(--g), transparent 55%);
                                        animation: fxpSpark 1.2s ease-out infinite; }
@keyframes fxpSpark {
  0%   { opacity: 1; transform: scale(.3); filter: blur(0); }
  100% { opacity: 0; transform: scale(1.9); filter: blur(2px); }
}
.fx-prev[data-fx="digit"]   .fxp-anim { background: none; color: var(--g);
                                        font-family: var(--disp); font-weight: 900; font-size: 18px;
                                        display: flex; align-items: center; justify-content: center;
                                        animation: fxDigit 1.5s ease-out infinite; }
.fx-prev[data-fx="digit"]   .fxp-anim::before { content: '7'; }
.fx-prev[data-fx="nova"]    .fxp-anim { background: radial-gradient(circle, #fff, var(--g) 40%, transparent 72%);
                                        animation: fxpNova 1.4s ease-out infinite; }

/* ── 카드 그림의 조각들 (2026-08-27) ─────────────────────────────
   ★ 왜 필요한가
     SHATTER·SPARKSTORM·QUAKE·NOVA 는 **여러 조각이 사방으로 튀는 것**이
     그 효과의 정체입니다. 그런데 카드에는 원이나 네모 **하나**만 있어서
     눌러본 것과 딴판이었습니다 ("사각형이 보이지도 않는다" 는 제보).
   ★ --a 는 튀어나갈 방향입니다 (상점과기록.js 가 각도를 넣어 줍니다). */
.fxp-bits { position: absolute; inset: 0; pointer-events: none; }
.fxp-bits i {
  position: absolute; left: 50%; top: 50%;
  width: 5px; height: 5px; margin: -2.5px 0 0 -2.5px;
  background: var(--g); border-radius: 1px;
  animation: fxpBit 1.4s ease-out infinite;
}
@keyframes fxpBit {
  0%   { opacity: 1; transform: rotate(var(--a)) translateX(0)    rotate(0deg)   scale(1); }
  100% { opacity: 0; transform: rotate(var(--a)) translateX(24px) rotate(180deg) scale(.5); }
}
/*  ★ 카드에서도 성격을 갈라 놓습니다 (실제와 같게)
      SHATTER : 큰 네모 조각이 **천천히** 돌며 흩어짐
      SPARK   : 작은 동그라미가 **빠르게** 멀리 퍼짐  */
.fx-prev[data-fx="shatter"] .fxp-bits i,
.fx-prev[data-fx="quake"]   .fxp-bits i {
  width: 8px; height: 8px; margin: -4px 0 0 -4px; border-radius: 1px;
  animation-duration: 1.6s;
}
.fx-prev[data-fx="spark"] .fxp-bits i,
.fx-prev[data-fx="nova"]  .fxp-bits i {
  border-radius: 50%; width: 3px; height: 3px; margin: -1.5px 0 0 -1.5px;
  animation-name: fxpBitFar; animation-duration: 1.1s;
}
/*  불똥은 더 멀리 갑니다 (파편은 24px, 불똥은 32px) */
@keyframes fxpBitFar {
  0%   { opacity: 1; transform: rotate(var(--a)) translateX(0)    scale(1); }
  100% { opacity: 0; transform: rotate(var(--a)) translateX(32px) scale(.3); }
}

/*  ── 실제 효과에 맞춘 손질 ──
    SHATTER : 가운데 덩어리는 **없습니다** — 파편만 튑니다 */
.fx-prev[data-fx="shatter"] .fxp-anim { display: none; }
/*  SPARKSTORM : 가운데 빛은 작게, 불똥이 주인공 */
.fx-prev[data-fx="spark"] .fxp-anim { width: 12px; height: 12px; margin: -6px 0 0 -6px; }
/*  RIPPLE·IMPLODE 는 실제로 고리가 **둘**입니다 (하나는 조금 늦게).
    ★ inset 을 -3px 로 좁혔습니다. -5px 이면 부모(26px)와 합쳐 36px 이 되어
      숫자 칸(34px)을 넘습니다. */
.fx-prev[data-fx="ripple"] .fxp-anim::after,
.fx-prev[data-fx="implode"] .fxp-anim::after {
  content: ''; position: absolute; inset: -3px;
  border: 2px solid var(--g); border-radius: 50%; opacity: .7;
  animation: inherit; animation-delay: .18s;
}
/*  QUAKE : 도는 네모를 빼고, **숫자 칸이 흔들립니다** (실제로 격자가 흔들리므로) */
.fx-prev[data-fx="quake"] .fxp-anim { display: none; }
.fx-prev[data-fx="quake"] .fxp-tile { animation: fxpShake 1.3s ease-out infinite; }
@keyframes fxpShake {
  0%, 62%, 100% { transform: translate(0, 0) }
  66% { transform: translate(-3px, 1px) }
  72% { transform: translate(3px, -1px) }
  78% { transform: translate(-2px, -1px) }
  84% { transform: translate(2px, 1px) }
}

/* 움직임 줄이기 설정이면 미리보기도 멈춥니다 */
@media (prefers-reduced-motion: reduce) {
  .fxp-anim, .fxp-bits i, .fx-prev .fxp-tile { animation: none !important; }
  .fxp-anim { opacity: .6; }
}

/* =====================================================================
    NEXT 숫자 — 넘어가는 연출
    옛 숫자는 위로 넘어가고, 새 숫자가 크게 다가와 제자리를 잡습니다.
    화면에서 제일 자주 바뀌는 곳이라, 넘어가는 게 보여야 리듬이 생깁니다.
   ===================================================================== */
.hnext {
  position: relative; display: inline-block;
  min-width: 1.7em; height: 1em; line-height: 1;
  perspective: 320px;
  vertical-align: baseline;
}
.hn-cur, .hn-out {
  position: absolute; left: 50%; top: 0;
  transform: translateX(-50%);
  transform-origin: 50% 60%;
  backface-visibility: hidden;
  white-space: nowrap;
}

/* 새 숫자 : 위쪽 뒤에서 크게 넘어와 제자리에 딱 */
.hn-in { animation: hnIn .24s cubic-bezier(.18,.9,.32,1.25); }
@keyframes hnIn {
  0%   { opacity: 0; transform: translateX(-50%) translateY(46%) rotateX(-78deg) scale(1.55); }
  55%  { opacity: 1; }
  100% { opacity: 1; transform: translateX(-50%) translateY(0) rotateX(0deg) scale(1); }
}

/* 옛 숫자 : 위로 넘어가며 사라짐 */
.hn-out { animation: hnOut .24s ease-in forwards; }
@keyframes hnOut {
  0%   { opacity: .9; transform: translateX(-50%) translateY(0) rotateX(0deg) scale(1); }
  100% { opacity: 0;  transform: translateX(-50%) translateY(-52%) rotateX(76deg) scale(.72); }
}

/* 바뀔 때마다 뒤에서 한 번 번쩍 */
.hnext::after {
  content: ''; position: absolute; left: 50%; top: 50%;
  width: 2.1em; height: 2.1em; margin: -1.05em 0 0 -1.05em;
  border-radius: 50%; pointer-events: none; opacity: 0;
  background: radial-gradient(circle, var(--neon) 0%, transparent 66%);
}
.hnext:has(.hn-in)::after { animation: hnGlow .3s ease-out; }
@keyframes hnGlow {
  0%   { opacity: .34; transform: scale(.55); }
  100% { opacity: 0;   transform: scale(1.25); }
}

@media (prefers-reduced-motion: reduce) {
  .hn-in, .hn-out { animation: none !important; }
  .hn-out { display: none; }
  .hnext::after { animation: none !important; }
}

/*  ★ QUAKE 의 옛 카드 그림(도는 네모)은 지웠습니다 — 실제 효과와 달랐습니다.
    지금은 위쪽 '실제 효과에 맞춘 손질' 에서 **숫자 칸이 흔들립니다.** */

/* ---------- BLACKHOLE (상자 전용) : 어둠이 빨아들이고 테두리만 빛남 ---------- */
.fx-hole {
  position: absolute; pointer-events: none; z-index: 5;
  width: 30px; height: 30px; margin: -15px 0 0 -15px; border-radius: 50%;
  background: radial-gradient(circle, #000 0%, #000 52%, transparent 72%);
  box-shadow: 0 0 16px var(--g), inset 0 0 10px var(--g);
  animation: fxHole .42s cubic-bezier(.4,0,.7,.5) forwards;
}
@keyframes fxHole {
  0%   { opacity: 0;   transform: scale(calc(1.6 * var(--k))); }
  25%  { opacity: 1; }
  100% { opacity: 0;   transform: scale(.05); }
}

/* ---------- PRISM (상자 전용) : 세 색 링이 어긋나며 퍼짐 ---------- */
.fx-ring.p1 { border-color: #00E5FF; box-shadow: 0 0 12px #00E5FF; }
.fx-ring.p2 { border-color: #FF2FD0; box-shadow: 0 0 12px #FF2FD0; animation-delay: .07s; }
.fx-ring.p3 { border-color: #8CFF00; box-shadow: 0 0 12px #8CFF00; animation-delay: .14s; }

/* 상점 미리보기 */
.fx-prev[data-fx="blackhole"] .fxp-anim {
  background: radial-gradient(circle, #000 0%, #000 52%, transparent 72%);
  box-shadow: 0 0 12px var(--g), inset 0 0 8px var(--g);
  animation: fxpHole 1.4s ease-out infinite;
}
.fx-prev[data-fx="prism"] .fxp-anim {
  border: 2px solid #FF2FD0;
  box-shadow: 0 0 10px #FF2FD0, 0 0 0 4px rgba(0,229,255,.5), 0 0 0 8px rgba(140,255,0,.35);
  animation: fxpRing 1.4s ease-out infinite;
}
/*  (QUAKE 카드 그림은 위에서 '숫자 칸 흔들림' 으로 바뀌었습니다) */

/* ---------- QUAKE : 격자가 실제로 흔들립니다 ----------
   ★ 왜 흔들림이 필요한가
     전에는 flash + ring + 파편이라 PULSE 와 첫인상이 똑같았습니다
     (둘 다 52px 흰 섬광으로 시작 → 그게 시야를 채워 나머지 차이가 묻힘).
     4000 크레딧을 내고 산 사람이 차이를 못 느끼면 다음 걸 안 삽니다.

   ★ 왜 0.22초로 짧은가
     명중은 초당 3~4번 납니다. 길게 흔들면 다음 숫자를 찾는 눈이 흔들려
     기록에 해롭습니다. '한 번 쿵' 하고 바로 멎어야 합니다. */
/*  ★ .pv-grid 도 같이 잡습니다 — 상점 미리보기의 격자입니다.
    안 넣으면 미리보기에서 QUAKE 가 안 흔들려 SHATTER 와 똑같아 보입니다 (실제로 그랬습니다). */
.grid.quake, .pv-grid.quake { animation: gridQuake .22s cubic-bezier(.36,.07,.19,.97); }
@keyframes gridQuake {
  0%   { transform: translate(0, 0); }
  15%  { transform: translate(-3px, 2px); }
  30%  { transform: translate(3px, -2px); }
  45%  { transform: translate(-2px, -2px); }
  60%  { transform: translate(2px, 2px); }
  80%  { transform: translate(-1px, 1px); }
  100% { transform: translate(0, 0); }
}
/* 연출을 끈 사람에게는 흔들지 않습니다 */
@media (prefers-reduced-motion: reduce) { .grid.quake, .pv-grid.quake { animation: none; } }

/* IMPLODE 두 번째 고리 — 살짝 늦게 들어와 '빨려든다'가 확실해집니다 */
.fx-implode.d2 { animation-delay: .08s; animation-duration: .4s; opacity: .7; }
