/* ============================================================
   도깨비 마을 — 건물 터치 반응 (2026-08-28 사람 직접 지시 1차·2차·7차·8차)
   ------------------------------------------------------------
   사람 원문 요지:
     "건물을 터치하면 즉시 화면을 전환하지 않는다 →
      선택 건물 약 2~3% 확대 → 건물별 고유 효과 → 약 0.7~1초 반응 →
      카메라가 건물 쪽으로 접근 → 입장"
     "과도한 튕김, 강한 흔들림, 화려한 RPG 이펙트는 사용하지 않는다"

   ★ 이 파일은 **덧칠만** 한다. 기존 배경·건물·간판·미니미·바텀시트는 하나도 안 건드린다.
     지도 위에 얹는 층 두 개(건물 렌즈 · 효과)가 전부이고, 반응이 끝나면 둘 다 사라진다.

   ★ [건물 렌즈] 가 무엇인가
     이 화면의 건물은 DOM 이 아니라 **배경 원화에 그려져 있다**(index.html .vg-art).
     그래서 [건물만 2.5% 키우기]를 하려면 그 건물이 있는 자리만큼 배경을 한 겹 더
     깔고 그 겹만 키워야 한다 — 그것이 .vg-lens 다. 배율 1 일 때는 밑그림과 화소가
     정확히 같아 보이지 않고, 키우는 동안만 그 건물이 앞으로 나온다.
     네 귀퉁이 이음매는 radial 마스크로 흐려 없앤다.

   ★ 새 숫자를 만들지 않았다
     이징·지속은 이미 이 화면이 쓰던 값 그대로다 —
       cubic-bezier(.22,.9,.3,1) · 0.5s = village.css:652 (.vg-camera 줌)
       카메라 1.22 배 = village.css:656
     새로 정한 것은 [확대율 1.025](사람 지시 "2~3%")와 [반응 820ms](지시 "0.7~1초")뿐이다.

   ★ 저사양 대비 (지시 [구현 시 중요사항])
     · 한 번에 살아 있는 렌즈는 **건물 1 + 오브젝트 2** 가 상한이다(village-react.js 렌즈상한).
     · transform / opacity 만 애니메이션한다 — 레이아웃을 흔드는 속성은 안 쓴다.
     · 효과 입자는 건물마다 5개 이하다.
     · prefers-reduced-motion 이면 렌즈·입자를 아예 만들지 않는다(JS 가 건너뛴다).
   ============================================================ */

/* ── 층 두 개 ────────────────────────────────────────────────
   .vg-lens-layer  : 배경 한 겹(건물·오브젝트 렌즈). 광원(z 4)보다 **아래** 라서
                     밤 조명이 렌즈 위에도 그대로 얹힌다.
   .vg-fx          : 빛·연기·꽃잎 같은 얹는 것. 미니미(z 7)보다 아래다. */
.vg-lens-layer{ position:absolute; inset:0; z-index:3; pointer-events:none; }
.vg-fx        { position:absolute; inset:0; z-index:6; pointer-events:none; }

/* ── 렌즈 한 장 ──────────────────────────────────────────────
   left/top/width/height 는 village-react.js 가 px 로 박는다(지도 실측 기준).
   귀퉁이 이음매는 마스크로 흐린다 — 마스크가 없으면 배율이 커지는 순간
   사각형 테두리가 보인다. */
.vg-lens{
  position:absolute; overflow:hidden; pointer-events:none;
  -webkit-mask-image: radial-gradient(closest-side, #000 58%, rgba(0,0,0,.55) 80%, transparent 100%);
          mask-image: radial-gradient(closest-side, #000 58%, rgba(0,0,0,.55) 80%, transparent 100%);
  opacity:0;
  transition: opacity .22s ease-out;
}
.vg-lens.on{ opacity:1; }

/* 실제로 움직이는 겹. transform-origin 은 JS 가 px 로 준다(건물은 밑변 가운데,
   등롱·그네는 매달린 점). */
.vg-lens-fit{
  position:absolute; left:0; top:0;
  transform: none;
  transition: transform .42s cubic-bezier(.22,.9,.3,1);
  will-change: transform;
}

/* 배경 두 장(낮·밤). 크기·자리는 JS 가 px 로 박는다.
   ★ 투명도 식은 village.css:998~999 의 **그 식 그대로**다 — 여기서 새로 고르지 않는다.
     (낮은 항상 1, 밤이 --dn 0.40 부터 덮는다) */
.vg-lens-art{ position:absolute; left:0; top:0; background-repeat:no-repeat; }
.vg-lens-day  { opacity:1; }
.vg-lens-night{ opacity:clamp(0, calc((var(--dn,0) - 0.40) / 0.2), 1); }

/* 건물 확대 — 사람 지시 "약 102~103%" */
.vg-lens.is-swell .vg-lens-fit{ transform: scale(1.025); }

/* 등롱·그네가 매달린 채 흔들리는 겹 (사주집 홍등 · 놀이터 그네).
   ±1.4° 뿐이다 — 지시 [강한 흔들림 금지]. */
@media (prefers-reduced-motion: no-preference){
  .vg-lens.is-sway  .vg-lens-fit{ animation: vg-rx-sway  1.35s ease-in-out 2; }
  .vg-lens.is-sway2 .vg-lens-fit{ animation: vg-rx-sway  1.35s ease-in-out 2 .18s; }
  .vg-lens.is-float .vg-lens-fit{ animation: vg-rx-float 1.60s ease-in-out 2; }
}
@keyframes vg-rx-sway {
  0%,100%{ transform: rotate(0deg); }
  28%    { transform: rotate(1.4deg); }
  68%    { transform: rotate(-1.1deg); }
}
@keyframes vg-rx-float{
  0%,100%{ transform: translate3d(0,0,0) rotate(0deg); }
  35%    { transform: translate3d(.35%,-.55%,0) rotate(.9deg); }
  70%    { transform: translate3d(-.25%,.2%,0) rotate(-.7deg); }
}

/* ── 효과 부품 ───────────────────────────────────────────────
   전부 --x/--y(지도 %) · --r(지도 폭 %) · --c(색) 로만 자리를 잡는다.
   .vg-light 와 같은 방식이라 지도가 커지고 작아져도 같이 따라간다. */
.vg-rx{
  position:absolute; left:var(--x); top:var(--y);
  transform:translate(-50%,-50%); pointer-events:none;
  will-change:opacity, transform;
}

/* ① 한 번 밝아지는 빛 (지붕 장식 · 등불 · 창 · 현관) */
.vg-rx-glow{
  width:var(--r,8%); aspect-ratio:1; border-radius:50%;
  background:radial-gradient(circle closest-side,
              var(--c,#ffe6b0) 0%,
              color-mix(in srgb, var(--c,#ffe6b0) 50%, transparent) 34%,
              transparent 72%);
  mix-blend-mode:screen; opacity:0;
}
@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  .vg-rx-glow{ background:radial-gradient(circle closest-side, var(--c,#ffe6b0) 0%, transparent 70%); }
}
@media (prefers-reduced-motion: no-preference){
  .vg-rx-glow{ animation: vg-rx-glow var(--dur,900ms) ease-in-out var(--delay,0ms) 1 both; }
}
@keyframes vg-rx-glow{
  0%  { opacity:0;                transform:translate(-50%,-50%) scale(.72); }
  38% { opacity:var(--peak,.85);  transform:translate(-50%,-50%) scale(1); }
  100%{ opacity:0;                transform:translate(-50%,-50%) scale(1.06); }
}

/* ② 작은 별빛 입자 (타로집) — 지시 "3~5개 정도만" */
.vg-rx-spark{
  width:var(--r,1.1%); aspect-ratio:1; border-radius:50%;
  background:radial-gradient(circle, #fff 0%, var(--c,#e6d4ff) 45%, transparent 72%);
  mix-blend-mode:screen; opacity:0;
}
@media (prefers-reduced-motion: no-preference){
  .vg-rx-spark{ animation: vg-rx-spark var(--dur,1100ms) ease-out var(--delay,0ms) 1 both; }
}
@keyframes vg-rx-spark{
  0%  { opacity:0;   transform:translate(-50%,-50%) translate3d(0,0,0)                    scale(.4); }
  25% { opacity:.95; }
  100%{ opacity:0;   transform:translate(-50%,-50%) translate3d(var(--dx,0),var(--dy,-140%),0) scale(1); }
}

/* ③ 가느다란 연기 (도사집 향로) — 지시 "가느다란 연기가 자연스럽게 올라옴" */
.vg-rx-smoke{
  width:var(--r,1.6%); height:calc(var(--r,1.6%) * 3.4);
  border-radius:50%;
  background:linear-gradient(to top,
              rgba(232,246,242,0) 0%,
              rgba(232,246,242,.58) 30%,
              rgba(232,246,242,.30) 70%,
              rgba(232,246,242,0) 100%);
  filter:blur(2px); opacity:0;
}
@media (prefers-reduced-motion: no-preference){
  .vg-rx-smoke{ animation: vg-rx-smoke var(--dur,2200ms) ease-out var(--delay,0ms) 1 both; }
}
@keyframes vg-rx-smoke{
  0%  { opacity:0;   transform:translate(-50%,-50%) translate3d(0,10%,0)   scale(.5,.5); }
  22% { opacity:.88; }
  100%{ opacity:0;   transform:translate(-50%,-50%) translate3d(var(--dx,18%),-190%,0) scale(1.5,1.35); }
}

/* ④ 꽃잎 (사주집) — 지시 "벚꽃잎 2~3장이 지나감"
   ★ 2026-09-06 #165 ③ — 색을 `--c` 로 뺐다. 값이 없으면 옛 벚꽃 분홍 그대로다.
     왜 : 4판 원화(0902)의 사주집 양옆은 벚나무가 아니라 **흰·연보라 꽃무리**다(실측 낮 #FFF9F5 · #E9E6FF ·
     #FFEEDF). 분홍 꽃잎을 그 위에 날리면 원화에 없는 색이 화면에 뜬다 — 2026-09-03 art-director 조건 ①
     [원화에 없는 불] 과 같은 자리다. 색은 village-react.js 의 각 꽃잎이 제 꽃무리에서 재서 준다. */
.vg-rx-petal{
  width:var(--r,1.5%); height:calc(var(--r,1.5%) * .74);
  border-radius: 60% 40% 62% 38% / 58% 62% 38% 42%;
  background:linear-gradient(140deg,
              color-mix(in srgb, var(--c,#f9b6cd) 55%, #fff) 0%,
              var(--c,#f9b6cd) 60%,
              color-mix(in srgb, var(--c,#f9b6cd) 84%, #000) 100%);
  opacity:0;
}
@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  .vg-rx-petal{ background:linear-gradient(140deg, #ffd7e6 0%, #f9b6cd 60%, #efa0bd 100%); }
}
@media (prefers-reduced-motion: no-preference){
  .vg-rx-petal{ animation: vg-rx-petal var(--dur,1800ms) ease-in-out var(--delay,0ms) 1 both; }
}
@keyframes vg-rx-petal{
  0%  { opacity:0;  transform:translate(-50%,-50%) translate3d(0,0,0) rotate(0deg); }
  18% { opacity:.9; }
  85% { opacity:.7; }
  100%{ opacity:0;  transform:translate(-50%,-50%) translate3d(var(--dx,120%),var(--dy,150%),0) rotate(220deg); }
}

/* ⑤ 금색 장식 위로 지나가는 빛 (도사집 지붕) — 지시 "작은 빛이 지나감" */
.vg-rx-sweep{
  width:var(--r,10%); height:calc(var(--r,10%) * .34);
  transform-origin:50% 50%;
  background:linear-gradient(100deg,
              transparent 0%, rgba(255,246,214,0) 34%,
              rgba(255,246,214,.85) 50%,
              rgba(255,246,214,0) 66%, transparent 100%);
  mix-blend-mode:screen; opacity:0; border-radius:40%;
}
@media (prefers-reduced-motion: no-preference){
  .vg-rx-sweep{ animation: vg-rx-sweep var(--dur,850ms) ease-in-out var(--delay,0ms) 1 both; }
}
@keyframes vg-rx-sweep{
  0%  { opacity:0;  transform:translate(-50%,-50%) translate3d(-58%,0,0) rotate(var(--rot,-14deg)); }
  30% { opacity:.9; }
  100%{ opacity:0;  transform:translate(-50%,-50%) translate3d( 58%,0,0) rotate(var(--rot,-14deg)); }
}

/* ── 고른 건물 표시 ──────────────────────────────────────────
   village.css:992 의 .is-picked(시트가 열렸을 때) 와 다른 단계다 —
   이쪽은 [눌린 직후 0.8초] 다. 안쪽 테두리 하나뿐이고 흔들지 않는다. */
.shop[data-door-id].is-reacting{
  animation:none;
  box-shadow:0 0 26px 8px rgba(255,246,214,.30) inset;
}

/* ── 카메라 진입 (8차) ───────────────────────────────────────
   시트에서 [상담받기]를 누른 뒤 화면이 바뀌기 전까지의 0.62초.
   1.22(시트 줌) → 1.52 로 **한 번 더** 다가간다. 지시 [멀미 나는 큰 줌 금지] 때문에
   0.3배 만 더 간다. 중심(--zx/--zy)은 village-shop.js 가 이미 문 쪽으로 잡아 두었다. */
.village-map.vg-entering .vg-camera{
  transition: transform .62s cubic-bezier(.32,.72,.28,1);
  transform: scale(1.52);
}
/* 문 쪽으로 시선 집중 — 가장자리를 살짝 어둡게 좁힌다(빛이 아니라 어둠으로 좁힌다). */
.vg-focus{
  position:absolute; inset:0; z-index:27; pointer-events:none; opacity:0;
  background:radial-gradient(circle at var(--zx,50%) var(--zy,50%),
              transparent 0%, transparent 22%, rgba(10,8,20,.55) 78%, rgba(8,6,16,.78) 100%);
  transition:opacity .5s ease-in;
}
.village-map.vg-entering .vg-focus{ opacity:1; }

/* ── 모션 축소 ───────────────────────────────────────────────
   지시 [prefers-reduced-motion 대응]. 렌즈·입자는 JS 가 아예 안 만들고,
   남는 것(테두리 표시·카메라)도 즉시 끝난다. */
@media (prefers-reduced-motion: reduce){
  .vg-lens, .vg-lens-fit,
  .village-map.vg-entering .vg-camera,
  .vg-focus{ transition-duration:.01ms; animation:none; }
  .vg-rx{ display:none; }
}

/* ── 하루가 가볍게 반응 (놀이터) ─────────────────────────────
   지시 "하루 프로필 또는 하루가 가볍게 반응". 간판 배지를 두 번 톡 띄운다 —
   상담소처럼 신비하게 만들지 않는다(지시). */
@media (prefers-reduced-motion: no-preference){
  .vg-sign.is-hi .vg-sign-face{ animation: vg-rx-haru .46s ease-in-out 2; }
}
@keyframes vg-rx-haru{
  0%,100%{ transform:translate3d(0,0,0)     scale(1); }
  45%    { transform:translate3d(0,-7%,0)   scale(1.05); }
}

/* ════════════════════════════════════════════════════════════
   2026-09-03 사람 지시 §4·§5·§26 (전체계획 #162)
   ── §5 진입 반응 0.82초 — 탭 순간 village-react.js 가 .vg-approach 를 붙인다 ──
   카메라가 그 집 **문**(--zx/--zy) 쪽으로 1.06 만 다가간다. 지시 [화면 전체를 크게 확대하지 않는다 ·
   3D 카메라처럼 움직이지 않는다] — 시트 줌 1.22 · 입장카메라 1.52 보다 훨씬 작다.
   시간 .7s 는 반응 820ms 안에서 끝나는 값이고 곡선은 .vg-entering 과 같은 것이다(새 곡선 없음).
   ★ 떼는 자리는 진입취소()(pageshow) 뿐이다 — 820ms 에 떼면 전환 직전에 카메라가 되튄다.
   ════════════════════════════════════════════════════════════ */
.village-map.vg-approach .vg-camera{
  transition: transform .7s cubic-bezier(.32,.72,.28,1);
  transform: scale(1.06);
}
/* 시선 집중 — .vg-focus(입장카메라 8차의 그 어둠)를 절반만 쓴다. 0.82초짜리 반응에 .78 어둠은 과하다. */
.village-map.vg-approach .vg-focus{ opacity:.55; transition-duration:.4s; }
/* 입장카메라(우체통 시트 → [가기])가 뒤따르면 그쪽 값이 이긴다 — 같은 속성이라 순서로 정한다. */
.village-map.vg-entering .vg-camera{ transform: scale(1.52); }
.village-map.vg-entering .vg-focus{ opacity:1; }

/* 창문빛↑ — index.html 의 .vg-light.is-window 중 그 건물 상자 안의 것. 밤 최대 .55 에서 .92 로,
   낮(--dn 0)에도 잠깐 켜진다(손님을 알아챈 창). 새 광원을 만들지 않는다. */
.village-map .vg-light.is-window.is-lit{
  opacity:.92;
  transition:opacity .45s ease-out;
}

/* ── §4 floating label — 상시 0 · 터치·접근·새 상태 때만 떴다 사라진다 ──
   결은 하단 하루 알약(.vg-haru · village-ui.css)과 같다 — 크림 반투명 · 얇은 --vg-line 테두리 ·
   --vg-sh-1 그림자 · 잉크 글자. 새 색·새 radius 를 만들지 않았다(토큰은 .village-body 에 있다).
   자리는 --x/--y(지도 %) — 그 상점 히트박스의 위 가운데. 층은 카메라 안(z 9 · 미니미 7 위). */
.vg-label-layer{ position:absolute; inset:0; z-index:9; pointer-events:none; }
.vg-label{
  position:absolute; left:var(--x,50%); top:var(--y,50%);
  transform:translate(-50%,-100%) translateY(6px); opacity:0;
  display:flex; flex-direction:column; align-items:center; gap:1px;
  padding:5px 12px 6px; border-radius:var(--vg-r-pill,999px);
  border:1px solid var(--vg-line,#dcc4a1);
  background:rgba(250,244,232,.88);
  -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
  box-shadow:var(--vg-sh-1,0 1px 4px rgba(40,26,6,.16));
  color:var(--vg-ink,#4a3312);
  font:600 .74rem/1.2 var(--font-ui,"Malgun Gothic",sans-serif);
  white-space:nowrap; pointer-events:none;
  transition:opacity .22s ease-out, transform .22s ease-out;
  will-change:opacity, transform;
}
.vg-label b{ font-weight:700; }
.vg-label span{ font-weight:500; font-size:.68rem; color:var(--vg-ink-sub,#6b512a); }
.vg-label.on{ opacity:1; transform:translate(-50%,-100%) translateY(0); }
.vg-label.off{ opacity:0; transform:translate(-50%,-100%) translateY(-4px); transition-duration:.26s; }

@media (prefers-reduced-motion: reduce){
  .village-map.vg-approach .vg-camera, .vg-label, .village-map .vg-light.is-window.is-lit{ transition-duration:.01ms; }
}

/* ── 2026-09-03 #167 §25 운명의 편지 — 우체통 위 작은 봉투 (village-letter.js) ──────────
   크기 20x14px(작은 봉투 · 히트박스는 그대로 .shop-worry 가 받는다 · pointer-events 0).
   재질 = 크림 종이(--vg-cream) + 1px 선(--vg-line) + 골드 봉인 점(--vg-gold-line · §17 [선·포인트 골드]).
   빨간 것 0 · 글자 0 · 느낌표 0 (§25). data-state="있음" 일 때만 보인다.
   움직임은 투명도 .78↔1 · 3.2s 숨쉬기 하나뿐(§16 [미세한 움직임] · 3D 0) — 모션 축소면 pulse 를 안 붙인다.
   그림자 .18 = --vg-sh-1 계열(village-ui.css:33) — 수채 위에서 스티커처럼 뜨지 않게(art-director 2026-09-03). */
.vg-letter{
  position:absolute; left:50%; top:50%; width:20px; height:14px;
  transform:translate(-50%,-100%);
  opacity:0; pointer-events:none; z-index:6;   /* 히트박스 5 위 · 미니미 층 7 아래 — 우체통 앞에 선 미니미가 봉투를 잠깐 가리는 쪽이 자연스럽다 */
  /* 밤은 미니미(village.css .village-npc-layer img)와 같은 계수로 같이 어두워진다 — 밤 원화 위에 낮 밝기 조각이 남지 않게 */
  filter:drop-shadow(0 1px 2px rgba(40,26,6,.18)) brightness(calc(1 - 0.30 * var(--dn,0)));
  transition:opacity .4s ease-out, filter var(--dn-dur, 2.4s) ease-in-out;
}
.vg-letter svg{ display:block; width:20px; height:14px; }
.vg-letter-paper{ fill:var(--vg-cream,#f8ecd9); stroke:var(--vg-line,#dcc4a1); stroke-width:1; }
.vg-letter-flap{ fill:none; stroke:var(--vg-line,#dcc4a1); stroke-width:1; stroke-linejoin:round; }
.vg-letter-seal{ fill:var(--vg-gold-line,#D9A441); }
.vg-letter.on{ opacity:1; }
@keyframes vg-letter-breathe{ 0%,100%{ opacity:.78; } 50%{ opacity:1; } }
.vg-letter.on.pulse{ animation:vg-letter-breathe 3.2s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce){ .vg-letter{ animation:none !important; transition:none; } }
