/* EkoVille — puzzle jigsaw + boss arena.
   Split of the old styles.css; index.html loads these in order (cascade order matters). */

/* ============================== PUZZLE (jigsaw) ============================== */
.puzzlewrap{display:flex;flex-direction:column;align-items:center;gap:12px;margin-top:4px}
/* Task line above the picture — bare mode skips the normal .enc-q prompt, so
   the puzzle carries its own (same sticker-heading style). */
.puzzletitle{font-family:var(--display);font-weight:800;font-size:18px;text-align:center;color:#fff;
  -webkit-text-stroke:2px var(--ink-line);paint-order:stroke fill;text-shadow:var(--drop);margin:2px 0 -2px}
.puzzleref{position:relative;margin-top:8px;cursor:pointer}
.puzzleref img{width:min(52vw,210px);height:min(52vw,210px);border-radius:18px;border:4px solid #fff;object-fit:cover;box-shadow:0 6px 16px rgba(0,0,0,.45)}
.puzzleref .pzhint{position:absolute;right:-8px;bottom:-8px;width:32px;height:32px;border-radius:50%;display:grid;place-items:center;
  font-size:16px;background:linear-gradient(180deg,var(--green-1),var(--green-2));border:3px solid #fff;box-shadow:0 3px 7px rgba(0,0,0,.35)}
.puzzleref:active img{transform:scale(.97)}
/* Tap-to-enlarge: a full-screen dark view of the reference picture. */
.puzzlezoom{position:fixed;inset:0;z-index:50;display:none;flex-direction:column;align-items:center;justify-content:center;gap:16px;
  background:rgba(6,34,14,.92);padding:20px;cursor:pointer}
.puzzlezoom.show{display:flex;animation:fade .18s ease}
.puzzlezoom img{width:min(90vw,80vh,560px);height:auto;border-radius:18px;border:5px solid #fff;box-shadow:0 12px 34px rgba(0,0,0,.6)}
.pzclose{font-family:var(--display);font-weight:800;font-size:16px;color:#fff;background:linear-gradient(180deg,var(--green-2),var(--green-d));
  padding:9px 22px;border-radius:99px;border:3px solid #fff;box-shadow:0 4px 10px rgba(0,0,0,.35)}
.puzzlereflab{position:absolute;top:-11px;left:50%;transform:translateX(-50%);font-family:var(--display);font-weight:800;font-size:12px;color:#fff;
  background:linear-gradient(180deg,var(--green-2),var(--green-d));padding:2px 12px;border-radius:99px;border:2px solid #fff;white-space:nowrap}
.puzzlegrid{display:grid;grid-template-columns:repeat(var(--n),1fr);gap:3px;width:min(96%,360px);aspect-ratio:1;padding:5px;
  border-radius:16px;background:rgba(8,58,16,.35);border:3px solid #fff;box-shadow:inset 0 2px 6px rgba(0,0,0,.35)}
.ptile{position:relative;aspect-ratio:1;padding:0;border:1.5px solid rgba(255,255,255,.5);border-radius:6px;background-color:transparent;
  background-size:calc(var(--n) * 100%) calc(var(--n) * 100%);background-repeat:no-repeat;cursor:pointer;
  transition:transform .12s var(--ease-back),box-shadow .12s,border-color .12s}
.ptile.sel{border-color:var(--gold-1);box-shadow:0 0 0 3px var(--gold-2),0 0 12px rgba(255,200,60,.85);transform:scale(1.06);z-index:2}
/* Tile on its own spot: locked in place — green frame + ✓ badge, not tappable. */
.ptile.locked{border-color:var(--green-1);box-shadow:0 0 0 2.5px var(--green-2),0 0 10px rgba(62,193,60,.55);
  cursor:default;animation:tilelock .45s var(--ease-back)}
.ptile.locked::after{content:'✓';position:absolute;right:3px;bottom:2px;font-size:13px;font-weight:800;color:#fff;
  text-shadow:0 0 4px rgba(20,110,30,.9),0 1px 2px rgba(0,45,10,.7)}
@keyframes tilelock{0%{transform:scale(1)}45%{transform:scale(1.1)}100%{transform:scale(1)}}
/* the solved sweep replaces the per-tile lock frames */
.puzzlegrid.solved .ptile.locked{border-color:rgba(255,255,255,.15);box-shadow:none}
.puzzlegrid.solved .ptile.locked::after{display:none}
.puzzlegrid.solved{animation:correctpop .4s var(--ease-back)}
.puzzlegrid.solved .ptile{border-color:rgba(255,255,255,.15);animation:puzzleglow .6s ease}
@keyframes puzzleglow{50%{box-shadow:0 0 12px rgba(147,242,122,.95)}}

/* ============================== BOSS ============================== */
/* Boss map marker — reuses .mon so the whole monster loop drives it; a boss just
   scales the sprite up and swaps the shiny cyan pulse for a menacing red one. */
.mon.boss{width:92px;height:108px}
.mon.boss .crt{width:84px;height:84px;filter:drop-shadow(0 5px 7px rgba(0,0,0,.4)) drop-shadow(0 0 8px rgba(255,60,90,.55))}
.mon.boss.near .crt{animation:bob .9s ease-in-out infinite}
.mon.boss .montime{margin-bottom:15px}
.mon.boss .bosscrown{position:absolute;top:-15px;left:50%;transform:translateX(-50%);z-index:3;font-size:26px;font-style:normal;
  filter:drop-shadow(0 2px 3px rgba(0,0,0,.45));animation:bob 1.6s ease-in-out infinite}
.mon.boss .bossping{position:absolute;left:50%;top:55%;width:80px;height:80px;margin:-40px 0 0 -40px;border-radius:50%;
  border:4px solid var(--red-2);opacity:0;pointer-events:none;animation:bossping 1.8s ease-out infinite}
.mon.boss .bossping.p2{animation-delay:.9s}
@keyframes bossping{0%{opacity:.6;transform:scale(.55)}70%{opacity:0}100%{opacity:0;transform:scale(1.55)}}
@media (hover:hover){.mon.boss{cursor:pointer}}

/* Boss fight panel chrome (the banner above the reused challenge). Red/dark so it
   reads as danger against the game's green theme. */
.boss-banner{background:linear-gradient(180deg,#3a1020,#5a1630);border:2px solid rgba(255,255,255,.4);border-radius:20px;
  padding:10px 12px;margin-bottom:10px;box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 4px 12px rgba(60,10,25,.4)}
.boss-banner.shielded{box-shadow:inset 0 0 0 2px var(--blue-1),0 0 16px rgba(90,170,255,.55)}
.boss-top{display:flex;justify-content:space-between;align-items:center;margin-bottom:7px}
.boss-name{font-family:var(--display);font-weight:800;font-size:16px;color:#fff;-webkit-text-stroke:2px #45081e;paint-order:stroke fill;text-shadow:var(--drop)}
.boss-round{font-family:var(--display);font-weight:800;font-size:12px;color:#ffd3dc;background:rgba(255,255,255,.14);padding:2px 9px;border-radius:99px}
.boss-hp{position:relative;height:16px;border-radius:99px;background:rgba(0,0,0,.38);border:2px solid rgba(255,255,255,.55);overflow:hidden;box-shadow:inset 0 2px 4px rgba(0,0,0,.35)}
.boss-hpfill{position:absolute;left:0;top:0;height:100%;border-radius:99px;background:linear-gradient(180deg,#ff8ea2,var(--red-2) 55%,var(--red-d));box-shadow:0 0 8px rgba(255,60,90,.7);transition:width .4s var(--ease-out)}
/* Rage bar: fills on misses; full = the boss takes a life. */
.boss-rage{position:relative;height:8px;margin-top:5px;border-radius:99px;background:rgba(0,0,0,.3);border:2px solid rgba(255,255,255,.4);overflow:hidden}
.boss-ragefill{position:absolute;left:0;top:0;height:100%;border-radius:99px;background:linear-gradient(90deg,#ffb347,#ff5a3c);box-shadow:0 0 6px rgba(255,90,60,.7);transition:width .2s var(--ease-out)}
.boss-meta{display:flex;align-items:center;gap:10px;margin-top:8px;min-height:22px}
.boss-lives{gap:4px;flex:0 0 auto}
.boss-combo{font-family:var(--display);font-weight:800;font-size:14px;color:#ffe066;text-shadow:0 2px 3px rgba(80,10,25,.5)}
/* Combat powers: their own clear row below the banner — big icon + label + a
   corner charge badge, greyed when unavailable, so "what does this do / can I use
   it" reads at a glance during a real-time fight. */
.boss-powerbar{display:flex;gap:8px;margin-bottom:10px;flex:0 0 auto}
.bpow{flex:1;position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;
  padding:8px 4px;border-radius:16px;border:3px solid #fff;cursor:pointer;color:#fff;
  font-family:var(--display);font-weight:800;box-shadow:0 4px 0 rgba(0,0,0,.3),inset 0 2px 0 rgba(255,255,255,.32)}
.bpow .bpico{font-size:25px;line-height:1;filter:drop-shadow(0 1px 2px rgba(0,0,0,.35))}
.bpow .bplab{font-size:13px;letter-spacing:.01em;-webkit-text-stroke:1.4px rgba(25,10,20,.5);paint-order:stroke fill}
.bpow.shield{background:linear-gradient(180deg,var(--blue-1),var(--blue-2))}
.bpow.strike{background:linear-gradient(180deg,var(--red-1),var(--red-2))}
.bpow.slow{background:linear-gradient(180deg,var(--gold-1),var(--gold-2));color:var(--gold-t)}
.bpow.slow .bplab{-webkit-text-stroke-color:rgba(120,70,0,.4)}
.bpow:disabled{filter:grayscale(.8);opacity:.5;cursor:default;box-shadow:none}
.bpow:active:not(:disabled){transform:translateY(2px);box-shadow:0 1px 0 rgba(0,0,0,.3)}
.bpn{position:absolute;top:-8px;right:-8px;min-width:24px;height:24px;padding:0 5px;border-radius:99px;background:#fff;color:var(--ink);
  font-family:var(--display);font-weight:800;font-size:13px;line-height:20px;text-align:center;border:3px solid rgba(0,0,0,.12);box-shadow:0 2px 5px rgba(0,0,0,.3)}
.bpow.slow .bpn{display:none}
.boss-powerbar.shielded .bpow.shield{box-shadow:0 0 0 3px #fff,0 0 14px 3px var(--blue-1),0 4px 0 rgba(0,0,0,.3)}

/* The arena: boss at the top raining trash, a movable 3-bin sorter at the bottom. */
/* A boss fight fills most of the screen (not just the bottom sheet): the panel
   grows to ~96% and the arena flexes to take every pixel the banner leaves. The
   falling physics read stage.clientHeight each frame, so a taller arena just works.
   `_result` drops `.boss-mode`, so the win/lose panel reverts to a normal sheet. */
.panel.boss-mode{height:96%;max-height:96%;display:flex;flex-direction:column;overflow:hidden}
.panel.boss-mode .boss-banner{flex:0 0 auto}
.panel.boss-mode .bossarena{flex:1 1 auto;height:auto;min-height:220px}
.bossarena{position:relative;height:min(340px,52vh);border-radius:20px;overflow:hidden;
  background:radial-gradient(120% 80% at 50% 0,#5a1630,#2a0c18 70%);border:2px solid rgba(255,255,255,.35);
  box-shadow:inset 0 2px 10px rgba(0,0,0,.5);touch-action:none;user-select:none;-webkit-user-select:none}
.bossarena.weak{background:radial-gradient(120% 80% at 50% 0,#8a5a10,#3a2408 70%)}
.bossarena.slowmo::after,.bossarena.weak::after{content:'';position:absolute;inset:0;pointer-events:none}
.bossarena.slowmo::after{box-shadow:inset 0 0 0 3px rgba(120,190,255,.6)}
.bossarena.missflash{animation:arenamiss .28s ease}
.bossarena.superflash{animation:arenasuper .4s ease}
@keyframes arenamiss{0%,100%{filter:none;transform:none}30%{filter:brightness(1.3) sepia(1) hue-rotate(-30deg) saturate(3);transform:translateX(-4px)}70%{transform:translateX(4px)}}
@keyframes arenasuper{0%,100%{filter:none}40%{filter:brightness(1.8) saturate(1.6)}}
.bossfighter{position:absolute;top:8px;left:50%;transform:translateX(-50%);z-index:2;width:84px;height:84px}
.bossfighter .crt{width:84px;height:84px;display:block;filter:drop-shadow(0 4px 6px rgba(0,0,0,.45));animation:bossglare 2.4s ease-in-out infinite}
.bossfighter .bosscrown{position:absolute;top:-14px;left:50%;transform:translateX(-50%);font-size:24px;font-style:normal;filter:drop-shadow(0 2px 3px rgba(0,0,0,.45))}
.bossfighter.throw .crt{animation:bossthrow .32s ease}
.bossfighter.boss-hurt .crt{animation:bosshurtflash .4s ease}
@keyframes bossglare{0%,100%{transform:translateY(0) rotate(-1deg)}50%{transform:translateY(-3px) rotate(1deg)}}
@keyframes bossthrow{0%,100%{transform:none}35%{transform:translateY(6px) scale(1.06,.94)}}
@keyframes bosshurtflash{0%,100%{filter:drop-shadow(0 4px 6px rgba(0,0,0,.45))}40%{filter:brightness(1.5) saturate(2) drop-shadow(0 0 12px rgba(255,60,90,.95))}}
.boss-dmg{position:absolute;top:-6px;left:50%;font-family:var(--display);font-weight:800;font-size:26px;color:#fff;
  -webkit-text-stroke:3px var(--red-d);paint-order:stroke fill;text-shadow:var(--drop);pointer-events:none;z-index:5;
  animation:bossdmg .9s ease-out forwards}
.boss-dmg.weak{-webkit-text-stroke-color:#a35c00;font-size:30px}
.boss-dmg.super{-webkit-text-stroke-color:var(--purple-d);font-size:34px}
@keyframes bossdmg{0%{opacity:0;transform:translate(-50%,8px) scale(.6)}25%{opacity:1;transform:translate(-50%,-8px) scale(1.1)}100%{opacity:0;transform:translate(-50%,-46px) scale(1)}}
.arenapieces{position:absolute;inset:0;z-index:3;pointer-events:none}
.apiece{position:absolute;left:0;top:0;will-change:transform;transition:opacity .24s ease}
.apiece .crt{width:100%;height:100%;filter:drop-shadow(0 2px 3px rgba(0,0,0,.45))}
.apiece.sorted{transition:transform .24s var(--ease-out),opacity .24s ease}
.apiece.wrongdrop{transition:transform .24s ease,opacity .24s ease;filter:grayscale(.4)}
/* The single catcher: the stylish roskis bin (as in the catch minigame), sliding
   the full stage width. No label — a small sample of the target material sits on
   the bin as a wordless "catch this", ringed in the target colour. */
.arenacatcher{position:absolute;bottom:6px;left:0;z-index:4;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;
  touch-action:none;pointer-events:none;transition:left .05s linear}
.arenacatcher .catchbinimg{width:56px;height:56px;object-fit:contain;filter:drop-shadow(0 3px 4px rgba(0,0,0,.45)) drop-shadow(0 0 5px var(--bc))}
.arenacatcher .catchtarget{position:relative;z-index:2;margin-bottom:-8px;display:grid;place-items:center;width:34px;height:34px;border-radius:50%;
  background:radial-gradient(circle,color-mix(in srgb,var(--bc) 45%,#fff),color-mix(in srgb,var(--bc) 78%,#1c0e16));
  border:2px solid #fff;box-shadow:0 2px 6px rgba(0,0,0,.45)}
.arenacatcher .catchtarget .crt{width:26px;height:26px;display:block}
.bossarena.weak .arenacatcher .catchbinimg{animation:catchpulse .5s ease-in-out infinite}
@keyframes catchpulse{0%,100%{filter:drop-shadow(0 3px 4px rgba(0,0,0,.45)) drop-shadow(0 0 5px var(--bc))}50%{filter:drop-shadow(0 3px 4px rgba(0,0,0,.45)) drop-shadow(0 0 15px var(--bc))}}

/* Menacing rotating ray-burst behind every falling piece. Deliberately cheap: a
   spinning conic disc rotated on its own layer (`will-change:transform`) — NO mask
   and no per-piece glow filter, both of which janked with several pieces at once.
   The opaque sprite covers the centre, so the spokes read as rays around it. */
.apiece::before{content:'';position:absolute;left:50%;top:50%;width:165%;height:165%;transform:translate(-50%,-50%);z-index:-1;pointer-events:none;border-radius:50%;
  background:repeating-conic-gradient(from 0deg,rgba(255,70,80,.5) 0 7deg,transparent 7deg 24deg);
  will-change:transform;animation:apieceray 3.6s linear infinite}
@keyframes apieceray{to{transform:translate(-50%,-50%) rotate(360deg)}}
/* Fast drops: a brighter, quicker-spinning burst so "this one's rushing" is fair to read. */
.apiece.fast::before{animation-duration:1.3s;background:repeating-conic-gradient(from 0deg,rgba(255,55,60,.75) 0 7deg,transparent 7deg 19deg)}
.apiece.fast .crt{filter:drop-shadow(0 2px 3px rgba(0,0,0,.45)) drop-shadow(0 0 6px rgba(255,40,50,.9))}
/* Big "heavy box" drops (kartonki boss). */
.apiece.big .crt{filter:drop-shadow(0 4px 6px rgba(0,0,0,.55))}
/* A dropped glass shattering (lasi boss): a white crack flash over the stage. */
.bossarena.shatterflash{animation:arenashatter .32s ease}
@keyframes arenashatter{0%,100%{box-shadow:inset 0 2px 10px rgba(0,0,0,.5)}35%{box-shadow:inset 0 0 60px 12px rgba(255,255,255,.7)}}

/* Start-of-fight card, centred in the arena: WHAT to catch + a 5-4-3-2-1 →
   "Taistele!" countdown, so both the instruction and the count are unmissable
   (a top toast alone was easy to miss). Fades out when the fight begins. */
.arenaintro{position:absolute;left:0;right:0;top:14%;z-index:6;display:flex;flex-direction:column;align-items:center;gap:8px;pointer-events:none;transition:opacity .4s ease}
.arenaintro.hide{opacity:0}
.arenaintro .ai-lead{font-family:var(--display);font-weight:800;font-size:17px;color:#fff;-webkit-text-stroke:2px var(--red-d);paint-order:stroke fill;text-shadow:var(--drop)}
.arenaintro .ai-target{display:flex;flex-direction:column;align-items:center;gap:2px;background:rgba(0,0,0,.4);border:3px solid #fff;border-radius:18px;padding:8px 18px;box-shadow:0 4px 12px rgba(0,0,0,.45)}
.arenaintro .ai-target .crt{width:56px;height:56px;filter:drop-shadow(0 2px 4px rgba(0,0,0,.4))}
.arenaintro .ai-target span{font-family:var(--display);font-weight:800;font-size:20px;color:#fff;-webkit-text-stroke:2px rgba(30,12,20,.9);paint-order:stroke fill}
.arenaintro .ai-num{min-height:70px;font-family:var(--display);font-weight:800;font-size:60px;line-height:1;color:#fff;
  -webkit-text-stroke:5px var(--red-d);paint-order:stroke fill;text-shadow:0 4px 10px rgba(0,0,0,.5);opacity:0;transform:scale(.4)}
.arenaintro .ai-num.show{animation:arenacue .7s ease-out}
.arenaintro .ai-num.go{-webkit-text-stroke-color:var(--gold-d);font-size:44px}
@keyframes arenacue{0%{opacity:0;transform:scale(.4)}25%{opacity:1;transform:scale(1.15)}100%{opacity:1;transform:scale(1)}}
.panel.boss-shake{animation:bossshake .42s ease}
@keyframes bossshake{0%,100%{transform:none}20%{transform:translateX(-8px)}40%{transform:translateX(7px)}60%{transform:translateX(-5px)}80%{transform:translateX(4px)}}

/* Boss-defeated celebration card (reuses the levelup-fx shell + rinki rays). */
.boss-fx .rkrays{position:absolute;left:50%;top:50%;width:560px;height:560px;pointer-events:none;border-radius:50%;
  background:repeating-conic-gradient(from 0deg,rgba(255,220,120,.5) 0deg 8deg,transparent 8deg 20deg);
  -webkit-mask:radial-gradient(circle,#000 24%,transparent 62%);mask:radial-gradient(circle,#000 24%,transparent 62%);
  animation:rkrays 14s linear infinite,rkraysfade 6s ease-out forwards}
.boss-fx .burstring.r3{border-color:var(--red-2);animation-delay:.26s}
.boss-fx .bosscard{position:relative;overflow:hidden;background:linear-gradient(180deg,#ff9db0,var(--red-2) 55%,var(--red-d));color:#fff;border:5px solid #fff}
.boss-fx .bosscrownbig{font-size:40px;line-height:1;filter:drop-shadow(0 3px 5px rgba(120,10,40,.5));animation:lustar 1.6s ease-in-out}
.boss-fx .bossshot{width:94px;height:94px;margin:4px auto 0}
.boss-fx .bossshot img{width:100%;height:100%;object-fit:contain;filter:drop-shadow(0 4px 6px rgba(0,0,0,.35))}
.boss-fx .rktitle{font-family:var(--display);font-weight:800;font-size:29px;line-height:1.05;margin-top:8px;color:#fff;
  -webkit-text-stroke:2.5px #7a1128;paint-order:stroke fill;text-shadow:0 3px 5px rgba(90,10,30,.4)}
.boss-fx .rkitems{display:inline-block;margin-top:9px;padding:5px 15px;border-radius:999px;background:rgba(0,0,0,.2);
  font-family:var(--display);font-weight:800;font-size:16px;color:#fff}
.boss-fx .rkbonus{font-family:var(--display);font-weight:800;font-size:26px;margin-top:11px;color:#fff8c9;
  -webkit-text-stroke:2px #7a1128;paint-order:stroke fill;text-shadow:0 2px 4px rgba(90,10,30,.4)}

@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}.mon.expiring{opacity:.45}.mon.boss .bossping{opacity:.4}}
:focus-visible{outline:3px solid #ffe066;outline-offset:2px}
