// Mars City Builder - landmark-map city development level.

const {
  useMemo: marsCityUseMemo,
  useState: marsCityUseState,
} = React;

const MARS_CITY_ASSET_BASE = "data/generated-assets/mars-city";
const MARS_CITY_MAP_ASSET = `${MARS_CITY_ASSET_BASE}/mars-city-huge-map-v1.png`;
const MARS_CITY_PROP_BASE = `${MARS_CITY_ASSET_BASE}/props`;
const MARS_CITY_CHARACTER_BASE = `${MARS_CITY_ASSET_BASE}/characters`;
const MARS_CITY_STARTING_BUDGET = 18;

const MARS_CITY_NARRATION = {
  start: "game_mars_city_start.mp3",
  homes: "game_mars_city_homes.mp3",
  power: "game_mars_city_power.mp3",
  food: "game_mars_city_food.mp3",
  water: "game_mars_city_water.mp3",
  roads: "game_mars_city_roads.mp3",
  wonder: "game_mars_city_wonder.mp3",
  balanced: "game_mars_city_balanced.mp3",
};

const MARS_CITY_NEED_ORDER = [
  { id: "air", label: "Air", icon: "AIR" },
  { id: "homes", label: "Homes", icon: "HOME" },
  { id: "food", label: "Food", icon: "FOOD" },
  { id: "water", label: "Water", icon: "H2O" },
  { id: "energy", label: "Energy", icon: "SUN" },
];

const MARS_CITY_NEED_LABELS = MARS_CITY_NEED_ORDER.reduce((labels, need) => {
  labels[need.id] = need.label;
  return labels;
}, {});

const MARS_CITY_PROP_SHEETS = [
  { id: "oxygen-tank", label: "Oxygen Tank", use: "air" },
  { id: "food-crate", label: "Food Crate", use: "food" },
  { id: "water-barrel", label: "Water Barrel", use: "water" },
  { id: "solar-toolkit", label: "Solar Toolkit", use: "power" },
  { id: "rover-wrench", label: "Rover Wrench", use: "roads" },
  { id: "seed-pod", label: "Seed Pod", use: "food" },
  { id: "habitat-door", label: "Habitat Door", use: "homes" },
  { id: "comms-tablet", label: "Comms Tablet", use: "command" },
  { id: "survey-flag", label: "Survey Flag", use: "roads" },
  { id: "sample-tube-rack", label: "Sample Tubes", use: "science" },
  { id: "power-battery", label: "Power Battery", use: "power" },
  { id: "air-filter", label: "Air Filter", use: "air" },
  { id: "greenhouse-tray", label: "Greenhouse Tray", use: "food" },
  { id: "bubble-house-kit", label: "Bubble House Kit", use: "homes" },
  { id: "road-cone", label: "Road Cone", use: "roads" },
  { id: "robot-helper", label: "Robot Helper", use: "community" },
  { id: "med-kit", label: "Med Kit", use: "community" },
  { id: "recycler-bin", label: "Recycler Bin", use: "community" },
  { id: "weather-station", label: "Weather Station", use: "safety" },
  { id: "antenna-beacon", label: "Antenna Beacon", use: "command" },
  { id: "mini-drill", label: "Mini Drill", use: "water" },
  { id: "cargo-drone", label: "Cargo Drone", use: "roads" },
  { id: "dome-window", label: "Dome Window", use: "homes" },
  { id: "rover-map", label: "Rover Map", use: "roads" },
  { id: "celebration-lamp", label: "Celebration Lamp", use: "community" },
].map((prop) => ({
  ...prop,
  frames: 4,
  atlas: `${MARS_CITY_PROP_BASE}/${prop.id}-sheet-v1.png`,
}));

const MARS_CITY_CHARACTER_SHEETS = [
  {
    id: "astronaut-resident",
    label: "Mars city resident",
    frames: 4,
    file: "astronaut-resident-sheet-v2.png",
    frameFiles: [
      "frames/astronaut-idle-slot-v2.png",
      "frames/astronaut-walk-left-slot-v2.png",
      "frames/astronaut-walk-right-slot-v2.png",
      "frames/astronaut-wave-slot-v2.png",
    ],
    frameWidth: 512,
    frameHeight: 768,
  },
  {
    id: "utility-cart",
    label: "Utility supply cart",
    frames: 4,
    frameWidth: 543,
    frameHeight: 724,
  },
  {
    id: "cargo-drone",
    label: "Cargo drone",
    frames: 4,
    frameWidth: 543,
    frameHeight: 724,
  },
  {
    id: "helper-robot",
    label: "Helper robot",
    frames: 4,
    frameWidth: 543,
    frameHeight: 724,
  },
  {
    id: "bubble-house",
    label: "Bubble house habitat",
    frames: 4,
    frameWidth: 512,
    frameHeight: 768,
  },
].map((character) => ({
  ...character,
  atlas: `${MARS_CITY_CHARACTER_BASE}/${character.file || `${character.id}-sheet-v1.png`}`,
}));

const MARS_CITY_CORE_DISTRICTS = [
  {
    id: "habitat",
    label: "Habitat Village",
    shortLabel: "Homes",
    cue: "Give astronauts a safe place to sleep.",
    atlas: `${MARS_CITY_ASSET_BASE}/habitat-village-atlas-v1.png`,
    x: 25,
    y: 60,
    air: 3,
    power: -1,
    food: -1,
    water: -1,
    people: 3,
    comfort: 1,
  },
  {
    id: "solar",
    label: "Solar Power Field",
    shortLabel: "Power",
    cue: "Turn sunlight into city energy.",
    atlas: `${MARS_CITY_ASSET_BASE}/solar-power-atlas-v1.png`,
    x: 62,
    y: 28,
    air: 0,
    power: 5,
    food: 0,
    water: 0,
    people: 0,
    comfort: 0,
  },
  {
    id: "biodome",
    label: "Biodome Farms",
    shortLabel: "Food",
    cue: "Grow plants under glass domes.",
    atlas: `${MARS_CITY_ASSET_BASE}/biodome-greenhouse-atlas-v1.png`,
    x: 44,
    y: 48,
    air: 1,
    power: -1,
    food: 4,
    water: -1,
    people: 0,
    comfort: 1,
  },
  {
    id: "water",
    label: "Ice Water Plant",
    shortLabel: "Water",
    cue: "Melt buried ice for the town.",
    atlas: `${MARS_CITY_ASSET_BASE}/water-ice-plant-atlas-v1.png`,
    x: 73,
    y: 63,
    air: 1,
    power: -1,
    food: 1,
    water: 4,
    people: 0,
    comfort: 1,
  },
  {
    id: "rover",
    label: "Rover Port",
    shortLabel: "Roads",
    cue: "Connect buildings with rover paths.",
    atlas: `${MARS_CITY_ASSET_BASE}/rover-port-atlas-v1.png`,
    x: 39,
    y: 72,
    air: 0,
    power: -1,
    food: 0,
    water: 0,
    people: 0,
    comfort: 3,
  },
  {
    id: "spire",
    label: "Command Spire",
    shortLabel: "Wonder",
    cue: "Light up the finished Mars city.",
    atlas: `${MARS_CITY_ASSET_BASE}/command-spire-atlas-v1.png`,
    x: 53,
    y: 39,
    air: 1,
    power: -1,
    food: 0,
    water: 0,
    people: 1,
    comfort: 4,
  },
];

const MARS_CITY_EXPANSION_DISTRICTS = [
  { id: "north-habs", label: "North Ridge Homes", shortLabel: "Ridge", cue: "Add quiet homes near the canyon rim.", atlas: `${MARS_CITY_ASSET_BASE}/habitat-village-atlas-v1.png`, x: 17, y: 36, air: 2, power: -1, food: -1, water: -1, people: 2, comfort: 2, unlockStage: 1 },
  { id: "east-habs", label: "East Bubble Block", shortLabel: "East Homes", cue: "Make another neighborhood for new residents.", atlas: `${MARS_CITY_ASSET_BASE}/habitat-village-atlas-v1.png`, x: 84, y: 45, air: 2, power: -1, food: -1, water: -1, people: 3, comfort: 1, unlockStage: 4 },
  { id: "south-habs", label: "South Crater Homes", shortLabel: "Crater", cue: "Build crater-side homes close to the water road.", atlas: `${MARS_CITY_ASSET_BASE}/habitat-village-atlas-v1.png`, x: 58, y: 82, air: 2, power: -1, food: -1, water: -1, people: 2, comfort: 2, unlockStage: 4 },
  { id: "solar-north", label: "North Solar Ridge", shortLabel: "North Sun", cue: "Spread panels across the bright high ground.", atlas: `${MARS_CITY_ASSET_BASE}/solar-power-atlas-v1.png`, x: 34, y: 22, air: 0, power: 4, food: 0, water: 0, people: 0, comfort: 0, unlockStage: 1 },
  { id: "solar-east", label: "East Solar Farm", shortLabel: "East Sun", cue: "Power the far side of the city.", atlas: `${MARS_CITY_ASSET_BASE}/solar-power-atlas-v1.png`, x: 81, y: 24, air: 0, power: 5, food: 0, water: 0, people: 0, comfort: 0, unlockStage: 2 },
  { id: "battery-yard", label: "Battery Yard", shortLabel: "Battery", cue: "Store energy for dusty afternoons.", atlas: `${MARS_CITY_ASSET_BASE}/solar-power-atlas-v1.png`, x: 48, y: 26, air: 0, power: 3, food: 0, water: 0, people: 0, comfort: 1, unlockStage: 2 },
  { id: "farm-west", label: "West Greenhouse Row", shortLabel: "West Farm", cue: "Grow extra food near the first neighborhood.", atlas: `${MARS_CITY_ASSET_BASE}/biodome-greenhouse-atlas-v1.png`, x: 20, y: 50, air: 1, power: -1, food: 3, water: -1, people: 0, comfort: 1, unlockStage: 2 },
  { id: "farm-east", label: "East Greenhouse Row", shortLabel: "East Farm", cue: "Feed the far neighborhoods.", atlas: `${MARS_CITY_ASSET_BASE}/biodome-greenhouse-atlas-v1.png`, x: 78, y: 54, air: 1, power: -1, food: 4, water: -1, people: 0, comfort: 1, unlockStage: 3 },
  { id: "orchard-dome", label: "Orchard Dome", shortLabel: "Orchard", cue: "Add fruit trees under warm lights.", atlas: `${MARS_CITY_ASSET_BASE}/biodome-greenhouse-atlas-v1.png`, x: 54, y: 58, air: 2, power: -1, food: 3, water: -1, people: 0, comfort: 2, unlockStage: 3 },
  { id: "ice-north", label: "North Ice Mine", shortLabel: "North Ice", cue: "Drill ice from the cold ridge.", atlas: `${MARS_CITY_ASSET_BASE}/water-ice-plant-atlas-v1.png`, x: 14, y: 72, air: 0, power: -1, food: 0, water: 4, people: 0, comfort: 0, unlockStage: 3 },
  { id: "ice-east", label: "East Ice Mine", shortLabel: "East Ice", cue: "Open a second water source near the far farms.", atlas: `${MARS_CITY_ASSET_BASE}/water-ice-plant-atlas-v1.png`, x: 88, y: 70, air: 0, power: -1, food: 0, water: 5, people: 0, comfort: 0, unlockStage: 4 },
  { id: "water-loop", label: "Water Loop Station", shortLabel: "Pipes", cue: "Move water around the city faster.", atlas: `${MARS_CITY_ASSET_BASE}/water-ice-plant-atlas-v1.png`, x: 64, y: 69, air: 0, power: -1, food: 1, water: 3, people: 0, comfort: 1, unlockStage: 4 },
  { id: "north-garage", label: "North Rover Garage", shortLabel: "N Garage", cue: "Send service rovers to the ridge.", atlas: `${MARS_CITY_ASSET_BASE}/rover-port-atlas-v1.png`, x: 31, y: 39, air: 0, power: -1, food: 0, water: 0, people: 0, comfort: 2, unlockStage: 4 },
  { id: "east-garage", label: "East Rover Garage", shortLabel: "E Garage", cue: "Connect the far farms, homes, and ice mine.", atlas: `${MARS_CITY_ASSET_BASE}/rover-port-atlas-v1.png`, x: 72, y: 43, air: 0, power: -1, food: 0, water: 0, people: 0, comfort: 3, unlockStage: 4 },
  { id: "cargo-yard", label: "Cargo Yard", shortLabel: "Cargo", cue: "Sort supplies before they travel across town.", atlas: `${MARS_CITY_ASSET_BASE}/rover-port-atlas-v1.png`, x: 46, y: 80, air: 0, power: -1, food: 1, water: 1, people: 0, comfort: 2, unlockStage: 5 },
  { id: "clinic-dome", label: "Clinic Dome", shortLabel: "Clinic", cue: "Give residents a care stop near the plaza.", atlas: `${MARS_CITY_ASSET_BASE}/command-spire-atlas-v1.png`, x: 42, y: 38, air: 1, power: -1, food: 0, water: 0, people: 0, comfort: 4, unlockStage: 5 },
  { id: "school-dome", label: "School Dome", shortLabel: "School", cue: "Make a learning stop for young explorers.", atlas: `${MARS_CITY_ASSET_BASE}/command-spire-atlas-v1.png`, x: 68, y: 34, air: 1, power: -1, food: 0, water: 0, people: 0, comfort: 4, unlockStage: 5 },
  { id: "observatory", label: "Dust Observatory", shortLabel: "Weather", cue: "Watch dust storms before they reach the city.", atlas: `${MARS_CITY_ASSET_BASE}/command-spire-atlas-v1.png`, x: 87, y: 31, air: 0, power: -1, food: 0, water: 0, people: 0, comfort: 3, unlockStage: 5 },
  { id: "plaza", label: "Community Plaza", shortLabel: "Plaza", cue: "Make the center of town feel alive.", atlas: `${MARS_CITY_ASSET_BASE}/command-spire-atlas-v1.png`, x: 49, y: 45, air: 1, power: -1, food: 0, water: 0, people: 1, comfort: 5, unlockStage: 5 },
  { id: "launch-field", label: "Launch Field", shortLabel: "Launch", cue: "Keep a landing and launch lane open for supply ships.", atlas: `${MARS_CITY_ASSET_BASE}/rover-port-atlas-v1.png`, x: 10, y: 43, air: 0, power: -1, food: 0, water: 0, people: 0, comfort: 2, unlockStage: 5 },
];

const MARS_CITY_DISTRICTS = [
  ...MARS_CITY_CORE_DISTRICTS,
  ...MARS_CITY_EXPANSION_DISTRICTS,
];

const MARS_CITY_CORE_DISTRICT_IDS = MARS_CITY_CORE_DISTRICTS.map((district) => district.id);

const MARS_CITY_QUESTS = [
  {
    id: "homes",
    title: "Landing Camp",
    prompt: "Your first astronauts need a safe place to breathe.",
    story: "Start with air, shelter, and a short road from the landing pad.",
    mayorNeed: "Air and homes come first.",
    narration: MARS_CITY_NARRATION.homes,
    unlock: "habitat",
    target: "habitat",
    propIds: ["oxygen-tank", "air-filter", "habitat-door", "bubble-house-kit"],
    choices: [
      {
        label: "Build bubble homes",
        result: "Cost 4 · helps homes and air",
        tradeoff: "Homes use food, water, and energy later.",
        needTags: ["homes", "air"],
        helps: "Residents get safe rooms and more clean air.",
        risk: "Food, water, and energy will dip until the city grows.",
        target: "habitat",
        cost: 4,
        rewardBudget: 6,
      },
      {
        label: "Set oxygen tanks",
        result: "Cost 3 · quick air safety",
        tradeoff: "Still needs homes next.",
        needTags: ["air"],
        helps: "Residents can breathe while homes are prepared.",
        risk: "Shelter still needs a mayor choice.",
        target: "habitat",
        cost: 3,
        rewardBudget: 5,
      },
    ],
  },
  {
    id: "power",
    title: "Solar Street",
    prompt: "Energy keeps lights, doors, and heaters working.",
    story: "Astronauts carry battery packs and open the first solar block.",
    mayorNeed: "Energy keeps every other need working.",
    narration: MARS_CITY_NARRATION.power,
    unlock: "solar",
    target: "solar",
    propIds: ["solar-toolkit", "power-battery", "weather-station"],
    choices: [
      {
        label: "Raise solar panels",
        result: "Cost 5 · gives lots of energy",
        tradeoff: "Panels do not make food or water.",
        needTags: ["energy"],
        helps: "Lights, heaters, and doors get steady power.",
        risk: "The city still needs food and water next.",
        target: "solar",
        cost: 5,
        rewardBudget: 7,
      },
      {
        label: "Charge batteries",
        result: "Cost 4 · stores backup energy",
        tradeoff: "The town still needs a farm.",
        needTags: ["energy"],
        helps: "Backup batteries keep the first homes running.",
        risk: "Batteries help power, not dinner.",
        target: "solar",
        cost: 4,
        rewardBudget: 6,
      },
    ],
  },
  {
    id: "food",
    title: "Green Food Dome",
    prompt: "A town needs food close to home.",
    story: "Seed pods and greenhouse trays turn the dome into a tiny farm.",
    mayorNeed: "Food feeds the new residents.",
    narration: MARS_CITY_NARRATION.food,
    unlock: "biodome",
    target: "biodome",
    propIds: ["seed-pod", "greenhouse-tray", "food-crate"],
    choices: [
      {
        label: "Plant seed pods",
        result: "Cost 6 · grows more food",
        tradeoff: "Plants need water and energy.",
        needTags: ["food", "water", "energy"],
        helps: "Seed pods grow meals close to home.",
        risk: "Plants ask for water and steady lights.",
        target: "biodome",
        cost: 6,
        rewardBudget: 8,
      },
      {
        label: "Deliver food crates",
        result: "Cost 5 · fills today’s pantry",
        tradeoff: "Crates help now; gardens help later.",
        needTags: ["food"],
        helps: "Residents get food right away.",
        risk: "A garden will feed the city longer.",
        target: "biodome",
        cost: 5,
        rewardBudget: 7,
      },
    ],
  },
  {
    id: "water",
    title: "Water Hunt",
    prompt: "Mars water starts as ice. Melt and clean it for the town.",
    story: "The crew drills ice, fills barrels, and sends water to homes.",
    mayorNeed: "Water helps people drink and plants grow.",
    narration: MARS_CITY_NARRATION.water,
    unlock: "water",
    target: "water",
    propIds: ["mini-drill", "water-barrel", "sample-tube-rack"],
    choices: [
      {
        label: "Drill for ice",
        result: "Cost 7 · finds hidden water ice",
        tradeoff: "Drills need steady energy.",
        needTags: ["water", "energy"],
        helps: "Ice becomes water for people and plants.",
        risk: "Drills use power while they work.",
        target: "water",
        cost: 7,
        rewardBudget: 9,
      },
      {
        label: "Fill water barrels",
        result: "Cost 6 · sends water to homes",
        tradeoff: "Barrels need roads to move fast.",
        needTags: ["water"],
        helps: "Water reaches homes and farms.",
        risk: "Rover roads will move supplies faster.",
        target: "water",
        cost: 6,
        rewardBudget: 8,
      },
    ],
  },
  {
    id: "roads",
    title: "Rover Work Yard",
    prompt: "Roads help neighbors reach work, homes, and supplies.",
    story: "Road cones, rover maps, and cargo drones connect the city.",
    mayorNeed: "Roads move air, food, water, and helpers.",
    narration: MARS_CITY_NARRATION.roads,
    unlock: "rover",
    target: "rover",
    propIds: ["road-cone", "rover-map", "rover-wrench", "cargo-drone", "survey-flag"],
    choices: [
      {
        label: "Mark safe roads",
        result: "Cost 8 · connects supply stops",
        tradeoff: "Road crews need energy.",
        needTags: ["homes", "food", "water", "energy"],
        helps: "Supplies can travel between homes, farms, and water.",
        risk: "Road crews spend energy to connect everyone.",
        target: "rover",
        cost: 8,
        rewardBudget: 10,
      },
      {
        label: "Repair rover routes",
        result: "Cost 7 · helps neighbors travel",
        tradeoff: "Routes work best with water and food ready.",
        needTags: ["homes", "food", "water"],
        helps: "Neighbors can reach work, food, and water.",
        risk: "Routes shine most when supplies are ready.",
        target: "rover",
        cost: 7,
        rewardBudget: 9,
      },
    ],
  },
  {
    id: "wonder",
    title: "Mars Town Day",
    prompt: "Balance air, homes, food, water, energy, roads, and community.",
    story: "The command spire lights up when the town can care for everyone.",
    mayorNeed: "A good mayor checks every need before celebrating.",
    narration: MARS_CITY_NARRATION.wonder,
    unlock: "spire",
    target: "spire",
    minLevel: 2,
    propIds: ["comms-tablet", "antenna-beacon", "robot-helper", "med-kit", "recycler-bin", "dome-window", "celebration-lamp"],
    choices: [
      {
        label: "Open command spire",
        result: "Cost 9 · checks the whole city",
        tradeoff: "Only shines when needs are balanced.",
        needTags: ["air", "homes", "food", "water", "energy"],
        helps: "The spire checks every resident need before celebration.",
        risk: "A celebration waits if any need is low.",
        target: "spire",
        cost: 9,
        rewardBudget: 12,
      },
      {
        label: "Light the plaza",
        result: "Cost 8 · boosts community",
        tradeoff: "Fun comes after basic needs.",
        needTags: ["homes", "energy"],
        helps: "The plaza helps the city feel like home.",
        risk: "Basic needs still come first.",
        target: "spire",
        cost: 8,
        rewardBudget: 11,
      },
    ],
  },
];

const MARS_CITY_ROADS = [
  { id: "homes-food", x: 29, y: 61, w: 20, r: -16, requires: ["habitat", "biodome"] },
  { id: "food-wonder", x: 47, y: 54, w: 15, r: -43, requires: ["biodome", "spire"] },
  { id: "wonder-power", x: 55, y: 42, w: 17, r: -28, requires: ["spire", "solar"] },
  { id: "wonder-water", x: 58, y: 52, w: 24, r: 37, requires: ["spire", "water"] },
  { id: "homes-rover", x: 28, y: 69, w: 16, r: 18, requires: ["habitat", "rover"] },
  { id: "rover-water", x: 43, y: 70, w: 32, r: -9, requires: ["rover", "water"] },
  { id: "launch-ridge", x: 12, y: 42, w: 23, r: -16, requires: ["launch-field", "north-garage"] },
  { id: "ridge-power", x: 32, y: 34, w: 30, r: -18, requires: ["north-garage", "solar"] },
  { id: "north-habs-line", x: 19, y: 38, w: 17, r: 7, requires: ["north-habs", "north-garage"] },
  { id: "solar-backbone", x: 47, y: 28, w: 35, r: -5, requires: ["battery-yard", "solar-east"] },
  { id: "east-spine", x: 70, y: 38, w: 20, r: 17, requires: ["east-garage", "east-habs"] },
  { id: "east-farm-road", x: 72, y: 50, w: 19, r: 42, requires: ["east-garage", "farm-east"] },
  { id: "east-ice-road", x: 78, y: 62, w: 15, r: 55, requires: ["east-garage", "ice-east"] },
  { id: "south-freight", x: 47, y: 78, w: 22, r: 8, requires: ["cargo-yard", "south-habs"] },
  { id: "west-farm-road", x: 20, y: 55, w: 24, r: -8, requires: ["farm-west", "biodome"] },
  { id: "north-ice-road", x: 16, y: 69, w: 24, r: 8, requires: ["ice-north", "rover"] },
  { id: "pipe-loop", x: 61, y: 70, w: 26, r: 0, requires: ["water-loop", "ice-east"] },
  { id: "civic-loop", x: 42, y: 41, w: 28, r: -9, requires: ["clinic-dome", "school-dome"] },
  { id: "plaza-loop", x: 49, y: 48, w: 16, r: 35, requires: ["plaza", "spire"] },
  { id: "observatory-road", x: 68, y: 33, w: 21, r: -11, requires: ["school-dome", "observatory"] },
];

const MARS_CITY_BUBBLE_HOUSES = [
  { id: "home-a", x: 20.5, y: 55.5, size: "small" },
  { id: "home-b", x: 24.5, y: 51.5, size: "medium" },
  { id: "home-c", x: 29.5, y: 57.5, size: "small" },
  { id: "home-d", x: 22.5, y: 66, size: "medium" },
  { id: "home-e", x: 32, y: 63.5, size: "large" },
  { id: "home-f", x: 16.5, y: 34, size: "small" },
  { id: "home-g", x: 20.5, y: 38.5, size: "medium" },
  { id: "home-h", x: 84.5, y: 42, size: "small" },
  { id: "home-i", x: 87.5, y: 49, size: "medium" },
  { id: "home-j", x: 57.5, y: 85, size: "small" },
  { id: "home-k", x: 61.5, y: 80, size: "medium" },
];

const MARS_CITY_SURFACE_SECTORS = [
  { id: "landing-grid", label: "Landing Grid", type: "civic", x: 5, y: 36, w: 16, h: 15, districtId: "launch-field" },
  { id: "north-village-grid", label: "North Homes Grid", type: "hab", x: 12, y: 28, w: 18, h: 15, districtId: "north-habs" },
  { id: "core-homes-grid", label: "Core Homes Grid", type: "hab", x: 18, y: 52, w: 20, h: 18, districtId: "habitat" },
  { id: "west-farm-grid", label: "West Farm Grid", type: "food", x: 14, y: 46, w: 16, h: 13, districtId: "farm-west" },
  { id: "north-ice-grid", label: "North Ice Grid", type: "water", x: 8, y: 66, w: 17, h: 13, districtId: "ice-north" },
  { id: "rover-yard-grid", label: "Rover Yard Grid", type: "traffic", x: 34, y: 67, w: 16, h: 12, districtId: "rover" },
  { id: "cargo-grid", label: "Cargo Grid", type: "traffic", x: 39, y: 75, w: 14, h: 12, districtId: "cargo-yard" },
  { id: "main-farm-grid", label: "Biodome Grid", type: "food", x: 38, y: 43, w: 18, h: 16, districtId: "biodome" },
  { id: "civic-grid", label: "Civic Grid", type: "civic", x: 43, y: 35, w: 16, h: 14, districtId: "spire" },
  { id: "clinic-grid", label: "Clinic Grid", type: "civic", x: 37, y: 32, w: 12, h: 10, districtId: "clinic-dome" },
  { id: "orchard-grid", label: "Orchard Grid", type: "food", x: 49, y: 53, w: 15, h: 13, districtId: "orchard-dome" },
  { id: "solar-core-grid", label: "Solar Core Grid", type: "energy", x: 56, y: 20, w: 16, h: 14, districtId: "solar" },
  { id: "battery-grid", label: "Battery Grid", type: "energy", x: 43, y: 20, w: 13, h: 12, districtId: "battery-yard" },
  { id: "north-solar-grid", label: "North Solar Grid", type: "energy", x: 28, y: 16, w: 15, h: 12, districtId: "solar-north" },
  { id: "east-solar-grid", label: "East Solar Grid", type: "energy", x: 76, y: 17, w: 18, h: 14, districtId: "solar-east" },
  { id: "school-grid", label: "School Grid", type: "civic", x: 63, y: 28, w: 12, h: 10, districtId: "school-dome" },
  { id: "east-garage-grid", label: "East Garage Grid", type: "traffic", x: 67, y: 39, w: 12, h: 11, districtId: "east-garage" },
  { id: "east-homes-grid", label: "East Homes Grid", type: "hab", x: 80, y: 38, w: 14, h: 16, districtId: "east-habs" },
  { id: "east-farm-grid", label: "East Farm Grid", type: "food", x: 73, y: 50, w: 15, h: 15, districtId: "farm-east" },
  { id: "water-grid", label: "Water Plant Grid", type: "water", x: 68, y: 58, w: 14, h: 14, districtId: "water" },
  { id: "east-ice-grid", label: "East Ice Grid", type: "water", x: 82, y: 65, w: 15, h: 12, districtId: "ice-east" },
  { id: "pipe-grid", label: "Pipe Station Grid", type: "water", x: 59, y: 64, w: 12, h: 12, districtId: "water-loop" },
  { id: "south-homes-grid", label: "South Homes Grid", type: "hab", x: 52, y: 77, w: 17, h: 15, districtId: "south-habs" },
  { id: "weather-grid", label: "Observatory Grid", type: "civic", x: 82, y: 25, w: 12, h: 11, districtId: "observatory" },
];

const MARS_CITY_RESOURCE_NODES = [
  { id: "ice-1", label: "ice", type: "water", x: 12, y: 75, districtId: "ice-north" },
  { id: "ice-2", label: "ice", type: "water", x: 90, y: 76, districtId: "ice-east" },
  { id: "sun-1", label: "sun", type: "energy", x: 36, y: 18, districtId: "solar-north" },
  { id: "sun-2", label: "sun", type: "energy", x: 83, y: 19, districtId: "solar-east" },
  { id: "food-1", label: "food", type: "food", x: 19, y: 49, districtId: "farm-west" },
  { id: "food-2", label: "food", type: "food", x: 77, y: 57, districtId: "farm-east" },
  { id: "air-1", label: "air", type: "air", x: 25, y: 59, districtId: "habitat" },
  { id: "air-2", label: "air", type: "air", x: 85, y: 46, districtId: "east-habs" },
  { id: "care-1", label: "care", type: "civic", x: 42, y: 38, districtId: "clinic-dome" },
  { id: "cargo-1", label: "cargo", type: "traffic", x: 46, y: 80, districtId: "cargo-yard" },
];

const MARS_CITY_ACTIVITY_MARKERS = [
  { id: "cart-homes", type: "cart", characterId: "utility-cart", x: 28, y: 63, dx: 70, dy: -18, delay: "0s", propId: "bubble-house-kit" },
  { id: "solar-run", type: "worker", characterId: "astronaut-resident", x: 56, y: 42, dx: 42, dy: -44, delay: "-0.8s", propId: "power-battery" },
  { id: "food-run", type: "worker", characterId: "astronaut-resident", x: 40, y: 58, dx: 54, dy: -56, delay: "-1.4s", propId: "food-crate" },
  { id: "water-run", type: "cart", characterId: "utility-cart", x: 69, y: 65, dx: -64, dy: -14, delay: "-2.2s", propId: "water-barrel" },
  { id: "drone-loop", type: "drone", characterId: "cargo-drone", x: 51, y: 38, dx: 120, dy: 46, delay: "-1.1s", propId: "cargo-drone" },
  { id: "road-crew", type: "worker", characterId: "helper-robot", x: 43, y: 71, dx: 90, dy: -26, delay: "-3s", propId: "road-cone" },
  { id: "plaza-light", type: "spark", characterId: "helper-robot", x: 54, y: 44, dx: 18, dy: -28, delay: "-1.7s", propId: "celebration-lamp" },
  { id: "comms-hop", type: "drone", characterId: "cargo-drone", x: 58, y: 36, dx: 86, dy: -20, delay: "-2.8s", propId: "antenna-beacon" },
  { id: "north-cart", type: "cart", characterId: "utility-cart", x: 20, y: 39, dx: 110, dy: -28, delay: "-3.4s", propId: "oxygen-tank" },
  { id: "ice-haul", type: "cart", characterId: "utility-cart", x: 15, y: 73, dx: 185, dy: -8, delay: "-4.1s", propId: "water-barrel" },
  { id: "east-haul", type: "cart", characterId: "utility-cart", x: 85, y: 70, dx: -150, dy: -34, delay: "-4.8s", propId: "water-barrel" },
  { id: "school-run", type: "worker", characterId: "astronaut-resident", x: 68, y: 35, dx: -80, dy: 34, delay: "-5.2s", propId: "comms-tablet" },
  { id: "weather-drone", type: "drone", characterId: "cargo-drone", x: 87, y: 31, dx: -220, dy: 20, delay: "-5.7s", propId: "weather-station" },
  { id: "farm-drone", type: "drone", characterId: "cargo-drone", x: 78, y: 55, dx: -135, dy: 42, delay: "-6.1s", propId: "seed-pod" },
  { id: "clinic-helper", type: "worker", characterId: "helper-robot", x: 42, y: 38, dx: 76, dy: 18, delay: "-6.6s", propId: "med-kit" },
  { id: "plaza-spark", type: "spark", characterId: "helper-robot", x: 49, y: 45, dx: 48, dy: -16, delay: "-7s", propId: "celebration-lamp" },
];

const MARS_CITY_RESIDENTS = [
  {
    id: "maya",
    x: 24,
    y: 62,
    dx: 10,
    dy: -5,
    delay: "0s",
    characterFrame: 0,
    needPropId: "oxygen-tank",
    toolPropIds: ["air-filter", "habitat-door"],
  },
  {
    id: "rio",
    x: 34,
    y: 57,
    dx: 16,
    dy: 2,
    delay: "-1.2s",
    characterFrame: 1,
    needPropId: "food-crate",
    toolPropIds: ["greenhouse-tray", "seed-pod"],
  },
  {
    id: "sol",
    x: 47,
    y: 50,
    dx: 10,
    dy: -8,
    delay: "-2.1s",
    characterFrame: 2,
    needPropId: "solar-toolkit",
    toolPropIds: ["power-battery", "weather-station"],
  },
  {
    id: "nix",
    x: 55,
    y: 45,
    dx: 14,
    dy: 7,
    delay: "-0.7s",
    characterFrame: 3,
    needPropId: "water-barrel",
    toolPropIds: ["mini-drill", "sample-tube-rack"],
  },
  {
    id: "una",
    x: 62,
    y: 57,
    dx: 11,
    dy: 8,
    delay: "-1.8s",
    characterFrame: 1,
    needPropId: "road-cone",
    toolPropIds: ["rover-map", "survey-flag"],
  },
  {
    id: "leo",
    x: 41,
    y: 70,
    dx: 18,
    dy: -4,
    delay: "-2.8s",
    characterFrame: 2,
    needPropId: "rover-wrench",
    toolPropIds: ["cargo-drone", "robot-helper"],
  },
  {
    id: "ada",
    x: 71,
    y: 62,
    dx: -10,
    dy: -7,
    delay: "-3.3s",
    characterFrame: 0,
    needPropId: "med-kit",
    toolPropIds: ["recycler-bin", "dome-window"],
  },
  {
    id: "kai",
    x: 37,
    y: 64,
    dx: -8,
    dy: -9,
    delay: "-2.4s",
    characterFrame: 3,
    needPropId: "comms-tablet",
    toolPropIds: ["antenna-beacon", "bubble-house-kit", "celebration-lamp"],
  },
];

function marsCityDistrictFor(id) {
  return MARS_CITY_DISTRICTS.find((district) => district.id === id) || null;
}

function createMarsCityState() {
  return MARS_CITY_DISTRICTS.reduce((state, district) => {
    state[district.id] = 0;
    return state;
  }, {});
}

function marsCityDistrictUnlockStage(district) {
  if (!district) return 0;
  if (Number.isFinite(district.unlockStage)) return district.unlockStage;
  const questIndex = MARS_CITY_QUESTS.findIndex(
    (quest) => quest.unlock === district.id || quest.target === district.id,
  );
  return questIndex >= 0 ? questIndex : 0;
}

function marsCityUnlockedDistrictIds(questIndex) {
  return MARS_CITY_DISTRICTS.filter(
    (district) => marsCityDistrictUnlockStage(district) <= questIndex,
  ).map((district) => district.id);
}

function scoreMarsCityDistricts(levels) {
  const totals = {
    air: 0,
    power: 0,
    food: 0,
    water: 0,
    people: 0,
    comfort: 0,
    built: 0,
  };
  MARS_CITY_DISTRICTS.forEach((district) => {
    const level = Math.max(0, Math.min(3, Number(levels[district.id]) || 0));
    if (!level) return;
    const multiplier = level === 3 ? 2 : level === 2 ? 1.5 : 1;
    totals.air += Math.round((district.air || 0) * multiplier);
    totals.power += Math.round((district.power || 0) * multiplier);
    totals.food += Math.round((district.food || 0) * multiplier);
    totals.water += Math.round((district.water || 0) * multiplier);
    totals.people += Math.round((district.people || 0) * multiplier);
    totals.comfort += Math.round((district.comfort || 0) * multiplier);
    totals.built += 1;
  });
  const airNeed = Math.max(1, Math.ceil(totals.people / 3));
  const balanceBonus =
    totals.air >= airNeed &&
    totals.power >= 0 &&
    totals.food >= 0 &&
    totals.water >= 0
      ? 3
      : 0;
  const coreBuilt = MARS_CITY_CORE_DISTRICT_IDS.filter(
    (districtId) => (levels[districtId] || 0) > 0,
  ).length;
  const cityScore = Math.max(
    0,
    Math.min(60, totals.built + totals.comfort + balanceBonus + Math.floor(totals.people / 2)),
  );
  return {
    ...totals,
    coreBuilt,
    airNeed,
    homesNeed: 1,
    homesReady: (levels.habitat || 0) > 0,
    score: cityScore,
    ready:
      coreBuilt >= MARS_CITY_CORE_DISTRICT_IDS.length &&
      totals.air >= airNeed &&
      (levels.habitat || 0) > 0 &&
      totals.power >= 0 &&
      totals.food >= 0 &&
      totals.water >= 0,
  };
}

function marsCityQuestComplete(quest, levels) {
  if (!quest) return false;
  return (levels[quest.target] || 0) >= (quest.minLevel || 1);
}

function marsCityAtlasPosition(level) {
  const frame = Math.max(0, Math.min(15, level * 5));
  const col = frame % 4;
  const row = Math.floor(frame / 4);
  return `${col * 33.333}% ${row * 33.333}%`;
}

function marsCityPropFor(propId) {
  return MARS_CITY_PROP_SHEETS.find((prop) => prop.id === propId) || null;
}

function marsCityPropFramePosition(frame) {
  const safeFrame = Math.max(0, Math.min(3, Number(frame) || 0));
  return `${safeFrame * 33.333}% 50%`;
}

function marsCityCharacterFor(characterId) {
  return (
    MARS_CITY_CHARACTER_SHEETS.find((character) => character.id === characterId) ||
    MARS_CITY_CHARACTER_SHEETS[0]
  );
}

function marsCityCharacterFramePosition(frame) {
  const safeFrame = Math.max(0, Math.min(3, Number(frame) || 0));
  return `${safeFrame * 33.333}% 50%`;
}

function MarsCityPropSprite({ propId, className = "", frame = 0 }) {
  const prop = marsCityPropFor(propId);
  if (!prop) return null;
  return (
    <span
      className={`mars-city-prop-sprite ${className}`}
      role="img"
      aria-label={prop.label}
      style={{
        backgroundImage: `url(${prop.atlas})`,
        backgroundPosition: marsCityPropFramePosition(frame),
      }}
    />
  );
}

function MarsCityCharacterSprite({ characterId, className = "", frame = 0 }) {
  const character = marsCityCharacterFor(characterId);
  if (!character) return null;
  const safeFrame = Math.max(0, Math.min(3, Number(frame) || 0));
  const frameWidth = Number(character.frameWidth) || 1;
  const frameHeight = Number(character.frameHeight) || 1;
  const frameFile = Array.isArray(character.frameFiles)
    ? character.frameFiles[safeFrame]
    : null;
  return (
    <span
      className={`mars-city-character-sprite ${className}`}
      role="img"
      aria-label={character.label}
      style={{
        aspectRatio: `${frameWidth} / ${frameHeight}`,
        ...(frameFile
          ? {}
          : {
              backgroundImage: `url(${character.atlas})`,
              backgroundPosition: marsCityCharacterFramePosition(safeFrame),
            }),
      }}
    >
      {frameFile ? (
        <img
          alt=""
          aria-hidden="true"
          draggable="false"
          src={`${MARS_CITY_CHARACTER_BASE}/${frameFile}`}
        />
      ) : null}
    </span>
  );
}

function marsCityBuildCost(districtId, levels) {
  const level = Math.max(0, Number(levels[districtId]) || 0);
  const districtIndex = Math.max(
    0,
    MARS_CITY_DISTRICTS.findIndex((district) => district.id === districtId),
  );
  return Math.min(12, 4 + districtIndex + level);
}

function marsCityBudgetLabel(value) {
  return `${Math.max(0, Number(value) || 0)} credits`;
}

function marsCityPlayNarration(file) {
  if (window.__narration && file) window.__narration.play(file);
}

function marsCityNeedLabel(needId) {
  return MARS_CITY_NEED_LABELS[needId] || needId;
}

function marsCityChoiceNeedText(choice) {
  const needTags = Array.isArray(choice && choice.needTags) ? choice.needTags : [];
  if (!needTags.length) return "Resident need";
  return needTags.map(marsCityNeedLabel).join(" + ");
}

function marsCityReadyNeedCount(needCards) {
  return needCards.filter((need) => need.ready).length;
}

function marsCityNeedCards(stats, levels) {
  const homesBuilt = levels.habitat || 0;
  const values = {
    air: {
      value: stats.air,
      need: stats.airNeed,
      ready: stats.air >= stats.airNeed,
      note: stats.air >= stats.airNeed ? "safe" : "needs filters",
    },
    homes: {
      value: homesBuilt,
      need: stats.homesNeed,
      ready: stats.homesReady,
      note: stats.homesReady ? "sheltered" : "needs homes",
    },
    food: {
      value: stats.food,
      need: 0,
      ready: stats.food >= 0,
      note: stats.food >= 0 ? "fed" : "needs crops",
    },
    water: {
      value: stats.water,
      need: 0,
      ready: stats.water >= 0,
      note: stats.water >= 0 ? "flowing" : "needs ice",
    },
    energy: {
      value: stats.power,
      need: 0,
      ready: stats.power >= 0,
      note: stats.power >= 0 ? "charged" : "needs sun",
    },
  };
  return MARS_CITY_NEED_ORDER.map((need) => ({ ...need, ...values[need.id] }));
}

function marsCityRoadIsActive(road, levels) {
  if (!road || !levels) return false;
  if (Array.isArray(road.requires) && road.requires.length) {
    return road.requires.every((districtId) => (levels[districtId] || 0) > 0);
  }
  if (levels.rover > 0) return true;
  return road.id
    .split("-")
    .some((part) =>
      part === "homes" ? levels.habitat > 0 : levels[part] > 0,
    );
}

function MarsCityBuilderLevel({ onExit }) {
  const [levels, setLevels] = marsCityUseState(() => createMarsCityState());
  const [questIndex, setQuestIndex] = marsCityUseState(0);
  const [selectedDistrict, setSelectedDistrict] = marsCityUseState("habitat");
  const [reward, setReward] = marsCityUseState(null);
  const [budget, setBudget] = marsCityUseState(MARS_CITY_STARTING_BUDGET);
  const [spentBudget, setSpentBudget] = marsCityUseState(0);
  const [mapView, setMapView] = marsCityUseState("build");
  const [mapScale, setMapScale] = marsCityUseState("wide");
  const unlockedIds = marsCityUseMemo(
    () => marsCityUnlockedDistrictIds(questIndex),
    [questIndex],
  );
  const stats = marsCityUseMemo(() => scoreMarsCityDistricts(levels), [levels]);
  const needCards = marsCityUseMemo(
    () => marsCityNeedCards(stats, levels),
    [stats, levels],
  );
  const currentQuest = MARS_CITY_QUESTS[questIndex] || null;
  const completedAllMissions =
    questIndex >= MARS_CITY_QUESTS.length - 1 &&
    marsCityQuestComplete(currentQuest, levels);
  const activeQuest = completedAllMissions ? null : currentQuest;
  const activeProps = activeQuest
    ? (activeQuest.propIds || [])
        .map((propId) => marsCityPropFor(propId))
        .filter(Boolean)
    : MARS_CITY_PROP_SHEETS.slice(-4);
  const selected = marsCityDistrictFor(selectedDistrict);
  const selectedBuildCost = selected ? marsCityBuildCost(selected.id, levels) : 0;
  const cityValue = budget + spentBudget + stats.score * 2 + stats.comfort;
  const incomeRate = Math.max(1, stats.built + Math.floor(stats.comfort / 2));
  const readyNeedCount = marsCityReadyNeedCount(needCards);
  const builtZoneCount = MARS_CITY_DISTRICTS.filter(
    (district) => (levels[district.id] || 0) > 0,
  ).length;
  const unlockedZoneCount = unlockedIds.length;
  const missionStepText = activeQuest
    ? `Mission ${questIndex + 1} of ${MARS_CITY_QUESTS.length}: ${activeQuest.mayorNeed}`
    : stats.ready
      ? "Mission complete: Mars City can care for its residents."
      : "Mission check: finish any needs before the city celebrates.";
  const nextActionText = activeQuest
    ? `Next: choose ${activeQuest.title.toLowerCase()} help, then open more districts.`
    : "Next: replay the mayor loop or return home.";
  const activityLevel = Math.min(
    MARS_CITY_ACTIVITY_MARKERS.length,
    Math.max(1, stats.built + Math.floor(stats.comfort / 2)),
  );
  const visibleHouseCount = Math.min(
    MARS_CITY_BUBBLE_HOUSES.length,
    (levels.habitat || 0) +
      (levels["north-habs"] || 0) +
      (levels["east-habs"] || 0) +
      (levels["south-habs"] || 0) +
      (levels.water ? 1 : 0) +
      (levels.biodome ? 1 : 0),
  );
  const visibleResidentCount = Math.min(
    MARS_CITY_RESIDENTS.length,
    Math.max(
      0,
      stats.people + (levels.rover ? 2 : 0),
      stats.built ? 2 + stats.built : 0,
    ),
  );
  const nextHint = stats.ready
    ? "Mars City is balanced and ready."
    : stats.air < stats.airNeed
      ? "The city needs more clean air."
      : !stats.homesReady
        ? "The city needs safe homes."
      : stats.power < 0
        ? "The city needs more energy."
        : stats.food < 0
          ? "The city needs more food."
          : stats.water < 0
            ? "The city needs more water."
            : activeQuest
              ? activeQuest.prompt
              : "Upgrade landmarks to make the city shine.";

  const selectDistrict = (districtId) => {
    if (!unlockedIds.includes(districtId)) return;
    setSelectedDistrict(districtId);
  };

  const buildDistrict = (districtId, budgetEvent = {}) => {
    if (!unlockedIds.includes(districtId)) return;
    const cost = Math.max(
      0,
      Number.isFinite(budgetEvent.cost)
        ? budgetEvent.cost
        : marsCityBuildCost(districtId, levels),
    );
    if (budget < cost) {
      setReward({
        title: "Budget check",
        text: `Save ${marsCityBudgetLabel(cost - budget)} more before building this.`,
        holdMission: true,
        narration: activeQuest ? activeQuest.narration : MARS_CITY_NARRATION.start,
      });
      return;
    }
    setSelectedDistrict(districtId);
    const nextLevels = {
      ...levels,
      [districtId]: Math.min(3, (levels[districtId] || 0) + 1),
    };
    const quest = MARS_CITY_QUESTS[questIndex] || null;
    const wasComplete = marsCityQuestComplete(quest, levels);
    const isComplete = marsCityQuestComplete(quest, nextLevels);
    setLevels(nextLevels);
    const budgetReturn = Number.isFinite(budgetEvent.rewardBudget)
      ? budgetEvent.rewardBudget
      : cost + Math.max(1, stats.built);
    setBudget((current) => current - cost + budgetReturn);
    setSpentBudget((current) => current + cost);
    if (!wasComplete && isComplete) {
      const district = marsCityDistrictFor(districtId);
      const isFinalMission = questIndex >= MARS_CITY_QUESTS.length - 1;
      const nextStats = scoreMarsCityDistricts(nextLevels);
      const balancedText = isFinalMission
        ? " Air, homes, food, water, and energy are checked."
        : "";
      const choiceText = budgetEvent.helps
        ? ` ${budgetEvent.helps}`
        : "";
      const rewardNarration =
        isFinalMission && nextStats.ready ? MARS_CITY_NARRATION.balanced : quest.narration;
      setReward({
        title: quest.title,
        text: district
          ? `${district.shortLabel} added.${choiceText}${balancedText} City grant returned ${marsCityBudgetLabel(budgetReturn)}.`
          : "District added to Mars City.",
        narration: rewardNarration,
      });
      marsCityPlayNarration(rewardNarration);
    } else if (districtId !== (quest && quest.target)) {
      const district = marsCityDistrictFor(districtId);
      setReward({
        title: district ? district.shortLabel : "City zone",
        text: district
          ? `${district.label} upgraded. ${district.cue} City grant returned ${marsCityBudgetLabel(budgetReturn)}.`
          : `Mars City zone upgraded. City grant returned ${marsCityBudgetLabel(budgetReturn)}.`,
        holdMission: true,
      });
    }
  };

  const buildMayorChoice = (choice) => {
    if (!choice) return;
    buildDistrict(choice.target || (activeQuest && activeQuest.target), choice);
  };

  const nextMission = () => {
    setReward(null);
    if (reward && reward.holdMission) return;
    setQuestIndex((current) =>
      Math.min(MARS_CITY_QUESTS.length - 1, current + 1),
    );
    const nextQuest = MARS_CITY_QUESTS[questIndex + 1];
    if (nextQuest) setSelectedDistrict(nextQuest.unlock);
  };

  const resetCity = () => {
    setLevels(createMarsCityState());
    setQuestIndex(0);
    setSelectedDistrict("habitat");
    setReward(null);
    setBudget(MARS_CITY_STARTING_BUDGET);
    setSpentBudget(0);
  };

  const hearMayor = () => {
    if (activeQuest && activeQuest.narration) {
      marsCityPlayNarration(activeQuest.narration);
      return;
    }
    marsCityPlayNarration(stats.ready ? MARS_CITY_NARRATION.balanced : MARS_CITY_NARRATION.start);
  };

  return (
    <main
      className={`mars-city-level mars-city-landmark-mode mars-city-buzz-${activityLevel}`}
      aria-label="Mars City Builder"
    >
      <section className="mars-city-command">
        <button className="mars-city-exit" type="button" onClick={onExit}>
          Back
        </button>
        <div>
          <p className="mars-city-kicker">Mars Landmark Map</p>
          <h1>Mars City Builder</h1>
          <p>{activeQuest ? activeQuest.prompt : "Upgrade the city landmarks."}</p>
        </div>
        <div className="mars-city-score" aria-live="polite">
          <strong>{stats.score}/60</strong>
          <span>{stats.ready ? "Ready" : "Building"}</span>
        </div>
        <div className="mars-city-budget" aria-live="polite">
          <strong>{budget}</strong>
          <span>Credits</span>
        </div>
      </section>

      <section className="mars-city-play mars-city-map-play">
        <div className="mars-city-tools" aria-label="Landmark districts">
          {MARS_CITY_DISTRICTS.map((district) => {
            const unlocked = unlockedIds.includes(district.id);
            const level = levels[district.id] || 0;
            return (
              <button
                className={`mars-city-tool ${district.id === selectedDistrict ? "selected" : ""} ${unlocked ? "" : "locked"}`}
                key={district.id}
                type="button"
                onClick={() => selectDistrict(district.id)}
                aria-pressed={district.id === selectedDistrict}
                disabled={!unlocked}
              >
                <b>{district.shortLabel.slice(0, 1)}</b>
                <span>{district.shortLabel}</span>
                <small>{unlocked ? `Level ${level}` : "Locked"}</small>
              </button>
            );
          })}
        </div>

        <div className="mars-city-map-shell">
          <div className="mars-city-map-controls" aria-label="Mars city map controls">
            {[
              { id: "build", label: "Build" },
              { id: "needs", label: "Needs" },
              { id: "traffic", label: "Traffic" },
              { id: "resources", label: "Resources" },
            ].map((view) => (
              <button
                className={mapView === view.id ? "selected" : ""}
                key={view.id}
                type="button"
                onClick={() => setMapView(view.id)}
                aria-pressed={mapView === view.id}
              >
                {view.label}
              </button>
            ))}
            <button
              className={mapScale === "detail" ? "selected" : ""}
              type="button"
              onClick={() => setMapScale((current) => (current === "wide" ? "detail" : "wide"))}
              aria-pressed={mapScale === "detail"}
            >
              {mapScale === "wide" ? "Zoom" : "Wide"}
            </button>
            <span>{builtZoneCount}/{MARS_CITY_DISTRICTS.length} built</span>
          </div>
          <div className="mars-city-map-viewport">
            <div
              className={`mars-city-map mars-city-map-${mapScale} mars-city-view-${mapView}`}
              style={{ backgroundImage: `url(${MARS_CITY_MAP_ASSET})` }}
              aria-label="Scrollable huge Mars city map"
            >
              <div className="mars-city-sector-layer" aria-hidden="true">
                {MARS_CITY_SURFACE_SECTORS.map((sector) => {
                  const unlocked = unlockedIds.includes(sector.districtId);
                  const built = (levels[sector.districtId] || 0) > 0;
                  const selectedSector = sector.districtId === selectedDistrict;
                  return (
                    <span
                      className={`mars-city-sector ${sector.type} ${unlocked ? "unlocked" : "locked"} ${built ? "built" : ""} ${selectedSector ? "selected" : ""}`}
                      key={sector.id}
                      style={{
                        left: `${sector.x}%`,
                        top: `${sector.y}%`,
                        width: `${sector.w}%`,
                        height: `${sector.h}%`,
                      }}
                    >
                      <small>{sector.label}</small>
                    </span>
                  );
                })}
              </div>
              <div className="mars-city-resource-layer" aria-hidden="true">
                {MARS_CITY_RESOURCE_NODES.map((node) => {
                  const active = (levels[node.districtId] || 0) > 0;
                  const unlocked = unlockedIds.includes(node.districtId);
                  return (
                    <span
                      className={`mars-city-resource ${node.type} ${active ? "active" : ""} ${unlocked ? "unlocked" : ""}`}
                      key={node.id}
                      style={{ left: `${node.x}%`, top: `${node.y}%` }}
                    >
                      {node.label}
                    </span>
                  );
                })}
              </div>
              <div className="mars-city-road-layer" aria-hidden="true">
                {MARS_CITY_ROADS.map((road) => (
                  <span
                    className={`mars-city-road ${marsCityRoadIsActive(road, levels) ? "active" : ""}`}
                    key={road.id}
                    style={{
                      left: `${road.x}%`,
                      top: `${road.y}%`,
                      width: `${road.w}%`,
                      rotate: `${road.r}deg`,
                    }}
                  />
                ))}
              </div>
              <div className="mars-city-unlock-report" aria-hidden="true">
                <strong>{unlockedZoneCount} zones open</strong>
                <span>{builtZoneCount} working sites</span>
              </div>
              <div className="mars-city-house-layer" aria-hidden="true">
                {MARS_CITY_BUBBLE_HOUSES.slice(0, visibleHouseCount).map((house, index) => (
                  <span
                    className={`mars-city-bubble-house ${house.size}`}
                    key={house.id}
                    style={{
                      left: `${house.x}%`,
                      top: `${house.y}%`,
                      animationDelay: `${index * 0.18}s`,
                    }}
                  >
                    <MarsCityCharacterSprite
                      characterId="bubble-house"
                      className={`bubble-house ${house.size}`}
                      frame={Math.min(3, index)}
                    />
                  </span>
                ))}
              </div>
              <div className="mars-city-people-layer" aria-hidden="true">
                {MARS_CITY_RESIDENTS.slice(0, visibleResidentCount).map((resident) => (
                  <span
                    className="mars-city-resident"
                    key={resident.id}
                    style={{
                      left: `${resident.x}%`,
                      top: `${resident.y}%`,
                      "--mars-city-walk-x": `${resident.dx}px`,
                      "--mars-city-walk-y": `${resident.dy}px`,
                      animationDelay: resident.delay,
                    }}
                  >
                    <i className="mars-city-resident-need">
                      <MarsCityPropSprite propId={resident.needPropId} frame={1} />
                    </i>
                    <MarsCityCharacterSprite
                      characterId="astronaut-resident"
                      className="resident"
                      frame={resident.characterFrame}
                    />
                    <em className="mars-city-resident-tools">
                      {(resident.toolPropIds || []).map((propId, index) => (
                        <MarsCityPropSprite
                          className="mini"
                          frame={index + 2}
                          key={propId}
                          propId={propId}
                        />
                      ))}
                    </em>
                  </span>
                ))}
              </div>
              <div className="mars-city-activity-layer" aria-hidden="true">
                {MARS_CITY_ACTIVITY_MARKERS.slice(0, activityLevel).map((marker, index) => (
                  <span
                    className={`mars-city-activity ${marker.type}`}
                    key={marker.id}
                    style={{
                      left: `${marker.x}%`,
                      top: `${marker.y}%`,
                      "--mars-city-activity-x": `${marker.dx}px`,
                      "--mars-city-activity-y": `${marker.dy}px`,
                      animationDelay: marker.delay,
                    }}
                  >
                    <MarsCityCharacterSprite
                      characterId={marker.characterId}
                      className={marker.type}
                      frame={index % 4}
                    />
                    <MarsCityPropSprite
                      className={marker.type === "spark" ? "sparkle attached" : "mini attached"}
                      frame={index % 4}
                      propId={marker.propId}
                    />
                  </span>
                ))}
              </div>
              {MARS_CITY_DISTRICTS.map((district) => {
                const level = levels[district.id] || 0;
                const unlocked = unlockedIds.includes(district.id);
                const active = district.id === selectedDistrict;
                return (
                  <button
                    className={`mars-city-landmark ${active ? "selected" : ""} ${unlocked ? "" : "locked"} ${level ? "built" : ""}`}
                    key={district.id}
                    type="button"
                    style={{
                      left: `${district.x}%`,
                      top: `${district.y}%`,
                    }}
                    onClick={() => buildDistrict(district.id)}
                    disabled={!unlocked}
                    aria-label={`${district.label}. ${unlocked ? `Level ${level}. ${district.cue}` : "Locked."}`}
                  >
                    <span
                      className="mars-city-landmark-art"
                      aria-hidden="true"
                      style={{
                        backgroundImage: `url(${district.atlas})`,
                        backgroundPosition: marsCityAtlasPosition(level),
                      }}
                    />
                    <span className="mars-city-landmark-tag">{district.shortLabel}</span>
                  </button>
                );
              })}
            </div>
          </div>
        </div>

        <aside className="mars-city-panel">
          <h2>{activeQuest ? activeQuest.title : "City balance"}</h2>
          <p>{nextHint}</p>
          <button className="mars-city-listen" type="button" onClick={hearMayor}>
            Hear mayor tip
          </button>
          {activeQuest && activeQuest.story ? (
            <p className="mars-city-storyline">{activeQuest.story}</p>
          ) : null}
          {activeQuest && activeQuest.mayorNeed ? (
            <p className="mars-city-mayor-need">{activeQuest.mayorNeed}</p>
          ) : null}
          <div className="mars-city-budget-row">
            <span>Budget {marsCityBudgetLabel(budget)}</span>
            <span>City value {marsCityBudgetLabel(cityValue)}</span>
            <span>Income +{incomeRate}</span>
          </div>
          <div className="mars-city-mission-card" aria-live="polite">
            <strong>{missionStepText}</strong>
            <span>{nextActionText}</span>
            <small>{readyNeedCount} of {needCards.length} resident needs ready</small>
          </div>
          <div className="mars-city-meters">
            {needCards.map((need) => (
              <span className={need.ready ? "ready" : "needs-help"} key={need.id}>
                <b>{need.icon}</b>
                <strong>
                  {need.label} {need.id === "air" || need.id === "homes"
                    ? `${need.value}/${need.need}`
                    : `${need.value >= 0 ? "+" : ""}${need.value}`}
                </strong>
                <small>{need.note}</small>
              </span>
            ))}
          </div>
          {activeQuest && activeQuest.choices ? (
            <div className="mars-city-mayor-choices" aria-label="Mayor choices">
              {activeQuest.choices.map((choice) => (
                <button
                  className={budget < (choice.cost || 0) ? "over-budget" : ""}
                  disabled={budget < (choice.cost || 0)}
                  key={choice.label}
                  type="button"
                  onClick={() => buildMayorChoice(choice)}
                >
                  <strong>{choice.label}</strong>
                  <span>{choice.result}</span>
                  <i>{marsCityChoiceNeedText(choice)}</i>
                  {choice.tradeoff ? <em>{choice.tradeoff}</em> : null}
                </button>
              ))}
            </div>
          ) : null}
          <div className="mars-city-prop-strip" aria-label="Current mission props">
            {activeProps.map((prop, index) => (
              <span className="mars-city-prop-card" key={prop.id}>
                <small>{prop.label}</small>
              </span>
            ))}
          </div>
          <div className="mars-city-selected">
            <strong>{selected ? selected.label : "Choose a district"}</strong>
            <span>{selected ? selected.cue : "Pick a landmark to build."}</span>
          </div>
          {!activeQuest ? (
            <div className="mars-city-finish">
              <strong>{stats.ready ? "City ready" : "Check the needs"}</strong>
              <span>
                {stats.ready
                  ? "Air, homes, food, water, and energy are balanced for the residents."
                  : nextHint}
              </span>
              <button type="button" onClick={resetCity}>Replay mayor loop</button>
              <button type="button" onClick={onExit}>Return home</button>
            </div>
          ) : null}
          <div className="mars-city-actions">
            <button type="button" onClick={resetCity}>Reset</button>
            {selected && unlockedIds.includes(selected.id) ? (
              <button
                disabled={budget < selectedBuildCost}
                type="button"
                onClick={() => buildDistrict(selected.id)}
              >
                Build ({selectedBuildCost})
              </button>
            ) : null}
          </div>
          <div className="mars-city-progress" aria-label="Mission progress">
            {MARS_CITY_QUESTS.map((quest, index) => (
              <span
                className={
                  index < questIndex || (completedAllMissions && index === questIndex)
                    ? "done"
                    : index === questIndex
                      ? "current"
                      : ""
                }
                key={quest.id}
              />
            ))}
          </div>
        </aside>
      </section>

      {reward ? (
        <div className="mars-city-reward" role="status" aria-live="polite">
          <strong>{reward.title}</strong>
          <span>{reward.text}</span>
          {reward.narration ? (
            <button type="button" onClick={() => marsCityPlayNarration(reward.narration)}>
              Hear again
            </button>
          ) : null}
          <button type="button" onClick={nextMission}>
            {reward.holdMission
              ? "Keep building"
              : questIndex >= MARS_CITY_QUESTS.length - 1
                ? "Finish city"
                : "Next mission"}
          </button>
        </div>
      ) : null}
    </main>
  );
}

window.SpaceExplorerMarsCityPropSheets = MARS_CITY_PROP_SHEETS;
window.SpaceExplorerMarsCity = MarsCityBuilderLevel;
