/* EkoVille — HUD pills, buttons, joystick, powers.
   Split of the old styles.css; index.html loads these in order (cascade order matters). */

/* ============================== HUD & controls ============================== */
.hud{position:absolute;top:0;left:0;right:0;padding:14px 16px;padding-top:calc(14px + env(safe-area-inset-top,0px));display:flex;justify-content:space-between;align-items:flex-start;gap:8px;z-index:500;pointer-events:none}
.hud-stats{display:flex;flex-direction:column;align-items:flex-start;gap:7px}
.hud-btns{display:flex;flex-direction:column;align-items:flex-end;gap:9px}
/* Candy resource pills: light rim, glossy top, chunky 3D base. */
.pill{--p1:var(--green-1);--p2:var(--green-2);--pd:var(--green-d);
  background:linear-gradient(180deg,var(--p1),var(--p2));color:#fff;border:none;border-radius:999px;
  padding:6px 13px;font-weight:800;font-size:16px;display:flex;align-items:center;gap:6px;
  font-variant-numeric:tabular-nums;white-space:nowrap;
  box-shadow:inset 0 2px 0 rgba(255,255,255,.55),inset 0 -3px 6px rgba(0,0,0,.12),0 4px 0 var(--pd),0 6px 12px rgba(50,30,70,.2);
  -webkit-text-stroke:2px var(--ink-line);paint-order:stroke fill;text-shadow:var(--drop)}
.pill.lvl{--p1:var(--gold-1);--p2:var(--gold-2);--pd:var(--gold-d)}
.pill.streak{--p1:#ffc48a;--p2:#ff7a2e;--pd:#c9520f}
.pill.mint{--p1:var(--green-1);--p2:var(--green-2);--pd:var(--green-d)}
.pill.km{--p1:var(--blue-1);--p2:var(--blue-2);--pd:var(--blue-d);cursor:pointer;pointer-events:auto}
.pill.bag{--p1:var(--purple-1);--p2:var(--purple-2);--pd:var(--purple-d)}
.pill.bag[hidden]{display:none}
/* Emoji must not carry the outline — it fuses them. */
.pill svg,.pemo{-webkit-text-stroke:0}
/* Top-left stat pills: the number sits on top, its label underneath. */
.hud-stats .pill{flex-direction:column;gap:1px;padding:5px 15px;cursor:pointer;pointer-events:auto}
.hud-stats .pill .pval{display:flex;align-items:center;gap:5px;line-height:1;font-size:17px}
.hud-stats .pill .plabel{font-size:10px;font-weight:800;letter-spacing:.4px;text-transform:uppercase;color:#fff;
  line-height:1;-webkit-text-stroke:1.4px var(--ink-line);paint-order:stroke fill}
.pill[hidden]{display:none}
/* km meter back at top-centre */
.kmtop{position:absolute;top:14px;top:calc(14px + env(safe-area-inset-top,0px));left:0;right:0;display:flex;justify-content:center;z-index:500;pointer-events:none}
/* XP meter: vertical bar, fills bottom→top */
/* XP meter: the bar plus an XP sticker under it. The bar alone was a nameless
   sliver of gold — nothing said what was filling up. */
.xpmeter{display:flex;flex-direction:column;align-items:center;gap:3px;margin-top:5px;pointer-events:none}
.xptag{width:30px;height:auto;filter:drop-shadow(0 2px 3px rgba(20,60,30,.45))}
.xptrack{position:relative;width:18px;height:104px;border-radius:99px;background:rgba(0,50,0,.28);overflow:hidden;border:2.5px solid rgba(255,255,255,.9);box-shadow:inset 0 1px 3px rgba(0,40,0,.35),0 3px 7px rgba(50,30,70,.2);pointer-events:none;transition:box-shadow .3s,width .2s}
.xptrack.gain{box-shadow:0 0 0 3px rgba(255,220,80,.6),0 0 16px rgba(255,200,60,.8);width:21px}
.xpfill{position:absolute;left:0;bottom:0;width:100%;height:0;border-radius:99px;background:linear-gradient(0deg,var(--gold-2),#fff2a0);box-shadow:0 0 10px rgba(255,220,80,.85);transition:height .45s var(--ease-out)}
/* Fx.xpGain drives the height every frame, so the transition has to be off or
   it smears each step — and lags a whole level wrap behind. */
.xpfill.tweening{transition:none}
.xpfill.gain{animation:xppulse .5s ease}
/* Sparks off the bar's rising edge: shorter than the .spark used by the stop
   minigame, since they fly in a 12px-wide lane. */
.spark.xpspark{width:11px;height:3px;z-index:701}
@keyframes xppulse{0%,100%{filter:brightness(1)}50%{filter:brightness(1.45)}}
/* Round icon button: white plastic chip with a 3D base. */
.iconbtn{pointer-events:auto;font:inherit;background:linear-gradient(180deg,#ffffff,#eef0f6);color:var(--purple-2);border:0;width:46px;height:46px;border-radius:15px;display:grid;place-items:center;cursor:pointer;
  box-shadow:inset 0 2px 0 #fff,0 4px 0 #c7cbd8,0 7px 14px rgba(50,30,70,.18);
  transition:transform .1s var(--ease-out),box-shadow .1s}
.iconbtn:active{transform:translateY(3px);box-shadow:inset 0 2px 0 #fff,0 1px 0 #c7cbd8}
.iconbtn svg{width:26px;height:26px}
.iconbtn.on{color:var(--gold-t);background:linear-gradient(180deg,var(--gold-1),var(--gold-2));box-shadow:inset 0 2px 0 rgba(255,255,255,.7),0 4px 0 var(--gold-d),0 7px 16px rgba(255,170,40,.5)}
/* badge-image HUD buttons (Saavutukset / Asetukset) keep the artwork bare */
.iconbtn.badgebtn{position:relative;background:none;box-shadow:none;width:46px;height:46px;border-radius:12px;padding:0}
.iconbtn.badgebtn:active{transform:translateY(3px);box-shadow:none}
.notif{position:absolute;top:-6px;right:-6px;min-width:24px;height:24px;padding:0 6px;border-radius:99px;background:linear-gradient(180deg,var(--red-1),var(--red-2));color:#fff;font-family:var(--display);font-weight:800;font-size:13px;line-height:19px;text-align:center;box-shadow:0 3px 6px rgba(0,0,0,.28);border:3px solid #fff;pointer-events:none;animation:notifpop .3s var(--ease-back)}
.notif[hidden]{display:none}
@keyframes notifpop{from{transform:scale(0)}to{transform:scale(1)}}
.iconbtn.badgebtn img{width:100%;height:100%;object-fit:contain;filter:drop-shadow(0 3px 5px rgba(0,0,0,.3))}
/* The pet sprite is trimmed to its content, so it reads bigger than the UI
   badges above it — drop it a notch clear of the missions button. */
#petBtn{margin-top:16px}
/* Pet face swapped (mood/stage changed): the new face pops in over-bright and
   settles, while a gold ring bursts off the button — reuses the noticering
   keyframes. Fx.sparks + .gained complete the show from JS. */
#petBtnImg.swap{animation:petswap .7s var(--ease-back)}
@keyframes petswap{0%{transform:scale(.4) rotate(-15deg);filter:brightness(2.2) drop-shadow(0 3px 5px rgba(0,0,0,.3))}
  55%{transform:scale(1.32) rotate(6deg);filter:brightness(1.35) drop-shadow(0 3px 5px rgba(0,0,0,.3))}
  100%{transform:scale(1);filter:drop-shadow(0 3px 5px rgba(0,0,0,.3))}}
#petBtn .petring{position:absolute;left:50%;top:50%;width:46px;height:46px;margin:-23px 0 0 -23px;
  border-radius:50%;border:4px solid var(--gold-2);pointer-events:none;animation:noticering .7s ease-out 2}
/* Speech bubble: the pet shouts what it wants — a white bubble to the LEFT of
   the button (the HUD column hugs the right edge) with a tail pointing at it. */
#petSpeech{position:absolute;right:calc(100% + 12px);top:50%;transform:translateY(-50%);
  background:linear-gradient(180deg,#ffffff,#eef0f6);color:var(--ink);border-radius:14px;
  padding:7px 12px;font-family:var(--display);font-weight:800;font-size:14px;
  width:max-content;max-width:min(200px,55vw);white-space:normal;text-align:center;line-height:1.25;
  box-shadow:0 4px 10px rgba(50,30,70,.28);pointer-events:none;z-index:2}
#petSpeech::after{content:'';position:absolute;left:100%;top:50%;margin-top:-7px;
  border:7px solid transparent;border-left-color:#eef0f6}
#petSpeech[hidden]{display:none}
#petSpeech.pop{animation:speechpop .4s var(--ease-back)}
@keyframes speechpop{from{transform:translateY(-50%) scale(.3);opacity:0}to{transform:translateY(-50%) scale(1);opacity:1}}
.iconbtn.pulse{animation:btnpulse 1.1s ease-in-out infinite;position:relative;z-index:1}
@keyframes btnpulse{0%,100%{transform:scale(1)}50%{transform:scale(1.16)}}
/* "Look here" for a returning player. Louder than .pulse on purpose — it runs
   once, for five seconds, against a screen the player has not read yet. Declared
   AFTER .pulse so it wins the animation property while both classes are on;
   when it ends, the quieter pulse simply resumes.
   Iteration counts are 5 × 1s, so the effect self-terminates even if the JS
   timeout that strips the class drifts. */
.iconbtn.notice{animation:btnnotice 1s var(--ease-back) 5;position:relative;z-index:2}
.iconbtn.notice::before,.iconbtn.notice::after{content:'';position:absolute;left:50%;top:50%;
  width:44px;height:44px;margin:-22px 0 0 -22px;border-radius:50%;pointer-events:none;
  border:4px solid var(--gold-2);animation:noticering 1s ease-out 5}
.iconbtn.notice::after{border-color:#fff;animation-delay:.28s}
@keyframes btnnotice{0%,72%,100%{transform:none}12%{transform:scale(1.26) rotate(-8deg)}
  30%{transform:scale(1.12) rotate(7deg)}48%{transform:scale(1.2) rotate(-4deg)}62%{transform:scale(1.06)}}
@keyframes noticering{from{transform:scale(.65);opacity:.95}to{transform:scale(2.4);opacity:0}}
@media (prefers-reduced-motion:reduce){.iconbtn.notice{animation:none}.iconbtn.notice::before,.iconbtn.notice::after{animation:none;opacity:.6;transform:scale(1.5)}}

/* Hint banner: gold sticker pill, same as the Saga armed-hint. */
.hint{position:absolute;bottom:150px;left:50%;transform:translateX(-50%);z-index:500;
  max-width:min(300px,78vw);text-align:center;line-height:1.3;
  background:linear-gradient(180deg,var(--gold-1),var(--gold-2));color:var(--gold-t);
  padding:10px 18px;border-radius:18px;font-weight:800;font-size:14px;border:3px solid #fff;
  box-shadow:0 6px 16px rgba(255,170,30,.55),inset 0 2px 0 #fff;
  pointer-events:none;opacity:0;transition:opacity .25s}
.hint.show{opacity:1}

#joy{position:absolute;left:24px;bottom:30px;bottom:calc(30px + env(safe-area-inset-bottom,0px));width:118px;height:118px;border-radius:50%;background:rgba(255,255,255,.42);border:3px solid rgba(255,255,255,.85);box-shadow:inset 0 2px 6px rgba(50,80,40,.18);z-index:500;touch-action:none}
#knob{position:absolute;left:50%;top:50%;width:54px;height:54px;border-radius:50%;background:linear-gradient(180deg,var(--green-1),var(--green-2) 55%,var(--green-d));border:4px solid #fff;transform:translate(-50%,-50%);box-shadow:0 5px 0 #1c5c1a,0 9px 16px rgba(30,80,20,.35),inset 0 -3px 8px rgba(0,0,0,.22)}

/* ============================== Powers (magnet + freeze) ==============================
   Bottom-right stack, opposite the (dev-only) joystick. Round chunky buttons so
   they read as "abilities", not as the square HUD tool buttons up top. */
.powers{position:absolute;right:18px;bottom:30px;bottom:calc(30px + env(safe-area-inset-bottom,0px));z-index:500;display:flex;flex-direction:row;gap:11px;align-items:flex-end}
/* Free zoom-out button: always visible, blue, a touch smaller than the ability
   buttons so it reads as a view control, not a charged power. Badge = cycle step. */
.zoombtn{position:relative;pointer-events:auto;font:inherit;width:52px;height:52px;border-radius:50%;border:4px solid #fff;cursor:pointer;padding:0;
  background:linear-gradient(180deg,var(--blue-1),var(--blue-2) 60%,var(--blue-d));
  box-shadow:inset 0 3px 0 rgba(255,255,255,.55),0 5px 0 var(--blue-d),0 10px 18px rgba(30,40,80,.32);
  display:grid;place-items:center}
.zoombtn:active{transform:translateY(4px);box-shadow:inset 0 3px 0 rgba(255,255,255,.5),0 1px 0 var(--blue-d),0 4px 10px rgba(30,40,80,.3)}
.zoombtn .zoomico{font-size:22px;line-height:1;filter:drop-shadow(0 2px 3px rgba(0,0,0,.3))}
.zoomn{position:absolute;right:-4px;bottom:-4px;min-width:24px;height:19px;padding:0 5px;border-radius:99px;
  background:#fff;color:var(--blue-d);font-family:var(--display);font-weight:800;font-size:10.5px;
  display:grid;place-items:center;box-shadow:0 2px 4px rgba(0,0,0,.25);font-variant-numeric:tabular-nums}
.powbtn{position:relative;pointer-events:auto;font:inherit;width:62px;height:62px;border-radius:50%;border:4px solid #fff;cursor:pointer;padding:0;
  background:linear-gradient(180deg,var(--p1),var(--p2) 60%,var(--pd));
  box-shadow:inset 0 3px 0 rgba(255,255,255,.55),0 5px 0 var(--pd),0 10px 20px rgba(30,40,80,.35);
  display:grid;place-items:center;animation:powin .32s var(--ease-back) both}
.powbtn[hidden]{display:none}
.powbtn.magnet{--p1:var(--red-1);--p2:var(--red-2);--pd:var(--red-d)}
.powbtn.freeze{--p1:var(--shiny-1);--p2:var(--shiny-2);--pd:var(--shiny-d)}
.powbtn.refresh{--p1:var(--gold-1);--p2:var(--gold-2);--pd:var(--gold-d)}
/* Cooling down: a dark conic wedge unwinds anticlockwise as `--p` (percent of
   cooldown REMAINING) shrinks, so the button visibly fills back up. */
.powbtn.refresh::after{content:'';position:absolute;inset:-1px;border-radius:50%;pointer-events:none;
  background:conic-gradient(from 180deg,rgba(20,25,50,.55) var(--p,0%),transparent 0)}
.powbtn.refresh:not(.ready){filter:saturate(.55)}
.powbtn.refresh:not(.ready) .powico{opacity:.75}
.powbtn.refresh.ready .powico{animation:refspin 2.6s linear infinite}
@keyframes refspin{0%,72%{transform:rotate(0)}100%{transform:rotate(360deg)}}
/* Only WHILE COOLING does the badge change shape: "4:12" is far wider than a
   charge count, so it hangs centred under the disc instead of off its right
   edge, where it collided with the screen edge. Ready, it falls back to the
   shared .pown pill showing "1", so the three powers read as one set. */
.powbtn.refresh:not(.ready) .pown{min-width:40px;font-size:11.5px;left:50%;right:auto;transform:translateX(-50%)}
.powbtn:active{transform:translateY(4px);box-shadow:inset 0 3px 0 rgba(255,255,255,.5),0 1px 0 var(--pd),0 4px 10px rgba(30,40,80,.3)}
.powbtn .powico{font-size:27px;line-height:1;filter:drop-shadow(0 2px 3px rgba(0,0,0,.3))}
/* Charge count, bottom-right of the disc — same badge language as .notif. */
.powbtn .pown{position:absolute;right:-5px;bottom:-4px;min-width:23px;height:23px;padding:0 5px;border-radius:99px;
  background:linear-gradient(180deg,#fff,#eef0f6);color:var(--ink);border:3px solid #fff;
  font-family:var(--display);font-weight:800;font-size:12.5px;line-height:17px;text-align:center;box-shadow:0 3px 6px rgba(0,0,0,.28)}
/* Freeze running: the button holds the state so the player can see it is spent. */
.powbtn.on{animation:powpulse 1.2s ease-in-out infinite}
@keyframes powin{from{transform:scale(.3);opacity:0}to{transform:none;opacity:1}}
@keyframes powpulse{0%,100%{box-shadow:inset 0 3px 0 rgba(255,255,255,.55),0 5px 0 var(--pd),0 10px 20px rgba(30,40,80,.35)}50%{box-shadow:inset 0 3px 0 rgba(255,255,255,.55),0 5px 0 var(--pd),0 0 0 9px rgba(18,198,232,.28),0 10px 20px rgba(30,40,80,.35)}}
/* Whole-screen frost while time is stopped — the freeze has no other on-map
   tell, and a timid tint reads as nothing at all on a busy street map. Sits
   under the marker pane (z 600) so the trash stays crisp on top of it. */
body.frozen #map::after{content:'';position:absolute;inset:0;z-index:400;pointer-events:none;
  background:radial-gradient(75% 65% at 50% 50%,rgba(169,244,255,.12) 20%,rgba(18,198,232,.5));
  box-shadow:inset 0 0 60px 12px rgba(255,255,255,.55);animation:fade .3s var(--ease-out) both}
/* Magnet arrival: the trash lands next to the player. */
.mon.pulled{animation:pullin .42s var(--ease-back) both}
@keyframes pullin{0%{transform:scale(2.1);opacity:.25}60%{transform:scale(.86)}100%{transform:none;opacity:1}}
/* Trash in flight toward the player (magnet): a red glow so the eye can track it. */
.mon.flying{animation:monfly .5s ease-in-out infinite}
@keyframes monfly{0%,100%{filter:drop-shadow(0 0 4px rgba(255,61,99,.75))}50%{filter:drop-shadow(0 0 11px rgba(255,61,99,1))}}

/* Magnet armed: press the button, then tap any trash on the map to pull it in.
   The trash pulses and the button glows red so the mode is unmistakable. */
.magnet-armed .mon{cursor:pointer;animation:magtarget 1s ease-in-out infinite}
@keyframes magtarget{0%,100%{filter:drop-shadow(0 0 0 rgba(255,61,99,0))}50%{filter:drop-shadow(0 0 9px rgba(255,61,99,.95))}}
.powbtn.magnet.armed{box-shadow:0 0 0 3px rgba(255,61,99,.85),0 0 16px rgba(255,61,99,.7);animation:magbtn .9s ease-in-out infinite}
@keyframes magbtn{0%,100%{transform:scale(1)}50%{transform:scale(1.08)}}

/* Power charge won, announced in the result panel */
.powerwon{margin-top:10px;background:linear-gradient(180deg,var(--purple-1),var(--purple-2));border:3px solid #fff;border-radius:18px;padding:8px 14px;box-shadow:0 4px 10px rgba(110,50,180,.4);animation:pop .3s var(--ease-back) both}
.powerwon .pw-t{display:block;font-family:var(--display);font-weight:800;font-size:16px;color:#fff;text-shadow:0 2px 3px rgba(60,20,110,.45)}
.powerwon .pw-s{display:block;font-size:13px;font-weight:600;color:#f3e8ff;margin-top:2px}

