/* The world map's art layer (js/engine/art/STYLE.md): what life.js adds to the map, i.e. culling and
   level of detail, the moving sprites, family colours on the names, the "ready" beacons, and
   the buildings' hover and press. House rules hold here too: every font-size is
   max(var(--text-floor), …), and all motion stops with reduced motion or html.motion-off. */

/* ===== culling and level of detail ===== */
/* an off-screen building's idle loops stop; far out, the small loops stop everywhere */
.bld.cull [class*="anim-"], .bld.cull [class*="act-"] { animation: none !important; }
.world[data-lod="far"] .bld:not(:hover):not(:focus-visible) [class*="anim-"] { animation: none !important; }

/* ===== the terrain picture ===== */
.world-terrain > img.terrain-img { display: block; width: 100%; height: 100%; user-select: none; -webkit-user-drag: none; pointer-events: none; }

/* ===== living sprites (life.js): small boxes in world coordinates, moved by transforms ===== */
.world-life { position: absolute; inset: 0; pointer-events: none; }
.life-sky { z-index: 4000; } /* over the buildings (their z-index is their y), under the screen-space names */
.lf { position: absolute; left: 0; top: 0; pointer-events: none; }
.lf.off { display: none; }
.lf > svg, .lf-in > svg { display: block; width: 100%; height: 100%; overflow: visible; }
.lf-in { width: 100%; height: 100%; }
/* sails and the pumps' wheels turn about their hub (the box is centred on it) */
.lf-sails { animation: lf-spin var(--d, 7s) linear infinite; }
@keyframes lf-spin { to { transform: rotate(360deg); } }
/* boats sail their reach and turn round at each end, bobbing on the swell */
.lf-sail { animation: lf-sail var(--d, 30s) ease-in-out infinite alternate; }
@keyframes lf-sail { from { transform: translateX(0); } to { transform: translateX(var(--len, 300px)); } }
.lf-sail > svg { animation: lf-face calc(var(--d, 30s) * 2) linear infinite; animation-delay: inherit; }
@keyframes lf-face { 0%, 49.9% { transform: scaleX(1) translateY(0); } 25% { transform: scaleX(1) translateY(-1.5px); } 50%, 100% { transform: scaleX(-1) translateY(0); } 75% { transform: scaleX(-1) translateY(-1.5px); } }
/* frogs hop now and then; fish leap out of the river */
.lf-hop { transform-origin: 50% 100%; animation: lf-hop 6.5s ease-in-out infinite; }
@keyframes lf-hop { 0%, 78%, 100% { transform: translate(0, 0) scale(1, 1); } 81% { transform: translate(0, 0) scale(1.15, 0.8); } 86% { transform: translate(-5px, -9px) scale(0.92, 1.1); } 91% { transform: translate(-9px, 0) scale(1.1, 0.88); } 95% { transform: translate(-9px, 0) scale(1, 1); } 97% { transform: translate(-4px, -4px); } }
.lf-jump { opacity: 0; animation: lf-jump 7s ease-out infinite; }
@keyframes lf-jump { 0%, 72% { opacity: 0; transform: translate(-8px, 6px) rotate(-40deg); } 74% { opacity: 1; } 79% { transform: translate(0, -14px) rotate(0deg); } 85% { opacity: 1; transform: translate(8px, 4px) rotate(45deg); } 87%, 100% { opacity: 0; transform: translate(9px, 8px) rotate(50deg); } }
/* glints on the water */
.lf-glint { opacity: 0; animation: lf-glint 4.5s ease-in-out infinite; }
@keyframes lf-glint { 0%, 60%, 100% { opacity: 0; transform: scale(0.3) rotate(0deg); } 75% { opacity: 0.9; transform: scale(1) rotate(45deg); } }
/* clouds drift to and fro; birds flap; tumbleweeds roll */
.lf-drift { transform: scale(var(--s, 1)); animation: lf-drift 90s linear infinite alternate; }
@keyframes lf-drift { from { transform: translateX(-90px) scale(var(--s, 1)); } to { transform: translateX(90px) scale(var(--s, 1)); } }
.lf-flap { animation: lf-flap 0.45s ease-in-out infinite alternate; }
@keyframes lf-flap { to { transform: scaleY(0.45); } }
.lf-roll { animation: lf-spin 1.1s linear infinite; }
/* Send Town: friends' speech bubbles pop up now and then; reactions float off the Wall; the LED
   tickers scroll (their strip slides along a window sheared to the board's face); a Rocket Run
   rocket goes up from the launchpad and pops at the top of its climb */
.lf-chat { animation: lf-chat var(--d, 7s) ease-in-out infinite; }
@keyframes lf-chat { 0%, 6% { opacity: 0; transform: scale(0.3); } 12%, 48% { opacity: 1; transform: scale(1); } 54%, 100% { opacity: 0; transform: scale(0.6); } }
.lf-react { opacity: 0; animation: lf-react 4.5s ease-out infinite; }
@keyframes lf-react { 0% { opacity: 0; transform: translate(0, 8px) scale(0.5); } 14% { opacity: 1; transform: translate(0, 0) scale(1); } 70% { opacity: 1; } 100% { opacity: 0; transform: translate(var(--dx, 6px), -64px) scale(1.1); } }
.lf-tick-win { position: absolute; overflow: hidden; transform: skewY(26.565deg); transform-origin: 0 0; }
.lf-tick { display: flex; width: max-content; animation: lf-tick 9s linear infinite; }
.lf-tick > svg { display: block; flex: none; }
@keyframes lf-tick { to { transform: translateX(var(--len, -240px)); } }
.lf-launch { opacity: 0; animation: lf-launch 11s cubic-bezier(0.45, 0, 0.8, 0.55) infinite; }
@keyframes lf-launch { 0%, 52% { opacity: 0; transform: translate(0, 0) rotate(0deg) scale(1); } 55% { opacity: 1; } 86% { opacity: 1; transform: translate(26px, -150px) rotate(14deg) scale(1); } 89%, 100% { opacity: 0; transform: translate(28px, -158px) rotate(14deg) scale(1.8); } }
/* far out (the level of detail "far") Send Town's people, bubbles, reactions, tickers and rockets, and the sea's
   glints, are a few pixels tall: they rest, so a pan over the whole realm stays at 60 fps */
.world[data-lod="far"] .lf-crowd, .world[data-lod="far"] .lf-chat, .world[data-lod="far"] .lf-react, .world[data-lod="far"] .lf-launch, .world[data-lod="far"] .lf-glint { display: none; }
.world[data-lod="far"] .lf-tick { animation: none; }
/* the lights go down with the sun: the sprites dim at night */
.world[data-phase="night"] .lf > svg, .world[data-phase="night"] .lf-in > svg { filter: brightness(0.62) saturate(0.85); }
.world[data-phase="dusk"] .lf > svg, .world[data-phase="dusk"] .lf-in > svg { filter: brightness(0.85); }
.world[data-phase="night"] .lf-sendy > svg, .world[data-phase="night"] .lf-trail > svg { filter: none; }

/* ===== buildings: family colours, hover, press ===== */
/* night: the windows' halos come up full; the lamps were already lit */
.world[data-phase="night"] .bld .lit, .world[data-phase="dusk"] .bld .lit { opacity: 1; }

/* ===== "something is ready here": a gold beacon on the tile ===== */
/* ::before is a pulsing ring on the ground (behind the art), ::after a soft column of light (over it) */
.bld.is-ready::before, .bld.is-ready::after { content: ""; position: absolute; pointer-events: none; }
.bld.is-ready::before {
  width: 92%; aspect-ratio: 2 / 1; left: calc(var(--ax, 0.5) * 100% - 46%); top: calc(var(--ay, 0.8) * 100%);
  border-radius: 50%; translate: 0 -50%;
  background: radial-gradient(closest-side, rgba(255, 210, 122, 0.85), rgba(255, 179, 64, 0.45) 55%, rgba(255, 179, 64, 0) 100%);
  animation: ready-ring 1.8s ease-out infinite;
}
@keyframes ready-ring { 0% { transform: scale(0.55); opacity: 1; } 100% { transform: scale(1.15); opacity: 0; } }
.bld.is-ready::after {
  width: 34%; left: calc(var(--ax, 0.5) * 100% - 17%); bottom: calc((1 - var(--ay, 0.8)) * 100%); height: 120%;
  border-radius: 50% 50% 0 0 / 18% 18% 0 0;
  background: linear-gradient(to top, rgba(255, 220, 140, 0.55), rgba(255, 179, 64, 0.22) 45%, rgba(255, 179, 64, 0));
  transform-origin: 50% 100%; animation: ready-beam 2.4s ease-in-out infinite;
}
@keyframes ready-beam { 0%, 100% { opacity: 0.55; transform: scaleX(0.8); } 50% { opacity: 1; transform: scaleX(1.05); } }
/* far out the beacon stays lit but holds still (it is a few pixels across there, and its animation repaints every frame) */
.world[data-lod="far"] .bld.is-ready::before, .world[data-lod="far"] .bld.is-ready::after { animation: none; }
/* the badge on its name sends out a ring too */
.map-label.is-ready .map-label-badge::after { content: ""; position: absolute; inset: -2px; border-radius: 999px; border: 2px solid var(--gold); animation: badge-ring 1.6s ease-out infinite; pointer-events: none; }
@keyframes badge-ring { 0% { transform: scale(1); opacity: 0.95; } 100% { transform: scale(1.9); opacity: 0; } }

/* ===== reduced motion ===== */
@media (prefers-reduced-motion: reduce) {
  .lf, .lf *, .bld.is-ready::before, .bld.is-ready::after, .map-label.is-ready .map-label-badge::after { animation: none !important; }
  .lf-glint, .lf-jump, .lf-sendy, .lf-trail, .lf-react, .lf-launch { display: none; }
}
html.motion-off .lf, html.motion-off .lf *, html.motion-off .bld.is-ready::before, html.motion-off .bld.is-ready::after, html.motion-off .map-label.is-ready .map-label-badge::after { animation: none !important; }
html.motion-off .lf-glint, html.motion-off .lf-jump, html.motion-off .lf-sendy, html.motion-off .lf-trail, html.motion-off .lf-react, html.motion-off .lf-launch { display: none; }
