  /* ── level map ── */
  .mapBar, #hud { display: flex; align-items: center; gap: 7px; width: 100%; max-width: 460px; margin: 0 auto; }
  .mapBar { position: sticky; top: 0; z-index: 5; padding: calc(env(safe-area-inset-top,0px) + 12px) 14px 12px; background: linear-gradient(var(--bg) 70%, transparent); }
  .mapTitle, .hudLevel { flex: 1; min-width: 0; font-weight: 700; font-size: 16px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .mapStars { color: var(--s2); font-size: 14px; font-weight: 700; white-space: nowrap; }
  #wardrobe .mapStars { cursor: pointer; -webkit-tap-highlight-color: transparent; }
  #wardrobe .mapStars:active { opacity: 0.7; }
  #map {
    position: fixed; inset: 0;
    background: url('../../assets/map/bg.webp') center / cover no-repeat;
    overflow: hidden;
    isolation: isolate;
  }
  /* full-screen background VIDEO: sits above the screen's webp background (kept as poster/fallback) but
     below all content (negative z-index inside the positioned screen). Muted+loop+playsinline → autoplays. */
  .bgVid { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: -1; pointer-events: none; }
  /* region-crossing backdrop swap (setStageVideo): hide the video INSTANTLY while the new clip decodes
     (the #game bg_stage.webp still shows through), then fade the first frame in — no stale-frame flash. */
  #game .bgVid { transition: opacity .28s ease; }
  #game .bgVid.bgSwap { opacity: 0; transition: none; }
  html[data-motion="reduced"] { #game .bgVid { transition: none; } }
  .levelTopHud {
    position: absolute; z-index: 8; left: 0; right: 0; top: calc(env(safe-area-inset-top,0px) + 10px);
    display: flex; justify-content: space-between; align-items: flex-start;
    padding: 0 14px; pointer-events: none;
  }
  .levelBackBtn {
    width: 60px !important; height: 60px !important; min-width: 0 !important;
    padding: 0 !important; border: none !important; box-shadow: none !important;
    background: url('../../assets/badge/back_round.png') center / contain no-repeat transparent !important;
    font-size: 0 !important; color: transparent !important; pointer-events: auto;
  }
  .levelBackBtn .ico { display: none; }
  #map .levelTitle {
    flex: 1; min-width: 0; max-width: 168px; margin: 0 8px; align-self: center;
    height: 48px; line-height: 48px; text-align: center;
    background: url('../../assets/badge/row_bar.png') center / 100% 100% no-repeat;
    color: #fff6e2; font-weight: 900; font-size: 20px;
    text-shadow: 0 2px 0 #4a3214, -1px 0 0 #4a3214, 1px 0 0 #4a3214, 0 2px 5px rgba(0,0,0,.5);
    overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  }
  #map .levelTopHud { padding-right: 14px; }   /* gear now sits BELOW the HUD → HUD flush to right edge */
  /* the gear (#titleSettings) drops by safe-area-inset-top, so the scroll content must too — else on a
     notched phone the gear shifts down onto the first region bar while the bar (fixed 130px pad) stays put */
  #map .mapScroll { padding-top: calc(env(safe-area-inset-top, 0px) + 130px);
                    padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 132px);  /* clear the shared bottom nav (taller) */
    /* top fade so section bars dissolve into the jungle as they scroll UNDER the floating HUD (no hard
       clip under the title/wallet/gear); a softer bottom fade mirrors it above the bottom nav. */
    -webkit-mask-image: linear-gradient(to bottom,
      transparent 0,
      transparent calc(env(safe-area-inset-top,0px) + 70px),
      #000 calc(env(safe-area-inset-top,0px) + 120px),
      #000 calc(100% - 96px),
      transparent calc(100% - 46px));
    mask-image: linear-gradient(to bottom,
      transparent 0,
      transparent calc(env(safe-area-inset-top,0px) + 70px),
      #000 calc(env(safe-area-inset-top,0px) + 120px),
      #000 calc(100% - 96px),
      transparent calc(100% - 46px)); }
  /* combined star+heart HUD bar (atlas hud_star: star row on top, heart row below) */
  .levelHudCombo {
    position: relative; pointer-events: auto;
    width: clamp(102px, 30vw, 138px); aspect-ratio: 243 / 144;
    background: url('../../assets/map/hud_star.png') center / 100% 100% no-repeat;
  }
  .levelHudCombo .hudStarVal, .levelHudCombo .hudLifeVal {
    position: absolute; left: 60%; transform: translate(-50%, -50%);
    color: #fff8ec; font-weight: 900; line-height: 1;
    font-size: clamp(13px, 3.7vw, 17px);
    text-shadow: 0 2px 0 #4a2c10, 1px 0 0 #4a2c10, -1px 0 0 #4a2c10, 0 -1px 0 #4a2c10, 0 2px 4px rgba(0,0,0,.5);
  }
  .levelHudCombo .hudStarVal { top: 28%; }
  .levelHudCombo .hudLifeVal { top: 71%; }
  /* game HUD reuses #hudCoins/#hudLives whose old ID rules force a tiny 13px → restore (and match) the
     combo value size; ID-level selector needed to beat #hudLives { font-size:13px }. */
  .levelHudCombo #hudCoins, .levelHudCombo #hudLives {
    color: #fff8ec; font-weight: 900; font-size: clamp(15px, 4.2vw, 18px);
  }
  /* jewel "+" buttons on the star / heart rows (right-end slots) */
  .levelHudCombo .hudPlus {
    position: absolute; right: 1px; width: clamp(22px, 5.9vw, 27px); height: clamp(22px, 5.9vw, 27px);
    min-width: 0; padding: 0; border-radius: 8px; border: 2px solid #ffe8a6;
    background:
      radial-gradient(circle at 33% 27%, #fff9c8 0 13%, #78f070 38%, #2aad48 67%, #126b38 100%);
    color: #fff; font-weight: 900; font-size: 0; line-height: 1;
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 2px 4px rgba(20,40,10,.5), inset 0 1px 0 rgba(255,255,255,.55);
    cursor: pointer; z-index: 3; pointer-events: auto;
    transform: translateY(-50%) rotate(45deg);
  }
  .levelHudCombo .hudPlus::after {
    content: "+";
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    transform: rotate(-45deg) translateY(-1px);
    color: #fff;
    font: 900 clamp(15px, 4.4vw, 19px)/1 system-ui, sans-serif;
    text-shadow: 0 2px 1px rgba(0,0,0,.45);
  }
  .levelHudCombo .hudPlusStar { top: 28%; }
  .levelHudCombo .hudPlusLife { top: 71%; }
  .levelHudCombo.assetLearnStar,
  .levelHudCombo.assetLearnHeart,
  .galCount.assetLearnStar,
  #achievements #achCount.assetLearnStar,
  #achievements #achCount.assetLearnHeart,
  .counterWalletItem.assetLearnStar,
  .counterWalletItem.assetLearnHeart {
    animation: assetHudGlow 1.35s ease-out both;
    filter: drop-shadow(0 0 13px rgba(255,222,108,.58)) drop-shadow(0 4px 7px rgba(0,0,0,.36));
  }
  .levelHudCombo.assetLearnStar .hudStarVal,
  .levelHudCombo.assetLearnHeart .hudLifeVal {
    animation: assetValuePop 1.15s ease-out both;
  }
  /* screen wallets (.counterWalletItem: gallery #galStars / badges #achStars·#achLives) center their
     number by LAYOUT (inline-flex), not by a base translate(-50%,-50%) like the in-game HUD value. So
     they need a SCALE-ONLY pop — assetValuePop's constant translate(-50%,-50%) would shove the number
     out of the chip, then snap it back when the class is removed (the "별빛 칩 숫자가 바깥으로 벗어났다
     원위치" bug, 2026-06-23). */
  .counterWalletItem.assetLearnStar .screenWalletVal,
  .counterWalletItem.assetLearnHeart .screenWalletVal {
    animation: screenValPop 1.15s ease-out both;
  }
  .levelHudCombo.assetLanded,
  #title .coinsBar.assetLanded,
  #title .livesBar.assetLanded,
  .galCount.assetLanded,
  #achievements #achCount.assetLanded,
  .counterWalletItem.assetLanded {
    animation: assetHudLand .42s ease-out both;
  }
  /* heart SPEND cue (e.g. a hint): the count pulses + flushes ruby, and a "−1" floats up off the heart
     row. Makes the cost legible at the HUD instead of a silent number swap. Keeps the base
     translate(-50%,-50%) so the value doesn't jump. */
  .gameHudCombo #hudLives.spendHeart { animation: heartSpendPulse .7s ease-out; will-change: transform, filter; }
  @keyframes heartSpendPulse {
    0%   { transform: translate(-50%,-50%) scale(1);    color: #fff8ec; filter: none; }
    22%  { transform: translate(-50%,-50%) scale(1.55); color: #ff5a4d; filter: drop-shadow(0 0 8px rgba(255,70,60,.95)); }
    55%  { transform: translate(-50%,-50%) scale(.88);  color: #ff5a4d; filter: drop-shadow(0 0 4px rgba(255,70,60,.6)); }
    100% { transform: translate(-50%,-50%) scale(1);    color: #fff8ec; filter: none; }
  }
  .gameHudCombo .heartSpendFly {
    position: absolute; left: 58%; top: 64%; transform: translate(-50%, -50%);
    z-index: 30; pointer-events: none; white-space: nowrap;
    color: #ff4d44; font-weight: 950; font-size: clamp(20px, 6vw, 28px); line-height: 1;
    text-shadow: 0 2px 0 #3a0d09, 1px 0 0 #3a0d09, -1px 0 0 #3a0d09, 0 -1px 0 #3a0d09, 0 3px 7px rgba(0,0,0,.55);
    animation: heartSpendFly 1.15s cubic-bezier(.2,.7,.3,1) both;
  }
  @keyframes heartSpendFly {
    0%   { opacity: 0; transform: translate(-50%, -50%) scale(.6); }
    18%  { opacity: 1; transform: translate(-50%, -90%) scale(1.15); }
    55%  { opacity: 1; transform: translate(-50%, -150%) scale(1); }
    100% { opacity: 0; transform: translate(-50%, -260%) scale(.9); }
  }
  html[data-motion="reduced"] {
    .gameHudCombo #hudLives.spendHeart { animation: none; }
    .gameHudCombo .heartSpendFly { animation: heartSpendFlyRM 1s linear both; }
  }
  @keyframes heartSpendFlyRM { 0% { opacity: 0; } 20% { opacity: 1; } 100% { opacity: 0; } }
  .assetHudLabel {
    position: fixed; z-index: 220; pointer-events: none;
    transform: translate(-50%, 0);
    white-space: nowrap;   /* "별빛 +n" is one line — never wrap (was overflowing to 2 lines near the screen edge) */
    padding: 7px 12px 8px;
    border: 2px solid rgba(255,229,152,.92);
    border-radius: 16px;
    background: linear-gradient(180deg, rgba(100,70,32,.96), rgba(58,36,18,.96));
    color: #fff5dc; font-weight: 900; font-size: 14px; line-height: 1;
    text-shadow: 0 2px 0 #442810, 0 2px 6px rgba(0,0,0,.42);
    box-shadow: 0 8px 18px rgba(15,20,8,.28), inset 0 1px 0 rgba(255,255,255,.22);
    animation: assetHudLabelIn 1.35s ease-out both;
  }
  .assetHudLabel.heart { border-color: rgba(255,166,178,.92); }
  .assetFlyStar {
    position: fixed; z-index: 221; pointer-events: none;
    width: 30px; height: 30px; margin: 0;
    filter: drop-shadow(0 0 8px rgba(255,228,102,.78)) drop-shadow(0 4px 6px rgba(0,0,0,.35));
  }
  /* ③ settle sparkles — the arriving star dissolves into a few warm dots at the counter (assetHud.js absorbSparkle) */
  .assetSpark {
    position: fixed; z-index: 222; pointer-events: none;
    width: 5px; height: 5px; border-radius: 50%;
    background: currentColor; box-shadow: 0 0 6px 1px currentColor;
  }
  @keyframes assetHudGlow {
    0% { filter: drop-shadow(0 0 0 rgba(255,222,108,0)) drop-shadow(0 4px 6px rgba(0,0,0,.26)); }
    28% { filter: drop-shadow(0 0 18px rgba(255,222,108,.9)) drop-shadow(0 4px 8px rgba(0,0,0,.38)); }
    100% { filter: drop-shadow(0 0 0 rgba(255,222,108,0)) drop-shadow(0 4px 6px rgba(0,0,0,.26)); }
  }
  @keyframes assetHudLand {
    0% { transform: scale(1); filter: drop-shadow(0 0 0 rgba(255,230,116,0)); }
    48% { transform: scale(1.06); filter: drop-shadow(0 0 18px rgba(255,230,116,.88)); }
    100% { transform: scale(1); filter: drop-shadow(0 0 0 rgba(255,230,116,0)); }
  }
  @keyframes assetValuePop {
    0%, 100% { transform: translate(-50%, -50%) scale(1); color: #fff8ec; }
    34% { transform: translate(-50%, -50%) scale(1.18); color: #fff0a8; }
  }
  /* layout-centered screen-wallet numbers: scale only (no translate → no position jump). */
  @keyframes screenValPop {
    0%, 100% { transform: scale(1); color: #fff8ec; }
    34% { transform: scale(1.18); color: #fff0a8; }
  }
  @keyframes assetHudLabelIn {
    0% { opacity: 0; transform: translate(-50%, 8px) scale(.92); }
    16%, 70% { opacity: 1; transform: translate(-50%, 0) scale(1); }
    100% { opacity: 0; transform: translate(-50%, -5px) scale(.98); }
  }
  html[data-motion="reduced"] {
    .levelHudCombo.assetLearnStar, .levelHudCombo.assetLearnHeart,
    #title .coinsBar.assetLearnStar, #title .livesBar.assetLearnHeart,
    .galCount.assetLearnStar,
    #achievements #achCount.assetLearnStar, #achievements #achCount.assetLearnHeart,
    .counterWalletItem.assetLearnStar, .counterWalletItem.assetLearnHeart,
    .levelHudCombo.assetLanded, #title .coinsBar.assetLanded, #title .livesBar.assetLanded,
    .galCount.assetLanded, #achievements #achCount.assetLanded, .counterWalletItem.assetLanded,
    .levelHudCombo.assetLearnStar .hudStarVal, .levelHudCombo.assetLearnHeart .hudLifeVal,
    #title .coinsBar.assetLearnStar #titleCoins, #title .livesBar.assetLearnHeart #titleLives,
    .counterWalletItem.assetLearnStar .screenWalletVal, .counterWalletItem.assetLearnHeart .screenWalletVal,
    .assetHudLabel { animation-duration: .01ms !important; animation-iteration-count: 1 !important; }
  }
  /* low-life regen countdown — small caption under the combo HUD, shown (via JS) only at ≤1 life */
  /* heart-regen countdown: tucked UNDER the heart value INSIDE the plaque (was floating in harsh red below
     the whole box). Warm amber + the HUD's dark outline so it reads as part of the heart, not error text. */
  .gameHudCombo .hudLifeTimer {
    display: none; position: absolute; top: 84%; left: 60%; transform: translateX(-50%);
    font-size: 8.5px; font-weight: 900; line-height: 1; white-space: nowrap; pointer-events: none;
    color: #ffdca6; text-shadow: 0 1px 0 #4a2c10, 1px 0 0 #4a2c10, -1px 0 0 #4a2c10, 0 2px 3px rgba(0,0,0,.55);
  }
  .mapScroll { flex: 1; min-height: 0; overflow-y: auto; width: 100%; max-width: 460px; margin: 0 auto; padding: 130px 12px calc(64px + env(safe-area-inset-bottom,0px)); display: flex; flex-direction: column; gap: 0; position: relative; z-index: 1; }
  /* 오늘의 복습 — Sha PULL entry: sits OUTSIDE .mapScroll so it's always visible (never scrolled away),
     a small idle Sha with a gentle glow dot signalling "there's something". Tap → choosable modal. */
  /* sits ABOVE the body-level hub nav (.titleNav: fixed, bottom:0, z-index:6) — same z + later DOM order
     meant the nav painted over Sha and hid it. Clear the nav band (~100px) and out-rank it (z:7). */
  .reviewSha {
    position: fixed; left: 10px; bottom: calc(100px + env(safe-area-inset-bottom,0px)); z-index: 7;
    width: 84px; height: 84px; padding: 0; cursor: pointer;
    /* kill ALL default/ui.css button chrome (border, raised-tile box-shadow, native appearance, focus ring)
       so only the Sha image shows — the leftover box-shadow read as a transparent square frame. */
    border: none; background: none; box-shadow: none !important; outline: none;
    -webkit-appearance: none; appearance: none; -webkit-tap-highlight-color: transparent;
    filter: drop-shadow(0 4px 10px rgba(0,0,0,.45));
    animation: reviewShaBob 3.6s ease-in-out infinite;
  }
  .reviewSha:focus, .reviewSha:focus-visible { outline: none; box-shadow: none !important; }
  .reviewSha img { width: 100%; height: 100%; object-fit: contain; -webkit-user-drag: none; }
  .reviewSha::after {
    content: ""; position: absolute; right: 6px; top: 4px; width: 12px; height: 12px; border-radius: 50%;
    background: radial-gradient(circle, #fff3c0 0%, #ffd24d 58%, transparent 72%);
    animation: reviewShaGlow 2s ease-in-out infinite;
  }
  .reviewSha:active { transform: translateY(1px); filter: drop-shadow(0 2px 6px rgba(0,0,0,.45)) brightness(.97); }
  @keyframes reviewShaBob { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
  @keyframes reviewShaGlow { 0%,100% { opacity: .5; transform: scale(.9); } 50% { opacity: 1; transform: scale(1.15); } }
  html[data-motion="reduced"] .reviewSha { animation: none; }
  html[data-motion="reduced"] .reviewSha::after { animation: none; }

  /* review modal: user-initiated (pull), warm card with Sha peeking over the top. Reuses the depth kit. */
  .reviewModal {
    position: fixed; inset: 0; z-index: 60; display: none; align-items: center; justify-content: center;
    background: radial-gradient(circle at 50% 42%, rgba(255,238,150,.16), transparent 54%), rgba(10,15,8,.82);
  }
  .reviewModal .rmCard {
    position: relative; width: min(320px, 86vw); padding: 60px 24px 22px; border-radius: 20px;
    background: var(--grad-card); border: 1px solid var(--line); box-shadow: var(--lift-lg), var(--sheen);
    text-align: center;
  }
  .reviewModal .rmSha {
    position: absolute; left: 50%; top: -46px; transform: translateX(-50%); width: 92px; height: 92px;
    object-fit: contain; filter: drop-shadow(0 6px 14px rgba(0,0,0,.45)); -webkit-user-drag: none;
  }
  .reviewModal .rmTitle { font-size: 17px; font-weight: 800; color: #3a2a16; margin-bottom: 6px; }
  .reviewModal .rmBody { font-size: 14px; color: #6b5a3c; margin-bottom: 4px; }
  .reviewModal .rmPayoff { font-size: 13px; font-weight: 700; color: #b07a1e; margin-bottom: 12px; }
  /* stageChoice: elite difficulty rule under the buttons — amethyst (matches elite grammar), informs the
     choice BEFORE entering (소거 off → solved pieces stay). Hidden when empty (non-elite reuse of the modal). */
  #stageChoice .scEliteHint {
    font-size: 12px; font-weight: 700; color: #6b4a9e; line-height: 1.25; margin-top: 10px;
    background: rgba(154,85,224,.1); border: 1px solid rgba(154,85,224,.28); border-radius: 8px; padding: 5px 10px;
  }
  #stageChoice .scEliteHint:empty { display: none; }
  /* opt-in mastery slot — growth frame: earned chips bright + ✓, not-yet chips calmly dimmed (NO deficit
     box). Detail-only (modal), never on the map/list (§5.4). */
  .reviewModal .rmMastery { margin: 10px 0 16px; padding-top: 12px; border-top: 1px solid rgba(138,115,80,.22); }   /* divider: goal (above) vs already-earned (below) read as two blocks */
  .reviewModal .rmMasteryTitle { font-size: 11px; font-weight: 800; letter-spacing: .3px; color: #8a7350; margin-bottom: 6px; }
  .reviewModal .rmChips { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; }
  .reviewModal .rmChip {
    font-size: 12px; font-weight: 700; padding: 3px 10px; border-radius: 999px; line-height: 1.4;
    background: rgba(120,100,60,.12); color: #6b5a3c; opacity: .42;   /* not-yet = calm, faded — "더 빛날 여지" */
  }
  .reviewModal .rmChip.on {   /* earned = warm gold, full */
    opacity: 1; color: #6b4e16;
    background: linear-gradient(180deg, rgba(255,224,138,.9), rgba(240,180,60,.8));
    box-shadow: inset 0 1px 0 rgba(255,247,220,.6), 0 1px 2px rgba(60,40,10,.25);
  }
  .reviewModal .rmBtns { display: flex; gap: 10px; justify-content: center; }
  .reviewModal .rmBtns button { flex: 1; max-width: 140px; }
  /* region = top of the hierarchy → a full-bleed carved STONE LINTEL (architectural band), visually
     distinct from the inset chapter plaques/panels. Gold-engraved name (larger than chapter names) +
     gold/dark rule edges + a downward cast shadow so the banner "owns" the section below it. */
  .adventureRegion {
    position: relative;
    margin: clamp(26px, 4vh, 40px) -12px 6px; padding: 13px 22px;   /* -12px = bleed past mapScroll's side padding → edge-to-edge */
    border: none; border-radius: 4px;
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    color: #ffe9b8; font-weight: 900; font-size: clamp(16px, 5vw, 23px); letter-spacing: .03em;
    background: linear-gradient(180deg, rgba(46,58,48,.95), rgba(20,30,26,.97));
    box-shadow:
      inset 0 2px 0 rgba(255,228,150,.5),      /* gold top rule (carved catch-light) */
      inset 0 -2px 0 rgba(120,80,20,.55),      /* dark bottom rule */
      inset 0 0 0 1px rgba(0,0,0,.3),
      0 16px 24px -10px rgba(5,14,12,.55);     /* downward cast → owns the section below */
  }
  .adventureRegion > :first-child {            /* region NAME — engraved gold, larger → reads as top-level */
    flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
    color: var(--gold-pale, #fff0a5);
    text-shadow: 0 2px 0 var(--gold-deep, #7a4a08), -1px 0 0 #4a2c10, 1px 0 0 #4a2c10, 0 3px 6px rgba(0,0,0,.5);
  }
  .adventureRegion b { color: #e9d9aa; font-size: 12px; }
  /* region completion = discrete chapter pips (NOT a star ratio) → distinct from the ★-gate bar */
  .adventureRegion .regionPips { display: inline-flex; align-items: center; gap: 5px; }
  .regionPips .rpip { width: 9px; height: 9px; border-radius: 50%;
    background: rgba(255,244,214,.16); box-shadow: inset 0 0 0 1.5px rgba(255,231,163,.4); }
  .regionPips .rpip.done {
    background: radial-gradient(circle at 38% 32%, #fff3c8, #ffce5e 55%, #e0a83a);
    box-shadow: inset 0 1px 0 rgba(255,255,255,.5), 0 0 6px rgba(255,205,80,.5); }
  .adventureRegion.active { box-shadow: inset 0 2px 0 rgba(255,232,160,.62), inset 0 -2px 0 rgba(120,80,20,.55), inset 0 0 0 1px rgba(0,0,0,.3), 0 0 20px rgba(255,205,80,.2), 0 16px 24px -10px rgba(5,14,12,.55); }
  .adventureRegion.complete { box-shadow: inset 0 2px 0 rgba(255,238,175,.78), inset 0 -2px 0 rgba(140,95,25,.6), inset 0 0 0 1px rgba(0,0,0,.3), 0 0 26px rgba(255,205,80,.34), 0 16px 24px -10px rgba(5,14,12,.55); }
  /* ── ★ soft-gate: sealed region lintel (cold stone gate) + "봉인 해제" 연출 ── */
  .adventureRegion.sealed { color: #cdd6e0;
    background: linear-gradient(180deg, rgba(40,46,58,.95), rgba(16,20,30,.97));
    box-shadow: inset 0 2px 0 rgba(180,195,215,.4), inset 0 -2px 0 rgba(28,36,52,.6), inset 0 0 0 1px rgba(0,0,0,.32), 0 16px 24px -10px rgba(5,10,18,.5); }
  .adventureRegion.sealed > :first-child { color: #dbe4ef; text-shadow: 0 2px 0 #2a3340, -1px 0 0 #1c2430, 1px 0 0 #1c2430, 0 3px 6px rgba(0,0,0,.5); }
  .regionSeal { display: flex; align-items: center; gap: 8px; }
  .regionSeal .sealCount { display: inline-flex; align-items: center; gap: 1px; font-weight: 900; font-size: 12px; color: #ffe7a3; white-space: nowrap; }
  .regionSeal .sealLock { width: 13px; height: 13px; opacity: .85; margin-right: 3px; }
  .regionSeal .sealCount i { color: rgba(255,231,163,.55); font-style: normal; }
  .regionSeal .sealStar { color: #ffce3a; margin-left: 2px; }
  .regionSeal .sealBar { width: 60px; height: 6px; border-radius: 999px; background: rgba(0,0,0,.35); overflow: hidden; box-shadow: inset 0 1px 2px rgba(0,0,0,.4); }
  .regionSeal .sealBar i { display: block; height: 100%; border-radius: 999px; background: linear-gradient(90deg, #ffce3a, #ffe7a3); transition: width .4s ease-out; }
  .adventureRegion.unsealing { position: relative; overflow: hidden; animation: sealBreak 1.5s cubic-bezier(.2,.7,.3,1); }
  .adventureRegion.unsealing::after { content: ""; position: absolute; inset: 0; pointer-events: none;
    background: linear-gradient(105deg, transparent 30%, rgba(255,236,170,.85) 50%, transparent 70%); transform: translateX(-120%); animation: sealSweep 1.5s ease-out; }
  @keyframes sealBreak { 0% { transform: scale(1); } 24% { transform: scale(1.05); filter: brightness(1.5); } 60% { transform: scale(.99); } 100% { transform: scale(1); filter: brightness(1); } }
  @keyframes sealSweep { 0% { transform: translateX(-120%); } 60% { transform: translateX(120%); opacity: 1; } 100% { transform: translateX(120%); opacity: 0; } }
  .adventureRegion.unsealing .regionSeal { animation: sealShatter .7s ease-out forwards; }
  @keyframes sealShatter { 0% { opacity: 1; transform: scale(1); } 100% { opacity: 0; transform: scale(1.3) rotate(6deg); } }
  html[data-motion="reduced"] { .adventureRegion.unsealing, .adventureRegion.unsealing::after, .adventureRegion.unsealing .regionSeal { animation: none; } .adventureRegion.unsealing::after { display: none; } }
  /* ── progressive reveal: folded chapter = carved STONE PLAQUE (row_bar) ──
     Same temple-stone material as the chapter panel/title — NOT a flat list bar. Reads as a "collapsed
     chapter" and stays clearly distinct from the slim green .adventureRegion bars (위계 복원).
     NO lock icon: a folded chapter is "아직 안 펼친 미개봉"(보류), not "막힘"(거부). Tap → expand (preview). */
  .chapterPlate {
    position: relative; display: flex; align-items: center; gap: 10px;
    margin: clamp(12px, 2.4vh, 20px) 4px; padding: 14px 30px;
    min-height: clamp(62px, 17vw, 80px); box-sizing: border-box;
    border: none; border-radius: 0; box-shadow: none; min-width: 0; width: auto; cursor: pointer; -webkit-tap-highlight-color: transparent;
    background: url('../../assets/badge/row_bar.png') center / 100% 100% no-repeat;
    color: #fff3d6; font-weight: 900; font-size: clamp(14px, 4vw, 17px); text-align: left;
    filter: drop-shadow(0 6px 14px rgba(5,18,18,.34));   /* depth follows the stone PNG alpha, NOT a rect box-shadow */
  }
  .chapterPlate:hover { box-shadow: none; }
  .chapterPlate:active { transform: translateY(1px); box-shadow: none; filter: drop-shadow(0 3px 8px rgba(5,18,18,.3)) brightness(.95); }
  .chapterPlate .cpChevron {   /* ▷ closed (points right) */
    flex: none; width: 0; height: 0; opacity: .85;
    border-left: 9px solid currentColor; border-top: 6px solid transparent; border-bottom: 6px solid transparent;
    filter: drop-shadow(0 1px 1px rgba(40,24,8,.6));
  }
  .chapterPlate .cpName {
    flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
    text-shadow: 0 2px 0 #4a3214, -1px 0 0 #4a3214, 1px 0 0 #4a3214, 0 2px 5px rgba(0,0,0,.45);
  }
  .chapterPlate .cpProg { flex: none; display: inline-flex; align-items: center; gap: 3px;
    color: #ffe7a8; font-size: clamp(12px, 3.2vw, 14px); white-space: nowrap;
    text-shadow: 0 1px 2px rgba(30,14,4,.85); }
  .chapterPlate .cpProg b { color: #ffce3a; }
  /* upcoming (미개봉) — cooler/dimmer carved stone */
  .chapterPlate.upcoming { filter: drop-shadow(0 6px 14px rgba(5,18,18,.34)) saturate(.82) brightness(.9); }
  /* Elite-conquered section (every stage elite-cleared) → gold plate even while folded, mirroring the
     expanded chapter's gold frame. Gild the carved stone (sepia→gold) + a warm lantern glow. */
  .chapterPlate.goldPlate {
    filter: drop-shadow(0 6px 16px rgba(180,120,20,.62)) sepia(.72) saturate(2) hue-rotate(-8deg) brightness(1.07);
  }
  .chapterPlate.goldPlate::before {   /* soft top-light sheen so it reads as polished gold, not flat tint */
    content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
    background: linear-gradient(180deg, rgba(255,240,190,.5), rgba(255,210,110,0) 55%);
    mix-blend-mode: soft-light;
  }
  .chapterPlate.goldPlate .cpProg b { color: #fff0b0; }
  .chapterPlate.upcoming .cpName { color: rgba(255,243,214,.82); }
  .chapterPlate.upcoming .cpProg { color: rgba(255,231,168,.7); }
  .chapterPlate.upcoming .cpProg b { color: rgba(255,206,58,.72); }
  /* done (지나온 길) — warm gold glow on the carved stone */
  .chapterPlate.done { filter: drop-shadow(0 0 12px rgba(255,205,80,.4)) drop-shadow(0 6px 14px rgba(5,18,18,.34)) saturate(1.12) brightness(1.04); }
  .chapterPlate.done .cpName { color: #fff8e0; }
  /* collapse chevron on an expanded foldable (non-frontier) chapter (△ → tap to fold) */
  .chFoldBtn {
    position: absolute; top: 14px; left: 14px; z-index: 6;
    width: 30px; height: 30px; min-width: 0; padding: 0; border-radius: 9px;
    border: 1px solid rgba(223,193,121,.4);
    background: linear-gradient(180deg, rgba(72,47,21,.92), rgba(48,30,14,.94));
    box-shadow: var(--lift-sm), inset 0 1px 0 rgba(255,240,200,.22);
    display: grid; place-items: center; cursor: pointer; pointer-events: auto;
  }
  .chFoldBtn .cpChevron.up {   /* △ points up */
    width: 0; height: 0; color: #ffe7a8; opacity: .85;
    border-bottom: 8px solid currentColor; border-left: 6px solid transparent; border-right: 6px solid transparent;
  }
  .chFoldBtn:active { transform: translateY(1px); box-shadow: var(--press); filter: brightness(.95); }
  /* star-locked node (region-start stage held by the ★ gate) — tappable, distinct gold lock vs hard lock */
  .node.starLocked { filter: saturate(.9) brightness(.97); }
  .node.starLocked .lockIco { color: #ffce6a; }
  /* gate modal: progress bar + hint */
  .gateBar { width: min(220px, 72%); height: 8px; margin: 2px auto 12px; border-radius: 999px; background: rgba(60,40,16,.28); overflow: hidden; box-shadow: inset 0 1px 2px rgba(40,24,4,.35); }
  .gateBar i { display: block; height: 100%; border-radius: 999px; background: linear-gradient(90deg, #ffce3a, #ffe7a3); }
  .gateHint { opacity: .82; font-size: 13px; }
  /* ── map: jungle-stone cards (header cap + stretchy stone body + footer cap) ── */
  .chapter {
    position: relative;
    margin: clamp(18px, 3.5vh, 32px) 0;
    padding: 92px 32px 18px;   /* reserve frame: top header, sides vine, bottom — content (nodes) sits in the dark body. bottom tightened 28→18 (owner 2026-06-25) so the last node row sits near the frame's bottom edge instead of leaving a dark empty band */
    isolation: isolate;
    content-visibility: auto;            /* off-screen chapters skip render/layout — keeps the map smooth at 300+ stages */
    contain-intrinsic-size: auto 360px;  /* placeholder height so the scrollbar stays stable (auto remembers actual) */
  }
  /* during the seal-break anchor scroll, force REAL chapter layout so scrollIntoView lands accurately —
     content-visibility's estimated (contain-intrinsic-size) heights otherwise mis-anchor the section (2026-06-24) */
  #mapScroll.measuring .chapter { content-visibility: visible; }
  /* soft-launch boundary teaser: next region behind mist + a gold "곧 공개" pill — invites forward, not a lock wall */
  #map .chapter.boundaryTeaser { text-align: center; }
  #map .chapter.boundaryTeaser .teaserMist {
    position: absolute; left: 16px; right: 16px; top: 64px; bottom: 16px; z-index: 0; border-radius: 16px;
    background-size: cover; background-position: center; background-repeat: no-repeat;
    filter: blur(6px) brightness(.48) saturate(.85);
  }
  #map .chapter.boundaryTeaser .chHeader,
  #map .chapter.boundaryTeaser .teaserBadge,
  #map .chapter.boundaryTeaser .teaserSub { position: relative; top: 0; z-index: 1; }   /* top:0 clears the base .chHeader's top:18px — relative+top shifted the name DOWN onto the badge (10px overlap) without pushing siblings */
  #map .chapter.boundaryTeaser .chName { color: #fff; text-shadow: 0 2px 8px rgba(0,0,0,.85); }
  #map .chapter.boundaryTeaser .teaserBadge {
    display: inline-block; margin: 8px auto 0; padding: 7px 20px 8px; border-radius: 999px;
    background: linear-gradient(180deg, #ffd66a, #e0a83a); color: #5a3a08; font-weight: 900; font-size: 15px;
    border: 1.5px solid rgba(255,255,255,.55); box-shadow: 0 4px 12px rgba(80,50,8,.45);
  }
  #map .chapter.boundaryTeaser .teaserSub {
    margin-top: 10px; color: rgba(255,245,225,.85); font-size: 12.5px; font-weight: 700;
    text-shadow: 0 1px 4px rgba(0,0,0,.7);
  }
  /* the panel as a 9-slice FRAME (on ::before so children position relative to the card, not inside the border) */
  .chapter::before {
    content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
    border-style: solid; border-color: transparent;
    border-width: 92px 26px 22px 26px;
    border-image: url('../../assets/map/panel_full.webp') 172 48 40 48 fill / 92px 26px 22px 26px / 0 stretch round;   /* sides+center TILE vertically (round) instead of stretching — tall chapters (many stages) no longer smear the vine/gem rails */
  }
  /* "왕국의 심장" 섹션(heart-chamber, 챕터 10~13) 패널 = 골드 스킨 (near-identical 679×330 → same slices) */
  .chapter.goldPanel::before {
    border-image: url('../../assets/stage/panel_full_gold.webp') 172 48 40 48 fill / 92px 26px 22px 26px / 0 stretch round;   /* sides+center TILE vertically (round) instead of stretching — tall chapters (many stages) no longer smear the vine/gem rails */
  }
  /* Elite-conquered chapter: a permanent CROWN seal (gold ring over an amethyst gem — the elite gem crowned),
     top-right of the frame. Distinct from .chapter.complete (all-3★ = soft gold GLOW only). CSS-only, no bitmap. */
  .chapter .eliteCrown {
    position: absolute; top: 10px; right: 14px; z-index: 4; width: 22px; height: 22px;
    border-radius: 50%; pointer-events: none;
    background: radial-gradient(circle at 42% 34%, #f7dc92 0 30%, #cf9e30 62%, #8a6412 100%);
    border: 1.5px solid #ffeaa0;
    box-shadow: 0 1px 3px rgba(70,44,8,.5), inset 0 1px 0 rgba(255,246,200,.65), 0 0 8px rgba(240,200,90,.5);
  }
  .chapter .eliteCrown::before {
    content: ""; position: absolute; inset: 28%; border-radius: 50%;
    background: radial-gradient(circle at 40% 32%, #ecd8ff 0 22%, #9a55e0 60%, #5f2ea0 100%);
    box-shadow: inset 0 0 2px rgba(50,20,90,.6), 0 0 4px rgba(154,85,224,.6);
  }
  /* Frame IGNITION — plays once on map arrival when a chapter first becomes all-elite (mapScreen.playGoldIgnite).
     The goldPanel skin is applied at the same instant; a light sweep + brightness pulse sell it catching into
     gold, then the crown pops in. Self-clearing (class removed after ~1.65s → static gold remains). */
  .chapter.goldIgnite { animation: chGoldPulse 1.5s ease-out; }
  .chapter.goldIgnite::after, .chapterPlate.goldIgnite::after {
    content: ""; position: absolute; inset: 0; z-index: 6; pointer-events: none;
    background: linear-gradient(115deg, transparent 34%, rgba(255,246,205,.8) 48%, rgba(255,222,120,.42) 56%, transparent 70%);
    background-size: 250% 100%;
    animation: chGoldSweep 1.35s ease-out;
  }
  @keyframes chGoldSweep { from { background-position: 135% 0; opacity: 1; } to { background-position: -55% 0; opacity: 0; } }
  @keyframes chGoldPulse { 0% { filter: none; } 34% { filter: brightness(1.26) saturate(1.12); } 100% { filter: none; } }
  .chapter.goldIgnite .eliteCrown { animation: chCrownPop .6s cubic-bezier(.3,1.5,.5,1) .5s both; }
  @keyframes chCrownPop { 0% { transform: scale(0) rotate(-28deg); opacity: 0; } 68% { transform: scale(1.16) rotate(5deg); opacity: 1; } 100% { transform: scale(1) rotate(0); opacity: 1; } }
  html[data-motion="reduced"] .chapter.goldIgnite,
  html[data-motion="reduced"] .chapterPlate.goldIgnite { animation: none; }
  html[data-motion="reduced"] .chapter.goldIgnite::after,
  html[data-motion="reduced"] .chapterPlate.goldIgnite::after { display: none; }
  html[data-motion="reduced"] .chapter.goldIgnite .eliteCrown { animation: none; }
  /* all-elite chapter celebration plaque — centered overlay shown as the frame ignites (인정→고조). Gold-stone
     plaque + crown (gold ring over amethyst gem), triumphant scale-in, holds, fades. Non-blocking. */
  #eliteChapterBanner {
    position: fixed; left: 50%; top: 42%; transform: translate(-50%, -50%) scale(.7);
    z-index: 60; pointer-events: none; opacity: 0;
    display: flex; flex-direction: column; align-items: center; gap: 3px;
    padding: 16px 30px 18px; border-radius: 18px;
    background: linear-gradient(180deg, #6b4c14, #3a2a0e);
    border: 2px solid rgba(255,226,140,.9);
    box-shadow: 0 10px 34px rgba(30,16,2,.55), 0 0 24px rgba(255,196,80,.5), inset 0 1px 0 rgba(255,244,200,.4);
    text-align: center;
  }
  #eliteChapterBanner .ecbCrown {
    position: relative; width: 40px; height: 40px; border-radius: 50%; margin-bottom: 4px;
    background: radial-gradient(circle at 42% 34%, #f7dc92 0 30%, #cf9e30 62%, #8a6412 100%);
    border: 2px solid #ffeaa0;
    box-shadow: 0 2px 5px rgba(70,44,8,.55), inset 0 1px 0 rgba(255,246,200,.65), 0 0 12px rgba(240,200,90,.6);
  }
  #eliteChapterBanner .ecbCrown::before {
    content: ""; position: absolute; inset: 30%; border-radius: 50%;
    background: radial-gradient(circle at 40% 32%, #ecd8ff 0 22%, #9a55e0 60%, #5f2ea0 100%);
    box-shadow: inset 0 0 3px rgba(50,20,90,.6), 0 0 6px rgba(154,85,224,.7);
  }
  #eliteChapterBanner .ecbTitle { font-size: 21px; font-weight: 950; color: #fff3c8; text-shadow: 0 2px 3px rgba(40,22,4,.7), 0 0 10px rgba(255,210,110,.5); letter-spacing: .5px; }
  #eliteChapterBanner .ecbSub { font-size: 12px; font-weight: 800; color: #e9cfa0; letter-spacing: 1.5px; text-transform: uppercase; }
  #eliteChapterBanner.show { animation: ecbIn 2.4s cubic-bezier(.2,1.4,.4,1) both; }
  @keyframes ecbIn {
    0% { opacity: 0; transform: translate(-50%,-50%) scale(.6); }
    12% { opacity: 1; transform: translate(-50%,-50%) scale(1.08); }
    20% { transform: translate(-50%,-50%) scale(1); }
    82% { opacity: 1; transform: translate(-50%,-50%) scale(1); }
    100% { opacity: 0; transform: translate(-50%,-50%) scale(.96); }
  }
  html[data-motion="reduced"] #eliteChapterBanner.show { animation: ecbInReduced 2.2s ease both; }
  @keyframes ecbInReduced { 0% { opacity: 0; } 10% { opacity: 1; } 85% { opacity: 1; } 100% { opacity: 0; } }
  .chapter.locked { opacity: .94; }
  /* chapter name on the title bar (top of header cap) */
  .chHeader {
    position: absolute; left: 0; right: 0; top: 18px; z-index: 3;
    display: flex; align-items: center; justify-content: center; pointer-events: none;
  }
  .chName {
    max-width: 64%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; text-align: center;
    color: #fff3d6; font-weight: 900; font-size: clamp(15px, 4.4vw, 21px);
    text-shadow: 0 2px 0 #2c2210, -1px 0 0 #2c2210, 1px 0 0 #2c2210, 0 0 5px rgba(0,0,0,.5);
  }
  /* gold star + count LEFT-anchored in the panel's progress-bar groove (groove starts ~24%, owner 2026-06-25)
     — "★ 9/15" sits to the LEFT so it balances against the centered chapter name above it. */
  .chapter::after {
    content: "\2605"; position: absolute; left: 40%; top: 67px; transform: translate(-50%, -50%);
    z-index: 4; color: #ffce3a; font-size: clamp(15px, 4.2vw, 20px); pointer-events: none;
    text-shadow: 0 1px 2px rgba(70,35,0,.75), 0 0 5px rgba(255,205,60,.55);
  }
  /* star count sits right of the star emblem so the pair reads "★ 9/15" (stars), not a level tally */
  .chStarBar {
    position: absolute; left: 44%; top: 74px; transform: translateY(-50%); z-index: 3;
    color: #fff2cf; font-weight: 900; font-size: clamp(11px, 3vw, 14px);
    text-shadow: 0 1px 2px rgba(30,14,4,.85); white-space: nowrap;
  }
  /* chapter restored-window entry (owner 2026-06-25): a compact 무결점-progress CHIP (gallery icon + ✦X/N) in the
     card's top-right that opens the window viewer. Replaced the full grayscale art thumbnail, which clashed with
     the painted map. The art itself now lives only in the viewer modal + the chapter victory card. */
  .chWindowChip {
    position: absolute; top: 8px; right: 12px; z-index: 6; pointer-events: auto; cursor: pointer;
    display: inline-flex; align-items: center; gap: 4px; padding: 3px 9px 3px 7px;
    border-radius: 999px; border: 1px solid var(--gold-deep);
    background: linear-gradient(180deg, rgba(72,47,21,.92), rgba(48,30,14,.94));
    color: #ffe7a8; font-size: 12px; font-weight: 900; line-height: 1;
    text-shadow: 0 1px 2px rgba(20,10,2,.7);
    box-shadow: var(--lift-sm), inset 0 1px 0 rgba(255,240,200,.22);
  }
  .chWindowChip .ico { width: 14px; height: 14px; flex: none; stroke-width: 2; }   /* gallery glyph; stroke = currentColor */
  .chWindowChip:active { transform: translateY(1px) scale(.97); filter: brightness(.95); box-shadow: var(--press); }
  /* 무결점 완성 → 밝은 금 + 글로우 */
  .chWindowChip.done { border-color: #ffe27a; color: #fff7d6; box-shadow: var(--lift-sm), inset 0 1px 0 rgba(255,255,255,.3), 0 0 9px rgba(255,210,90,.5); }
  /* ── window viewer modal (design-system: modal_canvas 9-slice stone plaque + the victory crystal window) ── */
  #windowView { position: fixed; inset: 0; z-index: 75; display: none; align-items: center; justify-content: center;
    background: radial-gradient(circle at 50% 40%, rgba(255,238,150,.12), transparent 55%), rgba(11,16,9,.9); }
  #windowView.show { display: flex; animation: winFade .26s ease-out both; }
  #windowView .wvCard {
    position: relative; width: min(88vw, 350px); padding: 30px 30px 26px; text-align: center;
    border-style: solid; border-width: 44px; border-image: url('../../assets/common/modal_canvas.webp') 120 fill / 44px / 0 stretch;
    background: linear-gradient(180deg, rgba(197,187,154,.9), rgba(151,141,115,.92)); background-clip: padding-box;
    box-shadow: 0 0 34px rgba(255,218,76,.3), 0 22px 46px rgba(45,23,7,.42); overflow: visible;
  }
  #windowView.show .wvCard { animation: winPrizeDrop .48s cubic-bezier(.12,1.18,.28,1) both; }
  #windowView #wvClose { position: absolute; top: -13px; right: -13px; z-index: 4; width: 40px; height: 40px; padding: 0;
    background: linear-gradient(180deg,#7a552c,#4a3118); border: 2px solid #ffe8a6; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 3px 7px rgba(30,16,4,.5), inset 0 1px 0 rgba(255,255,255,.3); }
  #windowView #wvClose:active { transform: translateY(1px) scale(.96); filter: brightness(.93); }
  #windowView #wvClose .ico { width: 18px; height: 18px; fill: #fff3d6; }
  /* the chapter window (reuses #chapterWin's crystal hexagon frame via shared selectors) */
  #windowView .wvArtWrap { position: relative; width: min(60vw, 214px); aspect-ratio: 421 / 432; margin: 2px auto 14px;
    filter: drop-shadow(0 6px 16px rgba(8,16,30,.5)); }
  #windowView.wvDone .wvArtWrap { filter: drop-shadow(0 0 18px rgba(255,214,96,.6)) drop-shadow(0 6px 16px rgba(8,16,30,.5)); }
  /* typography on the stone plaque — gold title (carved), warm-brown body (matches the modal_canvas surface) */
  #windowView .wvName { font-family: 'Jua', system-ui, sans-serif; font-weight: 400; font-size: clamp(22px, 6vw, 27px);
    color: var(--gold-pale); -webkit-text-stroke: 1.2px var(--gold-deep); paint-order: stroke fill; line-height: 1;
    text-shadow: 0 2px 0 var(--gold-deep), 0 4px 7px rgba(60,34,4,.45); }
  #windowView .wvProg { margin-top: 9px; display: inline-flex; align-items: center; gap: 5px; padding: 3px 13px; border-radius: 999px;
    background: rgba(40,26,10,.42); box-shadow: inset 0 1px 2px rgba(0,0,0,.35); color: #ffe9a6; font-weight: 900; font-size: 14px;
    text-shadow: 0 1px 1px rgba(0,0,0,.45); }
  #windowView.wvDone .wvProg { background: linear-gradient(180deg,#caa53a,#9c7a1f); color: #fff8e0; box-shadow: 0 2px 5px rgba(60,40,4,.4), inset 0 1px 0 rgba(255,255,255,.35); }
  #windowView .wvCond { margin: 13px auto 0; max-width: 240px; font-size: 12.5px; font-weight: 800; line-height: 1.5; color: #4a3115;
    text-shadow: 0 1px 0 rgba(255,255,255,.28); }
  #windowView .wvHint { margin: 7px auto 0; max-width: 250px; font-size: 12px; line-height: 1.45; font-weight: 700; color: #6e5230; }
  #windowView.wvDone .wvHint { color: #7a4a08; font-weight: 900; }
  .nodes { position: relative; z-index: 1; display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: clamp(5px, 1.6vw, 10px); align-content: start; }
  .node {
    position: relative; aspect-ratio: 133 / 130; border: none !important; border-radius: 0;
    background: url('../../assets/map/tile_blue.png') center / 100% 100% no-repeat transparent !important;
    box-shadow: none !important;
    display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 0;
    color: #fff; cursor: pointer; min-width: 0; padding: 0 !important;
    font-weight: 900; font-size: clamp(15px, 5vw, 23px);
    text-shadow: 0 2px 3px rgba(7,43,80,.9);
  }
  .node.cleared { filter: none; }
  /* time-seal node — ruby hourglass seal: fading light + ancient temple marker, not a modern timer badge. */
  .node.bossNode { filter: saturate(1.14) brightness(1.04) drop-shadow(0 0 7px rgba(255,110,80,.58)); }
  .node.bossNode::after {
    content: ""; position: absolute; inset: 8%; z-index: 0; pointer-events: none;
    border-radius: 30% 34% 32% 36%;
    border: 2px solid rgba(255,169,96,.54);
    box-shadow: inset 0 0 0 1px rgba(126,40,25,.36), 0 0 10px rgba(255,96,72,.36);
    opacity: .95;
  }
  .node .bossMark {
    position: absolute; top: -15px; left: 50%; transform: translateX(-50%);
    width: clamp(20px, 6vw, 28px); height: clamp(20px, 6vw, 28px);
    display: grid; place-items: center; z-index: 4; pointer-events: none;
    border-radius: 48% 52% 45% 55%; border: 2px solid #f0c15a;
    background:
      radial-gradient(circle at 36% 28%, rgba(255,238,182,.92) 0 10%, transparent 18%),
      radial-gradient(circle at 50% 54%, #e65b4c 0 38%, #8a2c25 70%, #461716 100%);
    box-shadow:
      inset 0 2px 0 rgba(255,244,202,.38),
      inset 0 -3px 5px rgba(40,8,4,.36),
      0 2px 5px rgba(26,8,2,.45),
      0 0 8px rgba(255,110,80,.56);
  }
  .node .bossMark::before,
  .node .bossMark::after {
    content: ""; position: absolute; top: 14px; width: 7px; height: 4px;
    border-radius: 70% 8% 70% 8%;
    background: linear-gradient(135deg, #8ab95b, #3f6b2e);
    border: 1px solid rgba(38,58,22,.65);
  }
  .node .bossMark::before { left: -4px; transform: rotate(-26deg); }
  .node .bossMark::after { right: -4px; transform: scaleX(-1) rotate(-26deg); }
  .node .bossMark svg { width: 70%; height: 70%; overflow: visible; }
  .bossHourglass {
    fill: none; stroke: #fff0b8; stroke-width: 2.05; stroke-linecap: round; stroke-linejoin: round;
    filter: drop-shadow(0 1px 1px rgba(56,10,4,.58));
  }
  .bossSand { fill: #ffd56c; stroke: #7f3d13; stroke-width: .55; filter: drop-shadow(0 1px 1px rgba(56,10,4,.45)); }
  .node.bossNode.current { filter: saturate(1.22) brightness(1.07) drop-shadow(0 0 10px rgba(255,110,80,.82)); }
  /* light-omen node — a lucky golden glint, distinct from the ruby time-seal peak marker. */
  .node.omenNode { filter: saturate(1.1) brightness(1.06) drop-shadow(0 0 8px rgba(255,215,105,.56)); }
  .node.omenNode::after {
    content: ""; position: absolute; inset: 12%; z-index: 0; pointer-events: none;
    border-radius: 34% 30% 36% 31%;
    border: 2px solid rgba(255,228,130,.62);
    box-shadow: inset 0 0 0 1px rgba(97,70,18,.28), 0 0 12px rgba(255,217,95,.38);
    opacity: .92;
  }
  .node .omenMark {
    position: absolute; top: -10px; right: 4%; z-index: 4; pointer-events: none;
    width: clamp(19px, 5.4vw, 26px); height: clamp(19px, 5.4vw, 26px);
    display: grid; place-items: center; border-radius: 50%;
    color: #5b3412; font-weight: 1000; font-size: clamp(12px, 3.7vw, 16px); line-height: 1;
    background:
      radial-gradient(circle at 36% 28%, rgba(255,255,232,.98) 0 13%, transparent 22%),
      radial-gradient(circle at 50% 54%, #ffe99b 0 38%, #f0b63c 68%, #92611e 100%);
    border: 2px solid #fff3c1;
    box-shadow: inset 0 1px 0 rgba(255,255,255,.55), inset 0 -3px 5px rgba(87,48,12,.26), 0 2px 5px rgba(28,17,6,.38), 0 0 9px rgba(255,219,96,.5);
    text-shadow: 0 1px 0 rgba(255,246,196,.55);
  }
  .node.omen-heart .omenMark { color: #6f1c1c; background: radial-gradient(circle at 35% 28%, #fff7de 0 12%, transparent 22%), radial-gradient(circle at 50% 55%, #ffdca2 0 35%, #f08270 68%, #8b2d28 100%); }
  .node.omen-hint .omenMark { color: #345120; background: radial-gradient(circle at 35% 28%, #fff7de 0 12%, transparent 22%), radial-gradient(circle at 50% 55%, #fff0a5 0 38%, #a8d95e 68%, #4f7b2e 100%); }
  .node.omen-jump .omenMark { color: #173f68; background: radial-gradient(circle at 35% 28%, #fff7de 0 12%, transparent 22%), radial-gradient(circle at 50% 55%, #e3f6ff 0 38%, #79c7e7 68%, #2f6a93 100%); }
  .node.omenTrailNode { filter: saturate(1.04) brightness(1.03) drop-shadow(0 0 5px rgba(255,218,105,.35)); }
  .node .trailMark {
    position: absolute; top: 4%; left: 7%; z-index: 4; pointer-events: none;
    width: 16px; height: 16px; display: grid; place-items: center; border-radius: 50%;
    color: #ffe59a; font-weight: 900; font-size: 11px; line-height: 1;
    background: radial-gradient(circle at 50% 45%, rgba(255,244,170,.38), rgba(116,88,28,.18) 68%, transparent 70%);
    text-shadow: 0 0 5px rgba(255,230,130,.7), 0 1px 1px rgba(45,28,6,.55);
  }
  /* locked node = stone slot + padlock sprite (lock baked in → hide svg lock) */
  .node.locked {
    cursor: default;
    background: url('../../assets/map/slot_lock.png') center / 100% 100% no-repeat transparent !important;
  }
  .node.locked .lockIco { display: none; }
  .node.locked:hover, .node:disabled { filter: none; transform: none; }
  .node.bossNode.locked:disabled { filter: saturate(1.02) brightness(.98) drop-shadow(0 0 8px rgba(255,110,80,.62)); }
  .node.bossNode.locked::after {
    border-color: rgba(255,184,104,.72);
    box-shadow: inset 0 0 0 1px rgba(126,40,25,.42), 0 0 13px rgba(255,96,72,.48);
    opacity: 1;
  }
  .node.bossNode.locked .bossMark { filter: saturate(1.08) brightness(1.08) drop-shadow(0 0 4px rgba(255,210,112,.52)); opacity: 1; }
  /* number always centered; the earned-stars row is absolute at the bottom so it never shifts the number */
  /* number: always truly centered (same Y whether cleared or not — consistent across the row).
     earned stars sit as a small overlay pinned to the bottom edge, so they never shift the number. */
  .node span:not(.ns):not(.bossMark):not(.omenMark):not(.trailMark):not(.eliteSocket) { position: relative; z-index: 1; line-height: 1; font-variant-numeric: tabular-nums; }
  .node .ns { position: absolute; z-index: 1; left: 0; right: 0; bottom: 7%; text-align: center; font-size: 11px; line-height: 1; letter-spacing: 0; color: #ffe06a; text-shadow: 0 1px 1px rgba(8,50,90,.6); }
  /* 엘리트 소켓 (프레스티지 트로피 슬롯). 3★(무결점) 노드의 좌상단에만 놓인다.
     빈 소켓 = 어두운 홈(엘리트 도전 대기 itch); .filled = 자수정 젬이 박힘 = 엘리트 정복(맵에서 보석색만 유채색 pop).
     별 등급은 하단 .ns가 소유. 돌 타일 색은 바꾸지 않는다. (2026-07: 별셋=무결점 통합 → 소켓=엘리트, 둘레 폐지.) */
  .node .eliteSocket {
    position: absolute; top: 6%; left: 6%; z-index: 3;
    width: 16px; height: 16px; border-radius: 50%;
    pointer-events: none;
    background: radial-gradient(circle at 50% 40%, #333a47, #171b24 72%);
    border: 1.5px solid rgba(122,132,152,.4);
    box-shadow: inset 0 1.5px 2.5px rgba(0,0,0,.62), inset 0 -1px 1px rgba(255,255,255,.06), 0 1px 1px rgba(8,50,90,.4);
  }
  .node .eliteSocket.filled {
    border: 1.5px solid #e9d8ff;
    background:
      radial-gradient(circle at 38% 30%, rgba(255,250,255,.95) 0 12%, transparent 22%),
      radial-gradient(circle at 50% 56%, #c79bf3 0 32%, #9a55e0 66%, #5f2ea0 100%);
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,.5), inset 0 -2px 3px rgba(60,24,110,.42),
      0 1px 3px rgba(30,10,60,.5), 0 0 7px rgba(154,85,224,.62);
  }
  .node.eliteGemSetting .eliteSocket {
    animation: eliteSocketWait .78s ease-in-out infinite;
  }
  .node .eliteSocket.socketSettle {
    animation: eliteSocketSet .72s cubic-bezier(.18,1.45,.3,1) both;
  }
  .node .eliteSocket.socketSettle::after {
    content: "";
    position: absolute;
    inset: -9px;
    border-radius: 50%;
    border: 2px solid rgba(218,183,255,.78);
    box-shadow: 0 0 10px rgba(181,112,255,.72);
    animation: eliteSocketRing .62s ease-out both;
  }
  .node.eliteGemSet .eliteSocket.filled {
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,.55), inset 0 -2px 3px rgba(60,24,110,.44),
      0 1px 3px rgba(30,10,60,.5), 0 0 12px rgba(190,118,255,.88);
  }
  .mapEliteGemFly {
    position: fixed;
    z-index: 120;
    width: 24px;
    height: 24px;
    pointer-events: none;
    border: 1.5px solid rgba(246,235,255,.92);
    border-radius: 48% 52% 48% 52%;
    background:
      radial-gradient(circle at 35% 27%, rgba(255,255,255,.96) 0 10%, transparent 20%),
      linear-gradient(135deg, #ead7ff 0 18%, #b878f3 45%, #6d2db5 100%);
    clip-path: polygon(50% 0, 88% 35%, 70% 100%, 30% 100%, 12% 35%);
    box-shadow:
      0 0 9px rgba(195,121,255,.9),
      0 0 22px rgba(124,64,205,.44),
      0 4px 9px rgba(16,6,35,.35);
  }
  .mapEliteGemFly::after {
    content: "";
    position: absolute;
    right: 75%;
    top: 45%;
    width: 52px;
    height: 3px;
    transform: rotate(-16deg);
    transform-origin: right center;
    background: linear-gradient(90deg, transparent, rgba(214,178,255,.78));
    filter: blur(.5px);
  }
  .mapEliteGemBurst {
    position: fixed;
    z-index: 119;
    width: 1px;
    height: 1px;
    pointer-events: none;
  }
  .mapEliteGemBurst::before {
    content: "";
    position: absolute;
    inset: -24px;
    border-radius: 50%;
    border: 2px solid rgba(221,191,255,.84);
    box-shadow: 0 0 14px rgba(180,102,255,.75), inset 0 0 12px rgba(255,255,255,.25);
    animation: mapEliteGemBurstRing .72s ease-out both;
  }
  .mapEliteGemBurst::after {
    content: "✦ ✧ ✦";
    position: absolute;
    left: -38px;
    top: -18px;
    width: 76px;
    color: #ead7ff;
    font-size: 14px;
    letter-spacing: 10px;
    text-shadow: 0 0 8px rgba(190,118,255,.9);
    animation: mapEliteGemSpark .72s ease-out both;
  }
  @keyframes eliteSocketWait {
    0%, 100% { filter: brightness(1); }
    50% { filter: brightness(1.32) drop-shadow(0 0 5px rgba(168,102,232,.55)); }
  }
  @keyframes eliteSocketSet {
    0% { transform: scale(.72); filter: brightness(1.7); }
    48% { transform: scale(1.28); filter: brightness(1.45); }
    100% { transform: scale(1); filter: brightness(1); }
  }
  @keyframes eliteSocketRing {
    0% { transform: scale(.35); opacity: .95; }
    100% { transform: scale(1.65); opacity: 0; }
  }
  @keyframes mapEliteGemBurstRing {
    0% { transform: scale(.3); opacity: .95; }
    100% { transform: scale(1.55); opacity: 0; }
  }
  @keyframes mapEliteGemSpark {
    0% { transform: translateY(2px) scale(.72); opacity: 0; }
    28% { opacity: 1; }
    100% { transform: translateY(-12px) scale(1.18); opacity: 0; }
  }
  html[data-motion="reduced"] {
    .node.eliteGemSetting .eliteSocket,
    .node .eliteSocket.socketSettle,
    .node .eliteSocket.socketSettle::after { animation: none; }
    .mapEliteGemFly,
    .mapEliteGemBurst { display: none; }
  }
  .node.cardStage2 .ns {
    color: #fff0a1;
    text-shadow: 0 1px 1px rgba(78,44,8,.78), 0 0 5px rgba(255,218,88,.52);
  }
  .node.cardStage3 .ns {
    color: #fff8c8;
    text-shadow: 0 1px 1px rgba(78,44,8,.82), 0 0 8px rgba(255,230,104,.72);
  }
  /* cleared-but-not-3★ node → tiny coin badge cueing "more 별빛 earnable here by improving your stars" (option 4) */
  /* "earn more here" sticker — pinned OUTSIDE the top-right corner (node has no overflow:hidden) so it
     never covers the centered number; a small gold "+" reads as "more to earn on a better solve" */
  /* flawless/gap state is carried by the stage seal and earn-more mark. Do not recolor the tile itself. */
  /* skipped node (passed via Jump Pass, 0★): a blue » badge top-left marks it as "skipped, not earned"
     — distinct from the gold + earn-more badge; both can show (you skipped it AND can still earn stars). */
  .node.skipped::before {
    content: "»"; position: absolute; top: 6%; left: 6%; z-index: 3;
    width: 17px; height: 17px; display: grid; place-items: center; border-radius: 50%;
    color: #fff; font-weight: 900; font-size: 11px; line-height: 1;
    background: radial-gradient(circle at 50% 32%, #9fd6ff, #4a90d9 55%, #2e6bb0);
    border: 1.5px solid #eaf4ff; box-shadow: 0 1px 2px rgba(8,50,90,.45);
  }
  /* Elite conquered is shown by the amethyst gem set into the .eliteSocket (above) — NO node frame.
     (2026-07: the earlier amethyst rim/.eliteDone was retired when 별셋=무결점 merged and the socket became
     the elite trophy; a whole-tile ring competed with .current/boss/omen rings.) */
  /* "play next" frontier node — emerald ring + gentle pulse so the eye knows where to go */
  .node.current { animation: nodePulse 1.7s ease-in-out infinite; }
  @keyframes nodePulse {
    0%,100% { box-shadow: 0 0 0 2px rgba(33,176,131,.55), var(--lift-sm), var(--sheen); }
    50%     { box-shadow: 0 0 0 5px rgba(33,176,131,.22), 0 6px 16px -4px rgba(33,176,131,.45), var(--sheen); }
  }
  html[data-motion="reduced"] { .node.current { animation: none; box-shadow: 0 0 0 2px rgba(33,176,131,.55), var(--lift-sm), var(--sheen); } }
  /* perfect chapter (all 3★) — gold glow */
  /* perfect chapter (all 3 stars): soft gold glow that follows the ornate frame (no rectangular ring) */
  .chapter.complete::before { filter: drop-shadow(0 0 7px rgba(255,205,80,.75)); }

  /* ── map Sha position marker (현재 도전 스테이지 타일 안에 표시) ── */
  .mapShaMark {
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: 44px; height: 52px;
    object-fit: contain; display: block;
    pointer-events: none;
    z-index: 4;
    animation: shaIdle 2.8s ease-in-out infinite;
  }
  @keyframes shaIdle {
    0%, 100% { transform: translate(-50%, -50%); }
    50%       { transform: translate(-50%, calc(-50% - 5px)); }
  }
  html[data-motion="reduced"] { .mapShaMark { animation: none; } }
