// Use React from global scope (loaded via CDN in index.html)
const { useState, useRef, useEffect, useLayoutEffect, useCallback, useMemo } = React;

// ============================================================================
// GAME ASSETS (all from stardewplanner.com pre-rendered PNGs)
// ============================================================================
const GAME_ASSETS = {
  // ===== BUILDINGS =====
  'building_barn': { src: 'assets/stardewplanner/building/barn.png', footprint: [7, 4] },
  'building_big_barn': { src: 'assets/stardewplanner/building/big_barn.png', footprint: [7, 4] },
  'building_deluxe_barn': { src: 'assets/stardewplanner/building/deluxe_barn.png', footprint: [7, 4] },
  'building_coop': { src: 'assets/stardewplanner/building/coop.png', footprint: [6, 3] },
  'building_big_coop': { src: 'assets/stardewplanner/building/big_coop.png', footprint: [6, 3] },
  'building_deluxe_coop': { src: 'assets/stardewplanner/building/deluxe_coop.png', footprint: [6, 3] },
  'building_shed': { src: 'assets/stardewplanner/building/shed.png', footprint: [7, 3] },
  'building_big_shed': { src: 'assets/stardewplanner/building/big_shed.png', footprint: [7, 3] },
  'building_silo': { src: 'assets/stardewplanner/building/silo.png', footprint: [3, 3] },
  'building_well': { src: 'assets/stardewplanner/building/well.png', footprint: [3, 3] },
  'building_stable': { src: 'assets/stardewplanner/building/stable.png', footprint: [4, 2] },
  'building_slime_hutch': { src: 'assets/stardewplanner/building/slime_hutch.png', footprint: [7, 4] },
  'building_fish_pond': { src: 'assets/stardewplanner/building/fish_pond.png', footprint: [5, 5] },
  'building_mill': { src: 'assets/stardewplanner/building/mill.png', footprint: [4, 2] },
  'building_greenhouse': { src: 'assets/stardewplanner/building/greenhouse.png', footprint: [7, 6], sourceRect: [0, 192, 112, 192] },
  'building_farmhouse': { src: 'assets/stardewplanner/building/farmhouse.png', footprint: [5, 3], sourceRect: [0, 0, 144, 144] },
  'building_farmhouse_upgrade1': { src: 'assets/stardewplanner/building/farmhouse.png', footprint: [5, 3], sourceRect: [0, 144, 144, 144] },
  'building_farmhouse_upgrade2': { src: 'assets/stardewplanner/building/farmhouse.png', footprint: [5, 3], sourceRect: [0, 288, 144, 144] },
  'building_shipping_bin': { src: 'assets/stardewplanner/building/shipping_bin.png', footprint: [2, 1] },
  'building_mailbox': { src: 'assets/stardewplanner/building/mailbox.png', footprint: [1, 1] },
  'building_log_cabin': { src: 'assets/stardewplanner/building/log_cabin.png', footprint: [5, 3] },
  'building_stone_cabin': { src: 'assets/stardewplanner/building/stone_cabin.png', footprint: [5, 3] },
  'building_plank_cabin': { src: 'assets/stardewplanner/building/plank_cabin.png', footprint: [5, 3] },
  'building_beach_cabin': { src: 'assets/stardewplanner/building/beach_cabin.png', footprint: [5, 3] },
  'building_neighbor_cabin': { src: 'assets/stardewplanner/building/neighbor_cabin.png', footprint: [5, 3] },
  'building_rustic_cabin': { src: 'assets/stardewplanner/building/rustic_cabin.png', footprint: [5, 3] },
  'building_trailer_cabin': { src: 'assets/stardewplanner/building/trailer_cabin.png', footprint: [5, 3] },
  'building_pet_bowl_spring': { src: 'assets/stardewplanner/building/pet_bowl-spring.png', footprint: [1, 1] },
  'building_pet_bowl_summer': { src: 'assets/stardewplanner/building/pet_bowl-summer.png', footprint: [1, 1] },
  'building_pet_bowl_fall': { src: 'assets/stardewplanner/building/pet_bowl-fall.png', footprint: [1, 1] },
  'building_pet_bowl_winter': { src: 'assets/stardewplanner/building/pet_bowl-winter.png', footprint: [1, 1] },
  'building_hay_pet_bowl_spring': { src: 'assets/stardewplanner/building/hay_pet_bowl-spring.png', footprint: [1, 1] },
  'building_hay_pet_bowl_summer': { src: 'assets/stardewplanner/building/hay_pet_bowl-summer.png', footprint: [1, 1] },
  'building_hay_pet_bowl_fall': { src: 'assets/stardewplanner/building/hay_pet_bowl-fall.png', footprint: [1, 1] },
  'building_hay_pet_bowl_winter': { src: 'assets/stardewplanner/building/hay_pet_bowl-winter.png', footprint: [1, 1] },
  'building_stone_pet_bowl_spring': { src: 'assets/stardewplanner/building/stone_pet_bowl-spring.png', footprint: [1, 1] },
  'building_stone_pet_bowl_summer': { src: 'assets/stardewplanner/building/stone_pet_bowl-summer.png', footprint: [1, 1] },
  'building_stone_pet_bowl_fall': { src: 'assets/stardewplanner/building/stone_pet_bowl-fall.png', footprint: [1, 1] },
  'building_stone_pet_bowl_winter': { src: 'assets/stardewplanner/building/stone_pet_bowl-winter.png', footprint: [1, 1] },

  // ===== END-GAME BUILDINGS =====
  'building_gold_clock': { src: 'assets/stardewplanner/building/gold_clock.png', footprint: [3, 2] },
  'building_desert_obelisk': { src: 'assets/stardewplanner/building/desert_obelisk.png', footprint: [3, 2] },
  'building_earth_obelisk': { src: 'assets/stardewplanner/building/earth_obelisk.png', footprint: [3, 2] },
  'building_water_obelisk': { src: 'assets/stardewplanner/building/water_obelisk.png', footprint: [3, 2] },
  'building_island_obelisk': { src: 'assets/stardewplanner/building/island_obelisk.png', footprint: [3, 2] },
  'building_junimo_hut': { src: 'assets/stardewplanner/building/junimo_hut.png', footprint: [3, 2] },
  'building_junimo_hut_spring': { src: 'assets/stardewplanner/building/junimo_hut-spring.png', footprint: [3, 2] },
  'building_junimo_hut_summer': { src: 'assets/stardewplanner/building/junimo_hut-summer.png', footprint: [3, 2] },
  'building_junimo_hut_fall': { src: 'assets/stardewplanner/building/junimo_hut-fall.png', footprint: [3, 2] },
  'building_junimo_hut_winter': { src: 'assets/stardewplanner/building/junimo_hut-winter.png', footprint: [3, 2] },

  // ===== TREES (seasonal) =====
  'tree_oak_spring': { src: 'assets/stardewplanner/tree/oak-spring.png', icon: 'assets/stardewplanner/tree/oak-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_oak_summer': { src: 'assets/stardewplanner/tree/oak-summer.png', icon: 'assets/stardewplanner/tree/oak-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_oak_fall': { src: 'assets/stardewplanner/tree/oak-fall.png', icon: 'assets/stardewplanner/tree/oak-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_oak_winter': { src: 'assets/stardewplanner/tree/oak-winter.png', icon: 'assets/stardewplanner/tree/oak-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_maple_spring': { src: 'assets/stardewplanner/tree/maple-spring.png', icon: 'assets/stardewplanner/tree/maple-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_maple_summer': { src: 'assets/stardewplanner/tree/maple-summer.png', icon: 'assets/stardewplanner/tree/maple-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_maple_fall': { src: 'assets/stardewplanner/tree/maple-fall.png', icon: 'assets/stardewplanner/tree/maple-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_maple_winter': { src: 'assets/stardewplanner/tree/maple-winter.png', icon: 'assets/stardewplanner/tree/maple-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mahogany_spring': { src: 'assets/stardewplanner/tree/mahogany-spring.png', icon: 'assets/stardewplanner/tree/mahogany-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mahogany_summer': { src: 'assets/stardewplanner/tree/mahogany-summer.png', icon: 'assets/stardewplanner/tree/mahogany-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mahogany_fall': { src: 'assets/stardewplanner/tree/mahogany-fall.png', icon: 'assets/stardewplanner/tree/mahogany-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mahogany_winter': { src: 'assets/stardewplanner/tree/mahogany-winter.png', icon: 'assets/stardewplanner/tree/mahogany-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_spruce_spring': { src: 'assets/stardewplanner/tree/spruce-spring.png', icon: 'assets/stardewplanner/tree/spruce-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_spruce_summer': { src: 'assets/stardewplanner/tree/spruce-summer.png', icon: 'assets/stardewplanner/tree/spruce-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_spruce_fall': { src: 'assets/stardewplanner/tree/spruce-fall.png', icon: 'assets/stardewplanner/tree/spruce-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_spruce_winter': { src: 'assets/stardewplanner/tree/spruce-winter.png', icon: 'assets/stardewplanner/tree/spruce-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mushroom_spring': { src: 'assets/stardewplanner/tree/mushroom-spring.png', icon: 'assets/stardewplanner/tree/mushroom-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mushroom_summer': { src: 'assets/stardewplanner/tree/mushroom-summer.png', icon: 'assets/stardewplanner/tree/mushroom-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mushroom_fall': { src: 'assets/stardewplanner/tree/mushroom-fall.png', icon: 'assets/stardewplanner/tree/mushroom-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mushroom_winter': { src: 'assets/stardewplanner/tree/mushroom-winter.png', icon: 'assets/stardewplanner/tree/mushroom-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mystic_spring': { src: 'assets/stardewplanner/tree/mystic-spring.png', icon: 'assets/stardewplanner/tree/mystic-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mystic_summer': { src: 'assets/stardewplanner/tree/mystic-summer.png', icon: 'assets/stardewplanner/tree/mystic-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mystic_fall': { src: 'assets/stardewplanner/tree/mystic-fall.png', icon: 'assets/stardewplanner/tree/mystic-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mystic_winter': { src: 'assets/stardewplanner/tree/mystic-winter.png', icon: 'assets/stardewplanner/tree/mystic-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mossy_oak_spring': { src: 'assets/stardewplanner/tree/mossy_oak-spring.png', icon: 'assets/stardewplanner/tree/mossy_oak-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mossy_oak_summer': { src: 'assets/stardewplanner/tree/mossy_oak-summer.png', icon: 'assets/stardewplanner/tree/mossy_oak-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mossy_oak_fall': { src: 'assets/stardewplanner/tree/mossy_oak-fall.png', icon: 'assets/stardewplanner/tree/mossy_oak-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mossy_oak_winter': { src: 'assets/stardewplanner/tree/mossy_oak-winter.png', icon: 'assets/stardewplanner/tree/mossy_oak-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mossy_maple_spring': { src: 'assets/stardewplanner/tree/mossy_maple-spring.png', icon: 'assets/stardewplanner/tree/mossy_maple-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mossy_maple_summer': { src: 'assets/stardewplanner/tree/mossy_maple-summer.png', icon: 'assets/stardewplanner/tree/mossy_maple-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mossy_maple_fall': { src: 'assets/stardewplanner/tree/mossy_maple-fall.png', icon: 'assets/stardewplanner/tree/mossy_maple-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mossy_maple_winter': { src: 'assets/stardewplanner/tree/mossy_maple-winter.png', icon: 'assets/stardewplanner/tree/mossy_maple-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mossy_spruce_spring': { src: 'assets/stardewplanner/tree/mossy_spruce-spring.png', icon: 'assets/stardewplanner/tree/mossy_spruce-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mossy_spruce_summer': { src: 'assets/stardewplanner/tree/mossy_spruce-summer.png', icon: 'assets/stardewplanner/tree/mossy_spruce-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mossy_spruce_fall': { src: 'assets/stardewplanner/tree/mossy_spruce-fall.png', icon: 'assets/stardewplanner/tree/mossy_spruce-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mossy_spruce_winter': { src: 'assets/stardewplanner/tree/mossy_spruce-winter.png', icon: 'assets/stardewplanner/tree/mossy_spruce-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mossy_mushroom_spring': { src: 'assets/stardewplanner/tree/mossy_mushroom-spring.png', icon: 'assets/stardewplanner/tree/mossy_mushroom-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mossy_mushroom_summer': { src: 'assets/stardewplanner/tree/mossy_mushroom-summer.png', icon: 'assets/stardewplanner/tree/mossy_mushroom-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mossy_mushroom_fall': { src: 'assets/stardewplanner/tree/mossy_mushroom-fall.png', icon: 'assets/stardewplanner/tree/mossy_mushroom-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },
  'tree_mossy_mushroom_winter': { src: 'assets/stardewplanner/tree/mossy_mushroom-winter.png', icon: 'assets/stardewplanner/tree/mossy_mushroom-icon.png', footprint: [1, 1], sourceRect: [0, 112, 48, 112], displayTiles: [3, 7] },

  // ===== FRUIT TREES (seasonal) =====
  'fruit_apple_spring': { src: 'assets/stardewplanner/fruit/apple-spring.png', icon: 'assets/stardewplanner/fruit/apple-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_apple_summer': { src: 'assets/stardewplanner/fruit/apple-summer.png', icon: 'assets/stardewplanner/fruit/apple-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_apple_fall': { src: 'assets/stardewplanner/fruit/apple-fall.png', icon: 'assets/stardewplanner/fruit/apple-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_apple_winter': { src: 'assets/stardewplanner/fruit/apple-winter.png', icon: 'assets/stardewplanner/fruit/apple-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_apricot_spring': { src: 'assets/stardewplanner/fruit/apricot-spring.png', icon: 'assets/stardewplanner/fruit/apricot-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_apricot_summer': { src: 'assets/stardewplanner/fruit/apricot-summer.png', icon: 'assets/stardewplanner/fruit/apricot-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_apricot_fall': { src: 'assets/stardewplanner/fruit/apricot-fall.png', icon: 'assets/stardewplanner/fruit/apricot-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_apricot_winter': { src: 'assets/stardewplanner/fruit/apricot-winter.png', icon: 'assets/stardewplanner/fruit/apricot-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_banana_spring': { src: 'assets/stardewplanner/fruit/banana-spring.png', icon: 'assets/stardewplanner/fruit/banana-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_banana_summer': { src: 'assets/stardewplanner/fruit/banana-summer.png', icon: 'assets/stardewplanner/fruit/banana-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_banana_fall': { src: 'assets/stardewplanner/fruit/banana-fall.png', icon: 'assets/stardewplanner/fruit/banana-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_banana_winter': { src: 'assets/stardewplanner/fruit/banana-winter.png', icon: 'assets/stardewplanner/fruit/banana-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_cherry_spring': { src: 'assets/stardewplanner/fruit/cherry-spring.png', icon: 'assets/stardewplanner/fruit/cherry-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_cherry_summer': { src: 'assets/stardewplanner/fruit/cherry-summer.png', icon: 'assets/stardewplanner/fruit/cherry-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_cherry_fall': { src: 'assets/stardewplanner/fruit/cherry-fall.png', icon: 'assets/stardewplanner/fruit/cherry-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_cherry_winter': { src: 'assets/stardewplanner/fruit/cherry-winter.png', icon: 'assets/stardewplanner/fruit/cherry-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_mango_spring': { src: 'assets/stardewplanner/fruit/mango-spring.png', icon: 'assets/stardewplanner/fruit/mango-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_mango_summer': { src: 'assets/stardewplanner/fruit/mango-summer.png', icon: 'assets/stardewplanner/fruit/mango-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_mango_fall': { src: 'assets/stardewplanner/fruit/mango-fall.png', icon: 'assets/stardewplanner/fruit/mango-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_mango_winter': { src: 'assets/stardewplanner/fruit/mango-winter.png', icon: 'assets/stardewplanner/fruit/mango-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_orange_spring': { src: 'assets/stardewplanner/fruit/orange-spring.png', icon: 'assets/stardewplanner/fruit/orange-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_orange_summer': { src: 'assets/stardewplanner/fruit/orange-summer.png', icon: 'assets/stardewplanner/fruit/orange-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_orange_fall': { src: 'assets/stardewplanner/fruit/orange-fall.png', icon: 'assets/stardewplanner/fruit/orange-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_orange_winter': { src: 'assets/stardewplanner/fruit/orange-winter.png', icon: 'assets/stardewplanner/fruit/orange-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_peach_spring': { src: 'assets/stardewplanner/fruit/peach-spring.png', icon: 'assets/stardewplanner/fruit/peach-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_peach_summer': { src: 'assets/stardewplanner/fruit/peach-summer.png', icon: 'assets/stardewplanner/fruit/peach-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_peach_fall': { src: 'assets/stardewplanner/fruit/peach-fall.png', icon: 'assets/stardewplanner/fruit/peach-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_peach_winter': { src: 'assets/stardewplanner/fruit/peach-winter.png', icon: 'assets/stardewplanner/fruit/peach-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_pomegranate_spring': { src: 'assets/stardewplanner/fruit/pomegranate-spring.png', icon: 'assets/stardewplanner/fruit/pomegranate-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_pomegranate_summer': { src: 'assets/stardewplanner/fruit/pomegranate-summer.png', icon: 'assets/stardewplanner/fruit/pomegranate-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_pomegranate_fall': { src: 'assets/stardewplanner/fruit/pomegranate-fall.png', icon: 'assets/stardewplanner/fruit/pomegranate-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },
  'fruit_pomegranate_winter': { src: 'assets/stardewplanner/fruit/pomegranate-winter.png', icon: 'assets/stardewplanner/fruit/pomegranate-icon.png', footprint: [1, 1], sourceRect: [0, 96, 48, 96], displayTiles: [3, 6] },

  // ===== CROPS =====
  'crop_parsnip': { src: 'assets/stardewplanner/crop/parsnip.png', icon: 'assets/stardewplanner/crop/parsnip-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_cauliflower': { src: 'assets/stardewplanner/crop/cauliflower.png', icon: 'assets/stardewplanner/crop/cauliflower-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_potato': { src: 'assets/stardewplanner/crop/potato.png', icon: 'assets/stardewplanner/crop/potato-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_garlic': { src: 'assets/stardewplanner/crop/garlic.png', icon: 'assets/stardewplanner/crop/garlic-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_kale': { src: 'assets/stardewplanner/crop/kale.png', icon: 'assets/stardewplanner/crop/kale-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_rhubarb': { src: 'assets/stardewplanner/crop/rhubarb.png', icon: 'assets/stardewplanner/crop/rhubarb-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_strawberry': { src: 'assets/stardewplanner/crop/strawberry.png', icon: 'assets/stardewplanner/crop/strawberry-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_rice': { src: 'assets/stardewplanner/crop/rice.png', icon: 'assets/stardewplanner/crop/rice-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_carrot': { src: 'assets/stardewplanner/crop/carrot.png', icon: 'assets/stardewplanner/crop/carrot-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_melon': { src: 'assets/stardewplanner/crop/melon.png', icon: 'assets/stardewplanner/crop/melon-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_blueberry': { src: 'assets/stardewplanner/crop/blueberry.png', icon: 'assets/stardewplanner/crop/blueberry-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_tomato': { src: 'assets/stardewplanner/crop/tomato.png', icon: 'assets/stardewplanner/crop/tomato-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_hops': { src: 'assets/stardewplanner/crop/hops.png', icon: 'assets/stardewplanner/crop/hops-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_radish': { src: 'assets/stardewplanner/crop/radish.png', icon: 'assets/stardewplanner/crop/radish-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_wheat': { src: 'assets/stardewplanner/crop/wheat.png', icon: 'assets/stardewplanner/crop/wheat-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_corn': { src: 'assets/stardewplanner/crop/corn.png', icon: 'assets/stardewplanner/crop/corn-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_sunflower': { src: 'assets/stardewplanner/crop/sunflower.png', icon: 'assets/stardewplanner/crop/sunflower-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_red_cabbage': { src: 'assets/stardewplanner/crop/red_cabbage.png', icon: 'assets/stardewplanner/crop/red_cabbage-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_summer_squash': { src: 'assets/stardewplanner/crop/summer_squash.png', icon: 'assets/stardewplanner/crop/summer_squash-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_pineapple': { src: 'assets/stardewplanner/crop/pineapple.png', icon: 'assets/stardewplanner/crop/pineapple-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_pumpkin': { src: 'assets/stardewplanner/crop/pumpkin.png', icon: 'assets/stardewplanner/crop/pumpkin-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_cranberry': { src: 'assets/stardewplanner/crop/cranberry.png', icon: 'assets/stardewplanner/crop/cranberry-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_grape': { src: 'assets/stardewplanner/crop/grape.png', icon: 'assets/stardewplanner/crop/grape-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_eggplant': { src: 'assets/stardewplanner/crop/eggplant.png', icon: 'assets/stardewplanner/crop/eggplant-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_yam': { src: 'assets/stardewplanner/crop/yam.png', icon: 'assets/stardewplanner/crop/yam-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_bok_choy': { src: 'assets/stardewplanner/crop/bok_choy.png', icon: 'assets/stardewplanner/crop/bok_choy-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_amaranth': { src: 'assets/stardewplanner/crop/amaranth.png', icon: 'assets/stardewplanner/crop/amaranth-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_artichoke': { src: 'assets/stardewplanner/crop/artichoke.png', icon: 'assets/stardewplanner/crop/artichoke-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_beet': { src: 'assets/stardewplanner/crop/beet.png', icon: 'assets/stardewplanner/crop/beet-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_broccoli': { src: 'assets/stardewplanner/crop/broccoli.png', icon: 'assets/stardewplanner/crop/broccoli-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_powdermelon': { src: 'assets/stardewplanner/crop/powdermelon.png', icon: 'assets/stardewplanner/crop/powdermelon-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_starfruit': { src: 'assets/stardewplanner/crop/starfruit.png', icon: 'assets/stardewplanner/crop/starfruit-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_ancient_fruit': { src: 'assets/stardewplanner/crop/ancient_fruit.png', icon: 'assets/stardewplanner/crop/ancient_fruit-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_cactus': { src: 'assets/stardewplanner/crop/cactus.png', icon: 'assets/stardewplanner/crop/cactus-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_coffee': { src: 'assets/stardewplanner/crop/coffee.png', icon: 'assets/stardewplanner/crop/coffee-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_pepper': { src: 'assets/stardewplanner/crop/pepper.png', icon: 'assets/stardewplanner/crop/pepper-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_sweetgem_berry': { src: 'assets/stardewplanner/crop/sweetgem_berry.png', icon: 'assets/stardewplanner/crop/sweetgem_berry-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_blue_jazz': { src: 'assets/stardewplanner/crop/blue_jazz.png', icon: 'assets/stardewplanner/crop/blue_jazz-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_fairy_rose': { src: 'assets/stardewplanner/crop/fairy_rose.png', icon: 'assets/stardewplanner/crop/fairy_rose-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_poppy': { src: 'assets/stardewplanner/crop/poppy.png', icon: 'assets/stardewplanner/crop/poppy-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_summer_spangle': { src: 'assets/stardewplanner/crop/summer_spangle.png', icon: 'assets/stardewplanner/crop/summer_spangle-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_tulip': { src: 'assets/stardewplanner/crop/tulip.png', icon: 'assets/stardewplanner/crop/tulip-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_bean': { src: 'assets/stardewplanner/crop/bean.png', icon: 'assets/stardewplanner/crop/bean-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_taro': { src: 'assets/stardewplanner/crop/taro.png', icon: 'assets/stardewplanner/crop/taro-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_qi_fruit': { src: 'assets/stardewplanner/crop/qi_fruit.png', icon: 'assets/stardewplanner/crop/qi_fruit-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_tea_sapling': { src: 'assets/stardewplanner/crop/tea_sapling-icon.png', icon: 'assets/stardewplanner/crop/tea_sapling-icon.png', footprint: [1, 1] },
  'crop_fiber_spring': { src: 'assets/stardewplanner/crop/fiber-spring.png', icon: 'assets/stardewplanner/crop/fiber-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_fiber_summer': { src: 'assets/stardewplanner/crop/fiber-summer.png', icon: 'assets/stardewplanner/crop/fiber-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_fiber_fall': { src: 'assets/stardewplanner/crop/fiber-fall.png', icon: 'assets/stardewplanner/crop/fiber-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },
  'crop_fiber_winter': { src: 'assets/stardewplanner/crop/fiber-winter.png', icon: 'assets/stardewplanner/crop/fiber-icon.png', footprint: [1, 1], sourceRect: [0, 48, 16, 16] },

  // ===== MACHINES & EQUIPMENT =====
  'machine_scarecrow': { src: 'assets/stardewplanner/machine/scarecrow.png', footprint: [1, 1] },
  'machine_deluxe_scarecrow': { src: 'assets/stardewplanner/machine/deluxe_scarecrow.png', footprint: [1, 1] },
  'machine_bee_house': { src: 'assets/stardewplanner/machine/bee_house.png', footprint: [1, 1] },
  'machine_keg': { src: 'assets/stardewplanner/machine/keg.png', footprint: [1, 1] },
  'machine_preserves_jar': { src: 'assets/stardewplanner/machine/preserves_jar.png', footprint: [1, 1] },
  'machine_loom': { src: 'assets/stardewplanner/machine/loom.png', footprint: [1, 1] },
  'machine_mayonnaise_machine': { src: 'assets/stardewplanner/machine/mayonnaise_machine.png', footprint: [1, 1] },
  'machine_seed_maker': { src: 'assets/stardewplanner/machine/seed_maker.png', footprint: [1, 1] },
  'machine_furnace': { src: 'assets/stardewplanner/machine/furnace.png', footprint: [1, 1] },
  'machine_lightning_rod': { src: 'assets/stardewplanner/machine/lightning_rod.png', footprint: [1, 1] },
  'machine_recycling_machine': { src: 'assets/stardewplanner/machine/recycling_machine.png', footprint: [1, 1] },
  'machine_crystalarium': { src: 'assets/stardewplanner/machine/crystalarium.png', footprint: [1, 1] },
  'machine_worm_bin': { src: 'assets/stardewplanner/machine/worm_bin.png', footprint: [1, 1] },
  'machine_tapper': { src: 'assets/stardewplanner/machine/tapper.png', icon: 'assets/stardewplanner/machine/tapper-icon.png', footprint: [1, 1] },
  'machine_heavy_tapper': { src: 'assets/stardewplanner/machine/heavy_tapper.png', icon: 'assets/stardewplanner/machine/heavy_tapper-icon.png', footprint: [1, 1] },
  'machine_garden_pot': { src: 'assets/stardewplanner/machine/garden_pot.png', footprint: [1, 1] },
  'machine_campfire': { src: 'assets/stardewplanner/machine/campfire.png', footprint: [1, 1] },
  'machine_cask': { src: 'assets/stardewplanner/machine/cask.png', footprint: [1, 1] },
  'machine_charcoal_kiln': { src: 'assets/stardewplanner/machine/charcoal_kiln.png', footprint: [1, 1] },
  'machine_ostrich_incubator': { src: 'assets/stardewplanner/machine/ostrich_incubator.png', footprint: [1, 1] },
  'machine_stone_chest': { src: 'assets/stardewplanner/machine/stone_chest.png', footprint: [1, 1] },
  // -- Processing --
  'machine_cheese_maker': { src: 'assets/stardewplanner/machine/cheese_maker.png', footprint: [1, 1] },
  'machine_oil_refiner': { src: 'assets/stardewplanner/machine/oil_refiner.png', footprint: [1, 1] },
  'machine_bone_mill': { src: 'assets/stardewplanner/machine/bone_mill.png', footprint: [1, 1] },
  'machine_geode_crusher': { src: 'assets/stardewplanner/machine/geode_crusher.png', footprint: [1, 1] },
  'machine_dehydrator': { src: 'assets/stardewplanner/machine/dehydrator.png', footprint: [1, 1] },
  'machine_fish_smoker': { src: 'assets/stardewplanner/machine/fish_smoker.png', footprint: [1, 1] },
  'machine_heavy_furnace': { src: 'assets/stardewplanner/machine/heavy_furnace.png', footprint: [1, 1] },
  'machine_bait_maker': { src: 'assets/stardewplanner/machine/bait_maker.png', footprint: [1, 1] },
  'machine_deconstructor': { src: 'assets/stardewplanner/machine/deconstructor.png', footprint: [1, 1] },
  'machine_sewing_machine': { src: 'assets/stardewplanner/machine/sewing_machine.png', footprint: [1, 1] },
  'machine_anvil': { src: 'assets/stardewplanner/machine/anvil.png', footprint: [1, 1] },
  'machine_mini_forge': { src: 'assets/stardewplanner/machine/mini_forge.png', footprint: [1, 1] },
  // -- Storage --
  'machine_wood_chest': { src: 'assets/stardewplanner/machine/wood_chest.png', footprint: [1, 1] },
  'machine_junimo_chest': { src: 'assets/stardewplanner/machine/junimo_chest.png', footprint: [1, 1] },
  'machine_stone_big_chest': { src: 'assets/stardewplanner/machine/stone_big_chest.png', footprint: [1, 1] },
  'machine_wooden_big_chest': { src: 'assets/stardewplanner/machine/wooden_big_chest.png', footprint: [1, 1] },
  'machine_mini_fridge': { src: 'assets/stardewplanner/machine/mini_fridge.png', footprint: [1, 1] },
  'machine_mini_shipping_bin': { src: 'assets/stardewplanner/machine/mini_shipping_bin.png', footprint: [1, 1] },
  'machine_hopper': { src: 'assets/stardewplanner/machine/hopper.png', footprint: [1, 1] },
  // -- Farm Automation --
  'machine_auto_grabber': { src: 'assets/stardewplanner/machine/auto_grabber.png', footprint: [1, 1] },
  'machine_auto_petter': { src: 'assets/stardewplanner/machine/auto_petter.png', footprint: [1, 1] },
  'machine_slime_egg_press': { src: 'assets/stardewplanner/machine/slime_egg_press.png', footprint: [1, 1] },
  'machine_slime_incubator': { src: 'assets/stardewplanner/machine/slime_incubator.png', footprint: [1, 1] },
  'machine_deluxe_worm_bin': { src: 'assets/stardewplanner/machine/deluxe_worm_bin.png', footprint: [1, 1] },
  'machine_mushroom_log': { src: 'assets/stardewplanner/machine/mushroom_log.png', footprint: [1, 1] },
  'machine_heater': { src: 'assets/stardewplanner/machine/heater.png', footprint: [1, 1] },
  // -- Utility --
  'machine_solar_panel': { src: 'assets/stardewplanner/machine/solar_panel.png', footprint: [1, 1] },
  'machine_wood_chipper': { src: 'assets/stardewplanner/machine/wood_chipper.png', footprint: [1, 1] },
  'machine_farm_computer': { src: 'assets/stardewplanner/machine/farm_computer.png', footprint: [1, 1] },
  'machine_telephone': { src: 'assets/stardewplanner/machine/telephone.png', footprint: [1, 1] },
  'machine_workbench': { src: 'assets/stardewplanner/machine/workbench.png', footprint: [1, 1] },
  'machine_coffee_maker': { src: 'assets/stardewplanner/machine/coffee_maker.png', footprint: [1, 1] },
  'machine_jukebox': { src: 'assets/stardewplanner/machine/jukebox.png', footprint: [1, 1] },
  'machine_mini_obelisk': { src: 'assets/stardewplanner/machine/mini_obelisk.png', footprint: [1, 1] },
  // -- Signs --
  'machine_wood_sign': { src: 'assets/stardewplanner/machine/wood_sign.png', footprint: [1, 1] },
  'machine_stone_sign': { src: 'assets/stardewplanner/machine/stone_sign.png', footprint: [1, 1] },
  'machine_dark_sign': { src: 'assets/stardewplanner/machine/dark_sign.png', footprint: [1, 1] },
  'machine_text_sign': { src: 'assets/stardewplanner/machine/text_sign.png', footprint: [1, 1] },
  // -- Lighting --
  'machine_barrel_brazier': { src: 'assets/stardewplanner/machine/barrel_brazier.png', footprint: [1, 1] },
  'machine_carved_brazier': { src: 'assets/stardewplanner/machine/carved_brazier.png', footprint: [1, 1] },
  'machine_gold_brazier': { src: 'assets/stardewplanner/machine/gold_brazier.png', footprint: [1, 1] },
  'machine_marble_brazier': { src: 'assets/stardewplanner/machine/marble_brazier.png', footprint: [1, 1] },
  'machine_skull_brazier': { src: 'assets/stardewplanner/machine/skull_brazier.png', footprint: [1, 1] },
  'machine_stone_brazier': { src: 'assets/stardewplanner/machine/stone_brazier.png', footprint: [1, 1] },
  'machine_stump_brazier': { src: 'assets/stardewplanner/machine/stump_brazier.png', footprint: [1, 1] },
  'machine_wooden_brazier': { src: 'assets/stardewplanner/machine/wooden_brazier.png', footprint: [1, 1] },
  'machine_iron_lamp_post': { src: 'assets/stardewplanner/machine/iron_lamp_post.png', footprint: [1, 1] },
  'machine_wood_lamp_post': { src: 'assets/stardewplanner/machine/wood_lamp_post.png', footprint: [1, 1] },
  // -- Rarecrows --
  'machine_rarecrow_1': { src: 'assets/stardewplanner/machine/rarecrow_1.png', footprint: [1, 1] },
  'machine_rarecrow_2': { src: 'assets/stardewplanner/machine/rarecrow_2.png', footprint: [1, 1] },
  'machine_rarecrow_3': { src: 'assets/stardewplanner/machine/rarecrow_3.png', footprint: [1, 1] },
  'machine_rarecrow_4': { src: 'assets/stardewplanner/machine/rarecrow_4.png', footprint: [1, 1] },
  'machine_rarecrow_5': { src: 'assets/stardewplanner/machine/rarecrow_5.png', footprint: [1, 1] },
  'machine_rarecrow_6': { src: 'assets/stardewplanner/machine/rarecrow_6.png', footprint: [1, 1] },
  'machine_rarecrow_7': { src: 'assets/stardewplanner/machine/rarecrow_7.png', footprint: [1, 1] },
  'machine_rarecrow_8': { src: 'assets/stardewplanner/machine/rarecrow_8.png', footprint: [1, 1] },
  // -- Statues --
  'machine_statue_of_endless_fortune': { src: 'assets/stardewplanner/machine/statue_of_endless_fortune.png', footprint: [1, 1] },
  'machine_statue_of_perfection': { src: 'assets/stardewplanner/machine/statue_of_perfection.png', footprint: [1, 1] },
  'machine_statue_of_true_perfection': { src: 'assets/stardewplanner/machine/statue_of_true_perfection.png', footprint: [1, 1] },
  'machine_statue_of_blessings': { src: 'assets/stardewplanner/machine/statue_of_blessings.png', footprint: [1, 1] },
  'machine_statue_of_the_dwarf_king': { src: 'assets/stardewplanner/machine/statue_of_the_dwarf_king.png', footprint: [1, 1] },

  // ===== SPRINKLERS =====
  'sprinkler_basic': { src: 'assets/stardewplanner/sprinkler/basic_sprinkler.png', footprint: [1, 1] },
  'sprinkler_quality': { src: 'assets/stardewplanner/sprinkler/quality_sprinkler.png', footprint: [1, 1] },
  'sprinkler_iridium': { src: 'assets/stardewplanner/sprinkler/iridium_sprinkler.png', footprint: [1, 1] },
  'sprinkler_basic_upgrade': { src: 'assets/stardewplanner/sprinkler/basic_sprinkler_upgrade.png', footprint: [1, 1] },
  'sprinkler_quality_upgrade': { src: 'assets/stardewplanner/sprinkler/quality_sprinkler_upgrade.png', footprint: [1, 1] },
  'sprinkler_iridium_upgrade': { src: 'assets/stardewplanner/sprinkler/iridium_sprinkler_upgrade.png', footprint: [1, 1] },

  // ===== FENCES (sprite sheets: 48x128, 3 cols x 4 rows, each cell 16x32) =====
  'fence_wood': { src: 'assets/stardewplanner/fence/wood_fence.png', icon: 'assets/stardewplanner/fence/wood_fence-icon.png', footprint: [1, 1] },
  'fence_stone': { src: 'assets/stardewplanner/fence/stone_fence.png', icon: 'assets/stardewplanner/fence/stone_fence-icon.png', footprint: [1, 1] },
  'fence_iron': { src: 'assets/stardewplanner/fence/iron_fence.png', icon: 'assets/stardewplanner/fence/iron_fence-icon.png', footprint: [1, 1] },
  'fence_hardwood': { src: 'assets/stardewplanner/fence/hardwood_fence.png', icon: 'assets/stardewplanner/fence/hardwood_fence-icon.png', footprint: [1, 1] },

  // ===== PATHS (sprite sheets: 64x64, 4 cols x 4 rows, each cell 16x16; seasonal: spring+winter exist, summer/fall use spring) =====
  'path_wood': { src: null, icon: 'assets/stardewplanner/path/wood_path-icon.png', footprint: [1, 1], pathName: 'wood_path' },
  'path_wood_floor': { src: null, icon: 'assets/stardewplanner/path/wood_floor-icon.png', footprint: [1, 1], pathName: 'wood_floor' },
  'path_stone_floor': { src: null, icon: 'assets/stardewplanner/path/stone_floor-icon.png', footprint: [1, 1], pathName: 'stone_floor' },
  'path_brick_floor': { src: null, icon: 'assets/stardewplanner/path/brick_floor-icon.png', footprint: [1, 1], pathName: 'brick_floor' },
  'path_weathered_floor': { src: null, icon: 'assets/stardewplanner/path/weathered_floor-icon.png', footprint: [1, 1], pathName: 'weathered_floor' },
  'path_straw_floor': { src: null, icon: 'assets/stardewplanner/path/straw_floor-icon.png', footprint: [1, 1], pathName: 'straw_floor' },
  'path_gravel': { src: null, icon: 'assets/stardewplanner/path/gravel_path-icon.png', footprint: [1, 1], pathName: 'gravel_path' },
  'path_cobblestone': { src: null, icon: 'assets/stardewplanner/path/cobblestone_path-icon.png', footprint: [1, 1], pathName: 'cobblestone_path' },
  'path_stepping_stone': { src: null, icon: 'assets/stardewplanner/path/stepping_stone_path-icon.png', footprint: [1, 1], pathName: 'stepping_stone_path' },
  'path_crystal': { src: null, icon: 'assets/stardewplanner/path/crystal_path-icon.png', footprint: [1, 1], pathName: 'crystal_path' },
  'path_crystal_floor': { src: null, icon: 'assets/stardewplanner/path/crystal_floor-icon.png', footprint: [1, 1], pathName: 'crystal_floor' },
  'path_rustic_plank': { src: null, icon: 'assets/stardewplanner/path/rustic_plank_floor-icon.png', footprint: [1, 1], pathName: 'rustic_plank_floor' },
  'path_stone_walkway': { src: null, icon: 'assets/stardewplanner/path/stone_walkway_floor-icon.png', footprint: [1, 1], pathName: 'stone_walkway_floor' },

  // ===== DIRT =====
  'dirt_spring': { src: 'assets/stardewplanner/dirt/hoe_dirt-spring.png', icon: 'assets/stardewplanner/dirt/hoe_dirt-icon.png', footprint: [1, 1] },
  'dirt_winter': { src: 'assets/stardewplanner/dirt/hoe_dirt-winter.png', icon: 'assets/stardewplanner/dirt/hoe_dirt-icon.png', footprint: [1, 1] },

  // ===== FURNITURE =====
  'furniture_blue_armchair_down': { src: 'assets/stardewplanner/furniture/blue_armchair_down.png', footprint: [1, 1] },
  'furniture_blue_armchair_left': { src: 'assets/stardewplanner/furniture/blue_armchair_left.png', footprint: [1, 1] },
  'furniture_blue_armchair_right': { src: 'assets/stardewplanner/furniture/blue_armchair_right.png', footprint: [1, 1] },
  'furniture_brown_armchair_down': { src: 'assets/stardewplanner/furniture/brown_armchair_down.png', footprint: [1, 1] },
  'furniture_brown_armchair_left': { src: 'assets/stardewplanner/furniture/brown_armchair_left.png', footprint: [1, 1] },
  'furniture_brown_armchair_right': { src: 'assets/stardewplanner/furniture/brown_armchair_right.png', footprint: [1, 1] },
  'furniture_green_armchair_down': { src: 'assets/stardewplanner/furniture/green_armchair_down.png', footprint: [1, 1] },
  'furniture_green_armchair_left': { src: 'assets/stardewplanner/furniture/green_armchair_left.png', footprint: [1, 1] },
  'furniture_green_armchair_right': { src: 'assets/stardewplanner/furniture/green_armchair_right.png', footprint: [1, 1] },
  'furniture_red_armchair_down': { src: 'assets/stardewplanner/furniture/red_armchair_down.png', footprint: [1, 1] },
  'furniture_red_armchair_left': { src: 'assets/stardewplanner/furniture/red_armchair_left.png', footprint: [1, 1] },
  'furniture_red_armchair_right': { src: 'assets/stardewplanner/furniture/red_armchair_right.png', footprint: [1, 1] },
  'furniture_yellow_armchair_down': { src: 'assets/stardewplanner/furniture/yellow_armchair_down.png', footprint: [1, 1] },
  'furniture_yellow_armchair_left': { src: 'assets/stardewplanner/furniture/yellow_armchair_left.png', footprint: [1, 1] },
  'furniture_yellow_armchair_right': { src: 'assets/stardewplanner/furniture/yellow_armchair_right.png', footprint: [1, 1] },
  'furniture_oak_table': { src: 'assets/stardewplanner/furniture/oak_table.png', footprint: [2, 2] },
  'furniture_birch_table': { src: 'assets/stardewplanner/furniture/birch_table.png', footprint: [2, 2] },
  'furniture_mahogany_table': { src: 'assets/stardewplanner/furniture/mahogany_table.png', footprint: [2, 2] },
  'furniture_walnut_table': { src: 'assets/stardewplanner/furniture/walnut_table.png', footprint: [2, 2] },
  'furniture_dark_bookcase': { src: 'assets/stardewplanner/furniture/dark_bookcase.png', footprint: [2, 2] },
  'furniture_modern_bookcase': { src: 'assets/stardewplanner/furniture/modern_bookcase.png', footprint: [2, 2] },
  'furniture_china_cabinet': { src: 'assets/stardewplanner/furniture/china_cabinet.png', footprint: [2, 2] },
  'furniture_small_fish_tank': { src: 'assets/stardewplanner/furniture/small_fish_tank.png', footprint: [2, 1] },
  'furniture_modern_fish_tank': { src: 'assets/stardewplanner/furniture/modern_fish_tank.png', footprint: [2, 1] },
  'furniture_large_fish_tank': { src: 'assets/stardewplanner/furniture/large_fish_tank.png', footprint: [3, 1] },
  'furniture_deluxe_fish_tank': { src: 'assets/stardewplanner/furniture/deluxe_fish_tank.png', footprint: [4, 1] },
  'furniture_cc_fish_tank': { src: 'assets/stardewplanner/furniture/CC_fish_tank.png', footprint: [4, 1] },
  'furniture_aquatic_sanctuary': { src: 'assets/stardewplanner/furniture/aquatic_sanctuary.png', footprint: [4, 1] },
  'furniture_plasma_tv': { src: 'assets/stardewplanner/furniture/plasma_tv.png', footprint: [2, 2] },
  'furniture_tropical_tv': { src: 'assets/stardewplanner/furniture/tropical_tv.png', footprint: [2, 2] },
  'furniture_blossom_rug': { src: 'assets/stardewplanner/furniture/blossom_rug.png', footprint: [3, 2] },
  'furniture_dark_rug_long': { src: 'assets/stardewplanner/furniture/dark_rug_long.png', footprint: [3, 2] },
  'furniture_dark_rug_tall': { src: 'assets/stardewplanner/furniture/dark_rug_tall.png', footprint: [2, 3] },
  'furniture_desert_rug_tall': { src: 'assets/stardewplanner/furniture/desert_rug_tall.png', footprint: [2, 3] },
  'furniture_fruit_salad_rug_long': { src: 'assets/stardewplanner/furniture/fruit_salad_rug_long.png', footprint: [3, 2] },
  'furniture_stone_flooring': { src: 'assets/stardewplanner/furniture/stone_flooring.png', footprint: [1, 1] },
  'furniture_bear_statue': { src: 'assets/stardewplanner/furniture/bear_statue.png', footprint: [1, 1] },
  'furniture_desert_flags': { src: 'assets/stardewplanner/furniture/desert_flags.png', footprint: [2, 1] },
  'furniture_monster_danglers': { src: 'assets/stardewplanner/furniture/monster_danglers.png', footprint: [2, 1] },
  'furniture_curly_tree': { src: 'assets/stardewplanner/furniture/curly_tree.png', footprint: [1, 1] },
  'furniture_deluxe_tree': { src: 'assets/stardewplanner/furniture/deluxe_tree.png', footprint: [1, 1] },
  'furniture_exotic_tree': { src: 'assets/stardewplanner/furniture/exotic_tree.png', footprint: [1, 1] },
  'furniture_long_palm': { src: 'assets/stardewplanner/furniture/long_palm.png', footprint: [1, 1] },
  'furniture_tree_of_winter_star': { src: 'assets/stardewplanner/furniture/tree_of_the_winter_star.png', footprint: [1, 1] },
  'furniture_seasonal_plant_1_spring': { src: 'assets/stardewplanner/furniture/seasonal_plant_1-spring.png', footprint: [1, 1] },
  'furniture_seasonal_plant_1_summer': { src: 'assets/stardewplanner/furniture/seasonal_plant_1-summer.png', footprint: [1, 1] },
  'furniture_seasonal_plant_1_fall': { src: 'assets/stardewplanner/furniture/seasonal_plant_1-fall.png', footprint: [1, 1] },
  'furniture_seasonal_plant_1_winter': { src: 'assets/stardewplanner/furniture/seasonal_plant_1-winter.png', footprint: [1, 1] },
  'furniture_seasonal_plant_2_spring': { src: 'assets/stardewplanner/furniture/seasonal_plant_2-spring.png', footprint: [1, 1] },
  'furniture_seasonal_plant_2_summer': { src: 'assets/stardewplanner/furniture/seasonal_plant_2-summer.png', footprint: [1, 1] },
  'furniture_seasonal_plant_2_fall': { src: 'assets/stardewplanner/furniture/seasonal_plant_2-fall.png', footprint: [1, 1] },
  'furniture_seasonal_plant_2_winter': { src: 'assets/stardewplanner/furniture/seasonal_plant_2-winter.png', footprint: [1, 1] },
  'furniture_seasonal_plant_3_spring': { src: 'assets/stardewplanner/furniture/seasonal_plant_3-spring.png', footprint: [1, 1] },
  'furniture_seasonal_plant_3_summer': { src: 'assets/stardewplanner/furniture/seasonal_plant_3-summer.png', footprint: [1, 1] },
  'furniture_seasonal_plant_3_fall': { src: 'assets/stardewplanner/furniture/seasonal_plant_3-fall.png', footprint: [1, 1] },
  'furniture_seasonal_plant_3_winter': { src: 'assets/stardewplanner/furniture/seasonal_plant_3-winter.png', footprint: [1, 1] },
  'furniture_seasonal_plant_4_spring': { src: 'assets/stardewplanner/furniture/seasonal_plant_4-spring.png', footprint: [1, 1] },
  'furniture_seasonal_plant_4_summer': { src: 'assets/stardewplanner/furniture/seasonal_plant_4-summer.png', footprint: [1, 1] },
  'furniture_seasonal_plant_4_fall': { src: 'assets/stardewplanner/furniture/seasonal_plant_4-fall.png', footprint: [1, 1] },
  'furniture_seasonal_plant_4_winter': { src: 'assets/stardewplanner/furniture/seasonal_plant_4-winter.png', footprint: [1, 1] },
  'furniture_seasonal_plant_5_spring': { src: 'assets/stardewplanner/furniture/seasonal_plant_5-spring.png', footprint: [1, 1] },
  'furniture_seasonal_plant_5_summer': { src: 'assets/stardewplanner/furniture/seasonal_plant_5-summer.png', footprint: [1, 1] },
  'furniture_seasonal_plant_5_fall': { src: 'assets/stardewplanner/furniture/seasonal_plant_5-fall.png', footprint: [1, 1] },
  'furniture_seasonal_plant_5_winter': { src: 'assets/stardewplanner/furniture/seasonal_plant_5-winter.png', footprint: [1, 1] },
  'furniture_seasonal_plant_6_spring': { src: 'assets/stardewplanner/furniture/seasonal_plant_6-spring.png', footprint: [1, 1] },
  'furniture_seasonal_plant_6_summer': { src: 'assets/stardewplanner/furniture/seasonal_plant_6-summer.png', footprint: [1, 1] },
  'furniture_seasonal_plant_6_fall': { src: 'assets/stardewplanner/furniture/seasonal_plant_6-fall.png', footprint: [1, 1] },
  'furniture_seasonal_plant_6_winter': { src: 'assets/stardewplanner/furniture/seasonal_plant_6-winter.png', footprint: [1, 1] },
  'furniture_seasonal_decor_spring': { src: 'assets/stardewplanner/furniture/seasonal_decor-spring.png', footprint: [2, 2] },
  'furniture_seasonal_decor_summer': { src: 'assets/stardewplanner/furniture/seasonal_decor-summer.png', footprint: [2, 2] },
  'furniture_seasonal_decor_fall': { src: 'assets/stardewplanner/furniture/seasonal_decor-fall.png', footprint: [2, 2] },
  'furniture_seasonal_decor_winter': { src: 'assets/stardewplanner/furniture/seasonal_decor-winter.png', footprint: [2, 2] },
};

// ============================================================================
// FARM TYPES (matching stardewplanner.com farm types)
// ============================================================================
const FARM_TYPES = {
  standard: { name: 'Standard Farm', width: 80, height: 65 },
  combat: { name: 'Wilderness Farm', width: 80, height: 65 },
  fishing: { name: 'Riverland Farm', width: 80, height: 65 },
  foraging: { name: 'Forest Farm', width: 80, height: 65 },
  mining: { name: 'Hill-top Farm', width: 80, height: 65 },
  island: { name: 'Beach Farm', width: 110, height: 110 },
  multiplayer: { name: 'Four Corners Farm', width: 80, height: 80 },
  ranching: { name: 'Meadowlands Farm', width: 100, height: 75 },
};

const SEASONS = ['spring', 'summer', 'fall', 'winter'];

// ============================================================================
// COLLISION MAPS (base64-encoded bitmaps: 1=blocked, 0=placeable)
// Extracted from stardewplanner.com — blocked if terrain contains X (cliff) or W (water)
// ============================================================================
const COLLISION_MAPS_B64 = {
  standard: '/z////8x//////9//+//P/////+eIH///z////////////8/////////////P//n/////////z////////////8+v//////////fPAH////wAAAAAAAB////4AAAAAAAAf///+AAAAAAAAAAAAPgAAAAAAAAAAAD4AAAAAAAAAAAA+AAAAAAAAAAAAPgAAAAAAAAAAAD4AAAAAAAAAAAAOAAAAAAAAAAAADgAAAAAAAAAAAA4AAAAAAAAAAAAOAAAAAAAAAAAAfgAAAAAAAAAAAH4AAAAAAAAAAAB+AAAAAAAAAAAAf+AAAAAAAAAAAH/gAAAAAAAAAAB/4AAAAAAAAAAAf+AAAAAAAAAAAH9gAAAAAAAAAAB+YAAAAAAAAAA+f+AAAAAAAAAAP3/gAAAAAAAAAD9/4AAAAAAAAAA/f+AAAAAAAAAAP3/gAAAAAAAAAB5/gAAAAAAAAAAAfwAAAAAAAAAAAH4AAAAAAAAAAAB+AAAAAAAAAAAAfgAAAAAAAAAAAH4AAAAAAAAAAAB+AAAAAAAAAAAAfgAAAAAAAAAAAH4AAAAAAAAAAAB+AAAAAAAAAAAAfgAAAAAAAAAAAH4AAAAAAAAAAAB+AAAAAAAAAAAAfgAAAAAAAAAAAH4AAAAAAAAAAAB+AAAAAP4AAAAAfgAAAAH/AAAAAH4AAAAD/8AAAAB+AAAAB//gAAAAfgAAAAf/4AAAAH4AAAAH/+AAAAB+AAAAA//gAAAAfgAAAAP/wAAAB/4AAAAA/4AAAAf+AAAAAHwAAAAH/gAAAAAAAAAAf/4AAAAAAAAAAH/+AAAAAAAAAAB//f/////z////////////8/////////////P/////8=',
  fishing: '//////8/////////////P/////////////////////////////////////n////////7///x///g//D/++//4fwD4H/gf/vv/AHcAAE/59/4D/gAAAABAAf/+A/4AAAAAQAH//gP+AAAAADAAAP8D/AAAAAAAAAD///wAAAAAAAAA///8AAAAAAAAAP///gAAAAAAAAD//54AAAAAAAAAP/AeAAAAAAAAAD/wHgAAAAAAAAA/+D4AAAAAAAAAP//+AAAAAAAAAf///gAAAAAAAAH///4AAAAAAAAB////AAAABwAAAf////4AAB/AAAH/////wAA/4AAB//////AAP+AAAf/////wAD/wAAH/////8AA/+AAB///4Af/Af/+H////8AD/////h////+AAf////4f////gAH//////////4AB/////////4+AAf////////4HgAH////////4B8AD////////4AfgB/gAB/4f/+AH///gAAH8B//wA///wAAA+Af//AH//4AAAPgH//4Af/8AAAB4AP/+AD/AAAAAeAB//gAfgAAAAHgAP/4AH4AAAAB4AD/+AB+AAAAAfAA//gAfwAAAAH4AP/4AH/AAAAA/gD/4AD/4AAAAP4B/4AP/+AAAAD/g/8f///wP4A//8f/////8H/AP//H////////8P//x////////////8f///wH///////H//+AA/////////+AAAP/////////gAAAf//wAB///4AAAD//4AAP//+AAAA//wAAB///4AAAP/4AAAf///AAAD/8AAAD///4AAA//AAAA///////v/w///v//////9/////9///////////////8=',
  foraging: '+/////8///8/////////P///P////////z////////////8//////////wP/P/////////wD/z/////////wA/8+v///////8APfPIH////wAAAAAAABf/e/8AAAAAAAAZ/nv/AAAAAAAAAAAAP/gAAAAAAAAAAD/4AAAAAAAAAAA/+AAAAAAAAAAAP/gAAAAAAAAAAD/4AAAAAAAAAAAP+AAAAAAAAAAAD//AAAAAAAAAAA//4AAAAAAAAAAP/+AAAAAAAAAAf/+AAAAAAAAAAH/gAAAAAAAAAAB/gAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAA+fwAAAAAAAAAAP3+AAAAAAAAAAD9/gAAAAAP4AAA/f4AAAAAP/AAAP3+AAAAAD/8AAB5/gAAAAB//gAAA/4APwAAf/+AAAX+AH+AAH//gAAZ/wD/gAB//4AAIf8B/4AA//+AACH/v/+AAP//gAAd/7//gAD//4AAA/+//4AA//8AAAf/v/+AAH//AH8B/7//AAA//gD/gf+//wAAB/AB/8H/v/gAAAAAAf/B+AHgAAAAAAH/wfgAAAAAAAAD/8H4AAAAAAAAA/+B+AAAAAAAAAP/AfgAAAAAAAAB/AH4AAAAAAAAAPgB+AAAAAAAOAAAAcQAAAAAB/wAAAH8AAAAAA/8AAAf/gAAAAAP/AAAP/4AAAAAD/gAAH//AAAAAAfwAAD//wAAAAAAAAAP////wAAAAAAAH/////4PAAAAAD////////OH////////////z////////////8/////////////P/////8=',
  mining: '//////8/////////////P////////////z/////////3//8/////////9///P/////////f//z/+///////3//8/v///////8D/fPAH////4D/AAADwB//+/4A/gAAA8AP/Hj+AP4AAAPAAAAAPhL+AAADwAAAAD4S/wAAAAAAAAA+E/8AAAAAAAAAPhP/AAAAAAAAAD/z/wAAAAAAAAA/8/0AAAAAAAAAD/O9wAAAAAAAAA/wPcAAAAAAAAAPgD3AAAAAAAAAf4A9wCYAAABmAH+APEAkD/AAJAB/gDx+Jf///iX//+A+f+f////n///gPn/n////5///4B5/5/gf/+f//+AfA+cAAAPnAH/gH4AAAAAAAAB/4A/AAAAAAAAAf+AP8AAAAAAAAH/gD///gAAAAAB/8AP///gAAAAAe/gB////AAAAAH/8AH///4AAAAB/////gP/AAAAAf2DAMYAf4AAAA/5///+AB+APx//+AAAAcAPwD8f//gAAABAB8A/H//4AAAAQAfAIAAB+AAAAcAHwCAAAfgAAAPAA8AgAAH4AAADwAPAIAAB+AAAA8ADwCMQAfgAiAMAA8AhEAH4AIgCAAPAPxAB+ACIDgADwD8fAf//j/4AA8A/H8H//4/+AAPAPx////+P/gAD4A8f////j/gAA+AAAf/4AAAAAAPwAAB/+AAAAAAB+AAAAfgAAAAAAP/4AAH58AAAAAB//4AB+fgAAAAAf//wAfn4AAAAAD///B/5+AAAAAAH//4f+fgAAAAAAAf/H/jwAAAAAAAAf//4AAAAAAAAAB//+AAAAAAAAAAP////////z////////////8/////////////P/////8=',
  combat: '//////8/////////////P////////////z////////////8/////////////P////////////z////////////8+v//////////fPAH////wD/wAAAAB////4APwAAAAAf///+AAAAAAAAAAAAPgAAAAAAAAAAAD4AAAAAAAAAAAA+AAAAAAAAAAAAPgAAAAAAAAAAAD4AAAAAAAAAAAAOAAAAAAAAAAAADwAAAAAAAAAAAA+AAAAAAAAAAAAPwAAAAAAAAAAA/+AAAAAAAAAAA//8AAAAAAAAAAP///AAAAAAAAAD///+AAAAAAAAA////8AAAP4AAAP////wAAH/AAAD////8AAD/8AAA/////AAB//gAAP////wAAf/4AAD////8AAf//AAA/////AAH//8AAP////wAB///AAD////8AAf//wAA/////AAH//8AAP/gP/AAB///AAD/wA/AAAP/+AAA/4AAAAAB//gAAP+AAAAAAP/wAAD/gAAAAAA/4AAA/4AAAAAAHwAAAP+AAAAAAAAAAAD/gAAAAAAAAAAD//4AAAAAAAAAD////AAAAAAAAA/////AAAAAAAAP////+AAAAAAAD/////4AAAAAAA//////AAAAAAAP/////4AAAAAAD/////+AAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAP/////8AAAAAAD//////gAAAAA///////4AAAAA///////+AAAHz////////gAAB/////////4AAAf////////+AAAH/////////gAAB///////////z////////////8/////////////P/////8=',
  island: '//////8//////////////////P/////53//////////z/9///3///////////8f/f//7//////////+AAf///+Pn///////+/gAH////h4////////v/gB////4AH///9///7/4Af///8AB////8H/+H+AH///+AAf////h//x/gBz//+AAH////4f/+f7Mc///AAD////+D//n/z/P4AAAAv////wf/9/8/x+AAAH7////8AB/j/P8AAAAB//////gAP4/z/AAAAA//////8AD+P8/wAAAAP//////AAfj/PwAAAAAP/////wAH42AAAAAAAD/////8AA+AAAAAAAAA//////gAPgAAAAAAAP//////4AD4AAAAAAAA////7/+AA+AAAAAAAAP///+P/wAfAAAAAAAAD/f////+AfgAAAAAAAA////+//4PwAAAAAAAAP////////4AAAAAAAAD///7////8AAAAAAAAA////+///+AAAAAAAAAP////v///AAAAAAAAAAv///7///gAAAAAAAAAIP//+///AAAAAAAAAAAAf/////gAAAAAAAAAAAH/////wAAAAAAAAAAAA//+//4AAAAAAAAAAAAP//v/+AAAAAAAAAAAAB//7//gAAAAAAAAAAAAf/+//4AAAAAAAAAAAAH//v/+AAAAAAAAAAAAB//7//gAAAAAAAAAAAAf/+//4AAAAAAAAAAAAH//v//AAAAAAAAAAAAB/////wAAAAAAAAAAAAf////8AAAAAAAAAAAAP//v//AAAAAAAAAAAAH//7//wAAAAAAAAAAf///+//+AAAAAAAAAAf////v//gAAAAAAAAAP////7//4AAAAAHAAAD////+//+AAAABz4AAB/////v//gAAAA++AAAf///////4AAAAPvgAAH///////+AAAAD74AAB/////v//gAAAA+AAAAf///////4AAAAPgAAAH///////+AAAAD4AAAA////////gAAAA+AAAAH///////8AAAAPgAAAA////////AAAAD4AAAAP///////wAAAA+AAAAD///////+AAAAPgAAAAf///////gAAAAAAAAAD///////8AAAAAAAAAAf///////gD/kAAAAAAAB//////4A//AAAAAAAAP///////P/wAAAAAAAB///////z/8AAAAAAAAf//////8//AAAAAAAAP///////P/wAAAAAAAD///////z/8AAAAAAAB///////87/AAAAAAAAf///////AAAAAAAAAAH/////3gAAAAAAAAAAB//////4AAAAAAAAAAAP/////+AAAAAAAAAAAB//////gAAAAOAAAAAAP/////4AAAAHwAAAAAD/////+AAAAB8AAAAAAf/////gD4AAfAHwAAAP/////8B/AAHwD8AAAD//////gfwAB8AeAAAB//////4H8AAfA/4AAAf/////+//AAHw//4ABX//////v/+AB8f//gB9//////7//gAfH//8Aff/////+//4AHx///gPv//////v/+AB8f//8AH//////7//YAAH///gX//////+///AAD///8D///////v//3AB////P///////b///wAf///z///////+///6AH///8////////v///wD////P///////7///9A////h///////+////wf///4f///////v///+P///8D///////7////j////B///////+////4////4////////v///+P///+f///////7////j////n////////////4////5////////////+P///+f////////////j////D/////////////////w/////////////////+P/////////////////j/////////////////4/////////////////+P////////////////7j////////////////94////////////////+iQB//8A==',
  multiplayer: '/3////c///////9////3P/////+f/////z////////////8/////////////P////////////z////////////8/////////////AAAf///wAAAADwAAH///4AAAAA8AAB///+AAAAAPAAAAAAPgAAAADwAAAAAD4AAAAA8AAAAAA+AAAAAPAAAAAAPgAAAAAAAAAAAD4AAAAAAAAAAAAOAAAAAPgAAAAADgAAAAD4AAAAAA4AAAAA+AAAAAAOAAAAAPwAAAAAfgAAAAD+AAAAAH4AAAAA/gAAAAB+AAAAAPIAAAAAfgAAAADyAAAAAH4AAAAAvgAAAAB+AAAAAP4AAAAAfgAAAAD+AAAAAH4AAAAA/gAAAAB+AAAHgP4AAAAAfgAAB4AAAAAAAH4AAAeAAAAAAAB+AAA/+AAeAAAPfgAAf/gAH4AAD34AAP/4AB/AAA9/AAH/+AAfsAAAf/5+//gAH///z//+f//4AB///8///n//2AAf///P//5//4AAD///z//+f/+AAAH//8///n//gAAB///P//5//4AAAf//z//+f/+AAAH//8/+AAAAAAAAAAAAfgAAAAOB4AAAAHIAAAAD8/AAAAByAAAAA/PwAAAAcgAAAAPz8AAAAHIAAAAD8/AAAAB+AAAAA/PwAAAAfgAAAAPh8AAAAH4AAAAAgHAAAAB+AAAAAIBAAAAAfgAAAACAwAAAAH4AAAAAgMAAAAB+AAAAAIDAAAAAfgAAAADjwAAAAH4AAAAAAAAAAAB+AAAAAICAAAAAfgAAAACAgAAAAH4AAAABAMAAAAD+AAAAAgHgAAAA/gAAAAfz8AAA8P4AAAAP8/AAAPf+AAAAD/PwAAD3/gAAAAvz8AAAB/4AP4Af8/wAAP//wH/Af//8AB////7/9////AAf//////////wAH///////8//8AB///////8P/////////////////////////////////////7////////////////////////////////////////////////////////////////gAH////////8=',
  ranching: '//////////5////////////////n///////////////+f///////////////5////////////////n///////////////+f///////////////5///////////////8H7///////+AP////wPv/////+/4AH///AA+//wP//4AAAf//wADh//g///gAAAH/4AAOH/wH//8AAAAf/gAAM/AAf//4AAA//+AAA4AAB///gAAP//wAAAAAAB///AAB///AAAAAAAH//8AAHx/4AAAAAAAf//5/AeB+AAAAAAAB/////HwAAAAAAAAAB//////AAAAAAAAAAf///j/8AAAAAAAAAD///wD/AAAAAAAAAAAD/8AAAAAAAAAAAAAAP/AAAAAAAAAAAAAAA/8AAAAAAAAAAAAAP//gAAAAAAAAAAAAA//+AAAAAAAAAAAAAD//4AAAAAAAAAAAAAP//wAAAAAAAAAAAAA///AAAAAAAAAAAAAD//8AAAAAAAAAAAAAP//wAAAAAAAAAAAAA///AAAAAAAAAAAAAD//8AAAAAAAAAAAAAP//4AAAAAAAAAAAAA///gAAAAAAAAAAAAD//+AAAAAAAAAAAAAP//4AAAAAAAAAAAAA///wAAAAAAAAAAAAD///AAAAAAAAAAAAAP//8AAAAAAAAAAAAA///wAAA+AAAAAAAAD///AAAP+AAAAAAAAP//8AAB/8AAAAAAAB///wAAH/8AAAAAAAH///AAA//4AAAAAAAf//4AAD//wAAAAAAD///gAAP//AAAAAAA///+AAA//8AAAAAAD///wAAB//wAAAAAA////AAAB/8AAAAAAD///8AAAAfAAAAAAAP///wAAAAAAAAAAAA////AAAAAAAAAAAAD///8AAAAAAAAAAAAP///wAAAAAAAAAfgA3///AAAAAAAAAH/AAB//8AAAAAAAAA/8AAH//wAAAAAAAAD/wAAf//A8AAAAAAAP/AAD//8H8AAAAAAAP4AB///wfwAAAAAAAPgAP///B+AAAAAAAAAAA///8B4AAAAAAAAAAD///wAAAAAAAAAAAAf///AAAAAAAAAAAAB///8AAAAAAAAAAAAH///wAAAAAAAAAAAA///////////P///////////////8////////////////z////////////////P///////////////8////////////////z////////////////P///////A=',
};

function decodeCollisionMap(farmType) {
  const b64 = COLLISION_MAPS_B64[farmType];
  if (!b64) return null;
  const farm = FARM_TYPES[farmType];
  const totalTiles = farm.width * farm.height;
  const raw = atob(b64);
  const blocked = new Uint8Array(totalTiles);
  for (let i = 0; i < totalTiles; i++) {
    const byteIdx = Math.floor(i / 8);
    const bitIdx = 7 - (i % 8);
    blocked[i] = (raw.charCodeAt(byteIdx) >> bitIdx) & 1;
  }
  return blocked;
}

// Pre-decode all collision maps at startup
const COLLISION_MAPS = {};
Object.keys(FARM_TYPES).forEach(farmType => {
  COLLISION_MAPS[farmType] = decodeCollisionMap(farmType);
});

// ============================================================================
// FARM BACKGROUND LOADING (3-layer compositing from PNGs)
// ============================================================================
async function loadFarmBackground(farmType, season) {
  const base = 'assets/stardewplanner/farm/';
  const layers = ['back', 'middle', 'front'];
  const images = [];

  for (const layer of layers) {
    const img = new Image();
    img.src = `${base}${farmType}_${layer}-${season}.png`;
    await new Promise((resolve, reject) => {
      img.onload = resolve;
      img.onerror = reject;
    });
    images.push(img);
  }

  // Composite layers onto an offscreen canvas
  const canvas = document.createElement('canvas');
  canvas.width = images[0].naturalWidth;
  canvas.height = images[0].naturalHeight;
  const ctx = canvas.getContext('2d');

  for (const img of images) {
    ctx.drawImage(img, 0, 0);
  }

  // Convert to an Image for rendering
  const composited = new Image();
  composited.src = canvas.toDataURL();
  await new Promise(resolve => { composited.onload = resolve; });
  return composited;
}

// ============================================================================
// SAVE/LOAD API HELPERS  (private-by-default: unguessable UUIDs + localStorage)
// ============================================================================
const SAVE_IDS_KEY = 'stardew_planner_save_ids';

function getSavedIds() {
  try {
    return JSON.parse(localStorage.getItem(SAVE_IDS_KEY)) || [];
  } catch { return []; }
}

function addSavedId(id) {
  const ids = getSavedIds();
  if (!ids.includes(id)) {
    ids.push(id);
    localStorage.setItem(SAVE_IDS_KEY, JSON.stringify(ids));
  }
}

function removeSavedId(id) {
  const ids = getSavedIds().filter(i => i !== id);
  localStorage.setItem(SAVE_IDS_KEY, JSON.stringify(ids));
}

async function fetchSaves() {
  try {
    const ids = getSavedIds();
    if (ids.length === 0) return [];
    const res = await fetch(`/api/saves?ids=${ids.join(',')}`);
    if (!res.ok) throw new Error('Failed to fetch saves');
    return await res.json();
  } catch (e) {
    console.error('Failed to fetch saves:', e);
    return [];
  }
}

async function saveToServer(saveData) {
  try {
    const res = await fetch('/api/saves', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(saveData),
    });
    if (!res.ok) throw new Error('Failed to save');
    const result = await res.json();
    if (result.id) addSavedId(result.id);
    return result;
  } catch (e) {
    console.error('Failed to save to server:', e);
    return null;
  }
}

async function deleteFromServer(id) {
  try {
    const res = await fetch(`/api/saves/${id}`, { method: 'DELETE' });
    if (!res.ok) throw new Error('Failed to delete');
    removeSavedId(id);
    return true;
  } catch (e) {
    console.error('Failed to delete save:', e);
    return false;
  }
}

// ============================================================================
// ASSET CATALOG
// ============================================================================
const createAssetCatalog = (season) => {
  const getAsset = (key) => GAME_ASSETS[key] || null;
  const getSeasonalAsset = (baseKey) => GAME_ASSETS[`${baseKey}_${season}`] || GAME_ASSETS[`${baseKey}_spring`] || null;
  // Path sprite sheets only exist for spring and winter; summer/fall use spring
  const pathSeason = (season === 'summer' || season === 'fall') ? 'spring' : season;
  const getPathAsset = (key) => {
    const asset = getAsset(key);
    if (asset?.pathName) return { ...asset, src: `assets/stardewplanner/path/${asset.pathName}-${pathSeason}.png` };
    return asset;
  };

  return {
    buildings: [
      { id: 'barn', name: 'Barn', ...getAsset('building_barn'), category: 'buildings' },
      { id: 'big_barn', name: 'Big Barn', ...getAsset('building_big_barn'), category: 'buildings' },
      { id: 'deluxe_barn', name: 'Deluxe Barn', ...getAsset('building_deluxe_barn'), category: 'buildings' },
      { id: 'coop', name: 'Coop', ...getAsset('building_coop'), category: 'buildings' },
      { id: 'big_coop', name: 'Big Coop', ...getAsset('building_big_coop'), category: 'buildings' },
      { id: 'deluxe_coop', name: 'Deluxe Coop', ...getAsset('building_deluxe_coop'), category: 'buildings' },
      { id: 'shed', name: 'Shed', ...getAsset('building_shed'), category: 'buildings' },
      { id: 'big_shed', name: 'Big Shed', ...getAsset('building_big_shed'), category: 'buildings' },
      { id: 'silo', name: 'Silo', ...getAsset('building_silo'), category: 'buildings' },
      { id: 'well', name: 'Well', ...getAsset('building_well'), category: 'buildings' },
      { id: 'stable', name: 'Stable', ...getAsset('building_stable'), category: 'buildings' },
      { id: 'slime_hutch', name: 'Slime Hutch', ...getAsset('building_slime_hutch'), category: 'buildings' },
      { id: 'fish_pond', name: 'Fish Pond', ...getAsset('building_fish_pond'), category: 'buildings' },
      { id: 'mill', name: 'Mill', ...getAsset('building_mill'), category: 'buildings' },
      { id: 'greenhouse', name: 'Greenhouse', ...getAsset('building_greenhouse'), category: 'buildings' },
      { id: 'farmhouse', name: 'Farmhouse', ...getAsset('building_farmhouse'), category: 'buildings' },
      { id: 'farmhouse_upgrade1', name: 'Farmhouse 1st Upgrade', ...getAsset('building_farmhouse_upgrade1'), category: 'buildings' },
      { id: 'farmhouse_upgrade2', name: 'Farmhouse 2nd Upgrade', ...getAsset('building_farmhouse_upgrade2'), category: 'buildings' },
      { id: 'shipping_bin', name: 'Shipping Bin', ...getAsset('building_shipping_bin'), category: 'buildings' },
      { id: 'mailbox', name: 'Mailbox', ...getAsset('building_mailbox'), category: 'buildings' },
      { id: 'pet_bowl', name: 'Pet Bowl', ...getSeasonalAsset('building_pet_bowl'), category: 'buildings' },
      { id: 'hay_pet_bowl', name: 'Hay Pet Bowl', ...getSeasonalAsset('building_hay_pet_bowl'), category: 'buildings' },
      { id: 'stone_pet_bowl', name: 'Stone Pet Bowl', ...getSeasonalAsset('building_stone_pet_bowl'), category: 'buildings' },
      { id: 'log_cabin', name: 'Log Cabin', ...getAsset('building_log_cabin'), category: 'buildings' },
      { id: 'stone_cabin', name: 'Stone Cabin', ...getAsset('building_stone_cabin'), category: 'buildings' },
      { id: 'plank_cabin', name: 'Plank Cabin', ...getAsset('building_plank_cabin'), category: 'buildings' },
      { id: 'beach_cabin', name: 'Beach Cabin', ...getAsset('building_beach_cabin'), category: 'buildings' },
      { id: 'neighbor_cabin', name: 'Neighbor Cabin', ...getAsset('building_neighbor_cabin'), category: 'buildings' },
      { id: 'rustic_cabin', name: 'Rustic Cabin', ...getAsset('building_rustic_cabin'), category: 'buildings' },
      { id: 'trailer_cabin', name: 'Trailer Cabin', ...getAsset('building_trailer_cabin'), category: 'buildings' },
    ].filter(a => a.src),

    endgame: [
      { id: 'gold_clock', name: 'Gold Clock', ...getAsset('building_gold_clock'), category: 'endgame' },
      { id: 'junimo_hut', name: 'Junimo Hut', ...getSeasonalAsset('building_junimo_hut'), category: 'endgame' },
      { id: 'desert_obelisk', name: 'Desert Obelisk', ...getAsset('building_desert_obelisk'), category: 'endgame' },
      { id: 'earth_obelisk', name: 'Earth Obelisk', ...getAsset('building_earth_obelisk'), category: 'endgame' },
      { id: 'water_obelisk', name: 'Water Obelisk', ...getAsset('building_water_obelisk'), category: 'endgame' },
      { id: 'island_obelisk', name: 'Island Obelisk', ...getAsset('building_island_obelisk'), category: 'endgame' },
    ].filter(a => a.src),

    crops: [
      { id: 'parsnip', name: 'Parsnip', ...getAsset('crop_parsnip'), category: 'crops' },
      { id: 'cauliflower', name: 'Cauliflower', ...getAsset('crop_cauliflower'), category: 'crops' },
      { id: 'potato', name: 'Potato', ...getAsset('crop_potato'), category: 'crops' },
      { id: 'garlic', name: 'Garlic', ...getAsset('crop_garlic'), category: 'crops' },
      { id: 'kale', name: 'Kale', ...getAsset('crop_kale'), category: 'crops' },
      { id: 'rhubarb', name: 'Rhubarb', ...getAsset('crop_rhubarb'), category: 'crops' },
      { id: 'strawberry', name: 'Strawberry', ...getAsset('crop_strawberry'), category: 'crops' },
      { id: 'rice', name: 'Rice', ...getAsset('crop_rice'), category: 'crops' },
      { id: 'carrot', name: 'Carrot', ...getAsset('crop_carrot'), category: 'crops' },
      { id: 'melon', name: 'Melon', ...getAsset('crop_melon'), category: 'crops' },
      { id: 'blueberry', name: 'Blueberry', ...getAsset('crop_blueberry'), category: 'crops' },
      { id: 'tomato', name: 'Tomato', ...getAsset('crop_tomato'), category: 'crops' },
      { id: 'hops', name: 'Hops', ...getAsset('crop_hops'), category: 'crops' },
      { id: 'radish', name: 'Radish', ...getAsset('crop_radish'), category: 'crops' },
      { id: 'wheat', name: 'Wheat', ...getAsset('crop_wheat'), category: 'crops' },
      { id: 'corn', name: 'Corn', ...getAsset('crop_corn'), category: 'crops' },
      { id: 'sunflower', name: 'Sunflower', ...getAsset('crop_sunflower'), category: 'crops' },
      { id: 'red_cabbage', name: 'Red Cabbage', ...getAsset('crop_red_cabbage'), category: 'crops' },
      { id: 'summer_squash', name: 'Summer Squash', ...getAsset('crop_summer_squash'), category: 'crops' },
      { id: 'pineapple', name: 'Pineapple', ...getAsset('crop_pineapple'), category: 'crops' },
      { id: 'pumpkin', name: 'Pumpkin', ...getAsset('crop_pumpkin'), category: 'crops' },
      { id: 'cranberry', name: 'Cranberry', ...getAsset('crop_cranberry'), category: 'crops' },
      { id: 'grape', name: 'Grape', ...getAsset('crop_grape'), category: 'crops' },
      { id: 'eggplant', name: 'Eggplant', ...getAsset('crop_eggplant'), category: 'crops' },
      { id: 'yam', name: 'Yam', ...getAsset('crop_yam'), category: 'crops' },
      { id: 'bok_choy', name: 'Bok Choy', ...getAsset('crop_bok_choy'), category: 'crops' },
      { id: 'amaranth', name: 'Amaranth', ...getAsset('crop_amaranth'), category: 'crops' },
      { id: 'artichoke', name: 'Artichoke', ...getAsset('crop_artichoke'), category: 'crops' },
      { id: 'beet', name: 'Beet', ...getAsset('crop_beet'), category: 'crops' },
      { id: 'broccoli', name: 'Broccoli', ...getAsset('crop_broccoli'), category: 'crops' },
      { id: 'powdermelon', name: 'Powdermelon', ...getAsset('crop_powdermelon'), category: 'crops' },
      { id: 'starfruit', name: 'Starfruit', ...getAsset('crop_starfruit'), category: 'crops' },
      { id: 'ancient_fruit', name: 'Ancient Fruit', ...getAsset('crop_ancient_fruit'), category: 'crops' },
      { id: 'tea_sapling', name: 'Tea Sapling', ...getAsset('crop_tea_sapling'), category: 'crops' },
      { id: 'fiber', name: 'Fiber', ...getSeasonalAsset('crop_fiber'), category: 'crops' },
      { id: 'cactus', name: 'Cactus Fruit', ...getAsset('crop_cactus'), category: 'crops' },
      { id: 'coffee', name: 'Coffee Bean', ...getAsset('crop_coffee'), category: 'crops' },
      { id: 'pepper', name: 'Hot Pepper', ...getAsset('crop_pepper'), category: 'crops' },
      { id: 'sweetgem_berry', name: 'Sweet Gem Berry', ...getAsset('crop_sweetgem_berry'), category: 'crops' },
      { id: 'blue_jazz', name: 'Blue Jazz', ...getAsset('crop_blue_jazz'), category: 'crops' },
      { id: 'fairy_rose', name: 'Fairy Rose', ...getAsset('crop_fairy_rose'), category: 'crops' },
      { id: 'poppy', name: 'Poppy', ...getAsset('crop_poppy'), category: 'crops' },
      { id: 'summer_spangle', name: 'Summer Spangle', ...getAsset('crop_summer_spangle'), category: 'crops' },
      { id: 'tulip', name: 'Tulip', ...getAsset('crop_tulip'), category: 'crops' },
      { id: 'bean', name: 'Green Bean', ...getAsset('crop_bean'), category: 'crops' },
      { id: 'taro', name: 'Taro Root', ...getAsset('crop_taro'), category: 'crops' },
      { id: 'qi_fruit', name: 'Qi Fruit', ...getAsset('crop_qi_fruit'), category: 'crops' },
    ].filter(a => a.src),

    trees: [
      { id: 'oak_tree', name: 'Oak Tree', ...getSeasonalAsset('tree_oak'), category: 'trees' },
      { id: 'maple_tree', name: 'Maple Tree', ...getSeasonalAsset('tree_maple'), category: 'trees' },
      { id: 'mahogany_tree', name: 'Mahogany Tree', ...getSeasonalAsset('tree_mahogany'), category: 'trees' },
      { id: 'apple_tree', name: 'Apple Tree', ...getSeasonalAsset('fruit_apple'), category: 'trees' },
      { id: 'apricot_tree', name: 'Apricot Tree', ...getSeasonalAsset('fruit_apricot'), category: 'trees' },
      { id: 'banana_tree', name: 'Banana Tree', ...getSeasonalAsset('fruit_banana'), category: 'trees' },
      { id: 'cherry_tree', name: 'Cherry Tree', ...getSeasonalAsset('fruit_cherry'), category: 'trees' },
      { id: 'mango_tree', name: 'Mango Tree', ...getSeasonalAsset('fruit_mango'), category: 'trees' },
      { id: 'orange_tree', name: 'Orange Tree', ...getSeasonalAsset('fruit_orange'), category: 'trees' },
      { id: 'peach_tree', name: 'Peach Tree', ...getSeasonalAsset('fruit_peach'), category: 'trees' },
      { id: 'pomegranate_tree', name: 'Pomegranate Tree', ...getSeasonalAsset('fruit_pomegranate'), category: 'trees' },
      { id: 'spruce_tree', name: 'Spruce Tree', ...getSeasonalAsset('tree_spruce'), category: 'trees' },
      { id: 'mushroom_tree', name: 'Mushroom Tree', ...getSeasonalAsset('tree_mushroom'), category: 'trees' },
      { id: 'mystic_tree', name: 'Mystic Tree', ...getSeasonalAsset('tree_mystic'), category: 'trees' },
      { id: 'mossy_oak_tree', name: 'Mossy Oak Tree', ...getSeasonalAsset('tree_mossy_oak'), category: 'trees' },
      { id: 'mossy_maple_tree', name: 'Mossy Maple Tree', ...getSeasonalAsset('tree_mossy_maple'), category: 'trees' },
      { id: 'mossy_spruce_tree', name: 'Mossy Spruce Tree', ...getSeasonalAsset('tree_mossy_spruce'), category: 'trees' },
      { id: 'mossy_mushroom_tree', name: 'Mossy Mushroom Tree', ...getSeasonalAsset('tree_mossy_mushroom'), category: 'trees' },
    ].filter(a => a.src),

    equipment: [
      // Sprinklers
      { id: 'basic_sprinkler', name: 'Basic Sprinkler', ...getAsset('sprinkler_basic'), category: 'equipment', subcategory: 'Sprinklers' },
      { id: 'quality_sprinkler', name: 'Quality Sprinkler', ...getAsset('sprinkler_quality'), category: 'equipment', subcategory: 'Sprinklers' },
      { id: 'iridium_sprinkler', name: 'Iridium Sprinkler', ...getAsset('sprinkler_iridium'), category: 'equipment', subcategory: 'Sprinklers' },
      { id: 'basic_sprinkler_upgrade', name: 'Basic Sprinkler+', ...getAsset('sprinkler_basic_upgrade'), category: 'equipment', subcategory: 'Sprinklers' },
      { id: 'quality_sprinkler_upgrade', name: 'Quality Sprinkler+', ...getAsset('sprinkler_quality_upgrade'), category: 'equipment', subcategory: 'Sprinklers' },
      { id: 'iridium_sprinkler_upgrade', name: 'Iridium Sprinkler+', ...getAsset('sprinkler_iridium_upgrade'), category: 'equipment', subcategory: 'Sprinklers' },
      // Scarecrows
      { id: 'scarecrow', name: 'Scarecrow', ...getAsset('machine_scarecrow'), category: 'equipment', subcategory: 'Scarecrows' },
      { id: 'deluxe_scarecrow', name: 'Deluxe Scarecrow', ...getAsset('machine_deluxe_scarecrow'), category: 'equipment', subcategory: 'Scarecrows' },
      { id: 'rarecrow_1', name: 'Rarecrow 1', ...getAsset('machine_rarecrow_1'), category: 'equipment', subcategory: 'Scarecrows' },
      { id: 'rarecrow_2', name: 'Rarecrow 2', ...getAsset('machine_rarecrow_2'), category: 'equipment', subcategory: 'Scarecrows' },
      { id: 'rarecrow_3', name: 'Rarecrow 3', ...getAsset('machine_rarecrow_3'), category: 'equipment', subcategory: 'Scarecrows' },
      { id: 'rarecrow_4', name: 'Rarecrow 4', ...getAsset('machine_rarecrow_4'), category: 'equipment', subcategory: 'Scarecrows' },
      { id: 'rarecrow_5', name: 'Rarecrow 5', ...getAsset('machine_rarecrow_5'), category: 'equipment', subcategory: 'Scarecrows' },
      { id: 'rarecrow_6', name: 'Rarecrow 6', ...getAsset('machine_rarecrow_6'), category: 'equipment', subcategory: 'Scarecrows' },
      { id: 'rarecrow_7', name: 'Rarecrow 7', ...getAsset('machine_rarecrow_7'), category: 'equipment', subcategory: 'Scarecrows' },
      { id: 'rarecrow_8', name: 'Rarecrow 8', ...getAsset('machine_rarecrow_8'), category: 'equipment', subcategory: 'Scarecrows' },
      // Processing
      { id: 'keg', name: 'Keg', ...getAsset('machine_keg'), category: 'equipment', subcategory: 'Processing' },
      { id: 'preserves_jar', name: 'Preserves Jar', ...getAsset('machine_preserves_jar'), category: 'equipment', subcategory: 'Processing' },
      { id: 'cheese_maker', name: 'Cheese Maker', ...getAsset('machine_cheese_maker'), category: 'equipment', subcategory: 'Processing' },
      { id: 'oil_refiner', name: 'Oil Refiner', ...getAsset('machine_oil_refiner'), category: 'equipment', subcategory: 'Processing' },
      { id: 'mayonnaise_machine', name: 'Mayonnaise Machine', ...getAsset('machine_mayonnaise_machine'), category: 'equipment', subcategory: 'Processing' },
      { id: 'loom', name: 'Loom', ...getAsset('machine_loom'), category: 'equipment', subcategory: 'Processing' },
      { id: 'furnace', name: 'Furnace', ...getAsset('machine_furnace'), category: 'equipment', subcategory: 'Processing' },
      { id: 'heavy_furnace', name: 'Heavy Furnace', ...getAsset('machine_heavy_furnace'), category: 'equipment', subcategory: 'Processing' },
      { id: 'charcoal_kiln', name: 'Charcoal Kiln', ...getAsset('machine_charcoal_kiln'), category: 'equipment', subcategory: 'Processing' },
      { id: 'crystalarium', name: 'Crystalarium', ...getAsset('machine_crystalarium'), category: 'equipment', subcategory: 'Processing' },
      { id: 'recycling_machine', name: 'Recycling Machine', ...getAsset('machine_recycling_machine'), category: 'equipment', subcategory: 'Processing' },
      { id: 'seed_maker', name: 'Seed Maker', ...getAsset('machine_seed_maker'), category: 'equipment', subcategory: 'Processing' },
      { id: 'bone_mill', name: 'Bone Mill', ...getAsset('machine_bone_mill'), category: 'equipment', subcategory: 'Processing' },
      { id: 'geode_crusher', name: 'Geode Crusher', ...getAsset('machine_geode_crusher'), category: 'equipment', subcategory: 'Processing' },
      { id: 'cask', name: 'Cask', ...getAsset('machine_cask'), category: 'equipment', subcategory: 'Processing' },
      { id: 'dehydrator', name: 'Dehydrator', ...getAsset('machine_dehydrator'), category: 'equipment', subcategory: 'Processing' },
      { id: 'fish_smoker', name: 'Fish Smoker', ...getAsset('machine_fish_smoker'), category: 'equipment', subcategory: 'Processing' },
      { id: 'bait_maker', name: 'Bait Maker', ...getAsset('machine_bait_maker'), category: 'equipment', subcategory: 'Processing' },
      { id: 'deconstructor', name: 'Deconstructor', ...getAsset('machine_deconstructor'), category: 'equipment', subcategory: 'Processing' },
      { id: 'anvil', name: 'Anvil', ...getAsset('machine_anvil'), category: 'equipment', subcategory: 'Processing' },
      { id: 'mini_forge', name: 'Mini Forge', ...getAsset('machine_mini_forge'), category: 'equipment', subcategory: 'Processing' },
      // Storage
      { id: 'wood_chest', name: 'Chest', ...getAsset('machine_wood_chest'), category: 'equipment', subcategory: 'Storage' },
      { id: 'stone_chest', name: 'Stone Chest', ...getAsset('machine_stone_chest'), category: 'equipment', subcategory: 'Storage' },
      { id: 'wooden_big_chest', name: 'Big Chest', ...getAsset('machine_wooden_big_chest'), category: 'equipment', subcategory: 'Storage' },
      { id: 'stone_big_chest', name: 'Big Stone Chest', ...getAsset('machine_stone_big_chest'), category: 'equipment', subcategory: 'Storage' },
      { id: 'junimo_chest', name: 'Junimo Chest', ...getAsset('machine_junimo_chest'), category: 'equipment', subcategory: 'Storage' },
      { id: 'mini_fridge', name: 'Mini Fridge', ...getAsset('machine_mini_fridge'), category: 'equipment', subcategory: 'Storage' },
      { id: 'mini_shipping_bin', name: 'Mini Shipping Bin', ...getAsset('machine_mini_shipping_bin'), category: 'equipment', subcategory: 'Storage' },
      { id: 'hopper', name: 'Hopper', ...getAsset('machine_hopper'), category: 'equipment', subcategory: 'Storage' },
      // Automation
      { id: 'auto_grabber', name: 'Auto-Grabber', ...getAsset('machine_auto_grabber'), category: 'equipment', subcategory: 'Automation' },
      { id: 'auto_petter', name: 'Auto-Petter', ...getAsset('machine_auto_petter'), category: 'equipment', subcategory: 'Automation' },
      { id: 'slime_incubator', name: 'Slime Incubator', ...getAsset('machine_slime_incubator'), category: 'equipment', subcategory: 'Automation' },
      { id: 'slime_egg_press', name: 'Slime Egg-Press', ...getAsset('machine_slime_egg_press'), category: 'equipment', subcategory: 'Automation' },
      { id: 'ostrich_incubator', name: 'Ostrich Incubator', ...getAsset('machine_ostrich_incubator'), category: 'equipment', subcategory: 'Automation' },
      { id: 'deluxe_worm_bin', name: 'Deluxe Worm Bin', ...getAsset('machine_deluxe_worm_bin'), category: 'equipment', subcategory: 'Automation' },
      { id: 'mushroom_log', name: 'Mushroom Log', ...getAsset('machine_mushroom_log'), category: 'equipment', subcategory: 'Automation' },
      { id: 'heater', name: 'Heater', ...getAsset('machine_heater'), category: 'equipment', subcategory: 'Automation' },
      // Utility
      { id: 'bee_house', name: 'Bee House', ...getAsset('machine_bee_house'), category: 'equipment', subcategory: 'Utility' },
      { id: 'tapper', name: 'Tapper', ...getAsset('machine_tapper'), category: 'equipment', subcategory: 'Utility' },
      { id: 'heavy_tapper', name: 'Heavy Tapper', ...getAsset('machine_heavy_tapper'), category: 'equipment', subcategory: 'Utility' },
      { id: 'lightning_rod', name: 'Lightning Rod', ...getAsset('machine_lightning_rod'), category: 'equipment', subcategory: 'Utility' },
      { id: 'solar_panel', name: 'Solar Panel', ...getAsset('machine_solar_panel'), category: 'equipment', subcategory: 'Utility' },
      { id: 'worm_bin', name: 'Worm Bin', ...getAsset('machine_worm_bin'), category: 'equipment', subcategory: 'Utility' },
      { id: 'garden_pot', name: 'Garden Pot', ...getAsset('machine_garden_pot'), category: 'equipment', subcategory: 'Utility' },
      { id: 'campfire', name: 'Campfire', ...getAsset('machine_campfire'), category: 'equipment', subcategory: 'Utility' },
      { id: 'wood_chipper', name: 'Wood Chipper', ...getAsset('machine_wood_chipper'), category: 'equipment', subcategory: 'Utility' },
      { id: 'workbench', name: 'Workbench', ...getAsset('machine_workbench'), category: 'equipment', subcategory: 'Utility' },
      { id: 'sewing_machine', name: 'Sewing Machine', ...getAsset('machine_sewing_machine'), category: 'equipment', subcategory: 'Utility' },
      { id: 'farm_computer', name: 'Farm Computer', ...getAsset('machine_farm_computer'), category: 'equipment', subcategory: 'Utility' },
      { id: 'telephone', name: 'Telephone', ...getAsset('machine_telephone'), category: 'equipment', subcategory: 'Utility' },
      { id: 'coffee_maker', name: 'Coffee Maker', ...getAsset('machine_coffee_maker'), category: 'equipment', subcategory: 'Utility' },
      { id: 'jukebox', name: 'Jukebox', ...getAsset('machine_jukebox'), category: 'equipment', subcategory: 'Utility' },
      { id: 'mini_obelisk', name: 'Mini-Obelisk', ...getAsset('machine_mini_obelisk'), category: 'equipment', subcategory: 'Utility' },
      // Signs
      { id: 'wood_sign', name: 'Wood Sign', ...getAsset('machine_wood_sign'), category: 'equipment', subcategory: 'Signs' },
      { id: 'stone_sign', name: 'Stone Sign', ...getAsset('machine_stone_sign'), category: 'equipment', subcategory: 'Signs' },
      { id: 'dark_sign', name: 'Dark Sign', ...getAsset('machine_dark_sign'), category: 'equipment', subcategory: 'Signs' },
      { id: 'text_sign', name: 'Text Sign', ...getAsset('machine_text_sign'), category: 'equipment', subcategory: 'Signs' },
      // Lighting
      { id: 'barrel_brazier', name: 'Barrel Brazier', ...getAsset('machine_barrel_brazier'), category: 'equipment', subcategory: 'Lighting' },
      { id: 'carved_brazier', name: 'Carved Brazier', ...getAsset('machine_carved_brazier'), category: 'equipment', subcategory: 'Lighting' },
      { id: 'gold_brazier', name: 'Gold Brazier', ...getAsset('machine_gold_brazier'), category: 'equipment', subcategory: 'Lighting' },
      { id: 'marble_brazier', name: 'Marble Brazier', ...getAsset('machine_marble_brazier'), category: 'equipment', subcategory: 'Lighting' },
      { id: 'skull_brazier', name: 'Skull Brazier', ...getAsset('machine_skull_brazier'), category: 'equipment', subcategory: 'Lighting' },
      { id: 'stone_brazier', name: 'Stone Brazier', ...getAsset('machine_stone_brazier'), category: 'equipment', subcategory: 'Lighting' },
      { id: 'stump_brazier', name: 'Stump Brazier', ...getAsset('machine_stump_brazier'), category: 'equipment', subcategory: 'Lighting' },
      { id: 'wooden_brazier', name: 'Wooden Brazier', ...getAsset('machine_wooden_brazier'), category: 'equipment', subcategory: 'Lighting' },
      { id: 'iron_lamp_post', name: 'Iron Lamp-post', ...getAsset('machine_iron_lamp_post'), category: 'equipment', subcategory: 'Lighting' },
      { id: 'wood_lamp_post', name: 'Wood Lamp-post', ...getAsset('machine_wood_lamp_post'), category: 'equipment', subcategory: 'Lighting' },
      // Statues
      { id: 'statue_of_endless_fortune', name: 'Statue of Endless Fortune', ...getAsset('machine_statue_of_endless_fortune'), category: 'equipment', subcategory: 'Statues' },
      { id: 'statue_of_perfection', name: 'Statue of Perfection', ...getAsset('machine_statue_of_perfection'), category: 'equipment', subcategory: 'Statues' },
      { id: 'statue_of_true_perfection', name: 'Statue of True Perfection', ...getAsset('machine_statue_of_true_perfection'), category: 'equipment', subcategory: 'Statues' },
      { id: 'statue_of_blessings', name: 'Statue of Blessings', ...getAsset('machine_statue_of_blessings'), category: 'equipment', subcategory: 'Statues' },
      { id: 'statue_of_the_dwarf_king', name: 'Statue of the Dwarf King', ...getAsset('machine_statue_of_the_dwarf_king'), category: 'equipment', subcategory: 'Statues' },
    ].filter(a => a.src),

    fences_paths: [
      { id: 'wood_fence', name: 'Wood Fence', ...getAsset('fence_wood'), category: 'fences_paths' },
      { id: 'stone_fence', name: 'Stone Fence', ...getAsset('fence_stone'), category: 'fences_paths' },
      { id: 'iron_fence', name: 'Iron Fence', ...getAsset('fence_iron'), category: 'fences_paths' },
      { id: 'hardwood_fence', name: 'Hardwood Fence', ...getAsset('fence_hardwood'), category: 'fences_paths' },
      { id: 'wood_path', name: 'Wood Path', ...getPathAsset('path_wood'), category: 'fences_paths' },
      { id: 'wood_floor', name: 'Wood Floor', ...getPathAsset('path_wood_floor'), category: 'fences_paths' },
      { id: 'stone_floor', name: 'Stone Floor', ...getPathAsset('path_stone_floor'), category: 'fences_paths' },
      { id: 'brick_floor', name: 'Brick Floor', ...getPathAsset('path_brick_floor'), category: 'fences_paths' },
      { id: 'weathered_floor', name: 'Weathered Floor', ...getPathAsset('path_weathered_floor'), category: 'fences_paths' },
      { id: 'straw_floor', name: 'Straw Floor', ...getPathAsset('path_straw_floor'), category: 'fences_paths' },
      { id: 'gravel_path', name: 'Gravel Path', ...getPathAsset('path_gravel'), category: 'fences_paths' },
      { id: 'cobblestone_path', name: 'Cobblestone Path', ...getPathAsset('path_cobblestone'), category: 'fences_paths' },
      { id: 'stepping_stone', name: 'Stepping Stone', ...getPathAsset('path_stepping_stone'), category: 'fences_paths' },
      { id: 'crystal_path', name: 'Crystal Path', ...getPathAsset('path_crystal'), category: 'fences_paths' },
      { id: 'crystal_floor', name: 'Crystal Floor', ...getPathAsset('path_crystal_floor'), category: 'fences_paths' },
      { id: 'rustic_plank', name: 'Rustic Plank Floor', ...getPathAsset('path_rustic_plank'), category: 'fences_paths' },
      { id: 'stone_walkway', name: 'Stone Walkway', ...getPathAsset('path_stone_walkway'), category: 'fences_paths' },
    ].filter(a => a.src),

    furniture: [
      { id: 'blue_armchair', name: 'Blue Armchair', ...getAsset('furniture_blue_armchair_down'), category: 'furniture' },
      { id: 'brown_armchair', name: 'Brown Armchair', ...getAsset('furniture_brown_armchair_down'), category: 'furniture' },
      { id: 'green_armchair', name: 'Green Armchair', ...getAsset('furniture_green_armchair_down'), category: 'furniture' },
      { id: 'red_armchair', name: 'Red Armchair', ...getAsset('furniture_red_armchair_down'), category: 'furniture' },
      { id: 'yellow_armchair', name: 'Yellow Armchair', ...getAsset('furniture_yellow_armchair_down'), category: 'furniture' },
      { id: 'oak_table', name: 'Oak Table', ...getAsset('furniture_oak_table'), category: 'furniture' },
      { id: 'birch_table', name: 'Birch Table', ...getAsset('furniture_birch_table'), category: 'furniture' },
      { id: 'mahogany_table', name: 'Mahogany Table', ...getAsset('furniture_mahogany_table'), category: 'furniture' },
      { id: 'walnut_table', name: 'Walnut Table', ...getAsset('furniture_walnut_table'), category: 'furniture' },
      { id: 'dark_bookcase', name: 'Dark Bookcase', ...getAsset('furniture_dark_bookcase'), category: 'furniture' },
      { id: 'modern_bookcase', name: 'Modern Bookcase', ...getAsset('furniture_modern_bookcase'), category: 'furniture' },
      { id: 'china_cabinet', name: 'China Cabinet', ...getAsset('furniture_china_cabinet'), category: 'furniture' },
      { id: 'small_fish_tank', name: 'Small Fish Tank', ...getAsset('furniture_small_fish_tank'), category: 'furniture' },
      { id: 'modern_fish_tank', name: 'Modern Fish Tank', ...getAsset('furniture_modern_fish_tank'), category: 'furniture' },
      { id: 'large_fish_tank', name: 'Large Fish Tank', ...getAsset('furniture_large_fish_tank'), category: 'furniture' },
      { id: 'deluxe_fish_tank', name: 'Deluxe Fish Tank', ...getAsset('furniture_deluxe_fish_tank'), category: 'furniture' },
      { id: 'cc_fish_tank', name: 'CC Fish Tank', ...getAsset('furniture_cc_fish_tank'), category: 'furniture' },
      { id: 'aquatic_sanctuary', name: 'Aquatic Sanctuary', ...getAsset('furniture_aquatic_sanctuary'), category: 'furniture' },
      { id: 'plasma_tv', name: 'Plasma TV', ...getAsset('furniture_plasma_tv'), category: 'furniture' },
      { id: 'tropical_tv', name: 'Tropical TV', ...getAsset('furniture_tropical_tv'), category: 'furniture' },
      { id: 'blossom_rug', name: 'Blossom Rug', ...getAsset('furniture_blossom_rug'), category: 'furniture' },
      { id: 'dark_rug_long', name: 'Dark Rug (Long)', ...getAsset('furniture_dark_rug_long'), category: 'furniture' },
      { id: 'dark_rug_tall', name: 'Dark Rug (Tall)', ...getAsset('furniture_dark_rug_tall'), category: 'furniture' },
      { id: 'desert_rug_tall', name: 'Desert Rug', ...getAsset('furniture_desert_rug_tall'), category: 'furniture' },
      { id: 'fruit_salad_rug', name: 'Fruit Salad Rug', ...getAsset('furniture_fruit_salad_rug_long'), category: 'furniture' },
      { id: 'stone_flooring', name: 'Stone Flooring', ...getAsset('furniture_stone_flooring'), category: 'furniture' },
      { id: 'bear_statue', name: 'Bear Statue', ...getAsset('furniture_bear_statue'), category: 'furniture' },
      { id: 'desert_flags', name: 'Desert Flags', ...getAsset('furniture_desert_flags'), category: 'furniture' },
      { id: 'monster_danglers', name: 'Monster Danglers', ...getAsset('furniture_monster_danglers'), category: 'furniture' },
      { id: 'curly_tree', name: 'Curly Tree', ...getAsset('furniture_curly_tree'), category: 'furniture' },
      { id: 'deluxe_tree', name: 'Deluxe Tree', ...getAsset('furniture_deluxe_tree'), category: 'furniture' },
      { id: 'exotic_tree', name: 'Exotic Tree', ...getAsset('furniture_exotic_tree'), category: 'furniture' },
      { id: 'long_palm', name: 'Long Palm', ...getAsset('furniture_long_palm'), category: 'furniture' },
      { id: 'tree_of_winter_star', name: 'Winter Star Tree', ...getAsset('furniture_tree_of_winter_star'), category: 'furniture' },
      { id: 'seasonal_plant_1', name: 'Seasonal Plant 1', ...getSeasonalAsset('furniture_seasonal_plant_1'), category: 'furniture' },
      { id: 'seasonal_plant_2', name: 'Seasonal Plant 2', ...getSeasonalAsset('furniture_seasonal_plant_2'), category: 'furniture' },
      { id: 'seasonal_plant_3', name: 'Seasonal Plant 3', ...getSeasonalAsset('furniture_seasonal_plant_3'), category: 'furniture' },
      { id: 'seasonal_plant_4', name: 'Seasonal Plant 4', ...getSeasonalAsset('furniture_seasonal_plant_4'), category: 'furniture' },
      { id: 'seasonal_plant_5', name: 'Seasonal Plant 5', ...getSeasonalAsset('furniture_seasonal_plant_5'), category: 'furniture' },
      { id: 'seasonal_plant_6', name: 'Seasonal Plant 6', ...getSeasonalAsset('furniture_seasonal_plant_6'), category: 'furniture' },
      { id: 'seasonal_decor', name: 'Seasonal Decor', ...getSeasonalAsset('furniture_seasonal_decor'), category: 'furniture' },
    ].filter(a => a.src),
  };
};

// ============================================================================
// SPRINKLER RANGE PATTERNS
// ============================================================================
const SPRINKLER_RANGES = {
  basic_sprinkler: [
    [0, -1], [-1, 0], [1, 0], [0, 1]
  ],
  quality_sprinkler: [
    [-1, -1], [0, -1], [1, -1],
    [-1, 0], [1, 0],
    [-1, 1], [0, 1], [1, 1]
  ],
  iridium_sprinkler: [
    ...Array.from({ length: 5 }, (_, i) => Array.from({ length: 5 }, (_, j) => [i - 2, j - 2]))
      .flat()
      .filter(([x, y]) => !(x === 0 && y === 0))
  ],
  basic_sprinkler_upgrade: [
    [0, -2], [-1, -1], [0, -1], [1, -1],
    [-2, 0], [-1, 0], [1, 0], [2, 0],
    [-1, 1], [0, 1], [1, 1], [0, 2]
  ],
  quality_sprinkler_upgrade: [
    ...Array.from({ length: 5 }, (_, i) => Array.from({ length: 5 }, (_, j) => [i - 2, j - 2]))
      .flat()
      .filter(([x, y]) => !(x === 0 && y === 0))
  ],
  iridium_sprinkler_upgrade: [
    ...Array.from({ length: 7 }, (_, i) => Array.from({ length: 7 }, (_, j) => [i - 3, j - 3]))
      .flat()
      .filter(([x, y]) => !(x === 0 && y === 0))
  ]
};

// ============================================================================
// AUTO-TILE SYSTEM (fences & paths connect to neighbors)
// ============================================================================
// Neighbor rules: key = combination of u/d/l/r for connected neighbors → [col, row] in sprite sheet
const PATH_NEIGHBOR_RULES = {
  '': [0,0], 'u': [0,3], 'd': [0,1], 'l': [3,3], 'r': [1,3],
  'ud': [0,2], 'ul': [3,2], 'ur': [1,2], 'dl': [3,0], 'dr': [1,0],
  'lr': [2,3], 'udl': [3,1], 'udr': [1,1], 'ulr': [2,2], 'dlr': [2,0],
  'udlr': [2,1],
};

const FENCE_NEIGHBOR_RULES = {
  '': [2,1], 'u': [0,1], 'd': [2,1], 'l': [0,3], 'r': [1,3],
  'ud': [0,1], 'ul': [2,2], 'ur': [0,2], 'dl': [2,0], 'dr': [0,0],
  'lr': [1,2], 'udl': [2,2], 'udr': [0,2], 'ulr': [2,3], 'dlr': [1,1],
  'udlr': [2,3],
};

const TILEABLE_IDS = new Set([
  'wood_fence', 'stone_fence', 'iron_fence', 'hardwood_fence',
  'wood_path', 'wood_floor', 'stone_floor', 'brick_floor',
  'weathered_floor', 'straw_floor', 'gravel_path', 'cobblestone_path',
  'stepping_stone', 'crystal_path', 'crystal_floor', 'rustic_plank',
  'stone_walkway',
]);

const FENCE_IDS = new Set(['wood_fence', 'stone_fence', 'iron_fence', 'hardwood_fence']);

function getAutoTileSourceRect(item, placedItems) {
  const isFence = FENCE_IDS.has(item.assetId);
  const rules = isFence ? FENCE_NEIGHBOR_RULES : PATH_NEIGHBOR_RULES;

  const hasUp = placedItems.some(i => i.x === item.x && i.y === item.y - 1 && i.assetId === item.assetId);
  const hasDown = placedItems.some(i => i.x === item.x && i.y === item.y + 1 && i.assetId === item.assetId);
  const hasLeft = placedItems.some(i => i.x === item.x - 1 && i.y === item.y && i.assetId === item.assetId);
  const hasRight = placedItems.some(i => i.x === item.x + 1 && i.y === item.y && i.assetId === item.assetId);

  const key = (hasUp ? 'u' : '') + (hasDown ? 'd' : '') + (hasLeft ? 'l' : '') + (hasRight ? 'r' : '');
  const [col, row] = rules[key] || rules[''];

  if (isFence) {
    return [col * 16, row * 32, 16, 32];
  } else {
    return [col * 16, row * 16, 16, 16];
  }
}

// ============================================================================
// CROPPED THUMBNAIL (for sprite sheets without separate icon files)
// ============================================================================
function CroppedThumbnail({ src, sourceRect, style }) {
  const canvasRef = useRef(null);
  useEffect(() => {
    const img = new Image();
    img.src = src;
    img.onload = () => {
      const canvas = canvasRef.current;
      if (!canvas) return;
      const [sx, sy, sw, sh] = sourceRect;
      canvas.width = sw;
      canvas.height = sh;
      const ctx = canvas.getContext('2d');
      ctx.imageSmoothingEnabled = false;
      ctx.drawImage(img, sx, sy, sw, sh, 0, 0, sw, sh);
    };
  }, [src, sourceRect]);
  return React.createElement('canvas', { ref: canvasRef, style });
}

// ============================================================================
// TOOLTIP COMPONENT (fast hover delay)
// ============================================================================
function Tooltip({ text, delay = 150, children }) {
  const [show, setShow] = React.useState(false);
  const [pos, setPos] = React.useState({ x: 0, y: 0 });
  const tooltipRef = React.useRef(null);
  const timeoutRef = React.useRef(null);

  const handleMouseEnter = (e) => {
    const rect = e.currentTarget.getBoundingClientRect();
    setPos({ x: rect.left + rect.width / 2, y: rect.bottom + 6 });
    timeoutRef.current = setTimeout(() => setShow(true), delay);
  };
  const handleMouseLeave = () => {
    clearTimeout(timeoutRef.current);
    setShow(false);
  };

  // Clamp tooltip position after render so it stays within viewport
  React.useEffect(() => {
    if (show && tooltipRef.current) {
      const el = tooltipRef.current;
      const r = el.getBoundingClientRect();
      const pad = 8;
      if (r.right > window.innerWidth - pad) {
        el.style.left = (window.innerWidth - pad - r.width) + 'px';
        el.style.transform = 'none';
      }
      if (r.left < pad) {
        el.style.left = pad + 'px';
        el.style.transform = 'none';
      }
    }
  }, [show, pos]);

  return React.createElement('div', {
    style: { position: 'relative', display: 'inline-flex' },
    onMouseEnter: handleMouseEnter,
    onMouseLeave: handleMouseLeave,
  },
    children,
    show && ReactDOM.createPortal(
      React.createElement('div', {
        ref: tooltipRef,
        style: {
          position: 'fixed', left: pos.x, top: pos.y,
          transform: 'translateX(-50%)',
          backgroundColor: '#4a3728', color: '#f7f1e8', padding: '7px 14px',
          borderRadius: '8px', fontSize: '12px', fontWeight: '600', whiteSpace: 'nowrap',
          pointerEvents: 'none', zIndex: 10000, border: 'none',
          boxShadow: '0 4px 16px rgba(74,55,40,0.25)',
          fontFamily: "'Nunito', system-ui, sans-serif",
        }
      }, text),
      document.body
    )
  );
}

// ============================================================================
// MODAL BASE COMPONENT (portal-based overlay)
// ============================================================================
function Modal({ isOpen, onClose, title, children, width }) {
  if (!isOpen) return null;

  const handleBackdropClick = (e) => {
    if (e.target === e.currentTarget) onClose();
  };

  return ReactDOM.createPortal(
    <div
      className="modal-backdrop"
      onClick={handleBackdropClick}
      style={{
        position: 'fixed', inset: 0,
        backgroundColor: 'rgba(30, 20, 10, 0.6)',
        backdropFilter: 'blur(4px)',
        WebkitBackdropFilter: 'blur(4px)',
        zIndex: 20000,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}
    >
      <div
        className="modal-content"
        style={{
          position: 'relative',
          width: width || '520px',
          maxWidth: '90vw',
          maxHeight: '85vh',
          backgroundColor: '#f7f1e8',
          borderRadius: '16px',
          border: '2px solid #ddd2c0',
          boxShadow: '0 16px 64px rgba(74,55,40,0.25)',
          display: 'flex', flexDirection: 'column',
          overflow: 'hidden',
          fontFamily: "'Nunito', system-ui, sans-serif",
        }}
      >
        <div style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          padding: '18px 24px',
          borderBottom: '1px solid #ddd2c0',
          background: 'linear-gradient(180deg, #faf6ef 0%, #f7f1e8 100%)',
        }}>
          <div style={{ fontSize: '18px', fontWeight: '700', color: '#4a3728' }}>{title}</div>
          <button
            onClick={onClose}
            className="tb-btn"
            style={{
              width: '32px', height: '32px',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              borderRadius: '50%', border: '1.5px solid #ddd2c0',
              backgroundColor: '#fff', cursor: 'pointer',
              color: '#a09080', fontSize: '18px', fontWeight: '700',
              transition: 'all 0.15s ease',
            }}
          >&times;</button>
        </div>
        <div style={{ padding: '20px 24px', overflowY: 'auto', flex: 1 }}>
          {children}
        </div>
      </div>
    </div>,
    document.body
  );
}

// ============================================================================
// SAVE MODAL COMPONENT
// ============================================================================
function SaveModal({ isOpen, onClose, farmType, season, placedItems, farmName, captureThumbnail, currentSaveId, currentSaveName, onSaved }) {
  const [name, setName] = React.useState('');
  const [thumbnail, setThumbnail] = React.useState(null);
  const [saving, setSaving] = React.useState(false);
  const [savedId, setSavedId] = React.useState(null);
  const [linkCopied, setLinkCopied] = React.useState(false);

  React.useEffect(() => {
    if (isOpen) {
      setName(currentSaveName || (farmName + ' - ' + new Date().toLocaleDateString()));
      setThumbnail(captureThumbnail());
      setSaving(false);
      setSavedId(null);
      setLinkCopied(false);
    }
  }, [isOpen]);

  const buildSaveData = () => ({
    ...(currentSaveId ? { id: currentSaveId } : {}),
    name,
    farmType,
    season,
    items: placedItems,
    thumbnail,
    savedAt: new Date().toISOString(),
  });

  const getShareUrl = (id) => `${window.location.origin}${window.location.pathname}?save=${id}`;

  const handleSaveToServer = async () => {
    if (!name.trim()) return;
    setSaving(true);
    const data = buildSaveData();
    const result = await saveToServer(data);
    setSaving(false);
    if (result) {
      onSaved(result.id, name);
      setSavedId(result.id);
    }
  };

  const handleCopyLink = () => {
    if (!savedId) return;
    navigator.clipboard.writeText(getShareUrl(savedId)).then(() => {
      setLinkCopied(true);
      setTimeout(() => setLinkCopied(false), 2000);
    });
  };

  const handleDownload = () => {
    if (!name.trim()) return;
    const data = buildSaveData();
    const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = (name || 'farm').replace(/[^a-zA-Z0-9_-]/g, '_') + '.json';
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
    onClose();
  };

  const btnBase = {
    flex: 1, padding: '12px 16px', borderRadius: '10px',
    fontSize: '14px', fontWeight: '700', cursor: 'pointer',
    fontFamily: "'Nunito', system-ui, sans-serif",
    transition: 'all 0.15s ease',
  };

  return (
    <Modal isOpen={isOpen} onClose={onClose} title="Save Farm" width="480px">
      {thumbnail && (
        <div style={{ textAlign: 'center', marginBottom: '16px' }}>
          <img
            src={thumbnail}
            style={{
              maxWidth: '100%', maxHeight: '200px',
              borderRadius: '8px', border: '1.5px solid #ddd2c0',
              boxShadow: '0 2px 8px rgba(74,55,40,0.1)',
            }}
          />
        </div>
      )}
      <div style={{ marginBottom: '16px' }}>
        <label style={{ display: 'block', fontSize: '13px', fontWeight: '700', color: '#6a5a45', marginBottom: '6px' }}>
          Farm Name
        </label>
        <input
          type="text"
          value={name}
          onChange={(e) => setName(e.target.value)}
          onKeyDown={(e) => { if (e.key === 'Enter') handleSaveToServer(); }}
          style={{
            width: '100%', padding: '10px 14px', borderRadius: '10px',
            border: '1.5px solid #ddd2c0', backgroundColor: '#fff',
            fontSize: '14px', fontFamily: "'Nunito', system-ui, sans-serif",
            color: '#4a3728', outline: 'none', boxSizing: 'border-box',
          }}
        />
      </div>
      {savedId ? (
        <div>
          <div style={{ fontSize: '13px', color: '#6a5a45', marginBottom: '10px', lineHeight: '1.5' }}>
            Farm saved! Bookmark or share this link to access your farm from any browser.
          </div>
          <div style={{
            display: 'flex', alignItems: 'center', gap: '8px',
            padding: '10px 12px',
            borderRadius: '10px',
            backgroundColor: '#f5f9f5', border: '1.5px solid #c4dfc0',
            marginBottom: '12px',
          }}>
            <span style={{ fontSize: '12px', color: '#4a7a40', fontWeight: '600', flex: 1, wordBreak: 'break-all' }}>
              {getShareUrl(savedId)}
            </span>
            <button
              onClick={handleCopyLink}
              title="Copy link"
              style={{
                padding: '4px 10px', border: '1.5px solid #c4dfc0',
                borderRadius: '6px',
                backgroundColor: linkCopied ? '#e8f5e8' : '#fff',
                color: linkCopied ? '#4a7a40' : '#6a5a45',
                fontSize: '11px', fontWeight: '700', cursor: 'pointer',
                fontFamily: "'Nunito', system-ui, sans-serif",
                transition: 'all 0.15s ease', whiteSpace: 'nowrap',
                flexShrink: 0,
              }}
            >{linkCopied ? 'Copied!' : 'Copy'}</button>
          </div>
          <button
            className="tb-btn"
            onClick={onClose}
            style={{
              ...btnBase, width: '100%',
              border: '1.5px solid #c4b8a4', backgroundColor: '#fff', color: '#6a5a45',
            }}
          >
            Done
          </button>
        </div>
      ) : (
        <div style={{ display: 'flex', gap: '10px' }}>
          <button
            className="tb-btn"
            onClick={handleSaveToServer}
            disabled={saving || !name.trim()}
            style={{
              ...btnBase,
              border: '1.5px solid #4a8aa5', backgroundColor: '#5a95b0', color: '#fff',
              opacity: saving || !name.trim() ? 0.6 : 1,
              cursor: saving ? 'wait' : 'pointer',
            }}
          >
            {saving ? 'Saving...' : (currentSaveId ? 'Update Save' : 'Save to Server')}
          </button>
          <button
            className="tb-btn"
            onClick={handleDownload}
            disabled={!name.trim()}
            style={{
              ...btnBase,
              border: '1.5px solid #5ea04e', backgroundColor: '#6aaa5a', color: '#fff',
              opacity: !name.trim() ? 0.6 : 1,
            }}
          >
            Download to Computer
          </button>
        </div>
      )}
    </Modal>
  );
}

// ============================================================================
// LOAD MODAL COMPONENT
// ============================================================================
function LoadModal({ isOpen, onClose, onLoad, onNewSave }) {
  const [saves, setSaves] = React.useState([]);
  const [loading, setLoading] = React.useState(false);

  React.useEffect(() => {
    if (isOpen) {
      setLoading(true);
      fetchSaves().then(data => {
        setSaves(data);
        setLoading(false);
      });
    }
  }, [isOpen]);

  const handleDelete = async (id, e) => {
    e.stopPropagation();
    const success = await deleteFromServer(id);
    if (success) {
      setSaves(prev => prev.filter(s => s.id !== id));
    }
  };

  const handleLoad = async (save) => {
    try {
      const res = await fetch(`/api/saves/${save.id}`);
      if (!res.ok) throw new Error('Failed to load');
      const fullData = await res.json();
      onLoad(fullData);
      onClose();
    } catch (e) {
      console.error('Failed to load save:', e);
    }
  };

  const handleImport = () => {
    const input = document.createElement('input');
    input.type = 'file';
    input.accept = '.json';
    input.onchange = (ev) => {
      const file = ev.target.files[0];
      if (!file) return;
      const reader = new FileReader();
      reader.onload = (re) => {
        try {
          const data = JSON.parse(re.target.result);
          onLoad(data);
          onClose();
        } catch (err) {
          alert('Invalid save file');
        }
      };
      reader.readAsText(file);
    };
    input.click();
  };

  const [hoveredCard, setHoveredCard] = React.useState(null);
  const [copiedId, setCopiedId] = React.useState(null);

  const handleCopyLink = (id, e) => {
    e.stopPropagation();
    const url = `${window.location.origin}${window.location.pathname}?save=${id}`;
    navigator.clipboard.writeText(url).then(() => {
      setCopiedId(id);
      setTimeout(() => setCopiedId(null), 2000);
    });
  };

  return (
    <Modal isOpen={isOpen} onClose={onClose} title="Load Farm" width="640px">
      <div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: '16px' }}>
        <button
          onClick={handleImport}
          onMouseEnter={(e) => { e.currentTarget.style.backgroundColor = '#f5efe5'; e.currentTarget.style.borderColor = '#c4b8a4'; }}
          onMouseLeave={(e) => { e.currentTarget.style.backgroundColor = '#fff'; e.currentTarget.style.borderColor = '#ddd2c0'; }}
          style={{
            padding: '8px 16px', borderRadius: '8px',
            border: '1.5px solid #ddd2c0', backgroundColor: '#fff',
            color: '#6a5a45', fontSize: '13px', fontWeight: '600',
            cursor: 'pointer', fontFamily: "'Nunito', system-ui, sans-serif",
            transition: 'all 0.15s ease',
          }}
        >
          Import from File
        </button>
      </div>

      {loading && (
        <div style={{ textAlign: 'center', padding: '40px 0', color: '#a09080' }}>
          Loading saves...
        </div>
      )}

      {!loading && (
        <div style={{
          display: 'grid',
          gridTemplateColumns: 'repeat(auto-fill, minmax(160px, 1fr))',
          gap: '14px',
        }}>
          {saves.map(save => (
            <div
              key={save.id}
              onClick={() => handleLoad(save)}
              onMouseEnter={() => setHoveredCard(save.id)}
              onMouseLeave={() => setHoveredCard(null)}
              style={{
                position: 'relative',
                backgroundColor: '#fff',
                borderRadius: '12px',
                border: hoveredCard === save.id ? '1.5px solid #c4b8a4' : '1.5px solid #ddd2c0',
                padding: '8px',
                cursor: 'pointer',
                transition: 'all 0.15s ease',
                boxShadow: hoveredCard === save.id ? '0 4px 14px rgba(74,55,40,0.13)' : '0 2px 8px rgba(74,55,40,0.06)',
                transform: hoveredCard === save.id ? 'translateY(-2px)' : 'none',
              }}
            >
              <div style={{ position: 'absolute', top: '4px', right: '4px', display: 'flex', gap: '3px', zIndex: 1 }}>
                <button
                  onClick={(e) => handleCopyLink(save.id, e)}
                  title="Copy share link"
                  style={{
                    width: '22px', height: '22px', borderRadius: '50%',
                    border: '1px solid #ddd2c0', backgroundColor: '#fff',
                    color: copiedId === save.id ? '#5a95b0' : '#8a7a65', fontSize: '11px', fontWeight: '700',
                    cursor: 'pointer', display: 'flex', alignItems: 'center',
                    justifyContent: 'center',
                    transition: 'all 0.15s ease',
                  }}
                >{copiedId === save.id ? '\u2713' : '\u{1F517}'}</button>
                <button
                  onClick={(e) => handleDelete(save.id, e)}
                  style={{
                    width: '22px', height: '22px', borderRadius: '50%',
                    border: '1px solid #ddd2c0', backgroundColor: '#fff',
                    color: '#c06060', fontSize: '12px', fontWeight: '700',
                    cursor: 'pointer', display: 'flex', alignItems: 'center',
                    justifyContent: 'center',
                    transition: 'all 0.15s ease',
                  }}
                >&times;</button>
              </div>
              <div style={{
                width: '100%', aspectRatio: '4 / 3',
                backgroundColor: '#ede4d5', borderRadius: '8px',
                overflow: 'hidden', marginBottom: '8px',
              }}>
                <img
                  src={`/api/saves/${save.id}/thumbnail`}
                  style={{ width: '100%', height: '100%', objectFit: 'cover' }}
                  onError={(e) => { e.target.style.display = 'none'; }}
                />
              </div>
              <div style={{
                fontSize: '13px', fontWeight: '700', color: '#4a3728',
                whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
              }}>{save.name}</div>
              <div style={{ fontSize: '11px', color: '#a09080', marginTop: '2px' }}>
                {new Date(save.savedAt).toLocaleDateString()} &middot; {save.itemCount} items
              </div>
            </div>
          ))}

          <div
            onClick={() => { onClose(); onNewSave(); }}
            onMouseEnter={() => setHoveredCard('new')}
            onMouseLeave={() => setHoveredCard(null)}
            style={{
              display: 'flex', flexDirection: 'column',
              alignItems: 'center', justifyContent: 'center',
              backgroundColor: hoveredCard === 'new' ? '#faf6f0' : '#fff',
              borderRadius: '12px',
              border: hoveredCard === 'new' ? '2px dashed #c4b8a4' : '2px dashed #ddd2c0',
              padding: '8px',
              cursor: 'pointer', minHeight: '140px',
              transition: 'all 0.15s ease',
              color: hoveredCard === 'new' ? '#8a7a65' : '#b0a090',
              transform: hoveredCard === 'new' ? 'translateY(-2px)' : 'none',
              boxShadow: hoveredCard === 'new' ? '0 4px 14px rgba(74,55,40,0.1)' : 'none',
            }}
          >
            <div style={{ fontSize: '36px', fontWeight: '300', lineHeight: 1 }}>+</div>
            <div style={{ fontSize: '12px', fontWeight: '600', marginTop: '8px' }}>New Save</div>
          </div>
        </div>
      )}

      {!loading && saves.length === 0 && (
        <div style={{ textAlign: 'center', padding: '20px 0 0', color: '#a09080', fontSize: '14px' }}>
          No saved farms yet. Create your first save!
        </div>
      )}
    </Modal>
  );
}

// ============================================================================
// VIEW ALL MODAL COMPONENT
// ============================================================================
function ViewAllModal({ isOpen, onClose, assetCatalog, favorites, toggleFavorite, onSelectAsset }) {
  const [filter, setFilter] = React.useState('all');
  const [search, setSearch] = React.useState('');
  const [hoveredHeart, setHoveredHeart] = React.useState(null);

  const allCategories = ['all', 'favorites', ...Object.keys(assetCatalog)];

  const filtered = React.useMemo(() => {
    let items;
    if (filter === 'favorites') {
      items = Object.values(assetCatalog).flat().filter(a => favorites.includes(a.id));
    } else if (filter === 'all') {
      items = Object.values(assetCatalog).flat();
    } else {
      items = assetCatalog[filter] || [];
    }
    if (search.trim()) {
      const q = search.trim().toLowerCase();
      items = items.filter(a => a.name.toLowerCase().includes(q));
    }
    return items;
  }, [filter, search, assetCatalog, favorites]);

  const tabStyle = {
    padding: '5px 12px', borderRadius: '20px', border: '1.5px solid #ddd2c0',
    backgroundColor: '#fff', color: '#6a5a45', fontSize: '12px', fontWeight: '600',
    cursor: 'pointer', fontFamily: "'Nunito', system-ui, sans-serif",
    transition: 'all 0.15s ease', whiteSpace: 'nowrap',
  };
  const tabActiveStyle = { backgroundColor: '#5a944e', border: '1.5px solid #5a944e', color: '#fff' };

  return (
    <Modal isOpen={isOpen} onClose={onClose} title="All Assets" width="720px">
      <div style={{ position: 'relative', marginBottom: '12px' }}>
        <input
          type="text"
          placeholder="Search assets..."
          value={search}
          onChange={(e) => setSearch(e.target.value)}
          style={{
            width: '100%', padding: '8px 14px', borderRadius: '10px',
            border: '1.5px solid #ddd2c0', backgroundColor: '#fff',
            fontSize: '13px', fontFamily: "'Nunito', system-ui, sans-serif",
            color: '#4a3728', outline: 'none', boxSizing: 'border-box',
          }}
        />
      </div>

      <div style={{ display: 'flex', flexWrap: 'wrap', gap: '6px', marginBottom: '14px' }}>
        {allCategories.map(cat => (
          <button
            key={cat}
            onClick={() => setFilter(cat)}
            style={{
              ...tabStyle,
              ...(filter === cat ? tabActiveStyle : {}),
            }}
          >
            {cat === 'favorites' ? '\u2665 Favorites' : cat.replace('_', ' & ').replace(/\b\w/g, c => c.toUpperCase())}
          </button>
        ))}
      </div>

      <div style={{
        display: 'grid',
        gridTemplateColumns: 'repeat(auto-fill, minmax(90px, 1fr))',
        gap: '10px',
        maxHeight: '55vh',
        overflowY: 'auto',
        padding: '2px',
      }}>
        {filtered.map(asset => {
          const isFav = favorites.includes(asset.id);
          return (
            <div
              key={asset.id}
              onClick={() => { onSelectAsset(asset); onClose(); }}
              style={{
                position: 'relative',
                display: 'flex', flexDirection: 'column', alignItems: 'center',
                padding: '8px 6px 6px', backgroundColor: '#fff', borderRadius: '10px',
                border: '1.5px solid #e8dfd0', cursor: 'pointer',
                boxShadow: '0 1px 3px rgba(74,55,40,0.05)',
                transition: 'all 0.15s ease',
              }}
              onMouseEnter={(e) => { e.currentTarget.style.borderColor = '#c4b8a4'; e.currentTarget.style.boxShadow = '0 3px 10px rgba(74,55,40,0.1)'; e.currentTarget.style.transform = 'translateY(-1px)'; }}
              onMouseLeave={(e) => { e.currentTarget.style.borderColor = '#e8dfd0'; e.currentTarget.style.boxShadow = '0 1px 3px rgba(74,55,40,0.05)'; e.currentTarget.style.transform = 'none'; }}
              title={`${asset.name}\nSize: ${asset.footprint?.[0]}x${asset.footprint?.[1]} tiles`}
            >
              <div
                onClick={(e) => toggleFavorite(asset.id, e)}
                onMouseEnter={(e) => { setHoveredHeart(asset.id); e.currentTarget.style.transform = 'scale(1.2)'; }}
                onMouseLeave={(e) => { setHoveredHeart(null); e.currentTarget.style.transform = 'scale(1)'; }}
                style={{
                  position: 'absolute', top: '4px', right: '4px',
                  width: '18px', height: '18px', display: 'flex',
                  alignItems: 'center', justifyContent: 'center',
                  cursor: 'pointer', transition: 'transform 0.15s ease',
                  zIndex: 1, borderRadius: '50%',
                  opacity: isFav || hoveredHeart === asset.id ? 1 : 0.4,
                }}
              >
                <svg width="14" height="14" viewBox="0 0 24 24">
                  <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"
                    fill={isFav ? '#e05555' : 'none'}
                    stroke={isFav ? '#e05555' : '#a09080'}
                    strokeWidth="2"
                  />
                </svg>
              </div>
              {asset.sourceRect && !asset.icon ? (
                <CroppedThumbnail src={asset.src} sourceRect={asset.sourceRect} style={{ width: '42px', height: '42px', objectFit: 'contain', imageRendering: 'pixelated' }} />
              ) : (
                <img
                  src={asset.icon || asset.src}
                  alt={asset.name}
                  style={{ width: '42px', height: '42px', objectFit: 'contain', imageRendering: 'pixelated' }}
                  onError={(e) => { e.target.style.display = 'none'; }}
                />
              )}
              <div style={{ fontSize: '10px', color: '#9a8a75', textAlign: 'center', marginTop: '4px', lineHeight: 1.1, wordBreak: 'break-word' }}>
                {asset.name}
              </div>
            </div>
          );
        })}
        {filtered.length === 0 && (
          <div style={{ gridColumn: '1 / -1', textAlign: 'center', padding: '30px 0', color: '#a09080', fontSize: '14px' }}>
            {filter === 'favorites' ? 'No favorites yet. Click the heart icon on any asset to add it.' : 'No assets found.'}
          </div>
        )}
      </div>
    </Modal>
  );
}

// ============================================================================
// MAIN COMPONENT
// ============================================================================
function getSessionState() {
  try {
    const raw = localStorage.getItem('stardew_session');
    if (!raw) return null;
    const session = JSON.parse(raw);
    if (Date.now() - session.timestamp > 24 * 60 * 60 * 1000) {
      localStorage.removeItem('stardew_session');
      return null;
    }
    return session;
  } catch (e) { return null; }
}

// ============================================================================
// FEEDBACK MODAL COMPONENT
// ============================================================================
function FeedbackModal({ isOpen, onClose }) {
  const [feedbackType, setFeedbackType] = useState('bug');
  const [subject, setSubject] = useState('');
  const [description, setDescription] = useState('');
  const [submitted, setSubmitted] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState(null);

  const handleSubmit = async () => {
    setSubmitting(true);
    setError(null);
    try {
      const res = await fetch('/api/feedback', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ type: feedbackType, subject, description }),
      });
      if (!res.ok) throw new Error('Failed to submit');
      setSubmitted(true);
    } catch (e) {
      setError('Something went wrong. Please try again.');
    } finally {
      setSubmitting(false);
    }
  };

  const handleClose = () => {
    setSubmitted(false);
    setError(null);
    setFeedbackType('bug');
    setSubject('');
    setDescription('');
    onClose();
  };

  const inputStyle = {
    width: '100%', padding: '10px 14px', borderRadius: '10px',
    border: '1.5px solid #ddd2c0', backgroundColor: '#fff',
    fontSize: '14px', fontFamily: "'Nunito', system-ui, sans-serif",
    color: '#4a3728', outline: 'none', boxSizing: 'border-box',
  };

  return (
    <Modal isOpen={isOpen} onClose={handleClose} title="Send Feedback" width="440px">
      {submitting ? (
        <div style={{ textAlign: 'center', padding: '40px 0' }}>
          <div style={{
            width: '36px', height: '36px', margin: '0 auto 16px',
            border: '3px solid #e8dfd0', borderTopColor: '#5a95b0', borderRadius: '50%',
            animation: 'spin 0.8s linear infinite',
          }} />
          <div style={{ fontSize: '15px', fontWeight: '700', color: '#4a3728' }}>Sending your feedback...</div>
          <div style={{ fontSize: '13px', color: '#9a8a75', marginTop: '6px' }}>This will only take a moment.</div>
        </div>
      ) : submitted ? (
        <div style={{ textAlign: 'center', padding: '24px 0' }}>
          <div style={{ fontSize: '36px', marginBottom: '12px', color: '#5a944e' }}>&#10003;</div>
          <div style={{ fontSize: '18px', fontWeight: '700', color: '#4a3728', marginBottom: '8px' }}>Thank you for your feedback!</div>
          <div style={{ fontSize: '13px', color: '#9a8a75', marginBottom: '20px' }}>We've received your message and will review it shortly.</div>
          <button
            className="tb-btn"
            onClick={handleClose}
            style={{
              padding: '10px 28px', borderRadius: '10px',
              border: '1.5px solid #4a8aa5', backgroundColor: '#5a95b0', color: '#fff',
              fontSize: '14px', fontWeight: '700', cursor: 'pointer',
              fontFamily: "'Nunito', system-ui, sans-serif",
            }}
          >Close</button>
        </div>
      ) : (
        <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
          <div>
            <label style={{ display: 'block', fontSize: '13px', fontWeight: '700', color: '#6a5a45', marginBottom: '6px' }}>
              Feedback Type
            </label>
            <select
              value={feedbackType}
              onChange={(e) => setFeedbackType(e.target.value)}
              style={{ ...inputStyle, cursor: 'pointer' }}
            >
              <option value="bug">Report a Bug</option>
              <option value="feature">Request a Feature</option>
            </select>
          </div>
          <div>
            <label style={{ display: 'block', fontSize: '13px', fontWeight: '700', color: '#6a5a45', marginBottom: '6px' }}>
              Subject
            </label>
            <input
              type="text"
              value={subject}
              onChange={(e) => setSubject(e.target.value)}
              placeholder="Brief summary..."
              style={inputStyle}
            />
          </div>
          <div>
            <label style={{ display: 'block', fontSize: '13px', fontWeight: '700', color: '#6a5a45', marginBottom: '6px' }}>
              Description
            </label>
            <textarea
              value={description}
              onChange={(e) => setDescription(e.target.value)}
              placeholder="Tell us more..."
              rows={5}
              style={{ ...inputStyle, resize: 'vertical', minHeight: '100px' }}
            />
          </div>
          {error && (
            <div style={{ color: '#c06060', fontSize: '13px', fontWeight: '600', textAlign: 'center' }}>{error}</div>
          )}
          <button
            className="tb-btn"
            onClick={handleSubmit}
            disabled={submitting || !subject.trim() || !description.trim()}
            style={{
              padding: '10px 20px', borderRadius: '10px',
              border: '1.5px solid #4a8aa5', backgroundColor: '#5a95b0', color: '#fff',
              fontSize: '14px', fontWeight: '700',
              cursor: submitting || !subject.trim() || !description.trim() ? 'default' : 'pointer',
              fontFamily: "'Nunito', system-ui, sans-serif",
              opacity: submitting || !subject.trim() || !description.trim() ? 0.5 : 1,
              transition: 'opacity 0.2s ease',
            }}
          >{submitting ? 'Submitting...' : 'Submit Feedback'}</button>
        </div>
      )}
    </Modal>
  );
}

function StardewFarmPlanner() {
  const session = useMemo(() => getSessionState(), []);
  const [farmType, setFarmType] = useState(session?.farmType || 'standard');
  const [season, setSeason] = useState(session?.season || 'spring');
  const [placedItems, setPlacedItems] = useState(session?.placedItems || []);
  const [selectedAsset, setSelectedAsset] = useState(null);
  const [selectedCategory, setSelectedCategory] = useState('buildings');
  const [selectedSubcategory, setSelectedSubcategory] = useState(null);
  const [searchQuery, setSearchQuery] = useState('');
  const [zoom, setZoom] = useState(100);
  const [showGrid, setShowGrid] = useState(false);
  const [showSprinklerRange, setShowSprinklerRange] = useState(true);
  const [tool, setTool] = useState('select');
  const [history, setHistory] = useState(session?.placedItems?.length ? [[...session.placedItems]] : [[]]);
  const [historyIndex, setHistoryIndex] = useState(0);
  const [hoveredTile, setHoveredTile] = useState(null);
  const [pan, setPan] = useState({ x: 0, y: 0 });
  const [isPanning, setIsPanning] = useState(false);
  const [panStart, setPanStart] = useState({ x: 0, y: 0 });
  const [farmBackground, setFarmBackground] = useState(null);
  const [selectedItems, setSelectedItems] = useState([]);
  const [selectionRect, setSelectionRect] = useState(null);
  const [isMovingSelection, setIsMovingSelection] = useState(false);
  const [moveStart, setMoveStart] = useState(null);
  const [moveOffset, setMoveOffset] = useState({ x: 0, y: 0 });

  const [isDragPlacing, setIsDragPlacing] = useState(false);
  const [tileMode, setTileMode] = useState('free'); // 'free', 'line', 'rect'
  const [tileDragStart, setTileDragStart] = useState(null);
  const [tileDragEnd, setTileDragEnd] = useState(null);
  const dragPlacedTilesRef = useRef(new Set());
  const dragPlaceItemsRef = useRef([]);

  const [windowSize, setWindowSize] = useState({ w: window.innerWidth, h: window.innerHeight });
  const [showSaveModal, setShowSaveModal] = useState(false);
  const [showLoadModal, setShowLoadModal] = useState(false);
  const [showClearConfirm, setShowClearConfirm] = useState(false);
  const [currentSaveId, setCurrentSaveId] = useState(null);
  const [currentSaveName, setCurrentSaveName] = useState(null);
  const [containerSize, setContainerSize] = useState({ width: 0, height: 0 });
  const [favorites, setFavorites] = useState(() => {
    try { return JSON.parse(localStorage.getItem('stardew_favorites') || '[]'); } catch { return []; }
  });
  const [showViewAll, setShowViewAll] = useState(false);
  const [showControls, setShowControls] = useState(false);
  const [controlsPinned, setControlsPinned] = useState(false);
  const [showFeedback, setShowFeedback] = useState(false);
  const controlsBtnRef = useRef(null);
  const controlsCardRef = useRef(null);

  const canvasRef = useRef(null);
  const containerRef = useRef(null);
  const isDraggingRef = useRef(false);
  const mouseDownPosRef = useRef(null);
  const selectionStartRef = useRef(null);
  const imageCacheRef = useRef({});

  const farm = FARM_TYPES[farmType];
  const TILE_SIZE = 16;

  // Compute scale factor so zoom=100 means the map fits the viewport exactly
  const fitScale = useMemo(() => {
    if (!containerSize.width || !containerSize.height) return 1;
    const mapW = farm.width * TILE_SIZE;
    const mapH = farm.height * TILE_SIZE;
    return Math.min(containerSize.width / mapW, containerSize.height / mapH);
  }, [containerSize.width, containerSize.height, farm.width, farm.height]);

  const scaledTile = TILE_SIZE * fitScale * (zoom / 100);

  const assetCatalog = useMemo(() => createAssetCatalog(season), [season]);
  const allAssets = useMemo(() =>
    Object.values(assetCatalog).flat(),
    [assetCatalog]
  );

  // Load farm background when farm type or season changes
  useEffect(() => {
    let cancelled = false;
    setFarmBackground(null);
    loadFarmBackground(farmType, season).then(img => {
      if (!cancelled) setFarmBackground(img);
    }).catch(err => {
      console.error('Failed to load farm background:', err);
    });
    return () => { cancelled = true; };
  }, [farmType, season]);

  // Load shared save from URL (?save=<uuid>)
  useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const shareId = params.get('save');
    if (!shareId) return;
    // Clear the ?save= param from URL without reload
    window.history.replaceState({}, '', window.location.pathname);
    (async () => {
      try {
        const res = await fetch(`/api/saves/${encodeURIComponent(shareId)}`);
        if (!res.ok) return;
        const data = await res.json();
        addSavedId(shareId);
        loadFarm(data);
      } catch (e) {
        console.error('Failed to load shared save:', e);
      }
    })();
  }, []);

  // Track viewport size for responsive scaling
  useEffect(() => {
    const onResize = () => setWindowSize({ w: window.innerWidth, h: window.innerHeight });
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);

  // Persist favorites to localStorage
  useEffect(() => {
    try { localStorage.setItem('stardew_favorites', JSON.stringify(favorites)); } catch {}
  }, [favorites]);

  const toggleFavorite = useCallback((assetId, e) => {
    if (e) { e.stopPropagation(); e.preventDefault(); }
    setFavorites(prev => prev.includes(assetId) ? prev.filter(id => id !== assetId) : [...prev, assetId]);
  }, []);

  // Auto-save session to localStorage (persists across refreshes for 24h)
  useEffect(() => {
    try {
      localStorage.setItem('stardew_session', JSON.stringify({
        farmType, season, placedItems, timestamp: Date.now(),
      }));
    } catch (e) { /* quota exceeded — ignore */ }
  }, [farmType, season, placedItems]);

  // Responsive scale factors (reference: 1920x1080)
  const vScale = Math.max(0.7, Math.min(1, windowSize.h / 900));
  const hScale = Math.max(0.75, Math.min(1, windowSize.w / 1400));
  const sidebarWidth = Math.round(Math.max(250, 310 * hScale));

  const saveToHistory = useCallback((newItems) => {
    const newHistory = history.slice(0, historyIndex + 1);
    newHistory.push([...newItems]);
    setHistory(newHistory);
    setHistoryIndex(newHistory.length - 1);
  }, [history, historyIndex]);

  const undo = useCallback(() => {
    if (historyIndex > 0) {
      setHistoryIndex(historyIndex - 1);
      setPlacedItems([...history[historyIndex - 1]]);
    }
  }, [history, historyIndex]);

  const redo = useCallback(() => {
    if (historyIndex < history.length - 1) {
      setHistoryIndex(historyIndex + 1);
      setPlacedItems([...history[historyIndex + 1]]);
    }
  }, [history, historyIndex]);

  const getItemAtTile = useCallback((tile) => {
    for (let i = placedItems.length - 1; i >= 0; i--) {
      const item = placedItems[i];
      const asset = allAssets.find(a => a.id === item.assetId);
      if (!asset?.footprint) continue;
      const [w, h] = asset.footprint;
      if (tile.x >= item.x && tile.x < item.x + w && tile.y >= item.y && tile.y < item.y + h) {
        return item;
      }
    }
    return null;
  }, [placedItems, allAssets]);

  useEffect(() => {
    const handleKeyDown = (e) => {
      if (e.key === 'v' || e.key === 'V') setTool('select');
      if (e.key === 'm' || e.key === 'M') setTool('multi_select');
      if (e.key === 'x' || e.key === 'X') setTool('delete');
      if (e.key === 'g' || e.key === 'G') setShowGrid(g => !g);
      if (e.key === 'r' || e.key === 'R') setShowSprinklerRange(r => !r);
      if (e.key === 'Escape') {
        if (showClearConfirm) { setShowClearConfirm(false); return; }
        if (showSaveModal) { setShowSaveModal(false); return; }
        if (showLoadModal) { setShowLoadModal(false); return; }
        if (showViewAll) { setShowViewAll(false); return; }
        setSelectedAsset(null);
        setSelectedItems([]);
        setSelectionRect(null);
      }
      if ((e.key === 'Backspace' || e.key === 'Delete') && selectedItems.length > 0) {
        e.preventDefault();
        const newItems = placedItems.filter(item => !selectedItems.includes(item.id));
        setPlacedItems(newItems);
        saveToHistory(newItems);
        setSelectedItems([]);
      }
      if ((e.ctrlKey || e.metaKey) && e.key === 'z') {
        e.preventDefault();
        if (e.shiftKey) redo();
        else undo();
      }
      if ((e.ctrlKey || e.metaKey) && e.key === 'y') {
        e.preventDefault();
        redo();
      }
    };
    window.addEventListener('keydown', handleKeyDown);
    return () => window.removeEventListener('keydown', handleKeyDown);
  }, [undo, redo, selectedItems, placedItems, saveToHistory, showClearConfirm, showSaveModal, showLoadModal, showViewAll]);

  // Close controls card on outside click (when not pinned)
  useEffect(() => {
    if (!showControls || controlsPinned) return;
    const handleClickOutside = (e) => {
      if (controlsBtnRef.current && controlsBtnRef.current.contains(e.target)) return;
      if (controlsCardRef.current && controlsCardRef.current.contains(e.target)) return;
      setShowControls(false);
    };
    document.addEventListener('mousedown', handleClickOutside);
    return () => document.removeEventListener('mousedown', handleClickOutside);
  }, [showControls, controlsPinned]);

  const getTileFromEvent = useCallback((e) => {
    const canvas = canvasRef.current;
    if (!canvas) return null;
    const rect = canvas.getBoundingClientRect();
    const x = Math.floor((e.clientX - rect.left) / scaledTile);
    const y = Math.floor((e.clientY - rect.top) / scaledTile);
    if (x < 0 || x >= farm.width || y < 0 || y >= farm.height) return null;
    return { x, y };
  }, [scaledTile, farm]);

  const isValidPlacement = useCallback((asset, x, y) => {
    if (!asset?.footprint) return false;
    const [w, h] = asset.footprint;

    if (x < 0 || y < 0 || x + w > farm.width || y + h > farm.height) {
      return false;
    }

    // Terrain collision check
    const collisionMap = COLLISION_MAPS[farmType];
    if (collisionMap) {
      for (let ty = y; ty < y + h; ty++) {
        for (let tx = x; tx < x + w; tx++) {
          if (collisionMap[ty * farm.width + tx]) return false;
        }
      }
    }

    for (const item of placedItems) {
      // Tileable items can overlap same-type at same position (will replace)
      if (TILEABLE_IDS.has(asset.id) && item.assetId === asset.id && item.x === x && item.y === y) continue;
      // Tileable items can replace other tileable items at same position
      if (TILEABLE_IDS.has(asset.id) && TILEABLE_IDS.has(item.assetId) && item.x === x && item.y === y) continue;

      const itemAsset = allAssets.find(a => a.id === item.assetId);
      if (!itemAsset?.footprint) continue;
      const [iw, ih] = itemAsset.footprint;

      if (x < item.x + iw && x + w > item.x &&
          y < item.y + ih && y + h > item.y) {
        return false;
      }
    }
    return true;
  }, [farm, farmType, placedItems, allAssets]);

  // Compute tiles for line/rect placement modes
  const computeTileModeTiles = useCallback((start, end, mode, assetId) => {
    if (!start || !end) return [];
    const tiles = [];
    const minX = Math.min(start.x, end.x);
    const maxX = Math.max(start.x, end.x);
    const minY = Math.min(start.y, end.y);
    const maxY = Math.max(start.y, end.y);

    if (mode === 'line') {
      // Snap to dominant axis
      const dx = Math.abs(end.x - start.x);
      const dy = Math.abs(end.y - start.y);
      if (dx >= dy) {
        // Horizontal line
        for (let x = minX; x <= maxX; x++) tiles.push({ x, y: start.y });
      } else {
        // Vertical line
        for (let y = minY; y <= maxY; y++) tiles.push({ x: start.x, y });
      }
    } else if (mode === 'rect') {
      const isFence = FENCE_IDS.has(assetId);
      if (isFence) {
        // Perimeter only for fences
        for (let x = minX; x <= maxX; x++) {
          tiles.push({ x, y: minY });
          if (minY !== maxY) tiles.push({ x, y: maxY });
        }
        for (let y = minY + 1; y < maxY; y++) {
          tiles.push({ x: minX, y });
          if (minX !== maxX) tiles.push({ x: maxX, y });
        }
      } else {
        // Fill for paths/floors
        for (let x = minX; x <= maxX; x++) {
          for (let y = minY; y <= maxY; y++) {
            tiles.push({ x, y });
          }
        }
      }
    }
    return tiles;
  }, []);

  // Place a tileable item, removing any existing tileable at that position
  const placeTileableItem = useCallback((asset, x, y, items) => {
    if (!isValidPlacement(asset, x, y)) return items;
    // Remove any existing tileable item at this position
    const filtered = items.filter(i => !(TILEABLE_IDS.has(i.assetId) && i.x === x && i.y === y));
    return [...filtered, { id: Date.now() + Math.random(), assetId: asset.id, x, y }];
  }, [isValidPlacement]);

  const handleCanvasClick = useCallback((e) => {
    if (isDraggingRef.current) {
      isDraggingRef.current = false;
      return;
    }
    const tile = getTileFromEvent(e);
    if (!tile) return;

    if (tool === 'delete') {
      const item = getItemAtTile(tile);
      if (item) {
        const newItems = placedItems.filter(i => i.id !== item.id);
        setPlacedItems(newItems);
        saveToHistory(newItems);
        setSelectedItems(prev => prev.filter(id => id !== item.id));
      }
    } else if (tool === 'select') {
      if (selectedAsset) {
        // Tileable items use placeTileableItem (handles replacement)
        if (TILEABLE_IDS.has(selectedAsset.id)) {
          const newItems = placeTileableItem(selectedAsset, tile.x, tile.y, placedItems);
          if (newItems !== placedItems) {
            setPlacedItems(newItems);
            saveToHistory(newItems);
          }
        } else if (isValidPlacement(selectedAsset, tile.x, tile.y)) {
          const newItem = { id: Date.now(), assetId: selectedAsset.id, x: tile.x, y: tile.y };
          const newItems = [...placedItems, newItem];
          setPlacedItems(newItems);
          saveToHistory(newItems);
        }
      } else {
        // Select placed item
        const item = getItemAtTile(tile);
        if (item) {
          setSelectedItems([item.id]);
        } else {
          setSelectedItems([]);
        }
      }
    } else if (tool === 'multi_select') {
      const item = getItemAtTile(tile);
      if (item) {
        setSelectedItems(prev =>
          prev.includes(item.id) ? prev.filter(id => id !== item.id) : [...prev, item.id]
        );
      } else {
        setSelectedItems([]);
      }
    }
  }, [tool, selectedAsset, placedItems, getTileFromEvent, isValidPlacement, saveToHistory, getItemAtTile, placeTileableItem]);

  const handleContextMenu = useCallback((e) => {
    e.preventDefault();
    if (selectedAsset) {
      // Drop the currently held item
      setSelectedAsset(null);
    } else {
      // Delete the item under the cursor
      const tile = getTileFromEvent(e);
      if (!tile) return;
      const item = getItemAtTile(tile);
      if (item) {
        const newItems = placedItems.filter(i => i.id !== item.id);
        setPlacedItems(newItems);
        saveToHistory(newItems);
        setSelectedItems(prev => prev.filter(id => id !== item.id));
      }
    }
  }, [selectedAsset, getTileFromEvent, getItemAtTile, placedItems, saveToHistory]);

  const handleMouseMove = useCallback((e) => {
    if (isPanning) {
      setPan({
        x: e.clientX - panStart.x,
        y: e.clientY - panStart.y,
      });
      return;
    }
    // Drag-to-place tileable items
    if (isDragPlacing && selectedAsset && TILEABLE_IDS.has(selectedAsset.id)) {
      const tile = getTileFromEvent(e);
      if (tile) {
        if (tileMode === 'free') {
          const tileKey = `${tile.x},${tile.y}`;
          if (!dragPlacedTilesRef.current.has(tileKey)) {
            dragPlacedTilesRef.current.add(tileKey);
            const newItems = placeTileableItem(selectedAsset, tile.x, tile.y, dragPlaceItemsRef.current);
            dragPlaceItemsRef.current = newItems;
            setPlacedItems(newItems);
          }
        } else {
          // line or rect: update end position for preview
          setTileDragEnd(tile);
        }
        setHoveredTile(tile);
      }
      return;
    }
    // Moving selected items
    if (isMovingSelection && moveStart) {
      const tile = getTileFromEvent(e);
      if (tile) {
        setMoveOffset({ x: tile.x - moveStart.x, y: tile.y - moveStart.y });
      }
      return;
    }
    // Multi-select drag rectangle
    if (selectionStartRef.current && tool === 'multi_select') {
      const tile = getTileFromEvent(e);
      if (tile && !isDraggingRef.current) {
        const dx = e.clientX - mouseDownPosRef.current.x;
        const dy = e.clientY - mouseDownPosRef.current.y;
        if (Math.abs(dx) > 4 || Math.abs(dy) > 4) {
          isDraggingRef.current = true;
        }
      }
      if (tile && isDraggingRef.current) {
        setSelectionRect({
          startX: Math.min(selectionStartRef.current.x, tile.x),
          startY: Math.min(selectionStartRef.current.y, tile.y),
          endX: Math.max(selectionStartRef.current.x, tile.x),
          endY: Math.max(selectionStartRef.current.y, tile.y),
        });
      }
      return;
    }
    // Detect drag start for panning (select/delete tools without special drag)
    if (mouseDownPosRef.current && !isDraggingRef.current && !selectionStartRef.current) {
      const dx = e.clientX - mouseDownPosRef.current.x;
      const dy = e.clientY - mouseDownPosRef.current.y;
      if (Math.abs(dx) > 4 || Math.abs(dy) > 4) {
        isDraggingRef.current = true;
        // Don't start panning when holding an asset — keep the preview following the cursor
        if (!(tool === 'select' && selectedAsset)) {
          setIsPanning(true);
          setPanStart({ x: mouseDownPosRef.current.x - pan.x, y: mouseDownPosRef.current.y - pan.y });
        }
      }
    }
    const tile = getTileFromEvent(e);
    setHoveredTile(tile);
  }, [getTileFromEvent, isPanning, panStart, pan, tool, isMovingSelection, moveStart, isDragPlacing, selectedAsset, placeTileableItem, tileMode]);

  const handleMouseDown = useCallback((e) => {
    if (e.button === 1 || (e.button === 0 && e.altKey)) {
      e.preventDefault();
      setIsPanning(true);
      isDraggingRef.current = true;
      setPanStart({ x: e.clientX - pan.x, y: e.clientY - pan.y });
      return;
    }
    if (e.button === 0) {
      mouseDownPosRef.current = { x: e.clientX, y: e.clientY };
      const tile = getTileFromEvent(e);
      if (!tile) return;

      // Multi-select: if clicking on a selected item, start moving; otherwise start drag rectangle
      if (tool === 'multi_select') {
        if (selectedItems.length > 0) {
          const item = getItemAtTile(tile);
          if (item && selectedItems.includes(item.id)) {
            setIsMovingSelection(true);
            setMoveStart(tile);
            setMoveOffset({ x: 0, y: 0 });
            return;
          }
        }
        selectionStartRef.current = tile;
        return;
      }
      // Select tool with tileable asset: start drag-placing based on tileMode
      if (tool === 'select' && selectedAsset && TILEABLE_IDS.has(selectedAsset.id)) {
        isDraggingRef.current = true;
        if (tileMode === 'free') {
          setIsDragPlacing(true);
          dragPlacedTilesRef.current = new Set();
          const tileKey = `${tile.x},${tile.y}`;
          dragPlacedTilesRef.current.add(tileKey);
          const newItems = placeTileableItem(selectedAsset, tile.x, tile.y, placedItems);
          dragPlaceItemsRef.current = newItems;
          setPlacedItems(newItems);
        } else {
          // line or rect mode: record start, preview during drag
          setTileDragStart(tile);
          setTileDragEnd(tile);
          setIsDragPlacing(true);
        }
        return;
      }
      // Select tool: start moving selected items if clicking on a selected one
      if (tool === 'select' && !selectedAsset && selectedItems.length > 0) {
        const item = getItemAtTile(tile);
        if (item && selectedItems.includes(item.id)) {
          setIsMovingSelection(true);
          setMoveStart(tile);
          setMoveOffset({ x: 0, y: 0 });
          return;
        }
      }
    }
  }, [pan, tool, selectedAsset, selectedItems, getItemAtTile, getTileFromEvent, placedItems, placeTileableItem, tileMode]);

  const handleMouseUp = useCallback(() => {
    // Finish drag-to-place
    if (isDragPlacing) {
      if (tileMode === 'free') {
        saveToHistory(dragPlaceItemsRef.current);
        dragPlacedTilesRef.current = new Set();
        dragPlaceItemsRef.current = [];
      } else if ((tileMode === 'line' || tileMode === 'rect') && tileDragStart && tileDragEnd && selectedAsset) {
        const tiles = computeTileModeTiles(tileDragStart, tileDragEnd, tileMode, selectedAsset.id);
        let items = [...placedItems];
        for (const t of tiles) {
          items = placeTileableItem(selectedAsset, t.x, t.y, items);
        }
        setPlacedItems(items);
        saveToHistory(items);
      }
      setIsDragPlacing(false);
      setTileDragStart(null);
      setTileDragEnd(null);
      isDraggingRef.current = true; // suppress click
    }

    // Finish multi-select drag
    if (selectionStartRef.current && selectionRect && tool === 'multi_select') {
      const { startX, startY, endX, endY } = selectionRect;
      const selected = placedItems.filter(item => {
        const asset = allAssets.find(a => a.id === item.assetId);
        if (!asset?.footprint) return false;
        const [w, h] = asset.footprint;
        return item.x + w > startX && item.x <= endX + 1 &&
               item.y + h > startY && item.y <= endY + 1;
      });
      setSelectedItems(selected.map(i => i.id));
      setSelectionRect(null);
    }
    selectionStartRef.current = null;

    // Finish moving selection
    if (isMovingSelection) {
      if (moveOffset.x !== 0 || moveOffset.y !== 0) {
        const newItems = placedItems.map(item => {
          if (selectedItems.includes(item.id)) {
            return { ...item, x: item.x + moveOffset.x, y: item.y + moveOffset.y };
          }
          return item;
        });
        setPlacedItems(newItems);
        saveToHistory(newItems);
      }
      // Suppress the click event so selection isn't toggled after a move
      isDraggingRef.current = true;
      setIsMovingSelection(false);
      setMoveStart(null);
      setMoveOffset({ x: 0, y: 0 });
    }

    setIsPanning(false);
    mouseDownPosRef.current = null;
  }, [tool, selectionRect, placedItems, allAssets, isMovingSelection, moveOffset, selectedItems, saveToHistory, isDragPlacing, tileMode, tileDragStart, tileDragEnd, selectedAsset, computeTileModeTiles, placeTileableItem]);

  // Measure container size for fit-to-view scaling
  useEffect(() => {
    const el = containerRef.current;
    if (!el) return;
    const ro = new ResizeObserver(entries => {
      const { width, height } = entries[0].contentRect;
      setContainerSize({ width, height });
    });
    ro.observe(el);
    return () => ro.disconnect();
  }, []);

  useEffect(() => {
    const el = containerRef.current;
    if (!el) return;
    const onWheel = (e) => {
      e.preventDefault();
      const delta = e.deltaY > 0 ? -3 : 3;
      setZoom(prev => Math.min(500, Math.max(100, prev + delta)));
    };
    el.addEventListener('wheel', onWheel, { passive: false });
    return () => el.removeEventListener('wheel', onWheel);
  }, []);

  const getSprinklerCoverage = useCallback(() => {
    const covered = new Set();
    placedItems.forEach(item => {
      const range = SPRINKLER_RANGES[item.assetId];
      if (range) {
        range.forEach(([dx, dy]) => {
          const tx = item.x + dx;
          const ty = item.y + dy;
          if (tx >= 0 && tx < farm.width && ty >= 0 && ty < farm.height) {
            covered.add(`${tx},${ty}`);
          }
        });
      }
    });
    return covered;
  }, [placedItems, farm]);

  // Draw canvas (double-buffered with persistent image cache)
  useLayoutEffect(() => {
    let cancelled = false;
    const canvas = canvasRef.current;
    if (!canvas) return;
    const cache = imageCacheRef.current;

    const canvasWidth = farm.width * scaledTile;
    const canvasHeight = farm.height * scaledTile;
    const dpr = window.devicePixelRatio || 1;

    // Create offscreen canvas for double-buffering (at device pixel resolution)
    const offscreen = document.createElement('canvas');
    offscreen.width = canvasWidth * dpr;
    offscreen.height = canvasHeight * dpr;
    const ctx = offscreen.getContext('2d');
    ctx.scale(dpr, dpr);
    ctx.imageSmoothingEnabled = false;

    if (farmBackground) {
      ctx.drawImage(farmBackground, 0, 0, canvasWidth, canvasHeight);
    } else {
      ctx.fillStyle = '#5a8a5a';
      ctx.fillRect(0, 0, canvasWidth, canvasHeight);
    }

    if (showGrid) {
      ctx.strokeStyle = 'rgba(0, 0, 0, 0.07)';
      ctx.lineWidth = 1;
      for (let x = 0; x <= farm.width; x++) {
        ctx.beginPath();
        ctx.moveTo(x * scaledTile, 0);
        ctx.lineTo(x * scaledTile, canvasHeight);
        ctx.stroke();
      }
      for (let y = 0; y <= farm.height; y++) {
        ctx.beginPath();
        ctx.moveTo(0, y * scaledTile);
        ctx.lineTo(canvasWidth, y * scaledTile);
        ctx.stroke();
      }
    }

    if (showSprinklerRange) {
      const coverage = getSprinklerCoverage();
      ctx.fillStyle = 'rgba(100, 149, 237, 0.25)';
      coverage.forEach(key => {
        const [x, y] = key.split(',').map(Number);
        ctx.fillRect(x * scaledTile, y * scaledTile, scaledTile, scaledTile);
      });
    }

    // Collect all image sources needed for this frame
    const neededSrcs = new Set();
    for (const item of placedItems) {
      const asset = allAssets.find(a => a.id === item.assetId);
      if (asset?.src) neededSrcs.add(asset.src);
    }
    if (selectedAsset?.src) neededSrcs.add(selectedAsset.src);

    // Find which images still need loading
    const uncached = [];
    for (const src of neededSrcs) {
      if (!cache[src]) {
        const img = new Image();
        img.src = src;
        cache[src] = img;
        uncached.push(img);
      } else if (!cache[src].complete) {
        uncached.push(cache[src]);
      }
    }

    const drawFrame = () => {
      // Sort items by depth (bottom edge y) so items lower on screen draw on top
      const sortedItems = [...placedItems].sort((a, b) => {
        const assetA = allAssets.find(aa => aa.id === a.assetId);
        const assetB = allAssets.find(bb => bb.id === b.assetId);
        const bottomA = a.y + ((assetA?.footprint || [1,1])[1]);
        const bottomB = b.y + ((assetB?.footprint || [1,1])[1]);
        return bottomA - bottomB;
      });

      // Draw placed items
      for (const item of sortedItems) {
        const asset = allAssets.find(a => a.id === item.assetId);
        if (!asset?.src) continue;
        const img = cache[asset.src];
        if (!img || !img.complete || !img.naturalWidth) continue;

        // Auto-tiled items (fences & paths) use dynamic sourceRect based on neighbors
        if (TILEABLE_IDS.has(item.assetId)) {
          const [sx, sy, sw, sh] = getAutoTileSourceRect(item, placedItems);
          const isFence = FENCE_IDS.has(item.assetId);
          const drawW = scaledTile;
          const drawH = isFence ? scaledTile * 2 : scaledTile;
          const drawX = item.x * scaledTile;
          const drawY = isFence ? (item.y - 1) * scaledTile : item.y * scaledTile;
          ctx.drawImage(img, sx, sy, sw, sh, drawX, drawY, drawW, drawH);
          continue;
        }

        const [fw, fh] = asset.footprint || [1, 1];
        const srcW = asset.sourceRect ? asset.sourceRect[2] : img.naturalWidth;
        const srcH = asset.sourceRect ? asset.sourceRect[3] : img.naturalHeight;
        const drawTilesW = asset.displayTiles ? asset.displayTiles[0] : (srcW / TILE_SIZE);
        const drawTilesH = asset.displayTiles ? asset.displayTiles[1] : (srcH / TILE_SIZE);
        const drawWidth = drawTilesW * scaledTile;
        const drawHeight = drawTilesH * scaledTile;
        const drawX = item.x * scaledTile - ((drawTilesW - fw) / 2) * scaledTile;
        const drawY = (item.y + fh) * scaledTile - drawHeight;

        if (asset.sourceRect) {
          const [sx, sy, sw, sh] = asset.sourceRect;
          ctx.drawImage(img, sx, sy, sw, sh, drawX, drawY, drawWidth, drawHeight);
        } else {
          ctx.drawImage(img, drawX, drawY, drawWidth, drawHeight);
        }
      }

      // Draw selection highlights
      for (const item of placedItems) {
        if (!selectedItems.includes(item.id)) continue;
        const asset = allAssets.find(a => a.id === item.assetId);
        if (!asset?.footprint) continue;
        const [fw, fh] = asset.footprint;
        const ox = isMovingSelection ? moveOffset.x : 0;
        const oy = isMovingSelection ? moveOffset.y : 0;
        ctx.fillStyle = 'rgba(0, 255, 0, 0.3)';
        ctx.fillRect(
          (item.x + ox) * scaledTile, (item.y + oy) * scaledTile,
          fw * scaledTile, fh * scaledTile
        );
      }

      // Draw multi-select drag rectangle
      if (selectionRect) {
        const rx = selectionRect.startX * scaledTile;
        const ry = selectionRect.startY * scaledTile;
        const rw = (selectionRect.endX - selectionRect.startX + 1) * scaledTile;
        const rh = (selectionRect.endY - selectionRect.startY + 1) * scaledTile;
        ctx.fillStyle = 'rgba(100, 149, 237, 0.15)';
        ctx.fillRect(rx, ry, rw, rh);
        ctx.strokeStyle = 'rgba(100, 149, 237, 0.8)';
        ctx.lineWidth = 2;
        ctx.setLineDash([6, 3]);
        ctx.strokeRect(rx, ry, rw, rh);
        ctx.setLineDash([]);
      }

      // Draw line/rect tile mode preview
      if (isDragPlacing && (tileMode === 'line' || tileMode === 'rect') && tileDragStart && tileDragEnd && selectedAsset) {
        const previewTiles = computeTileModeTiles(tileDragStart, tileDragEnd, tileMode, selectedAsset.id);
        for (const t of previewTiles) {
          const tileValid = isValidPlacement(selectedAsset, t.x, t.y);
          ctx.fillStyle = tileValid ? 'rgba(0, 255, 0, 0.3)' : 'rgba(255, 0, 0, 0.3)';
          ctx.fillRect(t.x * scaledTile, t.y * scaledTile, scaledTile, scaledTile);
        }
        // Draw preview sprites
        const img = cache[selectedAsset.src];
        if (img && img.complete && img.naturalWidth) {
          ctx.globalAlpha = 0.7;
          const isFence = FENCE_IDS.has(selectedAsset.id);
          // Build fake placedItems for auto-tile preview
          const fakeItems = previewTiles.map(t => ({ assetId: selectedAsset.id, x: t.x, y: t.y }));
          const allItemsForPreview = [...placedItems.map(i => ({ assetId: i.assetId, x: i.x, y: i.y })), ...fakeItems];
          for (const fakeItem of fakeItems) {
            const [sx, sy, sw, sh] = getAutoTileSourceRect(fakeItem, allItemsForPreview);
            const drawW = scaledTile;
            const drawH = isFence ? scaledTile * 2 : scaledTile;
            const drawX = fakeItem.x * scaledTile;
            const drawY = isFence ? (fakeItem.y - 1) * scaledTile : fakeItem.y * scaledTile;
            ctx.drawImage(img, sx, sy, sw, sh, drawX, drawY, drawW, drawH);
          }
          ctx.globalAlpha = 1;
        }
      }

      // Draw placement preview
      if (selectedAsset && hoveredTile && tool === 'select' && !isDragPlacing) {
        const valid = isValidPlacement(selectedAsset, hoveredTile.x, hoveredTile.y);
        const [fw, fh] = selectedAsset.footprint || [1, 1];

        ctx.fillStyle = valid ? 'rgba(0, 255, 0, 0.3)' : 'rgba(255, 0, 0, 0.3)';
        ctx.fillRect(hoveredTile.x * scaledTile, hoveredTile.y * scaledTile, fw * scaledTile, fh * scaledTile);

        if (selectedAsset.src) {
          const img = cache[selectedAsset.src];
          if (img && img.complete && img.naturalWidth) {
            ctx.globalAlpha = 0.7;

            if (TILEABLE_IDS.has(selectedAsset.id)) {
              // Preview tileable item using auto-tile sprite (show what it would look like with current neighbors)
              const previewItem = { assetId: selectedAsset.id, x: hoveredTile.x, y: hoveredTile.y };
              const [sx, sy, sw, sh] = getAutoTileSourceRect(previewItem, placedItems);
              const isFence = FENCE_IDS.has(selectedAsset.id);
              const drawW = scaledTile;
              const drawH = isFence ? scaledTile * 2 : scaledTile;
              const drawX = hoveredTile.x * scaledTile;
              const drawY = isFence ? (hoveredTile.y - 1) * scaledTile : hoveredTile.y * scaledTile;
              ctx.drawImage(img, sx, sy, sw, sh, drawX, drawY, drawW, drawH);
            } else {
              const srcW = selectedAsset.sourceRect ? selectedAsset.sourceRect[2] : img.naturalWidth;
              const srcH = selectedAsset.sourceRect ? selectedAsset.sourceRect[3] : img.naturalHeight;
              const drawTilesW = selectedAsset.displayTiles ? selectedAsset.displayTiles[0] : (srcW / TILE_SIZE);
              const drawTilesH = selectedAsset.displayTiles ? selectedAsset.displayTiles[1] : (srcH / TILE_SIZE);
              const drawWidth = drawTilesW * scaledTile;
              const drawHeight = drawTilesH * scaledTile;
              const drawX = hoveredTile.x * scaledTile - ((drawTilesW - fw) / 2) * scaledTile;
              const drawY = (hoveredTile.y + fh) * scaledTile - drawHeight;
              if (selectedAsset.sourceRect) {
                const [sx, sy, sw, sh] = selectedAsset.sourceRect;
                ctx.drawImage(img, sx, sy, sw, sh, drawX, drawY, drawWidth, drawHeight);
              } else {
                ctx.drawImage(img, drawX, drawY, drawWidth, drawHeight);
              }
            }
            ctx.globalAlpha = 1;
          }
        }

        if (showSprinklerRange && SPRINKLER_RANGES[selectedAsset.id]) {
          ctx.fillStyle = 'rgba(100, 149, 237, 0.4)';
          SPRINKLER_RANGES[selectedAsset.id].forEach(([dx, dy]) => {
            const tx = hoveredTile.x + dx;
            const ty = hoveredTile.y + dy;
            if (tx >= 0 && tx < farm.width && ty >= 0 && ty < farm.height) {
              ctx.fillRect(tx * scaledTile, ty * scaledTile, scaledTile, scaledTile);
            }
          });
        }
      }

      // Flip: copy offscreen to visible canvas in one operation
      if (cancelled) return;
      canvas.width = canvasWidth * dpr;
      canvas.height = canvasHeight * dpr;
      canvas.style.width = canvasWidth + 'px';
      canvas.style.height = canvasHeight + 'px';
      const mainCtx = canvas.getContext('2d');
      mainCtx.imageSmoothingEnabled = false;
      mainCtx.drawImage(offscreen, 0, 0);
    };

    if (uncached.length === 0) {
      // All images already cached — draw synchronously
      drawFrame();
    } else {
      // Load uncached images, then draw once all are ready
      let loaded = 0;
      const onLoad = () => {
        if (cancelled) return;
        loaded++;
        if (loaded >= uncached.length) drawFrame();
      };
      uncached.forEach(img => {
        if (img.complete) { onLoad(); return; }
        img.addEventListener('load', onLoad, { once: true });
        img.addEventListener('error', onLoad, { once: true });
      });
    }

    return () => { cancelled = true; };
  }, [farm, scaledTile, showGrid, showSprinklerRange, placedItems, selectedAsset, hoveredTile,
      tool, getSprinklerCoverage, isValidPlacement, allAssets, farmBackground,
      selectedItems, selectionRect, isMovingSelection, moveOffset,
      isDragPlacing, tileMode, tileDragStart, tileDragEnd, computeTileModeTiles]);

  const captureThumbnail = useCallback(() => {
    const canvas = canvasRef.current;
    if (!canvas) return null;
    const maxWidth = 320;
    const scale = Math.min(1, maxWidth / canvas.width);
    const thumbCanvas = document.createElement('canvas');
    thumbCanvas.width = Math.round(canvas.width * scale);
    thumbCanvas.height = Math.round(canvas.height * scale);
    const ctx = thumbCanvas.getContext('2d');
    ctx.imageSmoothingEnabled = true;
    ctx.imageSmoothingQuality = 'high';
    ctx.drawImage(canvas, 0, 0, thumbCanvas.width, thumbCanvas.height);
    return thumbCanvas.toDataURL('image/png');
  }, []);

  const saveFarm = useCallback(() => {
    setShowSaveModal(true);
  }, []);

  const loadFarm = useCallback((saveData) => {
    if (saveData.farmType) setFarmType(saveData.farmType);
    if (saveData.season) setSeason(saveData.season);
    if (saveData.items) {
      setPlacedItems(saveData.items);
      setHistory([saveData.items]);
      setHistoryIndex(0);
    }
    if (saveData.id) setCurrentSaveId(String(saveData.id));
    setCurrentSaveName(saveData.name || null);
    setZoom(100);
    setPan({ x: 0, y: 0 });
    setSelectedAsset(null);
    setSelectedItems([]);
  }, []);

  const handleSaveComplete = useCallback((newId, savedName) => {
    setCurrentSaveId(String(newId));
    if (savedName) setCurrentSaveName(savedName);
  }, []);

  const styles = {
    container: {
      display: 'flex',
      height: '100vh',
      backgroundColor: '#f0e8db',
      color: '#4a3728',
      fontFamily: "'Nunito', 'Segoe UI', system-ui, sans-serif",
    },
    sidebar: {
      width: sidebarWidth + 'px',
      backgroundColor: '#f7f1e8',
      padding: '0',
      overflowY: 'hidden',
      borderRight: '1px solid #ddd2c0',
      display: 'flex',
      flexDirection: 'column',
      boxShadow: '2px 0 16px rgba(74,55,40,0.08)',
      zIndex: 10,
    },
    sidebarHeader: {
      padding: '20px 20px 16px',
      borderBottom: '1px solid #ddd2c0',
      background: 'linear-gradient(180deg, #faf6ef 0%, #f7f1e8 100%)',
      textAlign: 'center',
    },
    sidebarContent: {
      flex: 1,
      overflow: 'hidden',
      padding: `${Math.round(8 * vScale)}px ${Math.round(12 * hScale)}px`,
      display: 'flex',
      flexDirection: 'column',
      gap: Math.round(8 * vScale) + 'px',
      minHeight: 0,
    },
    mainArea: {
      flex: 1,
      display: 'flex',
      flexDirection: 'column',
      overflow: 'hidden',
      background: '#ede4d5',
    },
    toolbar: {
      display: 'flex',
      gap: Math.round(6 * hScale) + 'px',
      padding: `${Math.round(8 * vScale)}px ${Math.round(14 * hScale)}px`,
      backgroundColor: '#f7f1e8',
      borderBottom: '1px solid #ddd2c0',
      alignItems: 'center',
      flexWrap: 'wrap',
    },
    canvasContainer: {
      flex: 1,
      overflow: 'hidden',
      padding: Math.round(16 * vScale) + 'px',
      display: 'flex',
      justifyContent: 'center',
      alignItems: 'center',
      background: 'radial-gradient(ellipse at center, #f0e8db 0%, #e8dfd0 100%)',
    },
    canvas: {
      border: '2px solid #ddd2c0',
      borderRadius: '12px',
      cursor: 'crosshair',
      boxShadow: '0 8px 32px rgba(74,55,40,0.08)',
    },
    pillToggle: {
      display: 'flex',
      backgroundColor: '#ede4d5',
      borderRadius: '24px',
      padding: '3px',
      gap: '2px',
      border: '1.5px solid #ddd2c0',
    },
    pillBtn: {
      width: Math.round(34 * vScale) + 'px',
      height: Math.round(34 * vScale) + 'px',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      borderRadius: '50%',
      border: 'none',
      cursor: 'pointer',
      backgroundColor: 'transparent',
      color: '#b0a090',
      transition: 'all 0.2s ease',
      outline: 'none',
    },
    pillBtnActive: {
      backgroundColor: '#5a944e',
      color: '#fff',
      boxShadow: '0 2px 8px rgba(106,170,90,0.35)',
    },
    select: {
      padding: `${Math.round(6 * vScale)}px ${Math.round(10 * hScale)}px`,
      backgroundColor: '#fff',
      border: '1.5px solid #ddd2c0',
      borderRadius: '8px',
      color: '#4a3728',
      cursor: 'pointer',
      fontFamily: 'inherit',
      fontSize: Math.round(13 * vScale) + 'px',
      fontWeight: '600',
      transition: 'all 0.2s ease',
      boxShadow: '0 1px 3px rgba(74,55,40,0.06)',
      outline: 'none',
    },
    sectionTitle: {
      fontSize: Math.round(10 * vScale) + 'px',
      color: '#9a8a75',
      textTransform: 'uppercase',
      letterSpacing: '1.2px',
      marginBottom: Math.round(4 * vScale) + 'px',
      fontWeight: '700',
    },
    categoryTabs: {
      display: 'flex',
      flexWrap: 'wrap',
      gap: Math.round(4 * vScale) + 'px',
      marginBottom: Math.round(4 * vScale) + 'px',
    },
    categoryTab: {
      padding: `${Math.round(5 * vScale)}px ${Math.round(10 * hScale)}px`,
      fontSize: Math.round(11.5 * vScale) + 'px',
      backgroundColor: '#fff',
      border: '1.5px solid #e8dfd0',
      borderRadius: '20px',
      color: '#9a8a75',
      cursor: 'pointer',
      fontFamily: 'inherit',
      fontWeight: '600',
      transition: 'all 0.2s ease',
      whiteSpace: 'nowrap',
      outline: 'none',
    },
    categoryTabActive: {
      backgroundColor: '#5a944e',
      border: '1.5px solid #5a944e',
      color: '#fff',
      boxShadow: '0 2px 8px rgba(106,170,90,0.3)',
    },
    assetGrid: {
      display: 'grid',
      gridTemplateColumns: 'repeat(3, 1fr)',
      gap: Math.round(6 * vScale) + 'px',
      overflowY: 'auto',
      padding: '2px',
      flex: 1,
      alignContent: 'start',
    },
    assetItem: {
      display: 'flex',
      flexDirection: 'column',
      alignItems: 'center',
      padding: `${Math.round(6 * vScale)}px 5px ${Math.round(5 * vScale)}px`,
      backgroundColor: '#fff',
      borderRadius: '10px',
      cursor: 'pointer',
      border: '1.5px solid #e8dfd0',
      outline: 'none',
      boxShadow: '0 1px 3px rgba(74,55,40,0.05)',
      transition: 'all 0.2s ease',
    },
    assetItemSelected: {
      border: '1.5px solid #c47a3a',
      backgroundColor: '#fef8f0',
      boxShadow: '0 0 0 2px rgba(196,122,58,0.25), 0 4px 12px rgba(196,122,58,0.15)',
    },
    assetImage: {
      width: Math.round(46 * vScale) + 'px',
      height: Math.round(46 * vScale) + 'px',
      objectFit: 'contain',
      imageRendering: 'pixelated',
    },
    assetName: {
      fontSize: Math.round(10.5 * vScale) + 'px',
      color: '#9a8a75',
      textAlign: 'center',
      marginTop: '3px',
      lineHeight: 1.1,
      fontWeight: '600',
    },
    stats: {
      fontSize: Math.round(11 * vScale) + 'px',
      color: '#9a8a75',
      padding: `${Math.round(4 * vScale)}px 10px`,
      backgroundColor: '#fff',
      borderRadius: '8px',
      marginTop: Math.round(4 * vScale) + 'px',
      flexShrink: 0,
      border: '1.5px solid #e8dfd0',
      boxShadow: '0 1px 3px rgba(74,55,40,0.05)',
      display: 'flex',
      gap: '12px',
    },
  };

  const categories = ['favorites', 'buildings', 'endgame', 'crops', 'trees', 'equipment', 'fences_paths', 'furniture'];
  const [hoveredHeartId, setHoveredHeartId] = useState(null);

  const isMac = /Mac|iPhone|iPad/.test(navigator.userAgent);
  const controlsContent = (
    <div style={{ display: 'flex', flexDirection: 'column', gap: '6px', fontSize: '12px', color: '#6a5a45' }}>
      <div style={{ fontSize: '11px', fontWeight: '700', color: '#9a8a75', textTransform: 'uppercase', letterSpacing: '0.5px', marginTop: '2px' }}>Keyboard</div>
      {[
        ['V', 'Select tool'],
        ['M', 'Multi-select tool'],
        ['X', 'Delete tool'],
        ['G', 'Toggle grid'],
        ['R', 'Toggle sprinkler range'],
        ['Esc', 'Deselect / close modals'],
        ['Del / Backspace', 'Delete selected items'],
        [isMac ? '\u2318Z' : 'Ctrl+Z', 'Undo'],
        [isMac ? '\u2318\u21e7Z / \u2318Y' : 'Ctrl+Shift+Z / Ctrl+Y', 'Redo'],
      ].map(([key, desc], i) => (
        <div key={i} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <span style={{ color: '#4a3728', fontWeight: '600' }}>{desc}</span>
          <kbd style={{
            backgroundColor: '#f4efe6', border: '1px solid #ddd2c0', borderRadius: '4px',
            padding: '1px 6px', fontSize: '11px', fontFamily: "'Nunito', system-ui, sans-serif",
            fontWeight: '700', color: '#6a5a45', whiteSpace: 'nowrap',
          }}>{key}</kbd>
        </div>
      ))}
      <div style={{ borderTop: '1px solid #ede6da', margin: '6px 0 2px' }} />
      <div style={{ fontSize: '11px', fontWeight: '700', color: '#9a8a75', textTransform: 'uppercase', letterSpacing: '0.5px' }}>Mouse</div>
      {[
        ['Left click', 'Place item / select'],
        ['Right click', 'Drop item / delete'],
        ['Middle / Alt+drag', 'Pan the map'],
        ['Scroll wheel', 'Zoom in / out'],
        ['Left drag (tile)', 'Paint tiles'],
      ].map(([key, desc], i) => (
        <div key={i} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <span style={{ color: '#4a3728', fontWeight: '600' }}>{desc}</span>
          <span style={{
            backgroundColor: '#f4efe6', border: '1px solid #ddd2c0', borderRadius: '4px',
            padding: '1px 6px', fontSize: '11px', fontFamily: "'Nunito', system-ui, sans-serif",
            fontWeight: '700', color: '#6a5a45', whiteSpace: 'nowrap',
          }}>{key}</span>
        </div>
      ))}
    </div>
  );

  return (
    <div style={styles.container}>
      <div style={styles.sidebar}>
        <div style={styles.sidebarHeader}>
          <div style={{
            fontFamily: "'Luckiest Guy', cursive",
            fontSize: '28px',
            color: '#c47a3a',
            letterSpacing: '1.5px',
            WebkitTextStroke: '1.5px #7a4a22',
            paintOrder: 'stroke fill',
            textShadow: '0 2px 0 #7a4a22, 0 3px 6px rgba(74,55,40,0.2)',
            lineHeight: 1.15,
          }}>
            Stardew Farm Planner
          </div>
          <div style={{ fontSize: '12px', color: '#9a8a75', marginTop: '4px', fontWeight: '500' }}>
            Farm layout planner for Stardew Valley
          </div>
        </div>

        <div style={styles.sidebarContent}>
        <div>
          <div style={styles.sectionTitle}>Farm Type</div>
          <select
            value={farmType}
            onChange={e => { setFarmType(e.target.value); setZoom(100); setPan({ x: 0, y: 0 }); }}
            style={{ ...styles.select, width: '100%' }}
          >
            {Object.entries(FARM_TYPES).map(([key, f]) => (
              <option key={key} value={key}>{f.name}</option>
            ))}
          </select>
        </div>

        <div>
          <div style={styles.sectionTitle}>Season</div>
          <div style={{ display: 'flex', gap: '6px' }}>
            {SEASONS.map(s => {
              const seasonColors = {
                spring: { bg: 'linear-gradient(180deg, #7cc06a 0%, #5ea04e 100%)', border: '#5ea04e', shadow: 'rgba(90,160,78,0.3)' },
                summer: { bg: 'linear-gradient(180deg, #e8a84a 0%, #d4914a 100%)', border: '#d4914a', shadow: 'rgba(212,145,74,0.3)' },
                fall: { bg: 'linear-gradient(180deg, #d47a4a 0%, #c06a3a 100%)', border: '#c06a3a', shadow: 'rgba(192,106,58,0.3)' },
                winter: { bg: 'linear-gradient(180deg, #6aaac4 0%, #5a95b0 100%)', border: '#5a95b0', shadow: 'rgba(90,149,176,0.3)' },
              };
              const isActive = season === s;
              const sc = seasonColors[s];
              return (
                <button
                  key={s}
                  onClick={(e) => { e.currentTarget.blur(); setSeason(s); }}
                  style={{
                    flex: 1,
                    padding: `${Math.round(6 * vScale)}px 4px`,
                    fontSize: Math.round(12 * vScale) + 'px',
                    fontFamily: 'inherit',
                    fontWeight: '600',
                    background: isActive ? sc.bg : '#fff',
                    border: isActive ? `1.5px solid ${sc.border}` : '1.5px solid #ddd2c0',
                    borderRadius: '10px',
                    color: isActive ? '#fff' : '#9a8a75',
                    cursor: 'pointer',
                    transition: 'all 0.2s ease',
                    boxShadow: isActive ? `0 3px 10px ${sc.shadow}` : '0 1px 3px rgba(74,55,40,0.06)',
                    textAlign: 'center',
                    outline: 'none',
                  }}
                >
                  {s.charAt(0).toUpperCase() + s.slice(1)}
                </button>
              );
            })}
          </div>
        </div>

        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', minHeight: 0 }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
            <div style={styles.sectionTitle}>Assets</div>
            <button
              onClick={() => setShowViewAll(true)}
              onMouseEnter={(e) => { e.currentTarget.style.backgroundColor = '#f0ebe3'; }}
              onMouseLeave={(e) => { e.currentTarget.style.backgroundColor = 'transparent'; }}
              style={{
                background: 'transparent', border: 'none', color: '#8a7a65',
                fontSize: Math.round(11.5 * vScale) + 'px', fontWeight: '600', cursor: 'pointer',
                fontFamily: "'Nunito', system-ui, sans-serif", padding: '2px 8px',
                borderRadius: '6px', transition: 'all 0.15s ease',
              }}
            >View All</button>
          </div>
          <div style={{ position: 'relative', marginBottom: Math.round(4 * vScale) + 'px' }}>
            <input
              type="text"
              placeholder="Search items..."
              value={searchQuery}
              onChange={e => { setSearchQuery(e.target.value); setSelectedSubcategory(null); }}
              style={{
                width: '100%', padding: `${Math.round(7 * vScale)}px 24px ${Math.round(7 * vScale)}px 10px`, fontSize: Math.round(13 * vScale) + 'px',
                backgroundColor: '#fff', border: '1.5px solid #ddd2c0', borderRadius: '8px',
                color: '#4a3728', outline: 'none', boxSizing: 'border-box',
                fontFamily: 'inherit', transition: 'all 0.2s ease',
                boxShadow: '0 1px 3px rgba(74,55,40,0.06)',
              }}
            />
            {searchQuery && (
              <button
                onClick={() => setSearchQuery('')}
                style={{
                  position: 'absolute', right: '8px', top: '50%', transform: 'translateY(-50%)',
                  background: 'none', border: 'none', color: '#b0a090', cursor: 'pointer',
                  fontSize: '14px', padding: '2px 4px', lineHeight: 1,
                }}
              >x</button>
            )}
          </div>
          <div style={styles.categoryTabs}>
            {categories.map(cat => (
              <button
                key={cat}
                onClick={(e) => { e.currentTarget.blur(); setSelectedCategory(cat); setSelectedSubcategory(null); }}
                style={{
                  ...styles.categoryTab,
                  ...(selectedCategory === cat ? styles.categoryTabActive : {}),
                }}
              >
                {cat === 'favorites' ? '\u2665 Faves' : cat.replace('_', ' & ').replace(/\b\w/g, c => c.toUpperCase())}
              </button>
            ))}
          </div>

          {(() => {
            const items = assetCatalog[selectedCategory] || [];
            const subcategories = [...new Set(items.map(a => a.subcategory).filter(Boolean))];
            if (subcategories.length > 0) {
              return (
                <div style={{ ...styles.categoryTabs, marginBottom: '8px' }}>
                  <button
                    onClick={(e) => { e.currentTarget.blur(); setSelectedSubcategory(null); }}
                    style={{
                      ...styles.categoryTab,
                      fontSize: '10px',
                      padding: '2px 6px',
                      ...(selectedSubcategory === null ? { backgroundColor: '#5a944e', border: '1.5px solid #5a944e', color: '#fff' } : {}),
                    }}
                  >All</button>
                  {subcategories.map(sub => (
                    <button
                      key={sub}
                      onClick={(e) => { e.currentTarget.blur(); setSelectedSubcategory(sub); }}
                      style={{
                        ...styles.categoryTab,
                        fontSize: '10px',
                        padding: '2px 6px',
                        ...(selectedSubcategory === sub ? { backgroundColor: '#5a944e', border: '1.5px solid #5a944e', color: '#fff' } : {}),
                      }}
                    >{sub}</button>
                  ))}
                </div>
              );
            }
            return null;
          })()}

          <div style={styles.assetGrid}>
            {(searchQuery.trim()
              ? Object.values(assetCatalog).flat().filter(asset =>
                  asset.name.toLowerCase().includes(searchQuery.trim().toLowerCase())
                )
              : selectedCategory === 'favorites'
                ? Object.values(assetCatalog).flat().filter(asset => favorites.includes(asset.id))
                : (assetCatalog[selectedCategory] || [])
                    .filter(asset => !selectedSubcategory || asset.subcategory === selectedSubcategory)
            ).map(asset => {
              const isFav = favorites.includes(asset.id);
              return (
              <div
                key={asset.id}
                onClick={(e) => {
                  e.currentTarget.blur();
                  setSelectedAsset(asset);
                  setTool('select');
                  setSelectedItems([]);
                }}
                onMouseEnter={() => setHoveredHeartId(asset.id)}
                onMouseLeave={() => setHoveredHeartId(null)}
                style={{
                  ...styles.assetItem,
                  ...(selectedAsset?.id === asset.id ? styles.assetItemSelected : {}),
                  position: 'relative',
                }}
                title={`${asset.name}\nSize: ${asset.footprint?.[0]}x${asset.footprint?.[1]} tiles`}
              >
                {(hoveredHeartId === asset.id || isFav) && (
                  <div
                    onClick={(e) => toggleFavorite(asset.id, e)}
                    onMouseEnter={(e) => { e.currentTarget.style.transform = 'scale(1.25)'; }}
                    onMouseLeave={(e) => { e.currentTarget.style.transform = 'scale(1)'; }}
                    style={{
                      position: 'absolute', top: '3px', right: '3px',
                      width: '16px', height: '16px', display: 'flex',
                      alignItems: 'center', justifyContent: 'center',
                      cursor: 'pointer', transition: 'transform 0.15s ease',
                      zIndex: 2,
                    }}
                  >
                    <svg width="12" height="12" viewBox="0 0 24 24">
                      <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"
                        fill={isFav ? '#e05555' : 'none'}
                        stroke={isFav ? '#e05555' : '#a09080'}
                        strokeWidth="2"
                      />
                    </svg>
                  </div>
                )}
                {asset.sourceRect && !asset.icon ? (
                  <CroppedThumbnail src={asset.src} sourceRect={asset.sourceRect} style={styles.assetImage} />
                ) : (
                  <img
                    src={asset.icon || asset.src}
                    alt={asset.name}
                    style={styles.assetImage}
                    onError={(e) => { e.target.style.display = 'none'; }}
                  />
                )}
                <div style={styles.assetName}>{asset.name}</div>
              </div>
              );
            })}
            {selectedCategory === 'favorites' && favorites.length === 0 && !searchQuery.trim() && (
              <div style={{ gridColumn: '1 / -1', textAlign: 'center', padding: '16px 0', color: '#a09080', fontSize: '11px' }}>
                No favorites yet. Hover over any asset and click the heart icon.
              </div>
            )}
          </div>
        </div>

        <div style={styles.stats}>
          <span><strong style={{ color: '#6a5a45' }}>Items:</strong> <span style={{ color: '#c47a3a', fontWeight: '700' }}>{placedItems.length}</span></span>
          <span><strong style={{ color: '#6a5a45' }}>Sprinklers:</strong> <span style={{ color: '#c47a3a', fontWeight: '700' }}>{getSprinklerCoverage().size}</span> tiles</span>
        </div>
        </div>
        <div style={{
          padding: '10px 14px',
          borderTop: '1px solid #ddd2c0',
          fontSize: '10px',
          color: '#b0a090',
          lineHeight: 1.45,
          textAlign: 'center',
          flexShrink: 0,
        }}>
          This is an unofficial fan tool. Many of the sprites and icons used in this app are from the original game. Stardew Valley and all game assets are property of ConcernedApe.
        </div>
      </div>

      <div style={styles.mainArea}>
        <div style={styles.toolbar}>
          <div style={styles.pillToggle}>
            <Tooltip text="Select (V)">
              <button
                className="tb-btn"
                onClick={() => { setTool('select'); }}
                style={{ ...styles.pillBtn, ...(tool === 'select' ? styles.pillBtnActive : {}) }}
              >
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                  <path d="M3 1L3 12L6 9L9.5 14L11.5 13L8 8L12 8L3 1Z" fill="currentColor"/>
                </svg>
              </button>
            </Tooltip>
            <Tooltip text="Multi-Select (M)">
              <button
                className="tb-btn"
                onClick={() => { setTool('multi_select'); setSelectedAsset(null); }}
                style={{ ...styles.pillBtn, ...(tool === 'multi_select' ? styles.pillBtnActive : {}) }}
              >
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                  <rect x="1.5" y="1.5" width="13" height="13" rx="1" stroke="currentColor" strokeWidth="1.5" strokeDasharray="3 2"/>
                </svg>
              </button>
            </Tooltip>
            <Tooltip text="Erase (X)">
              <button
                className="tb-btn"
                onClick={() => { setTool('delete'); setSelectedAsset(null); }}
                style={{ ...styles.pillBtn, ...(tool === 'delete' ? styles.pillBtnActive : {}) }}
              >
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                  <path d="M9.5 2L14.5 7L9 12.5L3.5 12.5L1.5 10.5L6.5 5.5L9.5 2Z" fill="currentColor"/>
                  <path d="M6.5 5.5L1.5 10.5L3.5 12.5L9 12.5" stroke="currentColor" strokeWidth="0" fill="currentColor" opacity="0.6"/>
                  <line x1="1" y1="14" x2="15" y2="14" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
                </svg>
              </button>
            </Tooltip>
          </div>
          <div style={{ borderLeft: '1px solid #ddd2c0', height: '28px', margin: '0 6px' }} />
          <div style={styles.pillToggle}>
            <Tooltip text="Undo (Ctrl+Z)">
              <button
                className="tb-btn"
                onClick={undo}
                disabled={historyIndex <= 0}
                style={{ ...styles.pillBtn, opacity: historyIndex <= 0 ? 0.35 : 1 }}
              >
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                  <path d="M4 7L1 4L4 1" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
                  <path d="M1 4H10C12.2 4 14 5.8 14 8C14 10.2 12.2 12 10 12H6" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
                </svg>
              </button>
            </Tooltip>
            <Tooltip text="Redo (Ctrl+Shift+Z)">
              <button
                className="tb-btn"
                onClick={redo}
                disabled={historyIndex >= history.length - 1}
                style={{ ...styles.pillBtn, opacity: historyIndex >= history.length - 1 ? 0.35 : 1 }}
              >
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                  <path d="M12 7L15 4L12 1" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
                  <path d="M15 4H6C3.8 4 2 5.8 2 8C2 10.2 3.8 12 6 12H10" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
                </svg>
              </button>
            </Tooltip>
          </div>
          {selectedAsset && TILEABLE_IDS.has(selectedAsset.id) && (
            <>
              <div style={{ borderLeft: '1px solid #ddd2c0', height: '28px', margin: '0 6px' }} />
              <div style={styles.pillToggle}>
                <Tooltip text="Free Draw">
                  <button
                    className="tb-btn"
                    onClick={() => setTileMode('free')}
                    style={{ ...styles.pillBtn, ...(tileMode === 'free' ? styles.pillBtnActive : {}) }}
                  >
                    <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                      <path d="M2 14C4 10 6 4 8 6C10 8 10 12 12 8C13 6 14 2 14 2" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" fill="none"/>
                    </svg>
                  </button>
                </Tooltip>
                <Tooltip text="Line">
                  <button
                    className="tb-btn"
                    onClick={() => setTileMode('line')}
                    style={{ ...styles.pillBtn, ...(tileMode === 'line' ? styles.pillBtnActive : {}) }}
                  >
                    <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                      <line x1="2" y1="14" x2="14" y2="2" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
                    </svg>
                  </button>
                </Tooltip>
                <Tooltip text={FENCE_IDS.has(selectedAsset.id) ? "Rectangle (Perimeter)" : "Rectangle (Fill)"}>
                  <button
                    className="tb-btn"
                    onClick={() => setTileMode('rect')}
                    style={{ ...styles.pillBtn, ...(tileMode === 'rect' ? styles.pillBtnActive : {}) }}
                  >
                    <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                      {FENCE_IDS.has(selectedAsset.id) ? (
                        <rect x="2" y="3" width="12" height="10" rx="1" stroke="currentColor" strokeWidth="1.5" fill="none"/>
                      ) : (
                        <rect x="2" y="3" width="12" height="10" rx="1" fill="currentColor"/>
                      )}
                    </svg>
                  </button>
                </Tooltip>
              </div>
            </>
          )}
          <div style={{ borderLeft: '1px solid #ddd2c0', height: '28px', margin: '0 6px' }} />
          <div style={styles.pillToggle}>
            <Tooltip text="Grid (G)">
              <button
                className="tb-btn"
                onClick={() => setShowGrid(!showGrid)}
                style={{ ...styles.pillBtn, ...(showGrid ? styles.pillBtnActive : {}) }}
              >
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                  <rect x="1" y="1" width="14" height="14" rx="1" stroke="currentColor" strokeWidth="1.5" fill="none"/>
                  <line x1="5.5" y1="1" x2="5.5" y2="15" stroke="currentColor" strokeWidth="1"/>
                  <line x1="10.5" y1="1" x2="10.5" y2="15" stroke="currentColor" strokeWidth="1"/>
                  <line x1="1" y1="5.5" x2="15" y2="5.5" stroke="currentColor" strokeWidth="1"/>
                  <line x1="1" y1="10.5" x2="15" y2="10.5" stroke="currentColor" strokeWidth="1"/>
                </svg>
              </button>
            </Tooltip>
            <Tooltip text="Sprinkler Range (R)">
              <button
                className="tb-btn"
                onClick={() => setShowSprinklerRange(!showSprinklerRange)}
                style={{ ...styles.pillBtn, ...(showSprinklerRange ? styles.pillBtnActive : {}) }}
              >
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                  <path d="M8 2C8 2 4 7 4 10C4 12.2 5.8 14 8 14C10.2 14 12 12.2 12 10C12 7 8 2 8 2Z" fill="currentColor"/>
                </svg>
              </button>
            </Tooltip>
          </div>
          <div style={{ borderLeft: '1px solid #ddd2c0', height: '28px', margin: '0 6px' }} />
          <div style={{ display: 'flex', alignItems: 'center', gap: Math.round(6 * hScale) + 'px' }}>
            <span style={{ fontSize: Math.round(10 * vScale) + 'px', color: '#9a8a75', fontWeight: '700', textTransform: 'uppercase', letterSpacing: '0.5px' }}>Zoom</span>
            <button
              className="tb-btn"
              onClick={() => setZoom(prev => Math.max(100, prev - 10))}
              style={{ width: Math.round(28 * vScale) + 'px', height: Math.round(28 * vScale) + 'px', display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#fff', border: '1.5px solid #ddd2c0', borderRadius: '8px', color: '#9a8a75', cursor: 'pointer', fontSize: '14px', fontWeight: 'bold', transition: 'all 0.2s ease', boxShadow: '0 1px 3px rgba(74,55,40,0.05)' }}
              title="Zoom out 10%"
            >&minus;</button>
            <input
              type="range"
              min={100}
              max={500}
              step={5}
              value={zoom}
              onChange={e => setZoom(Number(e.target.value))}
              style={{ width: Math.round(90 * hScale) + 'px', cursor: 'pointer' }}
            />
            <button
              className="tb-btn"
              onClick={() => setZoom(prev => Math.min(500, prev + 10))}
              style={{ width: Math.round(28 * vScale) + 'px', height: Math.round(28 * vScale) + 'px', display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#fff', border: '1.5px solid #ddd2c0', borderRadius: '8px', color: '#9a8a75', cursor: 'pointer', fontSize: '14px', fontWeight: 'bold', transition: 'all 0.2s ease', boxShadow: '0 1px 3px rgba(74,55,40,0.05)' }}
              title="Zoom in 10%"
            >+</button>
            <span style={{ minWidth: '38px', textAlign: 'center', fontSize: Math.round(12 * vScale) + 'px', color: '#6a5a45', fontWeight: '700', fontVariantNumeric: 'tabular-nums' }}>{zoom}%</span>
            <Tooltip text="Reset View">
              <button
                className="tb-btn"
                onClick={() => { setZoom(100); setPan({ x: 0, y: 0 }); }}
                style={{ ...styles.pillBtn, backgroundColor: '#fff', border: '1.5px solid #ddd2c0' }}
              >
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                  <path d="M2 3V7H6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
                  <path d="M2 7C3 4.5 5.3 2.5 8.2 2.5C11.5 2.5 14 5.1 14 8.3C14 11.5 11.5 13.5 8.2 13.5C6.2 13.5 4.4 12.6 3.2 11.2" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
                </svg>
              </button>
            </Tooltip>
          </div>
          <div style={{ flex: 1 }} />
          <Tooltip text="Save Farm">
            <button
              className="tb-btn"
              onClick={saveFarm}
              style={{ ...styles.pillBtn, backgroundColor: '#5a95b0', color: '#fff', border: '1.5px solid #4a8aa5', borderRadius: '10px' }}
            >
              <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                <path d="M2 1H11L14 4V14H2V1Z" fill="currentColor"/>
                <rect x="4" y="1" width="6" height="5" rx="0.5" fill="currentColor" stroke="#5a95b0" strokeWidth="1"/>
                <rect x="4" y="9" width="8" height="5" rx="0.5" fill="#5a95b0"/>
                <rect x="8" y="1.5" width="1.5" height="3.5" rx="0.3" fill="#5a95b0"/>
              </svg>
            </button>
          </Tooltip>
          <Tooltip text="Load Farm">
            <button
              className="tb-btn"
              onClick={() => setShowLoadModal(true)}
              style={{ ...styles.pillBtn, backgroundColor: '#6aaa5a', color: '#fff', border: '1.5px solid #5ea04e', borderRadius: '10px' }}
            >
              <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                <path d="M1 3H6L8 5H15V14H1V3Z" fill="currentColor"/>
                <path d="M1 3H6L8 5H15V6H1V3Z" fill="rgba(255,255,255,0.25)"/>
              </svg>
            </button>
          </Tooltip>
          <Tooltip text="Clear All">
            <button
              className="tb-btn"
              onClick={() => setShowClearConfirm(true)}
              style={{ ...styles.pillBtn, backgroundColor: '#c06060', color: '#fff', border: '1.5px solid #a85050', borderRadius: '10px' }}
            >
              <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                <path d="M5 2V1H11V2H14V4H2V2H5Z" fill="currentColor"/>
                <path d="M3 5H13L12 15H4L3 5Z" fill="currentColor"/>
              </svg>
            </button>
          </Tooltip>
          <div style={{ borderLeft: '1px solid #ddd2c0', height: '28px', margin: '0 6px' }} />
          <div ref={controlsBtnRef} style={{ position: 'relative' }}>
            <Tooltip text="Controls">
              <button
                className="tb-btn"
                onClick={() => {
                  if (controlsPinned) { setControlsPinned(false); setShowControls(false); }
                  else setShowControls(prev => !prev);
                }}
                style={{ ...styles.pillBtn, backgroundColor: (showControls || controlsPinned) ? '#4a3728' : '#fff', color: (showControls || controlsPinned) ? '#fff' : '#9a8a75', border: '1.5px solid #ddd2c0', borderRadius: '10px' }}
              >
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                  <rect x="1" y="4" width="14" height="9" rx="2" stroke="currentColor" strokeWidth="1.5" fill="none"/>
                  <rect x="3" y="6.5" width="2" height="1.5" rx="0.3" fill="currentColor"/>
                  <rect x="6" y="6.5" width="2" height="1.5" rx="0.3" fill="currentColor"/>
                  <rect x="9" y="6.5" width="2" height="1.5" rx="0.3" fill="currentColor"/>
                  <rect x="4.5" y="9" width="5" height="1.5" rx="0.3" fill="currentColor"/>
                </svg>
              </button>
            </Tooltip>
            {showControls && !controlsPinned && ReactDOM.createPortal(
              (() => {
                const btnRect = controlsBtnRef.current?.getBoundingClientRect();
                const cardRight = btnRect ? window.innerWidth - btnRect.right : 16;
                const cardTop = btnRect ? btnRect.bottom + 8 : 60;
                return (
                  <div ref={controlsCardRef} style={{
                    position: 'fixed', right: cardRight + 'px', top: cardTop + 'px', zIndex: 9999,
                    backgroundColor: '#fff', border: '1.5px solid #ddd2c0', borderRadius: '12px',
                    boxShadow: '0 8px 32px rgba(74,55,40,0.18)', padding: '16px 20px 14px',
                    fontFamily: "'Nunito', system-ui, sans-serif", width: '300px',
                    animation: 'modalScaleIn 0.15s ease-out',
                  }}>
                    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '12px' }}>
                      <span style={{ fontSize: '13px', fontWeight: '700', color: '#4a3728', textTransform: 'uppercase', letterSpacing: '0.5px' }}>Controls</span>
                      <div
                        onClick={() => { setControlsPinned(true); setShowControls(false); }}
                        onMouseEnter={(e) => { e.currentTarget.style.transform = 'rotate(0deg) scale(1.25)'; }}
                        onMouseLeave={(e) => { e.currentTarget.style.transform = 'rotate(45deg) scale(1)'; }}
                        title="Pin to side"
                        style={{
                          cursor: 'pointer', padding: '4px',
                          color: '#b0a090',
                          transform: 'rotate(45deg)',
                          transition: 'transform 0.15s ease, color 0.15s ease',
                          display: 'flex', alignItems: 'center', justifyContent: 'center',
                        }}
                      >
                        <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
                          <path d="M10.5 1.5L9.2 2.8L10.9 4.5L6.7 8.7L4.3 8L2.5 9.8L6.2 10.5L1.5 15.2L2.3 16L7 11.3L7.7 15L9.5 13.2L8.8 10.8L13 6.6L14.7 8.3L16 7L10.5 1.5Z"/>
                        </svg>
                      </div>
                    </div>
                    {controlsContent}
                  </div>
                );
              })(),
              document.body
            )}
          </div>
          <Tooltip text="Send Feedback">
            <button
              className="tb-btn"
              onClick={() => setShowFeedback(true)}
              style={{ ...styles.pillBtn, backgroundColor: '#fff', color: '#9a8a75', border: '1.5px solid #ddd2c0', borderRadius: '10px' }}
            >
              <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                <path d="M2 2.5H14C14.6 2.5 15 2.9 15 3.5V10.5C15 11.1 14.6 11.5 14 11.5H9L5.5 14.5V11.5H2C1.4 11.5 1 11.1 1 10.5V3.5C1 2.9 1.4 2.5 2 2.5Z" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" fill="none"/>
                <circle cx="5" cy="7" r="1" fill="currentColor"/>
                <circle cx="8" cy="7" r="1" fill="currentColor"/>
                <circle cx="11" cy="7" r="1" fill="currentColor"/>
              </svg>
            </button>
          </Tooltip>
        </div>

        <div style={{ flex: 1, display: 'flex', overflow: 'hidden' }}>
          <div ref={containerRef} style={styles.canvasContainer}>
            <canvas
              ref={canvasRef}
              style={{
                ...styles.canvas,
                transform: `translate(${pan.x}px, ${pan.y}px)`,
                cursor: isPanning ? 'grabbing' : isMovingSelection ? 'grabbing' : (tool === 'multi_select' && selectedItems.length > 0 ? 'grab' : (tool === 'select' && selectedAsset ? 'crosshair' : (tool === 'delete' || tool === 'multi_select' ? 'crosshair' : 'default'))),
              }}
              onClick={handleCanvasClick}
              onMouseMove={handleMouseMove}
              onMouseDown={handleMouseDown}
              onMouseUp={handleMouseUp}
              onMouseLeave={handleMouseUp}
              onContextMenu={handleContextMenu}
            />
          </div>
          {controlsPinned && (
            <div style={{
              width: '280px', backgroundColor: '#f7f1e8', borderLeft: '1px solid #ddd2c0',
              padding: '16px 18px 14px', overflowY: 'auto',
              fontFamily: "'Nunito', system-ui, sans-serif",
              boxShadow: '-2px 0 12px rgba(74,55,40,0.06)',
              display: 'flex', flexDirection: 'column',
            }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '12px' }}>
                <span style={{ fontSize: '13px', fontWeight: '700', color: '#4a3728', textTransform: 'uppercase', letterSpacing: '0.5px' }}>Controls</span>
                <div
                  onClick={() => { setControlsPinned(false); }}
                  onMouseEnter={(e) => { e.currentTarget.style.transform = 'scale(1.25)'; }}
                  onMouseLeave={(e) => { e.currentTarget.style.transform = 'scale(1)'; }}
                  title="Unpin"
                  style={{
                    cursor: 'pointer', padding: '4px',
                    color: '#4a3728',
                    transition: 'transform 0.15s ease',
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                  }}
                >
                  <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
                    <path d="M10.5 1.5L9.2 2.8L10.9 4.5L6.7 8.7L4.3 8L2.5 9.8L6.2 10.5L1.5 15.2L2.3 16L7 11.3L7.7 15L9.5 13.2L8.8 10.8L13 6.6L14.7 8.3L16 7L10.5 1.5Z"/>
                  </svg>
                </div>
              </div>
              {controlsContent}
            </div>
          )}
        </div>
      </div>
      <SaveModal
        isOpen={showSaveModal}
        onClose={() => setShowSaveModal(false)}
        farmType={farmType}
        season={season}
        placedItems={placedItems}
        farmName={farm.name}
        captureThumbnail={captureThumbnail}
        currentSaveId={currentSaveId}
        currentSaveName={currentSaveName}
        onSaved={handleSaveComplete}
      />
      <LoadModal
        isOpen={showLoadModal}
        onClose={() => setShowLoadModal(false)}
        onLoad={loadFarm}
        onNewSave={() => { setShowLoadModal(false); setShowSaveModal(true); }}
      />
      <ViewAllModal
        isOpen={showViewAll}
        onClose={() => setShowViewAll(false)}
        assetCatalog={assetCatalog}
        favorites={favorites}
        toggleFavorite={toggleFavorite}
        onSelectAsset={(asset) => { setSelectedAsset(asset); setTool('select'); setSelectedItems([]); }}
      />
      <FeedbackModal isOpen={showFeedback} onClose={() => setShowFeedback(false)} />
      <Modal isOpen={showClearConfirm} onClose={() => setShowClearConfirm(false)} title="Clear All Items" width="360px">
        <div style={{ padding: '20px 24px', textAlign: 'center' }}>
          <p style={{ margin: '0 0 20px', color: '#4a3728', fontSize: '14px', lineHeight: 1.5 }}>
            Are you sure you want to remove all placed items? This cannot be undone.
          </p>
          <div style={{ display: 'flex', gap: '10px', justifyContent: 'center' }}>
            <button
              onClick={() => setShowClearConfirm(false)}
              style={{
                padding: '8px 20px', borderRadius: '10px', border: '1.5px solid #ddd2c0',
                backgroundColor: '#fff', color: '#6a5a45', fontSize: '13px', fontWeight: '600',
                cursor: 'pointer', fontFamily: 'inherit',
              }}
            >Cancel</button>
            <button
              onClick={() => {
                setPlacedItems([]);
                setHistory([[]]);
                setHistoryIndex(0);
                setShowClearConfirm(false);
              }}
              style={{
                padding: '8px 20px', borderRadius: '10px', border: '1.5px solid #a85050',
                backgroundColor: '#c06060', color: '#fff', fontSize: '13px', fontWeight: '600',
                cursor: 'pointer', fontFamily: 'inherit',
              }}
            >Clear All</button>
          </div>
        </div>
      </Modal>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<StardewFarmPlanner />);
