/* Bagani: War of Isles — map chrome only (map.php shell + map_.php fragment).
   Never touches the isometric tile grid's pixel math — decoration only. */

.dv-map-shell {
  max-width: 960px;
  margin: 0 auto;
  padding: 1.6rem clamp(1rem, 3vw, 2rem) 2.5rem;
}

/* ---------- full-bleed town scene (town.php only) ----------
   town.php's own script (computeBaseScale/sceneApply) sizes .dv-scene-frame
   to whatever viewport space is left below the site nav and computes
   --scene-scale to contain that box -- the whole 640x480 world is always
   fully visible, letterboxed (the frame's own background shows through) on
   whichever axis has more room than the world needs, rather than cropping.
   This rule only has to get out of the way: strip the shell's centered
   max-width and padding so the frame's own inline width/height aren't
   clamped back down by them. map.php keeps using bare .dv-map-shell
   (max-width:960px above) untouched. */
.dv-town-shell {
  max-width: none;
  /* Actually zero, not the leftover 2.5rem this shipped with — that bottom
     padding was adding dead space computeBaseScale() never knew to account
     for, so the frame's own box could end up taller than what the viewport
     shows without a scrollbar, and the scene never quite touched the bottom
     edge either way. */
  padding: 0;
}

.dv-town-shell .dv-scene-frame {
  max-width: none;
}

.dv-map-layout {
  display: flex;
  gap: 1.4rem;
  align-items: flex-start;
  flex-wrap: wrap;
}

/* ---------- quick actions + scene zoom ---------- */

.dv-quickbar {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  justify-content: center;
  margin: 0 auto 1rem;
}

.dv-quickbar .dv-btn { padding: .7rem 1.3rem; font-size: .7rem; }

.dv-scene-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .4rem;
  margin-bottom: .6rem;
}

.dv-scene-label {
  font-family: var(--mono);
  font-size: .6rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--bone);
  margin-right: .2rem;
}

.dv-scene-pct {
  font-family: var(--mono);
  font-size: .7rem;
  color: var(--gold);
  min-width: 3.4rem;
  text-align: center;
}

/* The stage keeps its true 640x480 geometry; only the visual scale changes,
   so the image-map hotspot coordinates never have to be recomputed. */
.dv-scene-frame {
  --scene-scale: 1.35;
  width: calc(640px * var(--scene-scale));
  height: calc(480px * var(--scene-scale));
  max-width: 100%;
  margin: 0 auto;
  overflow: hidden;
}

.dv-scene-frame .dv-map-stage {
  transform: scale(var(--scene-scale));
  transform-origin: top left;
  margin: 0;
}

/* ---------- in-scene pop-up menus ---------- */

.dv-smenu { position: relative; }

.dv-sbtn.on { background: rgba(193,85,44,.92); border-color: var(--gold); color: #fff; }

.dv-smenu-pop {
  display: none;
  position: absolute;
  left: 0;
  bottom: calc(100% + .4rem);
  min-width: 190px;
  background: rgba(18,17,11,.97);
  border: 1px solid rgba(201,162,39,.55);
  box-shadow: 0 -8px 26px rgba(0,0,0,.6);
  z-index: 20;
  padding: .25rem;
}

.dv-smenu-pop.on { display: block; }

.dv-smenu-pop a {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .42rem .55rem;
  font-family: var(--mono);
  font-size: .64rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: #d8cdb0;
  text-decoration: none;
  white-space: nowrap;
}

.dv-smenu-pop a:hover { background: rgba(193,85,44,.9); color: #fff; }
.dv-smenu-pop a img { width: 18px; height: 18px; object-fit: contain; flex: none; }

/* keep the last menus from running off the right edge of the scene */
.dv-scene-actions .dv-smenu:nth-last-child(-n+3) .dv-smenu-pop { left: auto; right: 0; }

/* ---------- per-building popup ---------- */

/* Compact RTS-style popup: width/height/padding/spacing cut ~40-50% from
   what this shipped with. Font sizes are trimmed only slightly (these were
   already 9-10px) rather than by the same percentage -- cutting them by
   40-50% too would drop below legible size, which directly conflicts with
   keeping "Town Council · Lvl 2", upkeep, Upgrade/Move/Close readable.
   The real cause of it still looking huge wasn't cache: this sits inside
   .dv-map-stage, which is scaled by --scene-scale (contain-fit base scale
   times the user's zoom level) to fit the viewport -- on a wide screen that factor
   can be ~4x, so a 92px popup rendered at ~370px on screen even though the
   CSS genuinely said 92px. Counter-scaling by 1/--scene-scale here cancels
   that out so it's a constant physical size regardless of window size or
   zoom, the same way font sizes in real RTS UIs don't grow when you zoom
   the camera in. transform-origin matches the corner JS anchors it from
   (top-left, offset +20px/+20px from the building), so the counter-scale
   shrinks it toward that same corner instead of shifting it off-position. */
.dv-bmenu {
  display: none;
  position: absolute;
  z-index: 30;
  min-width: 92px;
  background: rgba(18,17,11,.97);
  border: 1px solid rgba(201,162,39,.6);
  box-shadow: 0 6px 16px rgba(0,0,0,.65);
  padding: .18rem;
  transform: scale(calc(1 / var(--scene-scale, 1)));
  transform-origin: top left;
}

.dv-bmenu.on { display: block; }

.dv-bmenu-info {
  padding: .18rem .28rem .22rem;
  border-bottom: 1px solid rgba(201,162,39,.3);
  margin-bottom: .15rem;
}

.dv-bmenu-head {
  font-family: var(--mono);
  font-size: .54rem;
  letter-spacing: .05em;
  text-transform: uppercase;
  /* this popup sits over the town scene art, not the page chrome, and keeps
     a dark translucent background regardless of site theme -- so its text
     stays hardcoded light-on-dark rather than following the --gold/--bone
     tokens, which are now tuned dark for the light theme's card surfaces. */
  color: #e0c157;
  margin: 0;
}

.dv-bmenu-sub {
  font-family: var(--mono);
  font-size: .52rem;
  letter-spacing: .03em;
  color: #c9c1a4;
  margin: .12rem 0 0;
}

.dv-bmenu a {
  display: block;
  padding: .22rem .32rem;
  font-family: var(--mono);
  font-size: .58rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: #d8cdb0;
  text-decoration: none;
}

.dv-bmenu a:hover { background: rgba(193,85,44,.9); color: #fff; }
.dv-bmenu a.raze:hover { background: #8c2f14; }

/* ---------- letterbox backdrop (town.php only) ----------
   Fills the empty side margins contain-fit leaves on any viewport wider
   than the world's fixed 4:3 ratio, without cropping or stretching the real
   map: a second, blurred/darkened copy of the same ground art, scaled to
   fully cover the viewport behind it. position:fixed removes it from
   layout entirely (it cannot push or resize .dv-scene-frame or anything
   else), and object-fit:cover recalculates on resize as pure CSS layout --
   no JS sizing needed the way .dv-scene-frame's own fit does. Sits behind
   everything simply by coming first in the DOM with no z-index of its own;
   every HUD element already painted above the real scene keeps doing so. */
.dv-scene-backdrop {
  position: fixed;
  top: var(--nav-h, 44px);
  left: 0; right: 0; bottom: 0;
  overflow: hidden;
  pointer-events: none;
}
.dv-scene-backdrop img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  /* scale(1.12) keeps the blur's own soft edge from ever showing a sharp
     border inside the viewport -- without it, a strong gaussian blur can
     reveal the source image's true edge as a faint seam a few px in. */
  transform: scale(1.12);
  filter: blur(34px) brightness(.4) saturate(1.05);
}
@media (prefers-reduced-motion: reduce) {
  .dv-scene-backdrop img { transition: none; }
}

/* ---------- buildings as positioned anchors ---------- */

.dv-scene-ground {
  position: absolute;
  left: 0; top: 0;
  width: 640px;
  height: 480px;
}

.dv-scene-bg {
  position: absolute;
  pointer-events: none;
  user-select: none;
  z-index: 0;
  /* Crossfade duration for a time-of-day change -- JS only ever flips
     opacity (0<->1) between the two stacked layers, this transition is what
     turns that flip into a gradual fade instead of a hard cut. */
  transition: opacity 7s ease-in-out;
}

@media (prefers-reduced-motion: reduce) {
  .dv-scene-bg { transition-duration: .2s; }
}

/* Flowing river, everything else on the ground held still. A repeating
   diagonal streak pattern scrolls underneath a mask cut to the exact shape
   of the water, so nothing outside that shape ever moves — the mask is the
   only thing keeping the animation from spilling onto the grass. */
.dv-scene-water {
  position: absolute;
  left: 0; top: 0;
  width: 640px;
  height: 480px;
  z-index: 1;
  pointer-events: none;
  -webkit-mask-size: 640px 480px;
  mask-size: 640px 480px;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  /* Explicit rather than left to the browser default, which is inconsistent
     between engines for a raster mask image (some read luminance, some read
     alpha) -- water_mask.png now carries the shape in its alpha channel, so
     this is pinned to match regardless of which browser renders it. */
  -webkit-mask-mode: alpha;
  mask-mode: alpha;
  mix-blend-mode: screen;
  opacity: .5;
  background-image: repeating-linear-gradient(65deg,
    rgba(255,255,255,0) 0px,
    rgba(190,225,255,.4) 5px,
    rgba(255,255,255,0) 12px,
    rgba(150,205,255,.3) 20px,
    rgba(255,255,255,0) 30px);
  background-size: 46px 46px;
  animation: dv-water-flow 2.6s linear infinite;
}

@keyframes dv-water-flow {
  from { background-position: 0 0; }
  to   { background-position: 46px 46px; }
}

@media (prefers-reduced-motion: reduce) {
  .dv-scene-water { animation: none; }
}

/* ---------- ambient wildlife (decorative only) -----------------------------
   Pure visual life for the town scene -- birds, butterflies, fireflies. None
   of this carries gameplay data (no species table, no click targets, no
   backend call); it's CSS-driven so it costs nothing but a few composited
   transforms, same performance profile as the existing water-flow animation
   above. Sits above buildings (z-index 3) since birds fly over rooftops;
   pointer-events:none throughout so it can never intercept a building click
   or drag. */
.dv-wildlife-layer {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  overflow: hidden;
}

.dv-bird {
  position: absolute;
  top: 12%;
  left: -8%;
  width: 14px;
  height: 14px;
  color: rgba(30, 26, 18, .55);
  animation: dv-bird-fly linear infinite;
  will-change: transform;
}
.dv-bird svg { width: 100%; height: 100%; display: block; animation: dv-bird-flap .5s ease-in-out infinite alternate; }
.dv-bird:nth-child(1) { top: 10%;  animation-duration: 22s; animation-delay: 0s; }
.dv-bird:nth-child(2) { top: 16%;  animation-duration: 28s; animation-delay: 6s; }
.dv-bird:nth-child(3) { top: 8%;   animation-duration: 25s; animation-delay: 14s; }

@keyframes dv-bird-fly {
  0%   { transform: translate(0, 0); opacity: 0; }
  4%   { opacity: 1; }
  50%  { transform: translate(370px, 26px); }
  96%  { opacity: 1; }
  100% { transform: translate(760px, -6px); opacity: 0; }
}
@keyframes dv-bird-flap { from { transform: scaleY(1); } to { transform: scaleY(.55); } }

.dv-butterfly {
  position: absolute;
  width: 10px;
  height: 10px;
  color: var(--ember-bright, #e07a4a);
  animation: dv-flutter 6s ease-in-out infinite;
  will-change: transform;
}
.dv-butterfly svg { width: 100%; height: 100%; display: block; animation: dv-wing-beat .28s ease-in-out infinite alternate; }
.dv-butterfly:nth-child(4) { left: 33%; top: 62%; animation-duration: 7s; }
.dv-butterfly:nth-child(5) { left: 61%; top: 70%; animation-duration: 8.5s; animation-delay: 1.5s; color: var(--gold, #c9a227); }

@keyframes dv-flutter {
  0%, 100% { transform: translate(0, 0); }
  20% { transform: translate(14px, -10px); }
  40% { transform: translate(4px, -18px); }
  60% { transform: translate(-12px, -8px); }
  80% { transform: translate(-4px, -16px); }
}
@keyframes dv-wing-beat { from { transform: scaleX(1); } to { transform: scaleX(.4); } }

/* Fireflies only show once #sceneGround carries .dv-night, toggled by a
   tiny client-side clock check (town.php) -- no server round trip, and
   nothing breaks if JS never runs (they just stay hidden, the safe default). */
.dv-firefly {
  position: absolute;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: #d9f27a;
  box-shadow: 0 0 6px 2px rgba(217, 242, 122, .8);
  opacity: 0;
  animation: dv-firefly-glow 3.2s ease-in-out infinite;
  /* Paused (and invisible via opacity:0 above) until .dv-night is present --
     this is the actual on/off switch; the night class alone doesn't hide
     them, since a paused animation can still be sitting mid-keyframe. */
  animation-play-state: paused;
}
.dv-scene-ground.dv-night .dv-firefly { animation-play-state: running; }
.dv-firefly:nth-child(6) { left: 20%; top: 74%; animation-delay: 0s; }
.dv-firefly:nth-child(7) { left: 46%; top: 80%; animation-delay: 1.1s; }
.dv-firefly:nth-child(8) { left: 70%; top: 72%; animation-delay: 2.2s; }
.dv-firefly:nth-child(9) { left: 58%; top: 86%; animation-delay: .6s; }

@keyframes dv-firefly-glow {
  0%, 100% { opacity: 0; transform: translate(0,0); }
  50% { opacity: .9; transform: translate(6px, -8px); }
}

@media (prefers-reduced-motion: reduce) {
  .dv-bird, .dv-bird svg, .dv-butterfly, .dv-butterfly svg, .dv-firefly { animation: none; opacity: 0; }
}

/* ---------- farm & wild animals (decorative only) --------------------------
   Same non-negotiable rule as the products in item_defs(): a kalabaw is not
   a Slot #37 next to a sword. These are ground-level decoration only -- no
   species table, no click target, no backend call, nothing that could be
   confused with the livestock-management system this game doesn't have.
   z-index 2, same tier as buildings but earlier in DOM order (this layer is
   printed before the buildings loop in town.php), so a building painted
   after it correctly occludes an animal standing "behind" it instead of
   floating on top of rooftops the way flying birds are allowed to. */
.dv-farm-layer {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  overflow: hidden;
}

.dv-animal {
  position: absolute;
  width: 20px;
  height: 20px;
  transform-origin: 50% 85%;
}
.dv-animal svg { width: 100%; height: 100%; display: block; }

/* Farm animals graze in place -- a slow sway plus an occasional head-dip,
   never leaving their spot (this is ambience, not a wandering simulation). */
.dv-animal.graze { animation: dv-graze 5s ease-in-out infinite; }
@keyframes dv-graze {
  0%, 100% { transform: rotate(0deg) translateY(0); }
  30% { transform: rotate(-2deg) translateY(0); }
  50% { transform: rotate(-2deg) translateY(2px); } /* head down, grazing */
  70% { transform: rotate(2deg) translateY(0); }
}

/* Chicken/duck peck faster and lower to the ground. */
.dv-animal.peck { animation: dv-peck 2.6s ease-in-out infinite; }
@keyframes dv-peck {
  0%, 60%, 100% { transform: translateY(0); }
  75% { transform: translateY(3px); }
  85% { transform: translateY(0); }
}

/* The wild boar doesn't live in the scene like the farm animals -- it's a
   rare, brief crossing at the forest edge, same "occasionally cross" idea
   as the instructions asked for, built the same way the birds cross
   (a long-duration keyframe pass with the visible window near the middle). */
.dv-animal.dash { top: 78%; left: -6%; animation: dv-boar-dash 46s ease-in-out infinite; animation-delay: 9s; }
@keyframes dv-boar-dash {
  0%, 82% { transform: translate(0, 0); opacity: 0; }
  84% { opacity: 1; }
  91% { transform: translate(210px, -4px); }
  96% { opacity: 1; }
  100% { transform: translate(260px, -6px); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .dv-animal { animation: none !important; }
}

/* ---------- hunting / fishing zones ----------------------------------------
   Real click targets (unlike the ambient wildlife layer, pointer-events:auto
   here), but meant to blend into the terrain -- the marker icon stays at
   opacity:0 until hovered/focused/tapped, per the brief ("only when hovered
   or selected"). The hit area itself is always there and always clickable
   (a player who already knows the spot shouldn't have to hover first to
   click it), just visually quiet until you're paying attention to it.
   z-index 2, same tier as buildings, so it never floats above a building
   sprite standing over part of it. */
.dv-zone {
  position: absolute;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border-radius: 50%;
  /* Reset -- this is a <button>, not a <span>, so it needs its own click/
     focus target independent of the dv-farm-layer's pointer-events:none
     decorations, but shouldn't look like a button. */
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  outline: none;
}
.dv-zone:focus-visible { box-shadow: 0 0 0 2px rgba(255,255,255,.4); }

.dv-zone-marker {
  width: 60%;
  height: 60%;
  opacity: 0;
  transform: scale(.7);
  transition: opacity .18s ease, transform .18s ease;
  filter: drop-shadow(0 0 4px rgba(0,0,0,.6));
}
.dv-zone:hover .dv-zone-marker,
.dv-zone:focus-visible .dv-zone-marker,
.dv-zone.dv-zone-touched .dv-zone-marker {
  opacity: 1;
  transform: scale(1);
}

/* A faint ever-present pulse (not the full marker) so the spot isn't
   completely undiscoverable on a first visit -- still well short of a
   permanent icon "covering the river artwork" the brief asked to avoid. */
.dv-zone::after {
  content: "";
  position: absolute;
  inset: 30%;
  border-radius: 50%;
  background: currentColor;
  opacity: .12;
  animation: dv-zone-pulse 3.4s ease-in-out infinite;
}
.dv-zone-hunt { color: #6b8f4e; }
.dv-zone-fish { color: #7fc4e8; }
.dv-zone-minahan { color: #c98f4e; }
.dv-zone-kagubatan { color: #4ea86b; }

@keyframes dv-zone-pulse {
  0%, 100% { opacity: .08; transform: scale(.85); }
  50% { opacity: .22; transform: scale(1.05); }
}

@media (prefers-reduced-motion: reduce) {
  .dv-zone::after { animation: none; opacity: .1; }
}

.dv-bld {
  position: absolute;
  z-index: 2;
  display: block;
  cursor: pointer;
  line-height: 0;
  transition: filter .12s ease;
  /* left/top (PHP-computed via grid_to_screen()) are the tile's true center
     point, not the sprite's top-left corner -- this transform anchors the
     sprite's own visual CENTER there (not its bottom edge: a tall sprite
     foot-anchored on a much smaller tile reads as floating above center
     even when its base measures out exactly right, since a human eye judges
     "centered" by the whole silhouette, not an invisible point at its
     feet). Building-specific corrections for sprites with asymmetric
     padding go through building_anchor_transform() (func.php), which
     overrides this per element via inline style when needed. */
  transform: translate(-50%, -50%);
}

.dv-bld img { display: block; pointer-events: none; user-select: none; }

/* Most buildings have no explicit width/height in building_layout() (only
   the wall does, which spans multiple tiles by design and is excluded here
   via :not([width])), so they render at whatever size the source PNG
   happens to be -- often bigger than the ~80px-wide diamond tile it's meant
   to sit on. Capping width and letting height follow keeps every sprite's
   own aspect ratio intact while fitting it onto roughly one tile.
   27px (was 68px, then 43px, now a further ~37% cut) matches the same scale
   the town hall (building 7) already used as an explicit fw/fh --
   building_layout()'s dx/dy for every other building were shifted by half
   the resulting pixel reduction on each axis (same technique, computed per
   building from each sprite's own aspect ratio) so shrinking this cap
   doesn't shift anything off its assigned grid position. */
.dv-bld img:not([width]) { max-width: 27px; height: auto; }

.dv-bld:hover { filter: brightness(1.18) drop-shadow(0 0 6px rgba(201,162,39,.85)); z-index: 4; }
/* :focus-visible only matches keyboard navigation in most browsers, not a
   mouse click -- without this reset, clicking a building (it's an <a>, so
   it's focusable) left the browser's own default outline showing (a dashed
   box, varies by browser) since nothing here styled plain :focus. */
/* No outline box on a building in any state -- clicking one used to leave a
   hard rectangle around the sprite (browser default focus ring on the <a>,
   or the keyboard-nav ring below). Removed by request. Keyboard users still
   get a visible cue from :focus-visible's glow, just not a box. */
.dv-bld:focus { outline: none; }
.dv-bld:focus-visible { outline: none; filter: brightness(1.3) drop-shadow(0 0 6px rgba(201,162,39,.95)); }

/* A plot marked for a first-time raise: no sprite exists yet, so a small dot
   marks the spot instead of leaving the floating progress bar above it with
   nothing to anchor to. */
.dv-bld-ghost { width: 22px; height: 22px; }
.dv-bld-ghost-mark {
  display: block;
  width: 14px;
  height: 14px;
  margin: 4px;
  border-radius: 50%;
  border: 2px dashed rgba(201,162,39,.8);
  background: rgba(0,0,0,.25);
  animation: dv-ghost-pulse 1.6s ease-in-out infinite;
}

@keyframes dv-ghost-pulse {
  0%, 100% { opacity: .55; transform: scale(.9); }
  50% { opacity: 1; transform: scale(1.05); }
}

@media (prefers-reduced-motion: reduce) {
  .dv-bld-ghost-mark { animation: none; opacity: .85; }
}

/* name/level pill, revealed on hover so the scene stays uncluttered.
   Split into an outer anchor (positioning + the counter-scale that undoes
   .dv-map-stage's ambient --scene-scale, so this renders at a constant
   physical size instead of ballooning on a wide/zoomed viewport) and an
   inner box (the actual visible pill). Centering is plain text-align, not a
   transform, specifically so there's only one transform function (scale) on
   the whole thing -- combining translate()+scale() correctly requires
   knowing which one CSS applies first, which is easy to get backwards. */
.dv-bld-tag-wrap {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  margin-bottom: 6px;
  text-align: center;
  transform: scale(calc(1 / var(--scene-scale, 1)));
  transform-origin: bottom center;
  opacity: 0;
  pointer-events: none;
  transition: opacity .12s ease;
}

.dv-bld-tag {
  display: inline-block;
  font-family: var(--mono);
  font-size: .58rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  white-space: nowrap;
  line-height: 1.4;
  color: #efe6cf;
  background: rgba(20,18,12,.9);
  border: 1px solid rgba(201,162,39,.55);
  padding: .12rem .4rem;
}

/* hardcoded, not var(--gold) -- this tag keeps its own dark translucent
   background over the town scene regardless of site theme (see .dv-bmenu-head). */
.dv-bld-tag b { color: #e0c157; font-weight: 500; }
.dv-bld:hover .dv-bld-tag-wrap { opacity: 1; }

/* drag state */
.dv-bld.dragging { z-index: 9; filter: brightness(1.25) drop-shadow(0 6px 10px rgba(0,0,0,.6)); }
.dv-scene-ground.editing .dv-bld { cursor: grab; }
.dv-scene-ground.editing .dv-bld.dv-bld-fixed { cursor: default; opacity: .5; }
.dv-scene-ground.editing .dv-bld-tag-wrap { opacity: 1; }

/* Green/red validity feedback while dragging (posOk() in town.php toggles
   these). The square `outline` box these used to draw was removed by
   request -- the coloured glow alone carries the same valid/blocked signal
   without painting a hard rectangle around the sprite. */
.dv-bld.dv-bld-ok { filter: brightness(1.25) drop-shadow(0 0 6px rgba(76,175,80,.85)); }
.dv-bld.dv-bld-blocked { filter: brightness(1.15) drop-shadow(0 0 6px rgba(229,72,77,.85)); }

/* Outer buildable bounds (matches building_pos_ok()'s 20-620/20-460 in
   func.php) -- the real per-spot legality (terrain/other buildings) is
   only visible on the dragged sprite itself via .dv-bld-ok/-blocked above,
   this is just the outer envelope. */
.dv-scene-ground.editing::after {
  content: "";
  position: absolute;
  left: 20px; top: 20px;
  width: 600px; height: 440px;
  border: 1px dashed rgba(201,162,39,.5);
  pointer-events: none;
  z-index: 1;
}

/* ---------- in-scene HUD (overlaid on the parchment frames) ----------
   Pinned to the two decorative bands only. The image-map hotspots run from
   y=110 to y=445, so these never sit over a clickable building. */

.dv-scene-top {
  position: absolute;
  left: 0; top: 0;
  width: 640px;
  height: 72px;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .6rem;
  padding: 0 .8rem;
  pointer-events: none; /* purely informational — never intercepts a click */
}

.dv-scene-town { display: flex; flex-direction: column; min-width: 0; }

.dv-scene-town b {
  font-family: var(--display);
  font-size: 1.05rem;
  color: #f4ecd8;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Used to sit on a light parchment header strip; the ground art now runs
     grass and forest right to the top edge, so this reads light-on-dark
     instead, with a shadow doing the contrast work rather than a background. */
  text-shadow: 0 1px 3px rgba(0,0,0,.9), 0 0 10px rgba(0,0,0,.55);
}

.dv-scene-town i {
  font-style: normal;
  font-family: var(--mono);
  font-size: .58rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: #d9d0b4;
  text-shadow: 0 1px 3px rgba(0,0,0,.9), 0 0 8px rgba(0,0,0,.5);
}

.dv-scene-res { display: flex; gap: .3rem; flex: none; }

.dv-scene-clock {
  display: inline-flex;
  align-items: center;
  flex: none;
  font-family: var(--mono);
  font-size: .7rem;
  letter-spacing: .02em;
  color: #f4e9c8;
  background: rgba(20,17,10,.55);
  border: 1px solid rgba(201,162,39,.45);
  border-radius: 3px;
  padding: .2rem .5rem;
  white-space: nowrap;
}

.dv-scene-chip {
  display: inline-flex;
  align-items: center;
  gap: .2rem;
  font-family: var(--mono);
  font-size: .68rem;
  color: #2b2417;
  background: rgba(255,252,240,.72);
  border: 1px solid rgba(90,72,40,.35);
  border-radius: 2px;
  padding: .15rem .35rem;
  white-space: nowrap;
}

.dv-scene-chip img { width: 14px; height: 14px; }

.dv-scene-chip i {
  font-style: normal;
  font-size: .82em;
  color: #4a6b3f;
  margin-left: .1rem;
}

.dv-scene-chip i:first-letter { font-weight: 700; }

/* ---------- fixed HUD bands (town view) ----------
   Both bands used to be absolutely positioned inside the 640x480 world that
   gets scaled and, with cover fit, cropped. A bar anchored to world-bottom
   scrolls off-screen behind overflow:hidden the moment the world is cropped
   taller than the frame -- these render as fixed, viewport-anchored siblings
   of the frame instead, so cropping and zoom can never carry them out of
   view again. */
/* No shared band any more -- .dv-scene-top-fixed itself carries no size,
   background or padding of its own; it's just the DOM parent the three HUD
   pieces below happen to sit in. Each one gets its own independent
   position:fixed placement and doesn't touch the map's layout, height, or
   margins in any way -- removing/moving any one of them can never reserve
   or free up space for the others, because there is no longer a container
   whose box they share. */
.dv-scene-top.dv-scene-top-fixed {
  position: fixed;
  left: 0;
  top: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}

.dv-scene-top.dv-scene-top-fixed .dv-scene-chip,
.dv-scene-top.dv-scene-top-fixed .dv-scene-view,
.dv-scene-top.dv-scene-top-fixed .dv-scene-clock { pointer-events: auto; }

/* town name / morale / upkeep -- top center, above the resource chips.
   (Player identity -- avatar/level/XP -- is the separate .dv-phud card,
   top-left; this is town state, grouped with resources instead.) */
.dv-scene-top.dv-scene-top-fixed .dv-scene-town {
  position: fixed;
  left: 50%;
  top: calc(var(--nav-h, 12px) + 1rem);
  transform: translateX(-50%);
  z-index: 41;
  align-items: center;
  text-align: center;
  background: rgba(20,18,12,.55);
  border: 1px solid rgba(201,162,39,.3);
  border-radius: 8px;
  padding: .3rem .7rem;
  max-width: 90vw;
}

/* resource chips -- top center, directly under the town name */
.dv-scene-top.dv-scene-top-fixed .dv-scene-res {
  position: fixed;
  left: 50%;
  top: calc(var(--nav-h, 12px) + 3.4rem);
  transform: translateX(-50%);
  z-index: 41;
  flex-wrap: wrap;
  justify-content: center;
  max-width: 92vw;
}

/* game clock -- top center, third row (below the resource chips). Same
   "every top-band child gets its own independent position:fixed" pattern
   as .dv-scene-town/.dv-scene-res/.dv-scene-view above -- .dv-scene-top
   itself has zero size, so anything without an explicit fixed placement
   here collapses to its (0,0) anchor instead of flowing where it visually
   looks like it should. */
.dv-scene-top.dv-scene-top-fixed .dv-scene-clock {
  position: fixed;
  left: 50%;
  top: calc(var(--nav-h, 12px) + 5.6rem);
  transform: translateX(-50%);
  z-index: 41;
}

/* zoom controls -- top right, clear of the icon rail further down */
.dv-scene-top.dv-scene-top-fixed .dv-scene-view {
  position: fixed;
  right: 1rem;
  top: calc(var(--nav-h, 12px) + 1rem);
  z-index: 41;
}

.dv-scene-view { display: flex; align-items: center; gap: .4rem; flex: none; }

.dv-viewbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  font-size: .8rem;
  line-height: 1;
  background: rgba(24,22,14,.82);
  border: 1px solid rgba(201,162,39,.5);
  border-radius: 3px;
  color: #efe6cf;
  cursor: pointer;
}

.dv-viewbtn:hover { background: rgba(193,85,44,.92); border-color: var(--gold); color: #fff; }

.dv-scene-actions.dv-scene-actions-fixed {
  position: fixed;
  left: 0; bottom: 0;
  width: 100%;
  height: auto;
  z-index: 40;
  /* flex-wrap alone is the safety net for a narrow window or a longer
     translated label -- it wraps to a second row instead of overflowing.
     An overflow-x:auto here looked like a harmless extra floor, but CSS
     forces overflow-y to compute as non-visible the moment overflow-x is
     anything but visible, which silently clipped the dropdown menus (they
     float above the bar via bottom:calc(100% + .4rem), so "above" became
     "clipped to a sliver" the instant this container started clipping on
     either axis). */
  flex-wrap: wrap;
  padding: .6rem 1rem;
  /* stays dark regardless of site theme, same rule as .dv-scene-top-fixed
     above: this overlays the scene's grass art directly, not the page, and
     its buttons (.dv-sbtn) are light-text-on-dark chips built for that. */
  background: linear-gradient(0deg, rgba(10,9,6,.88), rgba(10,9,6,0));
}

/* Floating progress bar directly over a building under construction — the
   label + bar hover right above the structure, the same way a build timer
   floats over a unit in a real-time strategy HUD. Anchored to the same x/y
   as the building sprite so drag/rearrange carries it along automatically.

   .dv-bld-prog-anchor (outer, positioned via PHP-computed left/top) carries
   only the counter-scale that undoes .dv-map-stage's ambient --scene-scale
   -- same reason and same "just one transform function" approach as
   .dv-bld-tag-wrap above. Horizontal centering is a fixed -32px margin
   (half of .dv-prog's 64px width, the widest element in the stack) rather
   than translateX(-50%), again to avoid combining translate+scale. */
.dv-bld-prog-anchor {
  position: absolute;
  z-index: 7;
  margin-left: -32px;
  transform: scale(calc(1 / var(--scene-scale, 1)));
  /* Pivoting from the bottom (not top) pins the counter-scaled box's own
     bottom edge to the PHP-computed anchor point and lets it grow upward
     at constant screen size -- with "top" pivoting instead, the visual gap
     to the sprite was a *world-space* pixel count multiplied by the current
     zoom, so it grew arbitrarily large on a wide/high-zoom window even
     though the bar itself stayed a fixed screen size. Paired with dropping
     the "-24" world-px offset below (town.php), the anchor point now sits
     exactly at the sprite's own top edge and the bar sits snug above it at
     any zoom. */
  transform-origin: bottom center;
  pointer-events: none;
}

.dv-bld-prog {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  filter: drop-shadow(0 2px 3px rgba(0,0,0,.65));
}

.dv-bld-prog-label {
  font-family: var(--display);
  font-size: .56rem;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: #fff;
  text-shadow: 0 1px 2px rgba(0,0,0,.9);
  white-space: nowrap;
}

.dv-bld-prog .dv-prog {
  width: 64px;
  height: 8px;
  border-radius: 5px;
  background: rgba(8,8,8,.78);
  border: 1px solid rgba(255,255,255,.25);
}

.dv-bld-prog .dv-prog i { border-radius: 4px; }

.dv-bld-prog-time {
  font-family: var(--mono);
  font-size: .56rem;
  color: var(--gold);
  text-shadow: 0 1px 2px rgba(0,0,0,.9);
  white-space: nowrap;
}

.dv-scene-actions {
  position: absolute;
  left: 0; bottom: 0;
  width: 640px;
  height: 36px;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .3rem;
  padding: 0 .5rem;
}

.dv-sbtn {
  display: inline-flex;
  align-items: center;
  gap: .28rem;
  font-family: var(--mono);
  font-size: .6rem;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: #efe6cf;
  background: rgba(24,22,14,.82);
  border: 1px solid rgba(201,162,39,.5);
  border-radius: 2px;
  padding: .28rem .5rem;
  text-decoration: none;
  transition: background .15s ease, border-color .15s ease, transform .15s ease;
  white-space: nowrap;
}

.dv-sbtn img { width: 15px; height: 15px; object-fit: contain; }

.dv-sbtn:hover {
  background: rgba(193,85,44,.92);
  border-color: var(--gold);
  transform: translateY(-2px);
  color: #fff;
}

.dv-sbtn:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* the hover-name strip the image map writes into */
#label {
  font-family: var(--display) !important;
  font-size: 1rem !important;
  letter-spacing: .02em;
  z-index: 4;
}

@media (max-width: 700px) {
  .dv-sbtn span { display: none; }  /* icons only on narrow screens */
  .dv-scene-town i { display: none; }
  .dv-scene-clock { display: none; }
}

/* ---------- town HUD (resources + vitals, above the scene) ---------- */

.dv-town-hud {
  max-width: 640px;
  margin: 0 auto 1rem;
  background: linear-gradient(180deg, var(--ink-2), var(--ink-3));
  border: 1px solid var(--line);
  border-top: 2px solid var(--moss);
}

.dv-town-res {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
}

.dv-town-res-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .15rem;
  padding: .7rem .3rem;
  border-right: 1px solid var(--line);
}

.dv-town-res-cell:last-child { border-right: none; }
.dv-town-res-cell img { width: 20px; height: 20px; }

.dv-town-res-val {
  font-family: var(--mono);
  font-size: .9rem;
  color: var(--parchment);
  white-space: nowrap;
}

.dv-town-res-val i { font-style: normal; color: var(--bone); font-size: .78em; }

.dv-town-res-rate {
  font-family: var(--mono);
  font-size: .64rem;
  letter-spacing: .04em;
  color: var(--moss-bright);
}

.dv-town-res-rate.neg { color: var(--ember-bright); }

.dv-town-vitals {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border-top: 1px solid var(--line);
}

.dv-town-vital {
  text-align: center;
  padding: .55rem .3rem;
  border-right: 1px solid var(--line);
}

.dv-town-vital:last-child { border-right: none; }

.dv-town-vital dt {
  font-family: var(--mono);
  font-size: .6rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--bone);
  margin: 0 0 .15rem;
}

.dv-town-vital dd {
  margin: 0;
  font-family: var(--mono);
  font-size: .84rem;
  color: var(--parchment);
}

/* ---------- building directory ---------- */

.dv-bgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: .6rem;
}

.dv-btile {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: .25rem;
  padding: .7rem .4rem .6rem;
  background: var(--ink);
  border: 1px solid var(--line);
  transition: border-color .15s ease, transform .15s ease, background .15s ease;
}

.dv-btile img {
  height: 46px;
  width: auto;
  object-fit: contain;
}

.dv-btile-name {
  font-family: var(--display);
  font-size: .8rem;
  color: var(--parchment);
  line-height: 1.2;
}

.dv-btile-lvl {
  font-family: var(--mono);
  font-size: .6rem;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--gold);
}

a.dv-btile:hover {
  border-color: var(--gold);
  transform: translateY(-2px);
  background: var(--ink-2);
}

.dv-btile.busy { border-color: var(--ember); }
.dv-btile.busy .dv-btile-lvl { color: var(--ember-bright); }

.dv-btile.empty img { filter: grayscale(1); opacity: .28; }
.dv-btile.empty .dv-btile-name { color: var(--bone); }
.dv-btile.empty .dv-btile-lvl { color: var(--bone); }
a.dv-btile.empty:hover img { filter: grayscale(.3); opacity: .7; }

.dv-btile.blocked { cursor: not-allowed; opacity: .5; }

@media (max-width: 560px) {
  .dv-town-res { grid-template-columns: repeat(3, 1fr); }
  .dv-town-vitals { grid-template-columns: repeat(2, 1fr); }
}

.dv-map-main { flex: none; }

/* ---------- town-scene minimap widget (town.php) ---------- */

.dv-town-minimap {
  position: fixed;
  right: 1.1rem;
  bottom: calc(var(--bottom-h, 74px) + 5.4rem);
  z-index: 40;
  width: 132px;
  background: linear-gradient(180deg, var(--ink-2), var(--ink-3));
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: .5rem;
  box-shadow: 0 10px 30px rgba(0,0,0,.5);
}

.dv-town-minimap-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-family: var(--mono);
  font-size: .6rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--parchment-dim);
  margin-bottom: .35rem;
}

.dv-town-minimap-head a {
  color: var(--gold);
  text-decoration: none;
  font-size: .85rem;
  line-height: 1;
}
.dv-town-minimap-head a:hover { color: var(--ember-bright); }

@media (max-width:760px) { .dv-town-minimap { display: none; } }

/* ---------- minimap ---------- */

.dv-minimap-panel {
  flex: none;
  width: 230px;
  background: linear-gradient(180deg, var(--ink-2), var(--ink-3));
  border: 1px solid var(--line);
  border-top: 2px solid var(--moss);
  padding: .9rem;
}

.dv-minimap-title {
  font-family: var(--mono);
  font-size: .66rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--gold);
  text-align: center;
  margin: 0 0 .7rem;
}

.dv-minimap {
  display: grid;
  gap: 1px;
  background: var(--ink);
  border: 1px solid var(--line);
  aspect-ratio: 1;
}

.dv-mini-cell {
  display: block;
  aspect-ratio: 1;
  cursor: pointer;
}

.dv-mini-void { background: var(--ink); }
.dv-mini-water { background: #2a4a52; }
.dv-mini-land { background: var(--moss); }
.dv-mini-mtn { background: var(--bone); opacity: .5; }
.dv-mini-town { background: var(--ember); }
.dv-mini-mine { background: var(--gold); }
.dv-mini-cell:hover { outline: 1px solid var(--parchment); outline-offset: -1px; }
.dv-mini-here { outline: 2px solid var(--parchment); outline-offset: -2px; }

.dv-minimap-hint {
  font-family: var(--mono);
  font-size: .62rem;
  color: var(--bone);
  text-align: center;
  margin: .7rem 0 0;
}

@media (max-width: 920px) {
  .dv-map-layout { flex-direction: column; align-items: center; }
  .dv-minimap-panel { width: 300px; }
}

.dv-map-controls {
  display: flex;
  align-items: center;
  gap: .6rem;
  flex-wrap: wrap;
  font-family: var(--mono);
  font-size: .74rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--bone);
  margin-bottom: .4rem;
}

.dv-map-controls input.dv-input {
  width: 4.2rem;
  text-align: center;
  padding: .4rem .3rem;
  display: inline-block;
}

.dv-map-controls button.dv-btn {
  padding: .5rem 1.1rem;
  font-size: .68rem;
}

#xmenu {
  display: inline-block;
  font-family: var(--mono);
  font-size: .72rem;
  color: var(--gold);
}

.dv-map-stage {
  position: relative;
  background: radial-gradient(ellipse at center, var(--ink-3) 0%, var(--ink) 72%);
  border: 1px solid var(--line);
  margin-top: .5rem;
  overflow: hidden;
  transform-origin: top left;
  cursor: grab;
}

.dv-map-stage.dv-map-dragging { cursor: grabbing; }

.dv-map-stage img { max-width: none; }

.dv-map-zoom {
  display: flex;
  align-items: center;
  gap: .4rem;
  margin-left: auto;
}

.dv-coord {
  font-family: var(--mono) !important;
  font-size: .68rem;
  color: var(--bone) !important;
  text-align: center;
}

/* descriptor panel — rebuilt by func.js:desc() on hover */

#descriptor { min-height: 92px; }

.dv-desc-card {
  background: linear-gradient(180deg, var(--ink-2), var(--ink-3));
  border: 1px solid var(--line);
  border-top: 2px solid var(--moss);
  padding: .9rem 1rem;
  width: 230px;
}

.dv-desc-title {
  font-family: var(--display);
  font-size: .95rem;
  color: var(--parchment);
  margin: 0 0 .6rem;
  text-align: center;
  border-bottom: 1px solid var(--line);
  padding-bottom: .5rem;
}

.dv-desc-meta { margin: 0; display: flex; flex-direction: column; gap: .35rem; }

.dv-desc-meta div { display: flex; justify-content: space-between; gap: .5rem; }

.dv-desc-meta dt {
  font-family: var(--mono);
  font-size: .64rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--bone);
  margin: 0;
}

.dv-desc-meta dd {
  margin: 0;
  font-size: .82rem;
  color: var(--parchment-dim);
  text-align: right;
}
