/* css/battle-town.css - turn-based battle screen, backdrops, town map, day/night and weather.
   The CSS files are one stylesheet cut into pieces in cascade order (index.html links them in this order): a later rule
   overrides an earlier one. Append new rules to latest.css unless you know nothing later overrides them. */
/* ===== Turn-based battle screen ===== */
body.in-battle .hud { display: none; }
body.in-battle .board-wrap { padding: 8px 10px 0; }
body.in-battle .screen { position: relative; overflow: hidden; padding-bottom: calc(6px + env(safe-area-inset-bottom)); }
#battleView { position: absolute; inset: 0; display: flex; flex-direction: column; text-align: center; overflow: hidden; }
#battleView.hidden { display: none !important; }

/* ===== battle backdrop: a per-district sky + silhouette scene, dimmed further for boss fights ===== */
.bt-backdrop { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.bt-bd-sky { position: absolute; inset: 0; background: linear-gradient(180deg, var(--bt-sky-top, var(--c-grey-20)), var(--bt-sky-bot, var(--c-grey-12)) 70%); transition: background 0.6s ease; }
.bt-bd-scene { position: absolute; left: 0; right: 0; bottom: 0; height: 42%; }
.bt-bd-scene::before, .bt-bd-scene::after { content: ''; position: absolute; inset: 0; }
.bt-bd-scrim { position: absolute; inset: 0; background: radial-gradient(ellipse at 50% 30%, transparent 30%, rgba(4,6,10,0.35) 100%); opacity: 0; transition: opacity 0.6s ease; }
#battleView.bt-boss .bt-bd-scrim { opacity: 1; }
#battleView.bt-boss .bt-bd-sky { filter: saturate(0.75) brightness(0.82); }
/* a faint grain layer so the backdrop reads as textured cloth/parchment rather than a flat gradient */
.bt-bd-texture { position: absolute; inset: 0; opacity: 0.1; mix-blend-mode: overlay; pointer-events: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size: 160px 160px; }
/* a soft wash of your avatar's color rising from your half of the field - the seed of a fully customizable
   battle background later on. The opponent's half is never tinted, so the split always reads clearly. */
.bt-bd-you { position: absolute; left: 0; right: 0; bottom: 0; height: 40%; pointer-events: none;
  background: linear-gradient(0deg, var(--side-accent, transparent) 0%, transparent 100%); opacity: 0.15; transition: background 0.4s ease; }

/* silhouette shapes per district, drawn as layered CSS shapes so no image assets are needed */
.bt-bd-scene .bt-silo { position: absolute; bottom: 0; opacity: 0.55; }
/* meadow (Town Square): rolling hill + a couple of simple pitched roofs */
#battleView[data-bt-biome="meadow"] .bt-bd-scene { background: radial-gradient(ellipse 70% 100% at 50% 100%, var(--bt-hill, var(--c-green-22)) 0%, transparent 62%); }
#battleView[data-bt-biome="meadow"] .bt-bd-scene::before { left: 8%; bottom: 6%; width: 22%; height: 30%; background: var(--bt-roof, var(--c-orange-28)); clip-path: polygon(0 100%, 50% 0, 100% 100%); opacity: 0.5; }
#battleView[data-bt-biome="meadow"] .bt-bd-scene::after { right: 10%; bottom: 4%; width: 16%; height: 22%; background: var(--bt-roof, var(--c-orange-28)); clip-path: polygon(0 100%, 50% 0, 100% 100%); opacity: 0.4; }
/* bazaar (Market Row): stall awnings, a wider flatter skyline */
#battleView[data-bt-biome="bazaar"] .bt-bd-scene { background: linear-gradient(0deg, var(--bt-hill, var(--c-amber-18)) 0%, transparent 60%); }
#battleView[data-bt-biome="bazaar"] .bt-bd-scene::before { left: 6%; bottom: 8%; width: 26%; height: 16%; background: var(--bt-roof, var(--c-orange-45)); clip-path: polygon(0 100%, 0 30%, 50% 0, 100% 30%, 100% 100%); opacity: 0.55; }
#battleView[data-bt-biome="bazaar"] .bt-bd-scene::after { right: 8%; bottom: 6%; width: 20%; height: 13%; background: var(--bt-roof, var(--c-orange-45)); clip-path: polygon(0 100%, 0 30%, 50% 0, 100% 30%, 100% 100%); opacity: 0.4; }
/* harbor: water line + boat silhouettes */
#battleView[data-bt-biome="harbor"] .bt-bd-scene { background: linear-gradient(0deg, var(--bt-hill, var(--c-cyan-17)) 0%, var(--bt-hill, var(--c-cyan-17)) 26%, transparent 62%); }
#battleView[data-bt-biome="harbor"] .bt-bd-scene::before { left: 8%; bottom: 16%; width: 26%; height: 16%; background: var(--bt-roof, var(--c-blue-34)); clip-path: polygon(0% 100%, 15% 25%, 40% 25%, 40% 0%, 60% 0%, 60% 25%, 100% 100%); opacity: 0.6; }
#battleView[data-bt-biome="harbor"] .bt-bd-scene::after { right: 10%; bottom: 14%; width: 20%; height: 12%; background: var(--bt-roof, var(--c-blue-34)); clip-path: polygon(0% 100%, 15% 30%, 45% 30%, 45% 0%, 55% 0%, 55% 30%, 100% 100%); opacity: 0.45; }
/* orchard (Hollow Garden): tree canopy silhouettes */
#battleView[data-bt-biome="orchard"] .bt-bd-scene { background: radial-gradient(ellipse 80% 100% at 50% 100%, var(--bt-hill, var(--c-green-18)) 0%, transparent 65%); }
#battleView[data-bt-biome="orchard"] .bt-bd-scene::before { left: 6%; bottom: 2%; width: 26%; height: 34%; background: var(--bt-roof, var(--c-green-30)); border-radius: 50% 50% 45% 45%; opacity: 0.5; }
#battleView[data-bt-biome="orchard"] .bt-bd-scene::after { right: 8%; bottom: 0; width: 22%; height: 28%; background: var(--bt-roof, var(--c-green-30)); border-radius: 50% 50% 45% 45%; opacity: 0.4; }

/* opponent/player portrait badges on each side bar */
.bt-portrait { position: relative; width: 44px; height: 44px; border-radius: 50%; background: var(--water-glow); display: flex; align-items: center; justify-content: center; font-size: 1.5rem; flex-shrink: 0; box-shadow: 0 1px 4px var(--shadow); box-sizing: border-box; border: var(--av-border-w, 0) var(--av-border-style, solid) var(--av-border-color, transparent); }
[data-accessory]:not([data-accessory=""])::after { content: attr(data-accessory); position: absolute; right: -3px; bottom: -3px; font-size: 0.8em; line-height: 1; filter: drop-shadow(0 1px 1.5px rgba(0,0,0,0.5)); pointer-events: none; }
.bt-portrait.av-glow { box-shadow: 0 1px 4px var(--shadow), 0 0 10px 2px var(--av-glow-color, transparent); }
#battleView.bt-boss .bt-portrait#btOppPortrait { background: var(--boss-glow, var(--c-red-61)); box-shadow: 0 0 8px 1px rgba(201,111,111,0.55); }


#battleView .bt-mini { width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--stone-edge); background: var(--stone); color: var(--ink); font-size: 1rem; display: flex; align-items: center; justify-content: center; flex-shrink: 0; cursor: pointer; padding: 0; font-family: inherit; box-shadow: 0 1px 4px var(--shadow); }
#battleView .bt-mini:active { transform: scale(0.9); }
#battleView.tip-open .coach { visibility: hidden; }   /* the sheet sits over it */
#battleView .tip { pointer-events: none; }   /* an info sheet: taps go through to what is under it, so it can never hide a button */
#battleView .card .stats, #battleView .card .nm { text-align: left; }
#battleView .card .nm { text-align: center; }

#battleView .side { position: relative; z-index: 2; flex-shrink:0; display:flex; align-items:center; gap:8px; padding:6px 10px; background:var(--stone); border:1px solid var(--stone-edge); border-left:4px solid var(--stone-edge); border-radius:14px; box-shadow:0 2px 8px var(--shadow); transition:box-shadow .3s ease, transform .3s ease; }
/* your side of the screen carries your avatar's accent color; the opponent's stays plain and district-neutral */
#battleView #btYouBar { border-left-color: var(--side-accent, var(--stone-edge)); }
#battleView .side.turn { box-shadow:0 0 0 2px var(--water), 0 2px 10px var(--shadow); }
#battleView .side .who { font-size:.78rem; font-weight:600; min-width:0; flex:1; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
#battleView .spirit { position:relative; flex:2; height:20px; background:var(--stone-edge); border-radius:999px; overflow:hidden; }
#battleView .spirit .fill { position:absolute; inset:0 auto 0 0; width:100%; background:linear-gradient(90deg,var(--rc-a),var(--rc-b)); border-radius:999px; transition:width .5s cubic-bezier(.34,1.2,.5,1), background .3s ease; }
#battleView .spirit.low .fill { background:linear-gradient(90deg,var(--c-orange-77),var(--lose-glow)); }
#battleView .spirit .num { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; font-size:.72rem; font-weight:700; color:var(--c-grey-100); text-shadow:0 1px 2px rgba(0,0,0,.35); }
#battleView .spirit.hit { animation:bt_shake .35s ease; }
#battleView .spirit.heal { animation:bt_pulseHeal .5s ease; }
@keyframes bt_shake { 0%,100%{transform:translateX(0)} 25%{transform:translateX(-4px)} 75%{transform:translateX(4px)} }
@keyframes bt_pulseHeal { 0%,100%{transform:scale(1)} 50%{transform:scale(1.06)} }
#battleView .deckcount { font-size:.68rem; color:var(--ink-soft); white-space:nowrap; }
/* Stays up for the whole match once a snack is eaten - the spirit bar alone doesn't show the bonus
   clearly (it raises max spirit right along with current spirit, so a full bar still reads as full),
   so this badge is the one persistent reminder that the buff is active. See eatSnack/btSyncSnackBadge. */
#battleView .snack-badge { font-size: 1.05rem; flex-shrink: 0; animation: pop-in 0.3s ease; }
#battleView .side.target-glow { box-shadow:0 0 0 3px var(--boss-glow), 0 0 18px rgba(201,111,111,.5); cursor:pointer; animation:bt_tglow 1s ease-in-out infinite; }
@keyframes bt_tglow { 50% { box-shadow:0 0 0 3px var(--boss-glow), 0 0 26px rgba(201,111,111,.8); } }
#battleView .field { position: relative; z-index: 2; flex:1; min-height:0; display:flex; flex-direction:column; justify-content:center; gap:6px; padding:8px 0; }
#battleView .board { flex:0 1 auto; min-height:0; display:grid; grid-template-columns:repeat(4,1fr); gap:6px; align-items:stretch; padding:8px 6px; background:var(--board-bg); border-radius:16px; position:relative; border:1px solid transparent; }
/* your board gets a faint wash of your avatar's color; the opponent's board is left plain */
#battleView #btYouBoard { border-color: var(--side-accent, transparent); }
#battleView #btYouBoard::before { content:''; position:absolute; inset:0; border-radius:16px; background:var(--side-accent, transparent); opacity:.07; pointer-events:none; }
#battleView .slot { min-height:0; width:100%; aspect-ratio:3/4; align-self:center; border:1.5px dashed var(--stone-edge); border-radius:12px; display:flex; align-items:stretch; justify-content:center; position:relative; }
#battleView .slot.drop { border-color:var(--water); background:rgba(127,179,172,.18); animation:bt_pulse 1.2s ease-in-out infinite; }
@keyframes bt_pulse { 50% { background:rgba(127,179,172,.32); } }
#battleView .divider { flex-shrink:0; text-align:center; font-size:.72rem; color:var(--ink-soft); min-height:18px; display:flex; align-items:center; justify-content:center; gap:8px; }
#battleView .coach { font-size:.72rem; color:var(--ink-soft); text-align:center; min-height:16px; padding:0 8px; }
#battleView .divider .msg { padding:2px 10px; background:var(--stone); border-radius:999px; border:1px solid var(--stone-edge); transition:opacity .3s; }
#battleView .card { position:relative; width:100%; height:100%; border-radius:12px; color:var(--c-grey-100); display:flex; flex-direction:column; align-items:center; justify-content:space-between; padding:5px 3px 4px; box-shadow:0 4px 10px var(--shadow); cursor:pointer; transition:transform .2s cubic-bezier(.34,1.56,.64,1), box-shadow .2s ease, opacity .3s ease, filter .3s ease; }
#battleView .rarity-common { background:linear-gradient(135deg,var(--rc-a),var(--rc-b)); }
#battleView .rarity-rare { background:linear-gradient(135deg,var(--c-indigo-82),var(--rare)); }
#battleView .rarity-ultra { background:linear-gradient(135deg,var(--c-blue-78),var(--epic)); }
#battleView .rarity-super { background:linear-gradient(135deg,var(--c-amber-76),var(--accent)); }
#battleView .rarity-mythic { background:linear-gradient(135deg,var(--c-pink-79),var(--mythic)); box-shadow:0 0 10px rgba(217,143,176,.5); }
#battleView .card .cost { position:absolute; top:-7px; left:-7px; width:22px; height:22px; border-radius:50%; background:var(--ink); color:var(--stone); font-size:.72rem; font-weight:700; display:flex; align-items:center; justify-content:center; box-shadow:0 2px 5px var(--shadow); z-index:2; }
#battleView .card .icon { font-size:clamp(1.6rem,7vw,2.3rem); line-height:1; margin-top:10px; }
#battleView .hand .card .icon { font-size:1.5rem; margin-top:8px; }
#battleView .card .nm { font-size:.66rem; opacity:.92; text-align:center; line-height:1.05; padding:0 1px; max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
#battleView .card .kws { font-size:.85rem; min-height:14px; display:flex; gap:2px; }
#battleView .hand .card .kws { font-size:.7rem; }
#battleView .hand .card .nm { font-size:.55rem; }
#battleView .card .stats { width:100%; display:flex; justify-content:space-between; padding:0 5px; font-weight:800; font-size:.95rem; }
#battleView .hand .card .stats { font-size:.78rem; padding:0 3px; }
#battleView .card .stats .pw { text-shadow:0 1px 2px rgba(0,0,0,.35); }
#battleView .card .stats .hp { color:var(--c-grey-100); text-shadow:0 1px 2px rgba(0,0,0,.35); }
#battleView .card .stats .hp.hurt { color:var(--c-orange-92); }
#battleView .card.exhausted { filter:saturate(.55) brightness(.92); }
#battleView .card .badge { position:absolute; z-index:3; line-height:1; pointer-events:none; }
#battleView .card .badge.zz { top:-7px; right:-4px; font-size:.85rem; }
#battleView .card .badge.grd { bottom:-10px; left:50%; transform:translateX(-50%); font-size:1.05rem; background:var(--stone); border-radius:999px; padding:1px 4px; box-shadow:0 1px 4px var(--shadow); }
#battleView .hand .card .badge.grd { display:none; }
#battleView .card.can-act { box-shadow:0 0 0 2.5px var(--accent), 0 4px 12px var(--shadow); animation:bt_ready 1.4s ease-in-out infinite; }
@keyframes bt_ready { 50% { box-shadow:0 0 0 2.5px var(--accent), 0 0 16px rgba(217,160,102,.7); } }
#battleView .card.selected { transform:translateY(-6px) scale(1.06); box-shadow:0 0 0 3px var(--c-grey-100), 0 0 0 5px var(--water), 0 8px 18px var(--shadow); animation:none; z-index:5; }
#battleView .card.targetable, #battleView .card.targetable.guarding { box-shadow:0 0 0 3.5px var(--boss-glow), 0 0 16px rgba(201,111,111,.6); animation:bt_tglow 1s ease-in-out infinite; }
#battleView .card.shielded::before { content:''; position:absolute; inset:-3px; border-radius:15px; border:2px solid rgba(255,255,255,.85); box-shadow:0 0 10px rgba(255,255,255,.6); pointer-events:none; }
#battleView .card.guarding { box-shadow:0 0 0 3px var(--ink), 0 4px 10px var(--shadow); }
#battleView .card.enter { animation:bt_cardIn .45s cubic-bezier(.34,1.56,.64,1); }
@keyframes bt_cardIn { 0% { transform:translateY(-30px) scale(.6); opacity:0 } 100% { transform:none; opacity:1 } }
#battleView .card.faint { animation:bt_faint .5s ease forwards; }
@keyframes bt_faint { 0% { transform:none; opacity:1 } 100% { transform:translateY(14px) scale(.7) rotate(-6deg); opacity:0; filter:grayscale(1) } }
#battleView .card.strike-up { animation:bt_strikeUp .42s cubic-bezier(.36,1.3,.4,1); z-index:6; }
#battleView .card.strike-down { animation:bt_strikeDown .42s cubic-bezier(.36,1.3,.4,1); z-index:6; }
@keyframes bt_strikeUp { 0% { transform:translateY(0) scale(1) rotate(0); } 18% { transform:translateY(5px) scale(.95) rotate(-2deg); } 55% { transform:translateY(-28px) scale(1.16) rotate(3deg); } 100% { transform:translateY(0) scale(1) rotate(0); } }
@keyframes bt_strikeDown { 0% { transform:translateY(0) scale(1) rotate(0); } 18% { transform:translateY(-5px) scale(.95) rotate(2deg); } 55% { transform:translateY(28px) scale(1.16) rotate(-3deg); } 100% { transform:translateY(0) scale(1) rotate(0); } }
#battleView .card.hurt-flash { animation:bt_hurtFlash .46s ease; }
@keyframes bt_hurtFlash { 0% { transform:none; filter:none; } 22% { filter:brightness(1.9) saturate(.3) drop-shadow(0 0 6px rgba(255,90,70,.85)); transform:translateX(-6px) scale(1.06); } 48% { transform:translateX(6px) scale(1.06); } 74% { transform:translateX(-2px) scale(1.02); } 100% { transform:none; filter:none; } }
#battleView .impact-spark { position:absolute; left:50%; top:50%; font-size:.85rem; pointer-events:none; z-index:22; animation:bt_impactSpark .55s ease-out forwards; }
@keyframes bt_impactSpark { 0% { opacity:1; transform:translate(-50%,-50%) scale(.5); } 100% { opacity:0; transform:translate(calc(-50% + var(--dx,10px)), calc(-50% + var(--dy,-10px))) scale(1.1); } }
#battleView .floater { position:absolute; left:50%; top:30%; transform:translateX(-50%); font-weight:800; font-size:1.15rem; pointer-events:none; z-index:20; text-shadow:0 2px 6px rgba(0,0,0,.4); animation:bt_floatUp 1s ease forwards; }
#battleView .floater.dmg { color:var(--c-red-82); }
#battleView .floater.heal { color:var(--c-green-83); }
#battleView .floater.blk { color:var(--c-blue-95); }
@keyframes bt_floatUp { 0% { opacity:0; transform:translate(-50%,6px) scale(.7) } 20% { opacity:1; transform:translate(-50%,-4px) scale(1.15) } 100% { opacity:0; transform:translate(-50%,-34px) scale(1) } }
#battleView .handwrap { position: relative; z-index: 2; flex-shrink:0; }
#battleView .energy { display:flex; align-items:center; justify-content:space-between; padding:2px 4px 6px; }
#battleView .gems { display:flex; gap:5px; align-items:center; }
#battleView .gem { width:15px; height:15px; border-radius:50%; background:var(--stone-edge); border:1.5px solid var(--stone-edge); transition:all .25s cubic-bezier(.34,1.56,.64,1); }
#battleView .gem.on { background:radial-gradient(circle at 35% 30%, var(--c-amber-95), var(--accent)); border-color:var(--c-orange-50); box-shadow:0 0 8px rgba(217,160,102,.6); transform:scale(1.1); }
#battleView .gem.spent { background:transparent; border-color:var(--ink-soft); opacity:.45; }
#battleView .endbtn { border:none; background:var(--side-accent, var(--rc-b)); color:var(--c-grey-100); padding:10px 20px; min-height:44px; border-radius:999px; font-size:.86rem; font-family:inherit; cursor:pointer; transition:transform .15s ease, background .2s ease, opacity .2s; }
#battleView .endbtn:active { transform:scale(.94); }
#battleView .endbtn:disabled { opacity:.45; }
#battleView .endbtn.turn-active { box-shadow:0 0 0 3px rgba(127,179,172,.3), 0 2px 8px var(--shadow); }
#battleView .endbtn.pulse { animation:bt_pulseBtn 1.4s ease-in-out infinite; }
@keyframes bt_pulseBtn { 50% { box-shadow:0 0 0 8px rgba(127,179,172,.4); } }
#battleView .hand { display:flex; justify-content:center; gap:4px; height:124px; align-items:flex-end; padding:8px 2px 2px; }
#battleView .hand .hcard { flex:1 1 0; min-width:0; max-width:74px; height:114px; position:relative; }
#battleView .hand .card { transform-origin:bottom center; }
#battleView .hand .card.unaffordable { opacity:.72; filter:saturate(.55); }
#battleView .hand .card.unaffordable .cost { background:var(--ink-soft); }
#battleView .hand .card.playable { box-shadow:0 0 0 2px var(--water-glow), 0 4px 10px var(--shadow); }
#battleView .hand .card.selected { transform:translateY(-18px) scale(1.12); }
#battleView .hand .card.draw { animation:bt_drawIn .45s cubic-bezier(.34,1.56,.64,1); }
@keyframes bt_drawIn { 0% { transform:translateY(40px) scale(.6); opacity:0 } 100% { transform:none; opacity:1 } }
#battleView .tip { position:absolute; left:10px; right:10px; bottom:calc(178px + env(safe-area-inset-bottom)); background:var(--stone); border:1px solid var(--stone-edge); border-radius:16px; padding:10px 12px; box-shadow:0 10px 30px var(--shadow); z-index:30; display:none; }
#battleView .tip.show { display:block; animation:bt_cardIn .3s ease; }
#battleView .tip .t-h { display:flex; align-items:center; gap:8px; margin-bottom:4px; }
#battleView .tip .t-h .ic { font-size:1.5rem; }
#battleView .tip .t-h b { font-size:.95rem; }
#battleView .tip .t-h small { color:var(--ink-soft); margin-left:auto; font-size:.7rem; }
#battleView .tip .kw { font-size:.76rem; margin:3px 0; line-height:1.35; }
#battleView .tip .kw b { color:var(--accent); }
#battleView .tip .hint { font-size:.7rem; color:var(--ink-soft); margin-top:5px; }
/* spells: a dashed inner frame and a "Spell" ribbon where a creature's stats would sit */
#battleView .card.spell { justify-content:flex-start; }
#battleView .card.spell::after { content:''; position:absolute; inset:3px; border-radius:9px; border:1.5px dashed rgba(255,255,255,.55); pointer-events:none; }
#battleView .card .spell-tag { margin-top:auto; font-size:.58rem; font-weight:800; letter-spacing:.04em; background:rgba(0,0,0,.28); border-radius:999px; padding:1px 6px; }
#battleView .card.spell-aim { box-shadow:0 0 0 3px var(--c-grey-100), 0 0 18px rgba(255,240,180,.9); }
#battleView .card.bounce-away { animation:bt_bounceAway .42s ease forwards; }
@keyframes bt_bounceAway { to { transform:translateY(-40px) scale(.6) rotate(8deg); opacity:0; } }
#battleView .spell-flash { position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); z-index:25; pointer-events:none; width:110px; padding:12px 8px 10px; border-radius:16px; display:flex; flex-direction:column; align-items:center; gap:4px; color:var(--c-grey-100);
  box-shadow:0 0 0 3px rgba(255,255,255,.7), 0 0 36px rgba(255,240,190,.7); animation:bt_spellFlash .9s ease forwards; }
#battleView .spell-flash .sf-icon { font-size:2.6rem; line-height:1; }
#battleView .spell-flash .sf-name { font-size:.8rem; font-weight:700; }
@keyframes bt_spellFlash { 0% { transform:translate(-50%,-50%) scale(.3) rotate(-10deg); opacity:0; } 25% { transform:translate(-50%,-50%) scale(1.1) rotate(2deg); opacity:1; } 70% { transform:translate(-50%,-50%) scale(1); opacity:1; } 100% { transform:translate(-50%,-50%) scale(1.25); opacity:0; } }
#battleView .bt-weather { font-size:.66rem; padding:2px 8px; border-radius:999px; background:rgba(0,0,0,.3); border:1px solid var(--stone-edge); color:var(--ink); white-space:nowrap; }
#battleView .bt-weather:empty { display:none; }
#battleView .divider { flex-wrap: wrap; row-gap: 4px; }
#battleView.tide-wash .field::after { content:''; position:absolute; inset:0; pointer-events:none; z-index:24; background:linear-gradient(90deg, transparent, rgba(120,200,255,.35), transparent); animation:bt_tide .9s ease forwards; }
@keyframes bt_tide { from { transform:translateX(-100%); } to { transform:translateX(100%); } }
/* card sleeves (Tinker's stall): an inner pattern layer and a corner emblem, only ever on your own cards */
#battleView .card .sleeve-fx { position:absolute; inset:0; border-radius:12px; pointer-events:none; opacity:.35; z-index:0; }
#battleView .card.sleeved > *:not(.sleeve-fx):not(.sleeve-mark):not(.cost):not(.badge):not(.floater):not(.impact-spark):not(.kws):not(.mastery-stars) { position:relative; z-index:1; }
#battleView .card .sleeve-mark { position:absolute; top:3px; right:4px; font-size:.62rem; line-height:1; z-index:2; filter:drop-shadow(0 1px 1px rgba(0,0,0,.5)); }
#battleView .card.sleeved { outline:2px solid var(--sleeve-edge, rgba(255,255,255,.6)); outline-offset:-2px; }
.sleeve-gilded .sleeve-fx { background:linear-gradient(135deg, rgba(255,230,150,.9) 0 10%, transparent 10% 45%, rgba(255,230,150,.7) 45% 50%, transparent 50% 90%, rgba(255,230,150,.9) 90%); }
.sleeve-leafy .sleeve-fx { background:radial-gradient(ellipse 30% 18% at 20% 85%, var(--c-green-83-2) 60%, transparent 62%), radial-gradient(ellipse 22% 14% at 80% 20%, var(--c-green-83-2) 60%, transparent 62%); }
.sleeve-starry .sleeve-fx { background:radial-gradient(circle at 20% 25%, var(--c-grey-100) 1.5px, transparent 2px), radial-gradient(circle at 70% 15%, var(--c-grey-100) 1px, transparent 1.5px), radial-gradient(circle at 80% 70%, var(--c-grey-100) 1.5px, transparent 2px), radial-gradient(circle at 35% 80%, var(--c-grey-100) 1px, transparent 1.5px), linear-gradient(180deg, var(--c-blue-23), var(--c-indigo-29)); opacity:.5; }
.sleeve-ember .sleeve-fx { background:linear-gradient(0deg, rgba(255,110,40,.95), transparent 60%); }
.sleeve-tide .sleeve-fx { background:repeating-radial-gradient(circle at 50% 120%, rgba(160,230,255,.9) 0 3px, transparent 3px 9px); }
.sleeve-petal .sleeve-fx { background:radial-gradient(ellipse 12% 8% at 25% 30%, var(--c-pink-91) 60%, transparent 64%), radial-gradient(ellipse 12% 8% at 70% 55%, var(--c-pink-91) 60%, transparent 64%), radial-gradient(ellipse 12% 8% at 40% 82%, var(--c-pink-91) 60%, transparent 64%); opacity:.6; }
.sleeve-swatch { width:40px; height:54px; border-radius:8px; position:relative; overflow:hidden; flex-shrink:0; background:linear-gradient(135deg,var(--water-glow),var(--water)); outline:2px solid var(--sleeve-edge, transparent); outline-offset:-2px; box-shadow:0 2px 6px var(--shadow); }
.sleeve-swatch .sleeve-fx { position:absolute; inset:0; opacity:.45; }
.sleeve-swatch .sleeve-mark { position:absolute; top:3px; right:4px; font-size:.6rem; }
@media (max-width:350px) {
#battleView .hand .card .nm { display:none; }
#battleView .hand .card .stats { font-size:.7rem; padding:0 2px; }
#battleView .hand .card .icon { font-size:1.25rem; margin-top:6px; }
#battleView .hand .card .cost { width:19px; height:19px; font-size:.66rem; top:-6px; left:-6px; }
#battleView .card .nm { font-size:.58rem; }
}

/* ===== Town map ===== */
.town-view { --t: 48px; position: relative; overflow: hidden; margin: 0 auto 10px; border-radius: 18px; background: var(--ground);
  box-shadow: 0 2px 10px var(--shadow), inset 0 0 0 3px rgba(255,255,255,0.16); touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
.town-world { position: absolute; left: 0; top: 0; will-change: transform; }

/* ===== day/night + weather overlays ===== */
.town-sky { position: absolute; inset: 0; z-index: 5; pointer-events: none; background: var(--sky-color, transparent); opacity: var(--sky-opacity, 0); transition: opacity 3s linear;
  -webkit-mask-image: var(--night-hole, none); mask-image: var(--night-hole, none); }
/* v1.78.0: no mix-blend-mode here (multiply / soft-light make the browser re-composite the whole map every frame) and no
   transition on the colour itself (repainted the full overlay for 3s after every tick); only opacity fades. The tint
   is a plain overlay, so its opacity is scaled down to read about as softly as soft-light did. */
.town-sky::after { content: ''; position: absolute; inset: 0; background: var(--sky-tint, transparent); opacity: calc(var(--sky-tint-opacity, 0) * .55); transition: opacity 3s linear; }
.town-vignette { position: absolute; inset: 0; z-index: 6; pointer-events: none; opacity: var(--vig-opacity, 0); transition: opacity 3s linear;
  background: radial-gradient(ellipse at center, transparent 45%, rgba(4,8,14,0.75) 100%);
  -webkit-mask-image: var(--night-hole, none); mask-image: var(--night-hole, none); }
.town-weather { position: absolute; inset: -20% -10%; z-index: 7; pointer-events: none; opacity: 0; transition: opacity 2.2s ease; overflow: hidden; }
.town-weather.on { opacity: 0.85; }

.town-weather .rain-layer { position: absolute; inset: 0; overflow: hidden; opacity: 0; transition: opacity 2.2s ease; }
.town-weather.weather-rain .rain-layer, .town-weather.weather-storm .rain-layer { opacity: 1; }
.town-weather .rain-drop { position: absolute; top: -10%; width: 1px; height: 5%; border-radius: 2px; background: linear-gradient(180deg, rgba(200,215,235,0), rgba(200,215,235,0.4) 65%, rgba(200,215,235,0.5)); animation: rain-fall linear infinite; will-change: transform; }
.town-weather.weather-storm .rain-drop { background: linear-gradient(180deg, rgba(200,215,235,0), rgba(200,215,235,0.5) 65%, rgba(210,225,245,0.6)); height: 6%; }
/* Animates transform (GPU-composited) instead of top - see HANDOFF §9: animating `top` on dozens of elements
   at once forces a layout recalc every frame, which is heavy enough on mid-range phones to drop/tear frames.
   --fall is set per-drop in JS (buildRainDrops()) to the actual pixel distance from -10% to 138% of the
   layer's real height, since transform's percentage units are relative to the element itself, not its parent. */
@keyframes rain-fall { from { transform: rotate(8deg) translateY(0); } to { transform: rotate(8deg) translateY(var(--fall, 500px)); } }
.town-weather .lightning { position: absolute; inset: 0; background: rgba(230,238,255,0.9); opacity: 0; }
.town-weather.weather-storm .lightning { animation: lightning-flash 7s ease-in-out infinite; }
@keyframes lightning-flash { 0%, 96%, 100% { opacity: 0; } 97% { opacity: 0.8; } 97.5% { opacity: 0.1; } 98% { opacity: 0.6; } 98.6% { opacity: 0; } }

.town-weather .snow-field { position: absolute; inset: 0; opacity: 0; transition: opacity 2.4s ease; }
.town-weather.weather-snow .snow-field { opacity: 1; }
.town-weather .snow-flake { position: absolute; top: -8%; border-radius: 50%; background: var(--c-grey-96); opacity: 0.85; animation: snow-fall linear infinite; box-shadow: 0 0 3px rgba(255,255,255,0.6); will-change: transform; }
/* transform instead of top/margin-left - see the rain-fall comment above for why. --fall is set in JS
   (buildSnowFlakes()) to the layer's real pixel height x1.26 (the -8% to 118% distance); --drift stays a
   small px offset, now applied via translateX instead of the layout-triggering margin-left. */
@keyframes snow-fall { from { transform: translate(0, 0); } to { transform: translate(var(--drift, 20px), var(--fall, 500px)); } }

/* Clear and cloudy used to be the only two weather kinds with no overlay at all - a faint sun burst in the top corner
   for clear, soft shadow-clouds for cloudy. The clear effect used to be a wide bright band sweeping across the whole
   map, which read as a screen flash (v1.60.0 replaced it). The burst is small, soft and stays put: it only breathes
   (a slow transform scale), and opacity is gated by the weather class rather than animated (HANDOFF §9). It is
   hidden at night. */
.town-weather .sun-burst { position: absolute; top: -26%; right: -24%; width: 100%; aspect-ratio: 1; border-radius: 50%; opacity: 0; pointer-events: none;
  background: radial-gradient(circle, rgba(255,240,196,0.42) 0, rgba(255,240,196,0.14) 26%, transparent 58%), repeating-conic-gradient(rgba(255,246,214,0.11) 0 5deg, transparent 5deg 20deg);
  -webkit-mask-image: radial-gradient(circle, var(--c-grey-0) 12%, transparent 68%); mask-image: radial-gradient(circle, var(--c-grey-0) 12%, transparent 68%);
  animation: sun-breathe 14s ease-in-out infinite; transition: opacity 3s ease; }
.town-weather.weather-clear .sun-burst { opacity: 0.8; }
.town-view.is-night .town-weather .sun-burst { opacity: 0; }
@keyframes sun-breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.07); } }
.town-weather .cloud-shadow { position: absolute; left: -20%; top: -20%; width: 55%; height: 140%; opacity: 0; border-radius: 50%; background: radial-gradient(ellipse at center, rgba(50,58,66,0.22), rgba(50,58,66,0.06) 55%, transparent 75%); animation: fog-drift 48s linear infinite; transition: opacity 2.4s ease; }
.town-weather.weather-cloudy .cloud-shadow { opacity: 1; }
.town-weather .cloud-shadow.c2 { left: 35%; top: -5%; animation-duration: 64s; animation-delay: -20s; }
.town-weather.weather-cloudy .cloud-shadow.c2 { opacity: 0.7; }

.sky-pill .hp-icon { transition: transform 0.4s ease; }
/* Every tile gets a grid line via outline (never clobbered by the inline box-shadow that path/water
   tiles use for their extra terrain-edge highlight) - that's the game's tile-grid look. */
/* The gap is a real inset: padding shrinks the background away from the tile's edges (box-sizing keeps
   the outer footprint exactly var(--t), so the grid math entities are placed against never moves), and
   background-clip stops that background from filling back out under the padding. Sprites/props still
   read inset:0 against the padding box, so trees keep overlapping into neighboring tiles as before. */
.town-tile { position: absolute; left: calc(var(--x) * var(--t)); top: calc(var(--y) * var(--t)); width: var(--t); height: var(--t); box-sizing: border-box; padding: 1.5px; background-clip: content-box; border-radius: 6px; outline: 1px solid rgba(0,0,0,0.32); outline-offset: -1px; }
.town-tile.g1 { background: var(--ground); } .town-tile.g2 { background: var(--ground2); } .town-tile.g3 { background: var(--ground3); }
.town-tile.t-path { background: var(--path); } .town-tile.t-water { background: var(--water); }
.town-tile .spr { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
.town-tile .spr.tree { inset: auto; left: -10%; top: -20%; width: 120%; height: 120%; }
.fish-hint { position: absolute; left: 14%; top: 38%; font-size: calc(var(--t) * 0.32); opacity: 0.6; pointer-events: none;
  text-shadow: 0 1px 1px rgba(0,0,0,0.35); animation: fish-drift var(--fdur, 4s) ease-in-out infinite; animation-delay: var(--fdelay, 0s); }
@keyframes fish-drift {
  0% { transform: translateX(0) scaleX(1); opacity: 0.35; }
  8% { opacity: 0.65; }
  45% { transform: translateX(60%) scaleX(1); opacity: 0.65; }
  50% { transform: translateX(60%) scaleX(-1); opacity: 0.65; }
  92% { opacity: 0.35; }
  100% { transform: translateX(0) scaleX(-1); opacity: 0.35; }
}
.bld { position: absolute; left: calc(var(--x) * var(--t)); top: calc(var(--y) * var(--t)); width: calc(var(--w) * var(--t)); height: calc(var(--h) * var(--t)); pointer-events: none; }
.bld svg { width: 100%; height: 100%; overflow: visible; }
.ent { position: absolute; left: calc(var(--x) * var(--t)); top: calc(var(--y) * var(--t)); width: var(--t); height: var(--t); display: flex; align-items: center; justify-content: center; pointer-events: none; }
.ent > span { font-size: calc(var(--t) * 0.6); line-height: 1; position: relative; z-index: 1; }
.ent-name {
  position: absolute; left: 50%; bottom: -15%; transform: translateX(-50%); z-index: 2;
  font-size: calc(var(--t) * 0.19); line-height: 1.1; color: var(--c-grey-100); font-weight: 600;
  text-shadow: 0 1px 2px rgba(0,0,0,0.9), 0 0 4px rgba(0,0,0,0.6); white-space: nowrap;
  pointer-events: none; max-width: 220%; overflow: hidden; text-overflow: ellipsis;
}
.ent.npc::before, .ent.boss::before, .ent.item::before { content: ''; position: absolute; bottom: 12%; width: 56%; height: 14%; border-radius: 50%; background: var(--tshadow); }
.ent.npc > span, .ent.boss > span { animation: idle 2.6s ease-in-out infinite; }
.ent.boss > span { font-size: calc(var(--t) * 0.68); text-shadow: 0 0 6px var(--boss-glow); animation: boss-aura 1.8s ease-in-out infinite, idle 2.6s ease-in-out infinite; }
@keyframes boss-aura { 50% { opacity: .86; } }   /* was a filter animation, which repainted every frame (v1.78.0) */
/* the 30-min appear/disappear cycle: a dramatic pop-in, and a shrink-and-fade on the way out */
.ent.boss.boss-appear-cycle > span { animation: boss-cycle-appear 0.7s cubic-bezier(0.34, 1.56, 0.64, 1), boss-aura 1.8s ease-in-out 0.7s infinite, idle 2.6s ease-in-out 0.7s infinite; }
@keyframes boss-cycle-appear {
  0% { transform: scale(0) rotate(-18deg); opacity: 0; filter: drop-shadow(0 0 20px var(--boss-glow)); }
  55% { transform: scale(1.3) rotate(8deg); opacity: 1; }
  100% { transform: scale(1) rotate(0deg); opacity: 1; }
}
.ent.boss.boss-vanish > span { animation: boss-cycle-vanish 0.6s ease-in forwards; }
@keyframes boss-cycle-vanish {
  0% { transform: scale(1); opacity: 1; filter: drop-shadow(0 0 6px var(--boss-glow)); }
  100% { transform: scale(0.15) translateY(-16%); opacity: 0; filter: drop-shadow(0 0 24px var(--boss-glow)); }
}
.ent.item > span { animation: bob 1.8s ease-in-out infinite; text-shadow: 0 2px 3px rgba(0,0,0,0.25); }
.ent.item.despawning { animation: fade-out 0.9s ease forwards; }
.ent.spirit { transition: left 500ms ease-in-out, top 500ms ease-in-out; }
.ent.spirit > span { font-size: calc(var(--t) * 0.46); opacity: 0.82; animation: spirit-drift 3.2s ease-in-out infinite; text-shadow: 0 0 5px rgba(255,255,255,0.4); }
@keyframes spirit-drift { 0%, 100% { transform: translateY(0) scale(1); opacity: 0.7; } 50% { transform: translateY(-5px) scale(1.06); opacity: 0.95; } }
/* No filter: blur() here - the radial-gradient's own falloff gives the soft edge, same reasoning as the
   cloud-shadow effect (see HANDOFF-HISTORY.md's animation postmortem): blur on a continuously-animated
   element is a GPU-compositing cost that's invisible in desktop testing but real on a phone. */
.chest-glow-core { position: absolute; left: 50%; top: 55%; width: 150%; height: 150%; transform: translate(-50%, -50%) scale(0.9); z-index: -1;
  border-radius: 50%; background: radial-gradient(circle, rgba(255,214,120,0.55) 0%, rgba(255,190,80,0.2) 55%, transparent 78%);
  animation: chest-pulse 2.2s ease-in-out infinite; }
.chest-icon { animation: bob 1.8s ease-in-out infinite; text-shadow: 0 2px 3px rgba(0,0,0,0.3); }
@keyframes chest-pulse { 0%, 100% { opacity: 0.45; transform: translate(-50%, -50%) scale(0.85); } 50% { opacity: 0.95; transform: translate(-50%, -50%) scale(1.2); } }
.ent.npc-arrive > span { animation: npc-arrive 0.8s cubic-bezier(0.34, 1.56, 0.64, 1), idle 2.6s ease-in-out 0.8s infinite; }
.ent.npc.rival > span { text-shadow: 0 0 6px rgba(255,210,110,0.9); }
.ent.npc.rival::after { content: ''; position: absolute; inset: 10%; border-radius: 50%; border: 2px dashed rgba(255,210,110,0.7); animation: rival-spin 6s linear infinite; pointer-events: none; }
@keyframes rival-spin { to { transform: rotate(360deg); } }
.ent.crop > span { font-size: calc(var(--t) * 0.5); text-shadow: 0 2px 2px rgba(0,0,0,0.3); }
.ent.crop::before { content: ''; position: absolute; left: 14%; right: 14%; bottom: 14%; height: 30%; border-radius: 40%; background: var(--c-orange-22); box-shadow: inset 0 2px 0 rgba(0,0,0,0.25); }
.ent.crop.ready > span { animation: bob 1.8s ease-in-out infinite; text-shadow: 0 0 6px rgba(255,230,140,0.95); }
.ent.crop .crop-bar { position: absolute; left: 20%; right: 20%; bottom: 6%; height: 4px; border-radius: 3px; background: rgba(0,0,0,0.35); overflow: hidden; z-index: 2; }
.ent.crop .crop-bar i { display: block; height: 100%; background: var(--c-green-66); }
#pickupOverlay, #levelUpOverlay { z-index: 22; }
#tipOverlay { z-index: 23; }
.tip-card p { color: var(--ink) !important; line-height: 1.5; }
.ent.prop { }
.ent.decoration::before { content: ''; position: absolute; bottom: 12%; width: 56%; height: 14%; border-radius: 50%; background: var(--tshadow); }
.ent.decoration > span { text-shadow: 0 2px 3px rgba(0,0,0,0.3); }
.ent .spr.prop { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.ent.player { }
.ent.player::before { content: ''; position: absolute; bottom: 8%; width: 60%; height: 15%; border-radius: 50%; background: var(--tshadow); }
.pl-badge { width: 74%; height: 74%; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: calc(var(--t) * 0.5); box-shadow: 0 3px 8px rgba(0,0,0,0.25), inset 0 0 0 2px rgba(255,255,255,0.55); position: relative; z-index: 1; transition: filter 0.6s ease; box-sizing: border-box; border: var(--av-border-w, 3px) var(--av-border-style, solid) var(--av-border-color, transparent); }
.pl-badge.av-glow { box-shadow: 0 3px 8px rgba(0,0,0,0.25), inset 0 0 0 2px rgba(255,255,255,0.55), 0 0 8px 1px var(--av-glow-color, transparent); }
.ent.player.walking .pl-badge { animation: hop 0.28s ease-in-out infinite; }
/* a lantern-like glow around the player at night, brighter while walking */
.pl-glow {
  position: absolute; left: 50%; top: 48%; width: 440%; height: 440%; transform: translate(-50%, -50%) scale(1); border-radius: 50%;
  background: radial-gradient(circle, rgba(255,205,120,0.48) 0%, rgba(255,200,110,0.22) 38%, transparent 78%);
  opacity: 0; pointer-events: none; z-index: 0; transition: opacity 0.6s ease;
}
.town-view.is-night .ent.player .pl-glow { opacity: 1; }
.town-view.is-night .ent.player.walking .pl-glow { opacity: 1; animation: pl-glow-pulse 0.9s ease-in-out infinite; }
@keyframes pl-glow-pulse { 0%, 100% { transform: translate(-50%, -50%) scale(1); } 50% { transform: translate(-50%, -50%) scale(1.14); } }
.ent.tap-ring::after { content: ''; width: 60%; height: 60%; border-radius: 50%; border: 3px solid rgba(255,255,255,0.9); animation: ring 0.5s ease-out forwards; }
@keyframes hop { 50% { transform: translateY(-14%); } }
@keyframes ring { from { transform: scale(0.5); opacity: 1; } to { transform: scale(1.3); opacity: 0; } }
