-----------------------------------------------------------------------
-- 1v1HUD - Mini panel (YOU vs OPPONENT) using the same pipeline as ResourceHUD
-- Update loop: Rule_AddInterval(Update1v1HUD, 1)
-----------------------------------------------------------------------

Core_RegisterModule("HUD_1v1")

local HUD = {}   -- DataContext table

-- Panel visibility defaults (will be updated based on game mode)
HUD["P1_Visible"] = "Visible"
HUD["P2_Visible"] = "Visible"
HUD["P3_Visible"] = "Visible"

-- Visibility flags (set by main.cpp before injection, defaults here for safety)
if g_1v1ShowUpgrades == nil then g_1v1ShowUpgrades = true end
if g_1v1ShowBuildings == nil then g_1v1ShowBuildings = false end
if g_1v1ShowUnits == nil then g_1v1ShowUnits = false end

-- layout knobs (mirrors your ResourceHUD; panel widened for future)
local COMPACT        = true
local NAME_COL_W     = COMPACT and 240 or 220
local SCORE_COL_W    = COMPACT and 0   or 0    -- not used now (score moved)
local ROW_HEIGHT     = COMPACT and 76  or 84
local ICON_SIZE      = COMPACT and 16  or 18
local NAME_FONT      = COMPACT and 12  or 13
local STAT_FONT      = COMPACT and 10  or 11
local RES_FONT       = COMPACT and 12  or 13
local PANEL_WIDTH    = COMPACT and 720 or 760
local POP_COL_W      = 200

local _PlayerSheepMax = {}

-- helpers copied from your ResourceHUD style
local function hexA(a, c) return string.format("#%02X%02X%02X%02X", a, c.r, c.g, c.b) end
local function hex(c)     return string.format("#%02X%02X%02X",      c.r, c.g, c.b) end

-- -------- score helpers (safe) --------
local function total_for(entry)
  -- Prefer precomputed score if provided by your Score module.
  if entry and type(entry.score) == "number" and entry.score > 0 then
    return entry.score
  end
  -- Fallback to Score_GetPlayerDataContext if available.
  if _score and _score.data_context and entry and Score_GetPlayerDataContext then
    local ok, ctx = pcall(Score_GetPlayerDataContext, entry.index)
    if ok and ctx and ctx.score then
      return math.floor(
        (ctx.score.military   or 0) +
        (ctx.score.economy    or 0) +
        (ctx.score.society    or 0) +
        (ctx.score.technology or 0)
      )
    end
  end
  return 0
end

-- ===== Helper Functions =====

-- ===============================
-- RELIC TRACKER (all players)
-- ===============================

local _RELIC_DEPOSITS = { "monastery","mosque","church","prayer_tent","pagoda" }
local _RelicTotalBaseline = nil  -- frozen once per match

local function Relic_CountNeutralOnGround()
  if not (EGroup_CreateUnique and EGroup_Filter and EGroup_Count and EGroup_Destroy) then return 0 end
  local eg = EGroup_CreateUnique("eg_rel_all")
  if World_GetAllEntities then World_GetAllEntities(eg) end
  EGroup_Filter(eg, "relic", FILTER_KEEP)  -- blueprint filter (accurate)
  local n = EGroup_Count(eg)
  EGroup_Destroy(eg)
  return n or 0
end

local function Relic_CountOwnedByPlayer(p)
  if not p then return 0 end
  local owned = 0
  local egAll = Player_GetAllEntities(p); if not egAll then return 0 end
  for _, b in ipairs(_RELIC_DEPOSITS) do
    local egB = EGroup_CreateUnique("eg_rel_bld_"..b)
    EGroup_AddEGroup(egB, egAll)
    EGroup_Filter(egB, b, FILTER_KEEP)
    local n = EGroup_Count(egB)
    for i=1,n do
      owned = owned + (Entity_GetStateModelFloat(EGroup_GetEntityAt(egB,i),"Relic_Count") or 0)
    end
    EGroup_Destroy(egB)
  end
  EGroup_Destroy(egAll)
  return math.floor(owned)
end

local function Relic_SetHUD(slot, owned)
  local key = "P"..tostring(slot).."_RelicLabel"
  local col = "P"..tostring(slot).."_RelicColour"
  HUD[key] = string.format("%d/%d", owned, _RelicTotalBaseline or 0)
  HUD[col] = "#B0FFFFFF"
end

local function Relic_ClearHUD(slot)
  HUD["P"..slot.."_RelicLabel"]  = ""
  HUD["P"..slot.."_RelicColour"] = "#00FFFFFF"
end

-- call this once when HUD is shown (same place you reset deer)
function ResetRelicTrackerAll()
  _RelicTotalBaseline = nil
  for s=1,4 do Relic_ClearHUD(s) end
end

-- freeze total on first call, then update each visible row
function UpdateRelicRows(pHandlesBySlot)
  if not _RelicTotalBaseline then
    _RelicTotalBaseline = Relic_CountNeutralOnGround()
  end
  for slot, ph in pairs(pHandlesBySlot) do
    if ph then
      Relic_SetHUD(slot, Relic_CountOwnedByPlayer(ph))
    else
      Relic_ClearHUD(slot)
    end
  end
end


-- =========================================
-- DEER TRACKER (Neutral/GAIA) - compact
-- =========================================
-- Count all GAIA deer using scar_deer only (works in your build: 28 detected)
-- Count only ALIVE deer (ignore corpses)
local function CountNeutralDeer()
  if not (EGroup_CreateUnique and EGroup_FilterResource and EGroup_Destroy) then
    return 0
  end

  local egNeutral = EGroup_CreateUnique("eg_neutral_src")
  local egDeer    = EGroup_CreateUnique("eg_deer_alive")

  -- 1. Get all GAIA units
  if World_GetAllNeutralEntities then
    World_GetAllNeutralEntities(egNeutral)
  end

  -- 2. Filter ONLY deer (use "scar_deer")
  pcall(EGroup_FilterResource, egNeutral, "scar_deer", egDeer)

  -- ✅ 3. Only count deer with health > 0 (alive, not dead carcasses)
  local alive = 0
  local ok, count = pcall(EGroup_CountSpawned, egDeer)
  if ok and type(count) == "number" then
    for i = 1, count do
      local ent = EGroup_GetEntityAt(egDeer, i)
      if ent and Entity_GetHealth and Entity_GetHealth(ent) > 0 then
        alive = alive + 1
      end
    end
  end

  -- cleanup
  pcall(EGroup_Destroy, egNeutral)
  pcall(EGroup_Destroy, egDeer)

  return alive
end


-- State
local _DeerBaseline   = 0     -- initial total (locks after first drop)
local _DeerLocked     = false
local _DeerLast       = nil   -- last seen count
local _DeerFlashTicks = 0     -- >0 = flashing; 6 ticks ~= 3 red blinks

-- Optional: call this at match start / HUD show if you want a fresh run
function ResetDeerTracker()
  _DeerBaseline, _DeerLocked, _DeerLast, _DeerFlashTicks = 0, false, nil, 0
  if HUD then
    HUD["P1_DeerLabel"]  = ""
    HUD["P1_DeerColour"] = "#00FFFFFF"
  end
end

-- Update P1 deer UI
function UpdateGaiaDeerTracker(showForLocal)
  -- hide when local row isn't shown (e.g., 2v2 enemy-only)
  if not showForLocal then
    HUD["P1_DeerLabel"]  = ""
    HUD["P1_DeerColour"] = "#00FFFFFF" -- transparent
    return
  end

  local cur = CountNeutralDeer() or 0

  -- grow baseline until the first drop, then lock
  if not _DeerLocked then
    if cur > _DeerBaseline then
      _DeerBaseline = cur
    elseif cur < _DeerBaseline and _DeerBaseline > 0 then
      _DeerLocked = true
    end
  end

  -- flash on ANY decrease since last tick; stack flashes per deer lost
  if _DeerLast and cur < _DeerLast then
    local lost = (_DeerLast - cur)
    _DeerFlashTicks = _DeerFlashTicks + (6 * lost)  -- 6 ticks ≈ 3 red blinks PER deer
  end
  _DeerLast = cur

  -- label "current/initial"
  local init = (_DeerBaseline > 0) and _DeerBaseline or cur
  HUD["P1_DeerLabel"] = string.format("%d/%d", cur, init)

  -- colour (flash if active)
  if _DeerFlashTicks > 0 then
    local redPhase = (_DeerFlashTicks % 2 == 0)
    HUD["P1_DeerColour"] = redPhase and "#FF4444" or "#FFFFFF"
    _DeerFlashTicks = _DeerFlashTicks - 1
  else
    HUD["P1_DeerColour"] = "#B0FFFFFF"
  end
end


-- Allies/Enemies classifier (matches ResourceHUD logic)
local function build_order_simple()
  local n  = (World_GetPlayerCount and World_GetPlayerCount()) or 0
  local lp = (Game_GetLocalPlayer and Game_GetLocalPlayer()) or nil
  if not lp or n <= 0 then return {}, {} end

  local allies, enemies = {}, {}
  for slot = 1, n do
    local p = World_GetPlayerAt and World_GetPlayerAt(slot) or nil
    if p and p ~= lp then
      -- skip Gaia
      local isGaia = (Player_IsGaia and Player_IsGaia(p)) == true
      if not isGaia then
        local rel = Player_ObserveRelationship and Player_ObserveRelationship(lp, p) or R_ENEMY
        if rel == R_ALLY then
          allies[#allies+1]  = p
        else
          enemies[#enemies+1] = p
        end
      end
    end
  end
  return allies, enemies
end

-- -------- age helpers (resolve blueprints once) --------
local function safe_bp(name)
  if not BP_GetUpgradeBlueprint then return nil end
  local ok, bp = pcall(BP_GetUpgradeBlueprint, name)
  return ok and bp or nil
end


local BP_FEUDAL    = safe_bp("feudal_age")
local BP_CASTLE    = safe_bp("castle_age")
local BP_IMPERIAL  = safe_bp("imperial_age")
local BP_PRO_SCOUTS         = safe_bp("UPGRADE_SCOUT_FORESTER")
local BP_PRO_SCOUTS_MON_IMP = safe_bp("UPGRADE_SCOUT_FORESTER_IMPROVED_MON")

local function GetAgeRoman(p)
  if not (Player_HasUpgrade and p) then return "I" end
  -- check highest to lowest to short-circuit quickly
  local ok4 = pcall(Player_HasUpgrade, p, BP_IMPERIAL); if ok4 and Player_HasUpgrade(p, BP_IMPERIAL) then return "IV" end
  local ok3 = pcall(Player_HasUpgrade, p, BP_CASTLE);   if ok3 and Player_HasUpgrade(p, BP_CASTLE)   then return "III" end
  local ok2 = pcall(Player_HasUpgrade, p, BP_FEUDAL);   if ok2 and Player_HasUpgrade(p, BP_FEUDAL)   then return "II" end
  return "I"
end

-- ======== Upgrades: blueprint + icon maps (Blacksmith + a couple misc) ========
local function _bp(name)
  local ok, bp = pcall(BP_GetUpgradeBlueprint, name)
  return ok and bp or nil
end

-- TIERED groups (we’ll collapse to highest)
local BP_MELEE_ARMOR   = { _bp("UPGRADE_MELEE_ARMOR_I"),   _bp("UPGRADE_MELEE_ARMOR_II"),   _bp("UPGRADE_MELEE_ARMOR_III") }
local BP_RANGED_ARMOR  = { _bp("UPGRADE_RANGED_ARMOR_I"),  _bp("UPGRADE_RANGED_ARMOR_II"),  _bp("UPGRADE_RANGED_ARMOR_III") }
local BP_MELEE_DAMAGE  = { _bp("UPGRADE_MELEE_DAMAGE_I"),  _bp("UPGRADE_MELEE_DAMAGE_II"),  _bp("UPGRADE_MELEE_DAMAGE_III") }
local BP_RANGED_DAMAGE = { _bp("UPGRADE_RANGED_DAMAGE_I"), _bp("UPGRADE_RANGED_DAMAGE_II"), _bp("UPGRADE_RANGED_DAMAGE_III") }

-- Wood harvest (eco) T1/T2/T3 — uses your exact blueprints
local BP_WOOD = {
  _bp("UPGRADE_ECON_RESOURCE_WOOD_HARVEST_RATE_2"), -- T1
  _bp("UPGRADE_ECON_RESOURCE_WOOD_HARVEST_RATE_3"), -- T2
  _bp("UPGRADE_ECON_RESOURCE_WOOD_HARVEST_RATE_4"), -- T3
}

-- ===== Extra singletons (Common / Civ) =====
local BP_TECH_BIOLOGY       = _bp("UPGRADE_TECH_UNIVERSITY_BIOLOGY")
local BP_TECH_ARCHITECTURE  = _bp("UPGRADE_TECH_UNIVERSITY_ARCHITECTURE_4")
local BP_SIEGE_CHEMISTRY    = _bp("UPGRADE_SIEGE_CHEMISTRY")
local BP_SIEGE_CORNED_GP    = _bp("UPGRADE_SIEGE_CORNED_GUNPOWDER") or _bp("UPGRADE_CORNED_GUNPOWDER")
local BP_SIEGE_MATHEMATICS  = _bp("UPGRADE_SIEGE_MATHEMATICS")
local BP_ADJUSTABLE_CROSS   = _bp("UPGRADE_SIEGE_ADJUSTABLE_CROSSBAR") or _bp("UPGRADE_ADJUSTABLE_CROSSBAR")
local BP_RELIGIOUS_HERBAL   = _bp("UPGRADE_UNIT_RELIGIOUS_HERBAL_MEDICINE_4")
-- Religious techs (match your JSONs; include improved_mon fallbacks + lowercase just in case)
local BP_RELIGIOUS_SANCTITY =
  _bp("UPGRADE_UNIT_RELIGIOUS_SANCTITY_4") or
  _bp("upgrade_unit_religious_sanctity_4") or
  _bp("upgrade_unit_religious_sanctity_4_improved_mon")

local BP_RELIGIOUS_PIETY =
  _bp("UPGRADE_UNIT_RELIGIOUS_PIETY_4") or
  _bp("upgrade_unit_religious_piety_4") or
  _bp("upgrade_unit_religious_piety_4_improved_mon")

local BP_RELIGIOUS_ZEALOUS =
  _bp("UPGRADE_UNIT_RELIGIOUS_ZEALOUS_4") or
  _bp("upgrade_unit_religious_zealous_4") or
  _bp("upgrade_unit_religious_zealous_4_improved_mon")

local BP_RELIGIOUS_TITHE =
  _bp("UPGRADE_UNIT_RELIGIOUS_TITHE_BARN_4") or
  _bp("upgrade_unit_religious_tithe_barn_4") or
  _bp("upgrade_unit_religious_tithe_barn_4_improved_mon")

local BP_SPEAR_TACTICS      = _bp("UPGRADE_SPEARMAN_ELITE_SPEAR_TACTICS")

-- ===== Unit rank tiers (COMMON) =====
local BP_SPEARMAN_RANK = {
  _bp("UPGRADE_UNIT_SPEARMAN_2"),
  _bp("UPGRADE_UNIT_SPEARMAN_3"),
  _bp("UPGRADE_UNIT_SPEARMAN_4"),
}
local BP_ARCHER_RANK = {
  false, -- no T2
  _bp("UPGRADE_UNIT_ARCHER_3"),
  _bp("UPGRADE_UNIT_ARCHER_4"),
}
local BP_HORSEMAN_RANK = {
  false, -- no T2
  _bp("UPGRADE_UNIT_HORSEMAN_3"),
  _bp("UPGRADE_UNIT_HORSEMAN_4"),
}

local BP_KNIGHT_RANK = {
  _bp("UPGRADE_UNIT_KNIGHT_3")  or _bp("UPGRADE_UNIT_KNIGHT_3_MON"),  -- Age III (includes Mongol Keshik III)
  _bp("UPGRADE_UNIT_KNIGHT_4")  or _bp("UPGRADE_UNIT_KNIGHT_4_MON"),  -- Age IV (includes Mongol Keshik IV)
}

local BP_MAA_RANK = {
  _bp("UPGRADE_UNIT_MANATARMS_2_ENG") or _bp("UPGRADE_UNIT_MANATARMS_2"), -- Age II
  _bp("UPGRADE_UNIT_MANATARMS_3"), -- Age III
  _bp("UPGRADE_UNIT_MANATARMS_4"), -- Age IV
}

-- ===== Civ techs =====
-- French (use exact tokens from your data; Bloodline is singular)
local BP_FRE_CANTLED_SADDLE  = _bp("UPGRADE_CAVALRY_CANTLED_SADDLE_FRE")

-- English
local BP_ENG_ENCLOSURES      = _bp("UPGRADE_FARM_IMPROVED_ENCLOSURES_ENG")

-- Rus
local BP_RUS_STRELTSY_REGT   = _bp("UPGRADE_STRELTSY_REGIMENT_RUS")

-- Mongols
local BP_MON_MANGUDAI_DMG    = _bp("UPGRADE_MANGUDAI_WEAPON_DAMAGE_MON")
local BP_MON_RAID_BOUNTY     = _bp("UPGRADE_RAID_BOUNTY_MON")
local BP_MON_FAST_PACK_TC    = _bp("UPGRADE_UNIT_TOWN_CENTER_FASTER_PACKING_1_MON")
local BP_MON_KHAN_SIGNAL     = _bp("UPGRADE_KHAN_SIGNAL_ARROW_MON")
local BP_MONGOL_LANCE        = _bp("UPGRADE_CAVALRY_GRAPERED_LANCE_MON") or _bp("upgrade_cavalry_grapered_lance_mon")  -- Age III  :contentReference[oaicite:3]{index=3}
local BP_MONGOL_BARDING      = _bp("UPGRADE_CAVALRY_BARDING_MON")        or _bp("upgrade_cavalry_barding_mon")        -- Age IV    


-- HRE
local BP_HRE_GOTHIC_PLATE    = _bp("UPGRADE_GOTHIC_PLATE_ARMOUR_HRE")

-- Singletons (existing)
local BP_SIEGE_ENGINEERS   = _bp("UPGRADE_SIEGE_ENGINEERS")
local BP_MILITARY_ACADEMY  = _bp("UPGRADE_TECH_MILITARY_ACADEMY")
local BP_WHEELBARROW       = _bp("UPGRADE_UNIT_TOWN_CENTER_WHEELBARROW_1")
-- ===== Single-tech blueprints (University/Town Center techs) =====
local BP_INCENDIARY_ARROWS = _bp("UPGRADE_RANGED_INCENDIARY") or _bp("upgrade_ranged_incendiary")  -- Incendiary Arrows (Age IV) :contentReference[oaicite:4]{index=4}
local BP_TEXTILES          = _bp("UPGRADE_VILLAGER_HEALTH")  or _bp("upgrade_villager_health")     -- Textiles (Age II)       :contentReference[oaicite:5]{index=5}
local BP_SILK_BOWSTRINGS   = _bp("UPGRADE_RANGED_ARCHER_SILKSTRING") or _bp("upgrade_ranged_archer_silkstring") -- Silk Bowstrings (Age IV) :contentReference[oaicite:6]{index=6}
local BP_ELITE_TACTICS     = _bp("UPGRADE_MANATARMS_ELITE_ARMY_TACTICS") or _bp("upgrade_manatarms_elite_army_tactics") -- Elite Army Tactics (Age IV) 

local BP_BLOODLINES = _bp("UPGRADE_CAVALRY_BLOODLINE_FRE")
                    or _bp("UPGRADE_LANDMARK_CAVALRY_BLOODLINE_FRE")
                    or _bp("upgrade_cavalry_bloodline_fre")
                    or _bp("upgrade_landmark_cavalry_bloodline_fre")  -- French Bloodlines :contentReference[oaicite:2]{index=2}

local BP_CHIVALRY   = _bp("UPGRADE_CAVALRY_CHIVALRY_FRE")
                    or _bp("UPGRADE_LANDMARK_CAVALRY_CHIVALRY_FRE")
                    or _bp("upgrade_cavalry_chivalry_fre")
                    or _bp("upgrade_landmark_cavalry_chivalry_fre")  -- French Chivalry :contentReference[oaicite:3]{index=3}

-- Define upgrade BPs for mining (gold) and stone
local BP_MINING_GOLD = {
  _bp("upgrade_econ_resource_gold_harvest_rate_2"),
  _bp("upgrade_econ_resource_gold_harvest_rate_3"),
  _bp("upgrade_econ_resource_gold_harvest_rate_4"),
}
local BP_MINING_STONE = {
  _bp("upgrade_econ_resource_stone_harvest_rate_2"),
  _bp("upgrade_econ_resource_stone_harvest_rate_3"),
  _bp("upgrade_econ_resource_stone_harvest_rate_4"),
}


-- Icon paths (exactly as you gave)
local ICONS = {
  wheelbarrow      = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/wheelbarrow.png",

  professional_scouts = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/hunting_scout.png",
  fire_arrows = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/fire_arrows.png",
  textiles     = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/silk_bowstring.png", -- per your request
  silk_bow     = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/silk_bowstrings.png",
  elite_tactics= "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/elite_army_tactics.png",


  melee_armor      = {
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/melee_armor_technology_1.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/melee_armor_technology_2.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/melee_armor_technology_3.png",
  },
  ranged_armor     = {
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/ranged_armor_technology_1.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/ranged_armor_technology_2.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/ranged_armor_technology_3.png",
  },
  melee_damage     = {
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/melee_damage_technology_1.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/melee_damage_technology_2.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/melee_damage_technology_3.png",
  },
  ranged_damage    = {
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/ranged_damage_technology_1.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/ranged_damage_technology_2.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/ranged_damage_technology_3.png",
  },

    knight_rank = {
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/knight_3.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/knight_4.png",
  },

    maa_rank = {
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/manatarms_2.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/manatarms_3.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/manatarms_4.png",
  },

  


  -- ✅ NEW: Wood (Lumber) upgrades
  lumber = {
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/double_bit_axe.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/wood2_lumber_preservation.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/wood3_cross_cut_saw.png",
  },

    mining = {
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/mining_picks.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/mining2_shaft_mining.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/mining3_cupellation.png",
  },

    -- ===== Extra icons (Common / Ranks / Civ) =====
  biology           = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/biology.png",
  architecture      = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/architecture.png",
  chemistry         = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/chemistry.png",
  corned_gunpowder  = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/corned_gunpowder.png",
  mathematics       = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/mathematics.png",
  adjustable_cross  = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/adjustable_crossbar.png",
  elite_spear       = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/elite_spear_tactics.png",
  
  --Religious
  herbal_medicine   = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/herbal_medicine.png",
  sanctity        = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/sanctity.png",
  piety           = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/piety.png",
  tithe_barn      = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/tithe_barn.png",
  zealous         = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/zealous.png",

  -- unit ranks
  spearman_rank = {
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/spearman_2.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/spearman_3.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/spearman_4.png",
  },
  archer_rank = {
    false,
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/archer_3.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/archer_4.png",
  },
  horseman_rank = {
    false,
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/horseman_3.png",
    "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/horseman_4.png",
  },

  -- civ techs
  cantled_saddle    = "pack://application:,,,/WPFGUI;component/icons/races/french/upgrades/cantled_saddle.png",
  eng_enclosures    = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/improved_enclosures.png",
  rus_streltsy_regt = "pack://application:,,,/WPFGUI;component/icons/races/rus/upgrades/streltsy_regiment.png",

  -- (your list had a 'mongols\\uprades' typo; I’ll mirror it so it resolves if your asset path has the same)
  mon_mangudai_dmg  = "pack://application:,,,/WPFGUI;component/icons/races/mongols/uprades/mangudai_archer_damage.png",
  mon_raid_bounty   = "pack://application:,,,/WPFGUI;component/icons/races/mongols/uprades/raid_bounty.png",
  mon_fast_packing  = "pack://application:,,,/WPFGUI;component/icons/races/mongols/uprades/faster_packing.png",
  mon_khan_signal   = "pack://application:,,,/WPFGUI;component/icons/races/mongols/uprades/signal_arrow_duration.png",
  grappered_lance   = "pack://application:,,,/WPFGUI;component/icons/races/mongols/uprades/steppe_lancers.png",
  steel_barding     = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/steel_barding.png",
  bloodlines        = "pack://application:,,,/WPFGUI;component/icons/races/french/upgrades/bloodlines.png",
  chivalry          = "pack://application:,,,/WPFGUI;component/icons/races/french/upgrades/chivalry.png",



  hre_gothic_plate  = "pack://application:,,,/WPFGUI;component/icons/races/hre/upgrades/gothic_plate.png",

  siege_engineers  = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/siege_engineers.png",
  military_academy = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/military_academy.png",
}


-- returns highest tier owned (3..1) or 0
local function highest_owned_tier(p, bp_list)
  if not (p and Player_HasUpgrade) then return 0 end
  for i = #bp_list, 1, -1 do
    local bp = bp_list[i]
    if bp then
      local ok, has = pcall(Player_HasUpgrade, p, bp)
      if ok and has then return i end
    end
  end
  return 0
end

local function set_upg_icon(key, slot, src, tip)
  HUD[string.format("%s_UpgIcon%d", key, slot)] = src or nil
  HUD[string.format("%s_UpgTip%d",  key, slot)] = tip or nil
end

-- fill icons leftâ†’right; returns next free slot index
local function bind_blacksmith_upgrades(key, p, start_slot)
  local slot = start_slot or 1

  -- 0) Wheelbarrow (non-blacksmith, but useful baseline you asked to keep)
  do
    local show = false
    if BP_WHEELBARROW then
      local ok, has = pcall(Player_HasUpgrade, p, BP_WHEELBARROW)
      show = ok and has
    end
    if show then
      set_upg_icon(key, slot, ICONS.wheelbarrow, "Wheelbarrow")
      slot = slot + 1
    end
  end

  -- Professional Scouts (Mill) — show if any civ variant is owned
  do
    local show = false
    -- You already defined these at the top:
    --   local BP_PRO_SCOUTS         = safe_bp("UPGRADE_SCOUT_FORESTER")
    --   local BP_PRO_SCOUTS_MON_IMP = safe_bp("UPGRADE_SCOUT_FORESTER_IMPROVED_MON")
    if BP_PRO_SCOUTS then
      local ok, has = pcall(Player_HasUpgrade, p, BP_PRO_SCOUTS)
      show = ok and has or false
    end
    if (not show) and BP_PRO_SCOUTS_MON_IMP then
      local ok, has = pcall(Player_HasUpgrade, p, BP_PRO_SCOUTS_MON_IMP)
      show = ok and has or false
    end
    if show then
      set_upg_icon(key, slot, ICONS.professional_scouts, "Professional Scouts")
      slot = slot + 1
    end
  end

  -- 1) Melee Armor
  do
    local t = highest_owned_tier(p, BP_MELEE_ARMOR)
    if t > 0 then
      set_upg_icon(key, slot, ICONS.melee_armor[t], "Melee Armor "..t)
      slot = slot + 1
    end
  end

  -- 2) Ranged Armor
  do
    local t = highest_owned_tier(p, BP_RANGED_ARMOR)
    if t > 0 then
      set_upg_icon(key, slot, ICONS.ranged_armor[t], "Ranged Armor "..t)
      slot = slot + 1
    end
  end

  -- 3) Melee Damage
  do
    local t = highest_owned_tier(p, BP_MELEE_DAMAGE)
    if t > 0 then
      set_upg_icon(key, slot, ICONS.melee_damage[t], "Melee Damage "..t)
      slot = slot + 1
    end
  end

  -- 4) Ranged Damage
  do
    local t = highest_owned_tier(p, BP_RANGED_DAMAGE)
    if t > 0 then
      set_upg_icon(key, slot, ICONS.ranged_damage[t], "Ranged Damage "..t)
      slot = slot + 1
    end
  end

  -- 4.5) Wood (Lumber) — collapse to highest tier owned
  do
    -- requires: local BP_WOOD = { _bp("UPGRADE_ECON_RESOURCE_WOOD_HARVEST_RATE_2"), _bp("UPGRADE_ECON_RESOURCE_WOOD_HARVEST_RATE_3"), _bp("UPGRADE_ECON_RESOURCE_WOOD_HARVEST_RATE_4") }
    -- and ICONS.lumber = { axe, preservation, cross_cut_saw }
    local t = highest_owned_tier(p, BP_WOOD)
    if t > 0 then
      set_upg_icon(key, slot, ICONS.lumber[t], "Lumber "..t)
      slot = slot + 1
    end
  end

  -- 5) Siege Engineers
  if BP_SIEGE_ENGINEERS then
    local ok, has = pcall(Player_HasUpgrade, p, BP_SIEGE_ENGINEERS)
    if ok and has then
      set_upg_icon(key, slot, ICONS.siege_engineers, "Siege Engineers")
      slot = slot + 1
    end
  end

  -- 6) Military Academy
  if BP_MILITARY_ACADEMY then
    local ok, has = pcall(Player_HasUpgrade, p, BP_MILITARY_ACADEMY)
    if ok and has then
      set_upg_icon(key, slot, ICONS.military_academy, "Military Academy")
      slot = slot + 1
    end
  end

  -- ===== KNIGHT RANK (common; show highest only)
  do
    local t = highest_owned_tier(p, BP_KNIGHT_RANK)
    if t > 0 and ICONS.knight_rank and ICONS.knight_rank[t] then
      -- t=1 -> III, t=2 -> IV
      local label = (t == 1 and "III") or (t == 2 and "IV") or tostring(t)
      set_upg_icon(key, slot, ICONS.knight_rank[t], "Knight Rank " .. label)
      slot = slot + 1
    end
  end

  -- ===== MAN-AT-ARMS RANK (common; show highest only)
  do
    local t = highest_owned_tier(p, BP_MAA_RANK)
    if t > 0 and ICONS.maa_rank and ICONS.maa_rank[t] then
      -- t=1 -> II, t=2 -> III, t=3 -> IV
      local label = (t == 1 and "II") or (t == 2 and "III") or (t == 3 and "IV") or tostring(t)
      set_upg_icon(key, slot, ICONS.maa_rank[t], "Man-at-Arms Rank " .. label)
      slot = slot + 1
    end
  end

  -- Unit rank (show highest owned only)
  do
    local t = highest_owned_tier(p, BP_SPEARMAN_RANK)
    if t > 0 and ICONS.spearman_rank[t] then
      set_upg_icon(key, slot, ICONS.spearman_rank[t], "Spearman Rank " .. (t+1))
      slot = slot + 1
    end
  end

  do
    local t = highest_owned_tier(p, BP_ARCHER_RANK)
    if t > 0 and ICONS.archer_rank[t] then
      local tierLabel = (t == 2 and "III") or (t == 3 and "IV") or tostring(t)
      set_upg_icon(key, slot, ICONS.archer_rank[t], "Archer Rank " .. tierLabel)
      slot = slot + 1
    end
  end

  do
    local t = highest_owned_tier(p, BP_HORSEMAN_RANK)
    if t > 0 and ICONS.horseman_rank[t] then
      local tierLabel = (t == 2 and "III") or (t == 3 and "IV") or tostring(t)
      set_upg_icon(key, slot, ICONS.horseman_rank[t], "Horseman Rank " .. tierLabel)
      slot = slot + 1
    end
  end

  -- GOLD MINING UPGRADES (show highest only)
  do
    local t = highest_owned_tier(p, BP_MINING_GOLD)
    if t > 0 and ICONS.mining[t] then
      set_upg_icon(key, slot, ICONS.mining[t], "Gold Mining Upgrade ("..(t+1)..")")
      slot = slot + 1
    end
  end

  -- STONE MINING UPGRADES (show highest only)
  do
    local t = highest_owned_tier(p, BP_MINING_STONE)
    if t > 0 and ICONS.mining[t] then
      set_upg_icon(key, slot, ICONS.mining[t], "Stone Mining Upgrade ("..(t+1)..")")
      slot = slot + 1
    end
  end

    -- ===== INCENDIARY ARROWS (Age IV) =====
  do
    local bp = BP_INCENDIARY_ARROWS
    if bp then
      local ok, has = pcall(Player_HasUpgrade, p, bp)
      if ok and has and ICONS.fire_arrows then
        set_upg_icon(key, slot, ICONS.fire_arrows, "Incendiary Arrows")
        slot = slot + 1
      end
    end
  end

  -- ===== TEXTILES (Age II) =====
  do
    local bp = BP_TEXTILES
    if bp then
      local ok, has = pcall(Player_HasUpgrade, p, bp)
      if ok and has and ICONS.textiles then
        set_upg_icon(key, slot, ICONS.textiles, "Textiles")
        slot = slot + 1
      end
    end
  end

  -- ===== SILK BOWSTRINGS (Age IV) =====
  do
    local bp = BP_SILK_BOWSTRINGS
    if bp then
      local ok, has = pcall(Player_HasUpgrade, p, bp)
      if ok and has and ICONS.silk_bow then
        set_upg_icon(key, slot, ICONS.silk_bow, "Silk Bowstrings")
        slot = slot + 1
      end
    end
  end

  -- ===== ELITE ARMY TACTICS (Age IV) =====
  do
    local bp = BP_ELITE_TACTICS
    if bp then
      local ok, has = pcall(Player_HasUpgrade, p, bp)
      if ok and has and ICONS.elite_tactics then
        set_upg_icon(key, slot, ICONS.elite_tactics, "Elite Army Tactics")
        slot = slot + 1
      end
    end
  end

  -- ===== GRAPPERED LANCE (Mongol Age III) =====
  do
    local bp = BP_MONGOL_LANCE
    if bp then
      local ok, has = pcall(Player_HasUpgrade, p, bp)
      if ok and has and ICONS.grappered_lance then
        set_upg_icon(key, slot, ICONS.grappered_lance, "Grappered Lance")
        slot = slot + 1
      end
    end
  end

  -- ===== STEEL BARDING (Mongol Age IV) =====
  do
    local bp = BP_MONGOL_BARDING
    if bp then
      local ok, has = pcall(Player_HasUpgrade, p, bp)
      if ok and has and ICONS.steel_barding then
        set_upg_icon(key, slot, ICONS.steel_barding, "Steel Barding")
        slot = slot + 1
      end
    end
  end

    -- ===== BLOODLINES (French) =====
  do
    local bp = BP_BLOODLINES
    if bp then
      local ok, has = pcall(Player_HasUpgrade, p, bp)
      if ok and has and ICONS.bloodlines then
        set_upg_icon(key, slot, ICONS.bloodlines, "Bloodlines")
        slot = slot + 1
      end
    end
  end

  -- ===== CHIVALRY (French) =====
  do
    local bp = BP_CHIVALRY
    if bp then
      local ok, has = pcall(Player_HasUpgrade, p, bp)
      if ok and has and ICONS.chivalry then
        set_upg_icon(key, slot, ICONS.chivalry, "Chivalry")
        slot = slot + 1
      end
    end
  end



  -- Common techs (university / siege / religious)
  if BP_TECH_BIOLOGY      and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_TECH_BIOLOGY)      and Player_HasUpgrade(p, BP_TECH_BIOLOGY)      then set_upg_icon(key, slot, ICONS.biology,           "Biology");             slot = slot + 1 end
  if BP_TECH_ARCHITECTURE and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_TECH_ARCHITECTURE) and Player_HasUpgrade(p, BP_TECH_ARCHITECTURE) then set_upg_icon(key, slot, ICONS.architecture,      "Architecture");        slot = slot + 1 end
  if BP_SIEGE_CHEMISTRY   and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_SIEGE_CHEMISTRY)   and Player_HasUpgrade(p, BP_SIEGE_CHEMISTRY)   then set_upg_icon(key, slot, ICONS.chemistry,         "Chemistry");           slot = slot + 1 end
  if BP_SIEGE_CORNED_GP   and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_SIEGE_CORNED_GP)   and Player_HasUpgrade(p, BP_SIEGE_CORNED_GP)   then set_upg_icon(key, slot, ICONS.corned_gunpowder,  "Corned Gunpowder");    slot = slot + 1 end
  if BP_SIEGE_MATHEMATICS and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_SIEGE_MATHEMATICS) and Player_HasUpgrade(p, BP_SIEGE_MATHEMATICS) then set_upg_icon(key, slot, ICONS.mathematics,       "Mathematics");         slot = slot + 1 end
  if BP_ADJUSTABLE_CROSS  and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_ADJUSTABLE_CROSS)  and Player_HasUpgrade(p, BP_ADJUSTABLE_CROSS)  then set_upg_icon(key, slot, ICONS.adjustable_cross,  "Adjustable Crossbar"); slot = slot + 1 end
  if BP_SPEAR_TACTICS     and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_SPEAR_TACTICS)     and Player_HasUpgrade(p, BP_SPEAR_TACTICS)     then set_upg_icon(key, slot, ICONS.elite_spear,       "Elite Spear Tactics"); slot = slot + 1 end
  if BP_RELIGIOUS_HERBAL  and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_RELIGIOUS_HERBAL)  and Player_HasUpgrade(p, BP_RELIGIOUS_HERBAL)  then set_upg_icon(key, slot, ICONS.herbal_medicine,   "Herbal Medicine");     slot = slot + 1 end
    -- Religious techs (new)
  if BP_RELIGIOUS_SANCTITY and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_RELIGIOUS_SANCTITY)
     and Player_HasUpgrade(p, BP_RELIGIOUS_SANCTITY) then
    set_upg_icon(key, slot, ICONS.sanctity, "Sanctity"); slot = slot + 1
  end

  if BP_RELIGIOUS_PIETY and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_RELIGIOUS_PIETY)
     and Player_HasUpgrade(p, BP_RELIGIOUS_PIETY) then
    set_upg_icon(key, slot, ICONS.piety, "Piety"); slot = slot + 1
  end

  if BP_RELIGIOUS_ZEALOUS and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_RELIGIOUS_ZEALOUS)
     and Player_HasUpgrade(p, BP_RELIGIOUS_ZEALOUS) then
    set_upg_icon(key, slot, ICONS.zealous, "Zealous"); slot = slot + 1
  end

  if BP_RELIGIOUS_TITHE and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_RELIGIOUS_TITHE)
     and Player_HasUpgrade(p, BP_RELIGIOUS_TITHE) then
    set_upg_icon(key, slot, ICONS.tithe_barn, "Tithe Barns"); slot = slot + 1
  end

  if BP_FRE_CANTLED_SADDLE and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_FRE_CANTLED_SADDLE) and Player_HasUpgrade(p, BP_FRE_CANTLED_SADDLE) then set_upg_icon(key, slot, ICONS.cantled_saddle,    "Cantled Saddle");      slot = slot + 1 end

  -- Civ techs — English
  if BP_ENG_ENCLOSURES     and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_ENG_ENCLOSURES)     and Player_HasUpgrade(p, BP_ENG_ENCLOSURES)     then set_upg_icon(key, slot, ICONS.eng_enclosures,    "Improved Enclosures"); slot = slot + 1 end

  -- Civ techs — Rus
  if BP_RUS_STRELTSY_REGT  and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_RUS_STRELTSY_REGT)  and Player_HasUpgrade(p, BP_RUS_STRELTSY_REGT)  then set_upg_icon(key, slot, ICONS.rus_streltsy_regt, "Streltsy Regiment");   slot = slot + 1 end

  -- Civ techs — Mongol
  if BP_MON_MANGUDAI_DMG   and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_MON_MANGUDAI_DMG)   and Player_HasUpgrade(p, BP_MON_MANGUDAI_DMG)   then set_upg_icon(key, slot, ICONS.mon_mangudai_dmg,  "Mangudai Weapon Dmg"); slot = slot + 1 end
  if BP_MON_RAID_BOUNTY    and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_MON_RAID_BOUNTY)    and Player_HasUpgrade(p, BP_MON_RAID_BOUNTY)    then set_upg_icon(key, slot, ICONS.mon_raid_bounty,   "Raid Bounty");         slot = slot + 1 end
  if BP_MON_FAST_PACK_TC   and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_MON_FAST_PACK_TC)   and Player_HasUpgrade(p, BP_MON_FAST_PACK_TC)   then set_upg_icon(key, slot, ICONS.mon_fast_packing,  "Faster Packing (TC)"); slot = slot + 1 end
  if BP_MON_KHAN_SIGNAL    and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_MON_KHAN_SIGNAL)    and Player_HasUpgrade(p, BP_MON_KHAN_SIGNAL)    then set_upg_icon(key, slot, ICONS.mon_khan_signal,   "Khan Signal Arrow");   slot = slot + 1 end

  -- Civ techs — HRE
  if BP_HRE_GOTHIC_PLATE   and Player_HasUpgrade and pcall(Player_HasUpgrade, p, BP_HRE_GOTHIC_PLATE)   and Player_HasUpgrade(p, BP_HRE_GOTHIC_PLATE)   then set_upg_icon(key, slot, ICONS.hre_gothic_plate,  "Gothic Plate Armour"); slot = slot + 1 end

  return slot
end


-- -------- villager totals (multi-fallback, safe) --------
local function GetTotalVillagers(p, vFood, vWood, vGold, vStone)
  if p and Player_GetNumVillagers then
    local ok, n = pcall(Player_GetNumVillagers, p)
    if ok and type(n) == "number" and n > 0 then return n end
  end
  if p and Player_GetNumWorkerSquads then
    local ok, n = pcall(Player_GetNumWorkerSquads, p)
    if ok and type(n) == "number" and n > 0 then return n end
  end
  if p and Player_GetEntityCountByUnitType then
    local ok, n = pcall(Player_GetEntityCountByUnitType, p, "Worker")
    if ok and type(n) == "number" and n > 0 then return n end
  end
  -- fallback to sum of per-resource splits (already integers from caller)
  return (vFood or 0) + (vWood or 0) + (vGold or 0) + (vStone or 0)
end

-- Count truly idle workers using Squad_IsIdle (standing around doing nothing)
-- Excludes trade carts, includes only actual villagers
local function CountIdleWorkers(p)
  local idle = 0
  local total = 0

  local ok, _ = pcall(function()
    if not p then return end
    if not Player_GetSquads then return end
    if not SGroup_Count then return end
    if not SGroup_GetSquadAt then return end
    if not Squad_IsIdle then return end
    if not Squad_IsOfType then return end

    local squads = Player_GetSquads(p)
    if not squads then return end

    local count = SGroup_Count(squads)
    for i = 1, count do
      local squad = SGroup_GetSquadAt(squads, i)
      if squad then
        -- Only count workers, exclude trade carts
        if Squad_IsOfType(squad, "worker") and not Squad_IsOfType(squad, "trade_cart") then
          total = total + 1
          -- True idle = standing around doing nothing
          if Squad_IsIdle(squad) then
            idle = idle + 1
          end
        end
      end
    end
  end)

  return idle, total
end

-- =========================================
-- SHEEP TRACKER (Compact v5)
-- =========================================
local function CountSheepForPlayer(p)
  if not p then return 0 end
  if not (EGroup_CreateUnique and Player_GetAllEntities and EGroup_AddEGroup and
          EGroup_FilterResource and EGroup_CountSpawned and EGroup_Destroy) then
    return 0
  end

  local pid     = (Player_GetID and Player_GetID(p)) or 0
  local egAll   = EGroup_CreateUnique("eg_p_all_"..tostring(pid))
  local egSheep = EGroup_CreateUnique("eg_p_sheep_"..tostring(pid))

  pcall(function()
    local owned = Player_GetAllEntities(p)     -- returns an EGroup in this build
    if owned then EGroup_AddEGroup(egAll, owned) end
    EGroup_FilterResource(egAll, "scar_sheep", egSheep)
  end)

  local n = 0
  local ok, count = pcall(EGroup_CountSpawned, egSheep)
  if ok and type(count) == "number" then n = count end

  -- best effort cleanup
  pcall(EGroup_Destroy, egAll)
  pcall(EGroup_Destroy, egSheep)
  return n
end

-- Count GAIA (neutral) sheep on the map safely
local function CountNeutralSheep()
  if not (EGroup_CreateUnique and EGroup_FilterResource and EGroup_CountSpawned and EGroup_Destroy) then
    return 0
  end
  local egNeutral = EGroup_CreateUnique("eg_neutral_all_src")
  local egSheep   = EGroup_CreateUnique("eg_neutral_sheep_dst")

  pcall(function()
    if World_GetAllNeutralEntities then
      World_GetAllNeutralEntities(egNeutral)
    elseif World_GetPlayerAt and Player_GetAllEntities and EGroup_AddEGroup then
      local gaia = World_GetPlayerAt(0)
      if gaia then
        local owned = Player_GetAllEntities(gaia)
        if owned then EGroup_AddEGroup(egNeutral, owned) end
      end
    end
    EGroup_FilterResource(egNeutral, "scar_sheep", egSheep)
  end)

  local n = 0
  local ok, count = pcall(EGroup_CountSpawned, egSheep)
  if ok and type(count) == "number" then n = count end

  -- best effort cleanup
  pcall(EGroup_Destroy, egNeutral)
  pcall(EGroup_Destroy, egSheep)
  return n
end

-- ===== Neutral (GAIA) sheep baseline tracker =====
-- We record the highest neutral count we ever observe, then lock it once it drops.
local _GaiaBaseline       = 0
local _GaiaBaselineLocked = false

local function UpdateGaiaSheepBaselines()
  local cur = CountNeutralSheep() or 0

  if not _GaiaBaselineLocked then
    if cur > _GaiaBaseline then
      _GaiaBaseline = cur                    -- keep growing until first drop
    elseif cur < _GaiaBaseline and _GaiaBaseline > 0 then
      _GaiaBaselineLocked = true             -- lock when it starts decreasing
    end
  end

  -- Bindings for UI:
  HUD["InitialGaiaSheep"]   = tostring(_GaiaBaseline) -- fixed (never decreases)
  HUD["GaiaSheepRemaining"] = tostring(cur)           -- live (decreases)
end

-- -------- find YOU and first non-ally opponent --------
local function build_1v1()
  local you = Game_GetLocalPlayer and Game_GetLocalPlayer() or nil
  if not you then return nil, nil end

  local n = (World_GetPlayerCount and World_GetPlayerCount()) or 0
  local opp = nil

  for i = 1, n do
    local p = World_GetPlayerAt and World_GetPlayerAt(i) or nil
    if p and p ~= you then
      local rel = (Player_ObserveRelationship and Player_ObserveRelationship(you, p)) or R_ENEMY
      if rel ~= R_ALLY then
        opp = p
        break
      end
    end
  end

  return you, opp
end

-- ===== Inline Upgrades (Wheelbarrow only for now) =====
local UPG_MAX_SLOTS = 20
local ICON_WHEELBARROW = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/wheelbarrow.png"

local BP_WB_COMMON, BP_WB_MON_TC, BP_WB_MON_IMPROVED = nil, nil, nil

local function Upg_ClearIcons(key)
  for i=1, UPG_MAX_SLOTS do
    HUD[string.format("%s_UpgIcon%d", key, i)] = nil
    HUD[string.format("%s_UpgTip%d",  key, i)] = nil
  end
  HUD[key.."_UpgCount"] = 0
end

local function Upg_SetIcon(key, idx, uri, tip)
  HUD[string.format("%s_UpgIcon%d", key, idx)] = uri
  HUD[string.format("%s_UpgTip%d",  key, idx)] = tip
end

local function Upg_TryField(root, path)
  local node = root
  for part in string.gmatch(path, "[^%.]+") do
    if type(node) ~= "table" then return nil end
    node = node[part]; if node == nil then return nil end
  end
  return node
end

local function Upg_TryBPs(names)
  if not BP_GetUpgradeBlueprint then return nil end
  for _, name in ipairs(names) do
    local ok, bp = pcall(BP_GetUpgradeBlueprint, name)
    if ok and bp then return bp end
  end
  return nil
end

local function Upg_Has(p, bp)
  if not (p and bp and Player_HasUpgrade) then return false end
  local ok, res = pcall(Player_HasUpgrade, p, bp)
  return ok and res == true
end

-- Resolve BPs once (COMMON + Mongol variants)
local function Upg_InitLocal()
  pcall(function()
    if type(UPG) == "table" then
      BP_WB_COMMON       = Upg_TryField(UPG, "COMMON.UPGRADE_UNIT_TOWN_CENTER_WHEELBARROW_1") or BP_WB_COMMON
      BP_WB_MON_TC       = Upg_TryField(UPG, "MONGOL.UPGRADE_UNIT_TOWN_CENTER_WHEELBARROW_1_MON") or BP_WB_MON_TC
      BP_WB_MON_IMPROVED = Upg_TryField(UPG, "MONGOL.UPGRADE_UNIT_WHEELBARROW_1_IMPROVED_MON") or BP_WB_MON_IMPROVED
    end
  end)
  pcall(function()
    if not BP_WB_COMMON then
      BP_WB_COMMON = Upg_TryBPs({
        "UPGRADE_UNIT_TOWN_CENTER_WHEELBARROW_1",
        "upgrade_unit_town_center_wheelbarrow_1",
        "UPGRADE_ECONOMY_WHEELBARROW",
        "wheelbarrow",
      })
    end
    if not BP_WB_MON_TC then
      BP_WB_MON_TC = Upg_TryBPs({
        "UPGRADE_UNIT_TOWN_CENTER_WHEELBARROW_1_MON",
        "upgrade_unit_town_center_wheelbarrow_1_mon",
      })
    end
    if not BP_WB_MON_IMPROVED then
      BP_WB_MON_IMPROVED = Upg_TryBPs({
        "UPGRADE_UNIT_WHEELBARROW_1_IMPROVED_MON",
        "upgrade_unit_wheelbarrow_1_improved_mon",
      })
    end
  end)
end

-- Safe upgrade checker (never crashes)
local function has_upg_safe(p, bp)
  if not (p and bp and Player_HasUpgrade) then return false end
  local ok, res = pcall(Player_HasUpgrade, p, bp)
  return ok and res == true
end


-- Paint upgrades for one player (Wheelbarrow only)
-- Paint upgrades for one player (Wheelbarrow + Professional Scouts)
local function Upg_Bind(key, p)
  if type(Upg_ClearIcons) == "function" then
    Upg_ClearIcons(key)
  else
    -- ultra-safe local clear if helper missing
    for i = 1, 10 do
      HUD[string.format("%s_UpgIcon%d", key, i)] = nil
      HUD[string.format("%s_UpgTip%d",  key, i)] = nil
    end
    HUD[key.."_UpgCount"] = 0
  end

  local slot = 1

  -- WHEELBARROW (handles common + Mongol variants)
  if has_upg_safe(p, BP_WB_COMMON) or has_upg_safe(p, BP_WB_MON_TC) or has_upg_safe(p, BP_WB_MON_IMPROVED) then
    Upg_SetIcon(key, slot, "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/wheelbarrow.png", "Wheelbarrow")
    slot = slot + 1
  end

  -- PROFESSIONAL SCOUTS (Mill)
  if has_upg_safe(p, BP_PRO_SCOUTS) or has_upg_safe(p, BP_PRO_SCOUTS_MON_IMP) then
    Upg_SetIcon(key, slot, "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/hunting_scout.png", "Professional Scouts")
    slot = slot + 1
  end

  HUD[key.."_UpgCount"] = slot - 1
end


-- ===== Building tracking for enemy (P2 only) =====
local BLD_MAX_SLOTS = 17

-- Building types and their display info (from game reference)
local BLD_DATA = {
  -- Core buildings
  { type = "scar_town_center",  name = "Town Center",   icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/town_center.png" },
  { type = "scar_house",        name = "House",         icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/house.png" },
  { type = "farm",              name = "Farm",          icon = "pack://application:,,,/WPFGUI;component/icons/resources/resource_farm_icon.png" },
  -- Military
  { type = "scar_barracks",     name = "Barracks",      icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/barracks.png" },
  { type = "scar_archeryrange", name = "Archery Range", icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/archery_range.png" },
  { type = "scar_stable",       name = "Stable",        icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/stable.png" },
  { type = "scar_siegeworkshop",name = "Siege Workshop",icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/siege_workshop.png" },
  -- Economy
  { type = "scar_miningcamp",   name = "Mining Camp",   icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/gold_mining_camp.png" },
  { type = "scar_lumbercamp",   name = "Lumber Camp",   icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/lumber_camp.png" },
  { type = "scar_gristmill",    name = "Mill",          icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/gristmill.png" },
  { type = "scar_market",       name = "Market",        icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/market.png" },
  -- Tech/Religion
  { type = "scar_blacksmith",   name = "Blacksmith",    icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/blacksmith.png" },
  { type = "scar_monastery",    name = "Monastery",     icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/monastery.png" },
  { type = "scar_university",   name = "University",    icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/university.png" },
  { type = "scar_arsenal",      name = "Arsenal",       icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/arsenal.png" },
  -- Defense
  { type = "scar_outpost",      name = "Outpost",       icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/outpost.png" },
  { type = "scar_keep",         name = "Keep",          icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/keep.png" },
  { type = "stone_wall",        name = "Stone Wall",    icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/stone_wall.png" },
  { type = "palisade_wall",     name = "Palisade Wall", icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/palisade_wall.png" },
  -- Other
  { type = "scar_dock",         name = "Dock",          icon = "pack://application:,,,/WPFGUI;component/icons/races/common/buildings/dock.png" },
  { type = "scar_mercenary_house",   name = "Mercenary House",   icon = "pack://application:,,,/WPFGUI;component/icons/races/byzantine/buildings/mercenary_house.png" },
  { type = "scar_military_college",  name = "Military College",  icon = "pack://application:,,,/WPFGUI;component/icons/races/crucible_mode/abilities/military_college.png" },
}

-- Format seconds
local function FormatTime(seconds)
  if seconds < 0 then return "?" end
  if seconds < 60 then return string.format("%ds", math.floor(seconds)) end
  return string.format("%d:%02d", math.floor(seconds/60), math.floor(seconds%60))
end

-- Landmark types to filter for
local LANDMARK_TYPES = {
  "wonder_dark_age",
  "wonder_feudal_age",
  "wonder_castle_age",
}

-- Track last landmark shown per player (persists after completion)
local g_LastLandmark = {
  P1 = { name = nil, pct = nil },
  P2 = { name = nil, pct = nil },
}

-- Update landmark panel - find landmark via EGroup filtering, get name via BP_GetEntityUIInfo
local function Landmark_PopulateEnemy(key, player)
  if not player then
    HUD[key .. "_LandmarkLabel"] = ""
    HUD[key .. "_LandmarkName"] = ""
    HUD[key .. "_LandmarkPct"] = ""
    return
  end
  if not g_1v1ShowBuildings then return end

  local ok, _ = pcall(function()
    local pid = Player_GetID(player)
    if not pid then return end

    local foundBuilding = false

    -- Search for landmark being built
    for _, lmkType in ipairs(LANDMARK_TYPES) do
      local eg = EGroup_CreateUnique("lmk_" .. pid .. "_" .. lmkType)
      local owned = Player_GetAllEntities(player)
      if owned then
        EGroup_AddEGroup(eg, owned)
        EGroup_Filter(eg, lmkType, FILTER_KEEP)
        local cnt = EGroup_Count(eg)
        if cnt > 0 then
          local ent = EGroup_GetEntityAt(eg, 1)
          if ent then
            local progress = Entity_GetBuildingProgress(ent)
            if progress and progress < 1 then
              -- Currently building
              local bp = Entity_GetBlueprint(ent)
              if bp then
                local uiInfo = BP_GetEntityUIInfo(bp)
                if uiInfo and uiInfo.screenName and uiInfo.screenName ~= "" then
                  local pct = math.floor(progress * 100)
                  HUD[key .. "_LandmarkLabel"] = "Landmark:"
                  HUD[key .. "_LandmarkName"] = uiInfo.screenName
                  HUD[key .. "_LandmarkPct"] = string.format("%d%%", pct)
                  g_LastLandmark[key].name = uiInfo.screenName
                  g_LastLandmark[key].pct = pct
                  foundBuilding = true
                  EGroup_Destroy(eg)
                  return
                end
              end
            end
          end
        end
        EGroup_Destroy(eg)
      end
    end

    -- No landmark being built - show last one as complete
    if not foundBuilding and g_LastLandmark[key] and g_LastLandmark[key].name then
      HUD[key .. "_LandmarkLabel"] = "Landmark:"
      HUD[key .. "_LandmarkName"] = g_LastLandmark[key].name
      HUD[key .. "_LandmarkPct"] = "100%"
    end
  end)
end

local function Bld_ClearIcons(key)
  for i=1, BLD_MAX_SLOTS do
    HUD[string.format("%s_BldIcon%d", key, i)] = nil
    HUD[string.format("%s_BldCount%d", key, i)] = nil
    HUD[string.format("%s_BldTip%d", key, i)] = nil
  end
end

local function Bld_SetSlot(key, idx, icon, count, tip)
  HUD[string.format("%s_BldIcon%d", key, idx)] = icon
  HUD[string.format("%s_BldCount%d", key, idx)] = count > 0 and tostring(count) or nil
  HUD[string.format("%s_BldTip%d", key, idx)] = tip
end

-- Count buildings (simplified - follows CountSheepForPlayer pattern)
local function Bld_CountForPlayer(player)
  local counts = {}
  if not player then return counts end

  -- Check required functions exist
  if not (EGroup_CreateUnique and Player_GetAllEntities and EGroup_AddEGroup and
          EGroup_Filter and EGroup_Count and EGroup_Destroy) then
    return counts
  end

  -- Check FILTER_KEEP exists
  if not FILTER_KEEP then return counts end

  local pid = (Player_GetID and Player_GetID(player)) or 0
  local token = "bld_" .. tostring(pid)

  for _, bld in ipairs(BLD_DATA) do
    local ok, cnt = pcall(function()
      local egAll = EGroup_CreateUnique("eg_" .. token .. "_all")
      local egBld = EGroup_CreateUnique("eg_" .. token .. "_" .. bld.type)

      local owned = Player_GetAllEntities(player)
      if owned then
        EGroup_AddEGroup(egAll, owned)
      end

      EGroup_AddEGroup(egBld, egAll)
      EGroup_Filter(egBld, bld.type, FILTER_KEEP)

      local n = EGroup_Count(egBld) or 0

      EGroup_Destroy(egAll)
      EGroup_Destroy(egBld)

      return n
    end)

    if ok and cnt then
      counts[bld.type] = cnt
    else
      counts[bld.type] = 0
    end
  end

  return counts
end

-- Populate building icons for P2 (enemy)
local function Bld_PopulateEnemy(key, player)
  Bld_ClearIcons(key)
  if not player then return end
  if not g_1v1ShowBuildings then return end

  local counts = Bld_CountForPlayer(player)
  local slot = 1

  for _, bld in ipairs(BLD_DATA) do
    local c = counts[bld.type] or 0
    if c > 0 and slot <= BLD_MAX_SLOTS then
      Bld_SetSlot(key, slot, bld.icon, c, bld.name .. ": " .. c)
      slot = slot + 1
    end
  end
end


-- ===== UNITS PANEL =====
local UNIT_MAX_SLOTS = 17

local UNIT_DATA = {
  -- Villagers
  { type = "scar_villager",           name = "Villager",          icon = "pack://application:,,,/WPFGUI;component/icons/races/hre_historic/units/gilded_villager.png" },
  { type = "scar_shield_villager",    name = "Shield Villager",   icon = "pack://application:,,,/WPFGUI;component/icons/races/hre_historic/units/gilded_villager.png" },
  { type = "scar_armed_villager",     name = "Armed Villager",    icon = "pack://application:,,,/WPFGUI;component/icons/races/hre_historic/units/gilded_villager.png" },
  -- Support
  { type = "scar_scout",              name = "Scout",             icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/scout.png" },
  { type = "scar_monk",               name = "Monk",              icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/monk.png" },
  { type = "scar_trade_cart",         name = "Trade Cart",        icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/trade_cart.png" },
  -- Infantry
  { type = "scar_spearman",           name = "Spearman",          icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/spearman.png" },
  { type = "scar_manatarms",          name = "Man-at-Arms",       icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/manatarms.png" },
  { type = "scar_landsknecht",        name = "Landsknecht",       icon = "pack://application:,,,/WPFGUI;component/icons/races/hre/units/landsknecht.png" },
  -- Ranged
  { type = "scar_archer",             name = "Archer",            icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/archer.png" },
  { type = "scar_crossbowman",        name = "Crossbowman",       icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/crossbowman.png" },
  { type = "scar_repeater_crossbow",  name = "Repeater Crossbow", icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/crossbowman.png" },
  { type = "scar_handcannon",         name = "Handcannoneer",     icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/handcannon.png" },
  { type = "scar_grenadier",          name = "Grenadier",         icon = "pack://application:,,,/WPFGUI;component/icons/races/chinese/units/grenadier_2.png" },
  -- Cavalry
  { type = "scar_horseman",           name = "Horseman",          icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/horseman.png" },
  { type = "scar_knight",             name = "Knight",            icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/knight.png" },
  { type = "scar_horsearcher",        name = "Horse Archer",      icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/horsearcher.png" },
  { type = "scar_firelancer",         name = "Fire Lancer",       icon = "pack://application:,,,/WPFGUI;component/images/daily_quests/firelancer.png" },
  -- Camel
  { type = "scar_camel_archer",       name = "Camel Archer",      icon = "pack://application:,,,/WPFGUI;component/icons/races/abbasid/units/camel_archer_2.png" },
  { type = "scar_camel_rider",        name = "Camel Rider",       icon = "pack://application:,,,/WPFGUI;component/icons/races/abbasid_historic/units/desert_rider_2.png" },
  -- Support unit
  { type = "scar_mehter",             name = "Mehter",            icon = "pack://application:,,,/WPFGUI;component/icons/races/ottoman/units/mehter.png" },
  -- Siege
  { type = "scar_ram",                name = "Ram",               icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/ram.png" },
  { type = "scar_siege_tower",        name = "Siege Tower",       icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/siegetower.png" },
  { type = "scar_mangonel",           name = "Mangonel",          icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/mangonel.png" },
  { type = "scar_springald",          name = "Springald",         icon = "pack://application:,,,/WPFGUI;component/icons/races/common/upgrades/springald.png" },
  { type = "scar_ribauldequin",       name = "Ribauldequin",      icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/ribauldequin.png" },
  { type = "scar_cannon",             name = "Cannon",            icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/cannon.png" },
  { type = "scar_bombard",            name = "Bombard",           icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/bombard.png" },
  { type = "scar_culverin",           name = "Culverin",          icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/culverin.png" },
  { type = "scar_trebuchet",          name = "Trebuchet",         icon = "pack://application:,,,/WPFGUI;component/icons/races/common/units/trebuchet.png" },
}

local function Unit_ClearIcons(key)
  for i=1, UNIT_MAX_SLOTS do
    HUD[string.format("%s_UnitIcon%d", key, i)] = nil
    HUD[string.format("%s_UnitCount%d", key, i)] = nil
    HUD[string.format("%s_UnitTip%d", key, i)] = nil
  end
end

local function Unit_SetSlot(key, idx, icon, count, tip)
  HUD[string.format("%s_UnitIcon%d", key, idx)] = icon
  HUD[string.format("%s_UnitCount%d", key, idx)] = count > 0 and tostring(count) or nil
  HUD[string.format("%s_UnitTip%d", key, idx)] = tip
end

-- Count units for player using Cardinal_ConvertTypeToSquadBlueprint + Squad_GetBlueprint comparison
local function Unit_CountForPlayer(player)
  local counts = {}
  if not player then return counts end

  -- Initialize all counts to 0
  for _, unit in ipairs(UNIT_DATA) do
    counts[unit.type] = 0
  end

  -- Check required functions
  if not Player_GetSquads then return counts end
  if not SGroup_Count then return counts end
  if not SGroup_GetSquadAt then return counts end
  if not Squad_GetBlueprint then return counts end
  if not Cardinal_ConvertTypeToSquadBlueprint then return counts end

  -- Pre-compute blueprints for all unit types
  local blueprints = {}
  for _, unit in ipairs(UNIT_DATA) do
    local ok, bp = pcall(Cardinal_ConvertTypeToSquadBlueprint, unit.type, player)
    if ok and bp then
      blueprints[unit.type] = bp
    end
  end

  -- Get all squads for player
  local squads = Player_GetSquads(player)
  if not squads then return counts end

  local ok, squadCount = pcall(SGroup_Count, squads)
  if not ok or type(squadCount) ~= "number" then return counts end

  -- Count each squad by comparing blueprints
  for i = 1, squadCount do
    local squad = SGroup_GetSquadAt(squads, i)
    if squad then
      local sqBpOk, sqBp = pcall(Squad_GetBlueprint, squad)
      if sqBpOk and sqBp then
        for _, unit in ipairs(UNIT_DATA) do
          if blueprints[unit.type] and sqBp == blueprints[unit.type] then
            counts[unit.type] = counts[unit.type] + 1
            break
          end
        end
      end
    end
  end

  return counts
end

-- Populate unit icons for P2 (enemy)
local function Unit_PopulateEnemy(key, player)
  Unit_ClearIcons(key)
  if not player then return end
  if not g_1v1ShowUnits then return end

  local counts = Unit_CountForPlayer(player)
  local slot = 1

  for _, unit in ipairs(UNIT_DATA) do
    local c = counts[unit.type] or 0
    if c > 0 and slot <= UNIT_MAX_SLOTS then
      Unit_SetSlot(key, slot, unit.icon, c, unit.name .. ": " .. c)
      slot = slot + 1
    end
  end
end


-- One row of cells that fits into the parent Grid's columns (MAIN row only)
local function row_cells_xaml(key, row_index)
  local ICON_VIL    = 18   -- villager / idle / deer / relic
  local ICON_POP    = 20   -- population (reuse for pop/sheep/herd icons)
  local ICON_ARMY   = 16   -- army line icons
  local ICON_WORKER = 16   -- worker mini icon
  local ICON_RES    = 24   -- resource icons

  return string.format([[

    <!-- ===== Row %d background (transparent gradient) ===== -->
    <Border Grid.Row="%d" Grid.Column="0" Grid.ColumnSpan="5"
            HorizontalAlignment="Stretch" ClipToBounds="True"
            CornerRadius="8" Margin="0,0,0,6"
            BorderThickness="1" Opacity="0.95"
            UseLayoutRounding="True" SnapsToDevicePixels="True">
      <Border.Background>
        <LinearGradientBrush StartPoint="0,0" EndPoint="1,0.3">
          <GradientStop Color="#10FFFFFF" Offset="0.0"/>
          <GradientStop Color="#05FFFFFF" Offset="0.5"/>
          <GradientStop Color="#08FFFFFF" Offset="1.0"/>
        </LinearGradientBrush>
      </Border.Background>
      <Border.BorderBrush>
        <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
          <GradientStop Color="{Binding [%s_BorderColor]}" Offset="0.0"/>
          <GradientStop Color="#15FFFFFF" Offset="0.5"/>
          <GradientStop Color="{Binding [%s_BorderColor]}" Offset="1.0"/>
        </LinearGradientBrush>
      </Border.BorderBrush>
    </Border>

    <!-- ===== 1) SCORE (premium with glow) ===== -->
    <Border Grid.Row="%d" Grid.Column="0" CornerRadius="10,0,0,10" Padding="8,4,8,4" Margin="4,4,4,4"
            UseLayoutRounding="True" SnapsToDevicePixels="True">
      <Border.Background>
        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
          <GradientStop Color="{Binding [%s_HeaderBg]}" Offset="0.0"/>
          <GradientStop Color="#40000000" Offset="1.0"/>
        </LinearGradientBrush>
      </Border.Background>
      <Border.BorderBrush>
        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
          <GradientStop Color="#50FFFFFF" Offset="0.0"/>
          <GradientStop Color="#10FFFFFF" Offset="1.0"/>
        </LinearGradientBrush>
      </Border.BorderBrush>
      <Border.BorderThickness>0,0,1,0</Border.BorderThickness>
      <TextBlock Text="{Binding [%s_Score]}" Foreground="#FFFFFF" FontSize="%d" FontWeight="Bold"
                 HorizontalAlignment="Center" VerticalAlignment="Center">
        <TextBlock.Effect>
          <DropShadowEffect Color="#000000" BlurRadius="4" ShadowDepth="1" Opacity="0.5"/>
        </TextBlock.Effect>
      </TextBlock>
    </Border>

    <!-- ===== 2) NAME + VILLAGERS (premium styling) ===== -->
    <Border Grid.Row="%d" Grid.Column="1" Padding="10,4,10,8"
            UseLayoutRounding="True" SnapsToDevicePixels="True">
      <Border.BorderBrush>
        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
          <GradientStop Color="#30FFFFFF" Offset="0.0"/>
          <GradientStop Color="#10FFFFFF" Offset="1.0"/>
        </LinearGradientBrush>
      </Border.BorderBrush>
      <Border.BorderThickness>0,0,1,0</Border.BorderThickness>
      <StackPanel Orientation="Vertical" VerticalAlignment="Center">
        <TextBlock Text="{Binding [%s_Name]}" Foreground="{Binding [%s_Accent]}"
                   FontSize="%d" FontWeight="Bold" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis">
          <TextBlock.Effect>
            <DropShadowEffect Color="#000000" BlurRadius="6" ShadowDepth="1" Opacity="0.6"/>
          </TextBlock.Effect>
        </TextBlock>

        <StackPanel Orientation="Horizontal" Margin="0,4,0,0">
          <Image Source="pack://application:,,,/WPFGUI;component/icons/races/common/units/villager.png"
                 Width="%d" Height="%d" Margin="0,0,5,0" Opacity="0.95">
            <Image.Effect>
              <DropShadowEffect Color="#000000" BlurRadius="3" ShadowDepth="1" Opacity="0.4"/>
            </Image.Effect>
          </Image>
          <TextBlock Text="{Binding [%s_TotalVillager], StringFormat=Total Villagers: {0}}"
                     Foreground="#7DD3FC" FontSize="%d" FontWeight="SemiBold"/>
        </StackPanel>

        <StackPanel Orientation="Horizontal" Margin="0,3,0,0">
          <Image Source="pack://application:,,,/WPFGUI;component/icons/resources/resource_idle_villager_zzz.png"
                 Width="%d" Height="%d" Margin="0,0,5,0" Opacity="0.95">
            <Image.Effect>
              <DropShadowEffect Color="#000000" BlurRadius="3" ShadowDepth="1" Opacity="0.4"/>
            </Image.Effect>
          </Image>
          <TextBlock Text="{Binding [%s_IdleVillager], StringFormat=Idle Villagers: {0}}"
                     Foreground="{Binding [%s_IdleVillagerColour]}" FontSize="%d" FontWeight="SemiBold"/>
        </StackPanel>

        <!-- Deer + Relics in a Grid for tidy alignment -->
        <Grid Margin="0,2,0,0" UseLayoutRounding="True" SnapsToDevicePixels="True" VerticalAlignment="Center">
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="8"/>   <!-- spacer -->
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
          </Grid.ColumnDefinitions>

          <!-- Deer -->
          <Image Grid.Column="0"
                 Source="pack://application:,,,/WPFGUI;component/icons/gaia/animals/deer.png"
                 Width="%d" Height="%d" Margin="0,0,4,0" Opacity="0.95" VerticalAlignment="Center"/>
          <TextBlock Grid.Column="1" Text="{Binding [%s_DeerLabel]}"
                     Foreground="{Binding [%s_DeerColour]}" FontSize="%d" FontWeight="Bold" VerticalAlignment="Center"/>

          <!-- Relics held -->
          <Image Grid.Column="3"
                 Source="pack://application:,,,/WPFGUI;component/icons/resources/resource_relics_held_icon.png"
                 Width="%d" Height="%d" Margin="0,0,4,0" Opacity="0.95" VerticalAlignment="Center"/>
          <TextBlock Grid.Column="4" Text="{Binding [%s_RelicLabel]}"
                     Foreground="{Binding [%s_RelicColour]}" FontSize="%d" FontWeight="Bold" VerticalAlignment="Center"/>
        </Grid>
      </StackPanel>
    </Border>

    <!-- ===== 3) POP + SHEEP + HERD (premium styling) ===== -->
    <Border Grid.Row="%d" Grid.Column="2" Padding="12,4,12,8"
            UseLayoutRounding="True" SnapsToDevicePixels="True">
      <Border.BorderBrush>
        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
          <GradientStop Color="#30FFFFFF" Offset="0.0"/>
          <GradientStop Color="#10FFFFFF" Offset="1.0"/>
        </LinearGradientBrush>
      </Border.BorderBrush>
      <Border.BorderThickness>0,0,1,0</Border.BorderThickness>
      <Grid VerticalAlignment="Center" UseLayoutRounding="True" SnapsToDevicePixels="True">
        <Grid.RowDefinitions>
          <RowDefinition Height="Auto"/>
          <RowDefinition Height="Auto"/>
          <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <!-- Row 0: Population -->
        <Grid Grid.Row="0">
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
          </Grid.ColumnDefinitions>
          <Image Grid.Column="0" Source="pack://application:,,,/WPFGUI;component/icons/races/common/buildings/house.png"
                 Width="%d" Height="%d" Margin="0,0,5,0" Opacity="0.95" VerticalAlignment="Center">
            <Image.Effect>
              <DropShadowEffect Color="#000000" BlurRadius="3" ShadowDepth="1" Opacity="0.4"/>
            </Image.Effect>
          </Image>
          <TextBlock Grid.Column="1" Text="{Binding [%s_Pop]}" Foreground="#F0F0F0" FontSize="%d" FontWeight="Bold"
                     TextWrapping="NoWrap" VerticalAlignment="Center"/>
          <Border Grid.ColumnSpan="2" VerticalAlignment="Bottom" Margin="0,2,0,0">
            <Border.BorderBrush>
              <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                <GradientStop Color="#00FFFFFF" Offset="0.0"/>
                <GradientStop Color="#30FFFFFF" Offset="0.5"/>
                <GradientStop Color="#00FFFFFF" Offset="1.0"/>
              </LinearGradientBrush>
            </Border.BorderBrush>
            <Border.BorderThickness>0,0,0,1</Border.BorderThickness>
          </Border>
        </Grid>

        <!-- Row 1: Sheep (player-owned ONLY) -->
        <Grid Grid.Row="1" Margin="0,4,0,0">
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
          </Grid.ColumnDefinitions>
          <Image Grid.Column="0" Source="pack://application:,,,/WPFGUI;component/icons/gaia/animals/sheep.png"
                 Width="%d" Height="%d" Margin="0,0,5,0" Opacity="0.95" VerticalAlignment="Center">
            <Image.Effect>
              <DropShadowEffect Color="#000000" BlurRadius="3" ShadowDepth="1" Opacity="0.4"/>
            </Image.Effect>
          </Image>
          <TextBlock Grid.Column="1" Text="{Binding [%s_SheepTotal]}" Foreground="#F0F0F0" FontSize="%d" FontWeight="Bold"
                     TextWrapping="NoWrap" VerticalAlignment="Center"/>
          <Border Grid.ColumnSpan="2" VerticalAlignment="Bottom" Margin="0,2,0,0">
            <Border.BorderBrush>
              <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                <GradientStop Color="#00FFFFFF" Offset="0.0"/>
                <GradientStop Color="#30FFFFFF" Offset="0.5"/>
                <GradientStop Color="#00FFFFFF" Offset="1.0"/>
              </LinearGradientBrush>
            </Border.BorderBrush>
            <Border.BorderThickness>0,0,0,1</Border.BorderThickness>
          </Border>
        </Grid>

        <!-- Row 2: Herd (GAIA remaining / initial) -->
        <Grid Grid.Row="2" Margin="0,4,0,0">
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
          </Grid.ColumnDefinitions>
          <Image Grid.Column="0" Source="pack://application:,,,/WPFGUI;component/icons/races/common/abilities/herd.png"
                 Width="%d" Height="%d" Margin="0,0,5,0" Opacity="0.95" VerticalAlignment="Center">
            <Image.Effect>
              <DropShadowEffect Color="#000000" BlurRadius="3" ShadowDepth="1" Opacity="0.4"/>
            </Image.Effect>
          </Image>
          <TextBlock Grid.Column="1" Text="{Binding [GaiaSheepRemaining]}" Foreground="#D4D4D8" FontSize="%d" FontWeight="Bold"
                     TextWrapping="NoWrap" VerticalAlignment="Center" Margin="0,0,2,0"/>
          <TextBlock Grid.Column="2" Text="/" Foreground="#71717A" FontSize="%d" FontWeight="Bold"
                     TextWrapping="NoWrap" VerticalAlignment="Center" Margin="0,0,2,0"/>
          <TextBlock Grid.Column="3" Text="{Binding [InitialGaiaSheep]}" Foreground="#D4D4D8" FontSize="%d" FontWeight="Bold"
                     TextWrapping="NoWrap" VerticalAlignment="Center"/>
        </Grid>
      </Grid>
    </Border>

    <!-- ===== 4) ARMY (premium styling) ===== -->
    <Border Grid.Row="%d" Grid.Column="3" Padding="10,4,10,8"
            UseLayoutRounding="True" SnapsToDevicePixels="True">
      <Border.BorderBrush>
        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
          <GradientStop Color="#30FFFFFF" Offset="0.0"/>
          <GradientStop Color="#10FFFFFF" Offset="1.0"/>
        </LinearGradientBrush>
      </Border.BorderBrush>
      <Border.BorderThickness>0,0,1,0</Border.BorderThickness>
      <StackPanel Orientation="Vertical" VerticalAlignment="Center">

        <StackPanel Orientation="Horizontal" Margin="0,0,0,3">
          <Image Source="pack://application:,,,/WPFGUI;component/icons/races/common/buildings/barracks.png"
                 Width="%d" Height="%d" Margin="0,0,6,0" Opacity="0.95">
            <Image.Effect>
              <DropShadowEffect Color="#000000" BlurRadius="3" ShadowDepth="1" Opacity="0.4"/>
            </Image.Effect>
          </Image>
          <TextBlock Text="{Binding [%s_Army], StringFormat=Total: {0}}"
                     Foreground="#F87171" FontSize="%d" FontWeight="Bold"
                     TextWrapping="NoWrap" TextTrimming="None">
            <TextBlock.Effect>
              <DropShadowEffect Color="#7F1D1D" BlurRadius="8" ShadowDepth="0" Opacity="0.5"/>
            </TextBlock.Effect>
          </TextBlock>
        </StackPanel>

        <StackPanel Orientation="Horizontal" Margin="0,3,0,0">
          <Image Source="pack://application:,,,/WPFGUI;component/icons/races/common/units/spearman.png"
                 Width="%d" Height="%d" Margin="0,0,6,0" Opacity="0.9">
            <Image.Effect>
              <DropShadowEffect Color="#000000" BlurRadius="2" ShadowDepth="1" Opacity="0.3"/>
            </Image.Effect>
          </Image>
          <TextBlock Text="{Binding [%s_ArmyInf], StringFormat=Inf: {0}}" Foreground="#D4D4D8" FontSize="%d" FontWeight="SemiBold"
                     TextWrapping="NoWrap" TextTrimming="None"/>
        </StackPanel>

        <StackPanel Orientation="Horizontal" Margin="0,3,0,0">
          <Image Source="pack://application:,,,/WPFGUI;component/icons/races/common/units/archer.png"
                 Width="%d" Height="%d" Margin="0,0,6,0" Opacity="0.9">
            <Image.Effect>
              <DropShadowEffect Color="#000000" BlurRadius="2" ShadowDepth="1" Opacity="0.3"/>
            </Image.Effect>
          </Image>
          <TextBlock Text="{Binding [%s_ArmyRanged], StringFormat=Rng: {0}}" Foreground="#D4D4D8" FontSize="%d" FontWeight="SemiBold"
                     TextWrapping="NoWrap" TextTrimming="None"/>
        </StackPanel>

        <StackPanel Orientation="Horizontal" Margin="0,3,0,0">
          <Image Source="pack://application:,,,/WPFGUI;component/icons/races/common/units/knight.png"
                 Width="%d" Height="%d" Margin="0,0,6,0" Opacity="0.9">
            <Image.Effect>
              <DropShadowEffect Color="#000000" BlurRadius="2" ShadowDepth="1" Opacity="0.3"/>
            </Image.Effect>
          </Image>
          <TextBlock Text="{Binding [%s_ArmyCav], StringFormat=Cav: {0}}" Foreground="#D4D4D8" FontSize="%d" FontWeight="SemiBold"
                     TextWrapping="NoWrap" TextTrimming="None"/>
        </StackPanel>

        <StackPanel Orientation="Horizontal" Margin="0,3,0,0">
          <Image Source="pack://application:,,,/WPFGUI;component/icons/races/common/units/mangonel.png"
                 Width="%d" Height="%d" Margin="0,0,6,0" Opacity="0.9">
            <Image.Effect>
              <DropShadowEffect Color="#000000" BlurRadius="2" ShadowDepth="1" Opacity="0.3"/>
            </Image.Effect>
          </Image>
          <TextBlock Text="{Binding [%s_ArmySiege], StringFormat=Sig: {0}}" Foreground="#D4D4D8" FontSize="%d" FontWeight="SemiBold"
                     TextWrapping="NoWrap" TextTrimming="None"/>
        </StackPanel>

      </StackPanel>
    </Border>

    <!-- ===== 5) RESOURCES (premium styling) ===== -->
    <Grid Grid.Row="%d" Grid.Column="4" Margin="8,4,12,4"
          UseLayoutRounding="True" SnapsToDevicePixels="True">
      <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="Auto"/>
      </Grid.ColumnDefinitions>

      <!-- FOOD -->
      <Border Grid.Column="0" CornerRadius="6" Padding="6,4" Margin="4,0">
        <Border.Background>
          <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
            <GradientStop Color="#0CFBBF24" Offset="0.0"/>
            <GradientStop Color="#05FBBF24" Offset="1.0"/>
          </LinearGradientBrush>
        </Border.Background>
        <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
          <Image Source="pack://application:,,,/WPFGUI;component/icons/resources/resource_food_icon.png"
                 Width="%d" Height="%d" Margin="0,0,0,3" Opacity="1">
            <Image.Effect>
              <DropShadowEffect Color="#000000" BlurRadius="4" ShadowDepth="1" Opacity="0.4"/>
            </Image.Effect>
          </Image>
          <TextBlock Text="{Binding [%s_Food]}" Foreground="#FDE047" FontSize="%d" FontWeight="Bold" HorizontalAlignment="Center">
            <TextBlock.Effect>
              <DropShadowEffect Color="#854D0E" BlurRadius="6" ShadowDepth="0" Opacity="0.4"/>
            </TextBlock.Effect>
          </TextBlock>
          <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,3,0,0">
            <Image Source="pack://application:,,,/WPFGUI;component/icons/resources/resource_popcap_icon.png"
                   Width="%d" Height="%d" Margin="0,0,4,0" Opacity="0.85"/>
            <TextBlock Text="{Binding [%s_FoodWorkers], StringFormat=({0})}" Foreground="#A3A3A3" FontSize="%d" FontWeight="SemiBold"/>
          </StackPanel>
        </StackPanel>
      </Border>

      <!-- WOOD -->
      <Border Grid.Column="1" CornerRadius="6" Padding="6,4" Margin="4,0">
        <Border.Background>
          <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
            <GradientStop Color="#0CA16207" Offset="0.0"/>
            <GradientStop Color="#05A16207" Offset="1.0"/>
          </LinearGradientBrush>
        </Border.Background>
        <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
          <Image Source="pack://application:,,,/WPFGUI;component/icons/resources/resource_wood_icon.png"
                 Width="%d" Height="%d" Margin="0,0,0,3" Opacity="1">
            <Image.Effect>
              <DropShadowEffect Color="#000000" BlurRadius="4" ShadowDepth="1" Opacity="0.4"/>
            </Image.Effect>
          </Image>
          <TextBlock Text="{Binding [%s_Wood]}" Foreground="#CA8A04" FontSize="%d" FontWeight="Bold" HorizontalAlignment="Center">
            <TextBlock.Effect>
              <DropShadowEffect Color="#451A03" BlurRadius="6" ShadowDepth="0" Opacity="0.4"/>
            </TextBlock.Effect>
          </TextBlock>
          <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,3,0,0">
            <Image Source="pack://application:,,,/WPFGUI;component/icons/resources/resource_popcap_icon.png"
                   Width="%d" Height="%d" Margin="0,0,4,0" Opacity="0.85"/>
            <TextBlock Text="{Binding [%s_WoodWorkers], StringFormat=({0})}" Foreground="#A3A3A3" FontSize="%d" FontWeight="SemiBold"/>
          </StackPanel>
        </StackPanel>
      </Border>

      <!-- GOLD -->
      <Border Grid.Column="2" CornerRadius="6" Padding="6,4" Margin="4,0">
        <Border.Background>
          <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
            <GradientStop Color="#0EFFD700" Offset="0.0"/>
            <GradientStop Color="#06FFD700" Offset="1.0"/>
          </LinearGradientBrush>
        </Border.Background>
        <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
          <Image Source="pack://application:,,,/WPFGUI;component/icons/resources/resource_gold_icon.png"
                 Width="%d" Height="%d" Margin="0,0,0,3" Opacity="1">
            <Image.Effect>
              <DropShadowEffect Color="#000000" BlurRadius="4" ShadowDepth="1" Opacity="0.4"/>
            </Image.Effect>
          </Image>
          <TextBlock Text="{Binding [%s_Gold]}" Foreground="#FACC15" FontSize="%d" FontWeight="Bold" HorizontalAlignment="Center">
            <TextBlock.Effect>
              <DropShadowEffect Color="#713F12" BlurRadius="8" ShadowDepth="0" Opacity="0.5"/>
            </TextBlock.Effect>
          </TextBlock>
          <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,3,0,0">
            <Image Source="pack://application:,,,/WPFGUI;component/icons/resources/resource_popcap_icon.png"
                   Width="%d" Height="%d" Margin="0,0,4,0" Opacity="0.85"/>
            <TextBlock Text="{Binding [%s_GoldWorkers], StringFormat=({0})}" Foreground="#A3A3A3" FontSize="%d" FontWeight="SemiBold"/>
          </StackPanel>
        </StackPanel>
      </Border>

      <!-- STONE -->
      <Border Grid.Column="3" CornerRadius="6" Padding="6,4" Margin="4,0">
        <Border.Background>
          <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
            <GradientStop Color="#0AA1A1AA" Offset="0.0"/>
            <GradientStop Color="#05A1A1AA" Offset="1.0"/>
          </LinearGradientBrush>
        </Border.Background>
        <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
          <Image Source="pack://application:,,,/WPFGUI;component/icons/resources/resource_stone_icon.png"
                 Width="%d" Height="%d" Margin="0,0,0,3" Opacity="1">
            <Image.Effect>
              <DropShadowEffect Color="#000000" BlurRadius="4" ShadowDepth="1" Opacity="0.4"/>
            </Image.Effect>
          </Image>
          <TextBlock Text="{Binding [%s_Stone]}" Foreground="#D4D4D8" FontSize="%d" FontWeight="Bold" HorizontalAlignment="Center">
            <TextBlock.Effect>
              <DropShadowEffect Color="#3F3F46" BlurRadius="6" ShadowDepth="0" Opacity="0.4"/>
            </TextBlock.Effect>
          </TextBlock>
          <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,3,0,0">
            <Image Source="pack://application:,,,/WPFGUI;component/icons/resources/resource_popcap_icon.png"
                   Width="%d" Height="%d" Margin="0,0,4,0" Opacity="0.85"/>
            <TextBlock Text="{Binding [%s_StoneWorkers], StringFormat=({0})}" Foreground="#A3A3A3" FontSize="%d" FontWeight="SemiBold"/>
          </StackPanel>
        </StackPanel>
      </Border>
    </Grid>
  ]],
  -- row background
  row_index, row_index, key, key,

  -- score
  row_index, key, key, RES_FONT,

  -- name + villagers
  row_index, key, key, NAME_FONT,
  ICON_VIL, ICON_VIL, key, STAT_FONT,
  ICON_VIL, ICON_VIL, key, key, STAT_FONT,

  -- deer + relic (aligned grid)
  ICON_VIL, ICON_VIL, key, key, STAT_FONT,
  ICON_VIL, ICON_VIL, key, key, STAT_FONT,

  -- POP mini-grid
  row_index,
  ICON_POP, ICON_POP, key, STAT_FONT,                         -- pop

  -- sheep (owned only)
  ICON_POP, ICON_POP, key, STAT_FONT,

  -- herd (remaining / initial)
  ICON_POP, ICON_POP, STAT_FONT, STAT_FONT, STAT_FONT,

  -- army
  row_index, ICON_ARMY, ICON_ARMY, key, STAT_FONT,
  ICON_ARMY, ICON_ARMY, key, STAT_FONT,
  ICON_ARMY, ICON_ARMY, key, STAT_FONT,
  ICON_ARMY, ICON_ARMY, key, STAT_FONT,
  ICON_ARMY, ICON_ARMY, key, STAT_FONT,

  -- resources
  row_index,
  ICON_RES, ICON_RES, key, RES_FONT, ICON_WORKER, ICON_WORKER, key, STAT_FONT,
  ICON_RES, ICON_RES, key, RES_FONT, ICON_WORKER, ICON_WORKER, key, STAT_FONT,
  ICON_RES, ICON_RES, key, RES_FONT, ICON_WORKER, ICON_WORKER, key, STAT_FONT,
  ICON_RES, ICON_RES, key, RES_FONT, ICON_WORKER, ICON_WORKER, key, STAT_FONT
  )
end



-- Full-width Upgrades strip row (spans all 5 columns) - Premium styling
local function upgrades_row_xaml(key, row_index)
  local parts = {}
  parts[#parts+1] = string.format([[
    <!-- ===== Upgrades row (full width, transparent) ===== -->
    <Border Grid.Row="%d" Grid.Column="0" Grid.ColumnSpan="5"
            Margin="4,-2,4,4" Padding="8,5,8,5"
            CornerRadius="6"
            UseLayoutRounding="True" SnapsToDevicePixels="True">
      <Border.Background>
        <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
          <GradientStop Color="#0C00D4FF" Offset="0.0"/>
          <GradientStop Color="#0600D4FF" Offset="0.5"/>
          <GradientStop Color="#0C00D4FF" Offset="1.0"/>
        </LinearGradientBrush>
      </Border.Background>
      <Border.BorderBrush>
        <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
          <GradientStop Color="#1800D4FF" Offset="0.0"/>
          <GradientStop Color="#0CFFFFFF" Offset="0.5"/>
          <GradientStop Color="#1800D4FF" Offset="1.0"/>
        </LinearGradientBrush>
      </Border.BorderBrush>
      <Border.BorderThickness>0,1,0,0</Border.BorderThickness>
      <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center">]], row_index)

  -- emit N image slots with enhanced styling
  for i = 1, UPG_MAX_SLOTS do
    local margin = (i == UPG_MAX_SLOTS) and "0,0,0,0" or "0,0,4,0"
    parts[#parts+1] = string.format([[
        <Border CornerRadius="3" Padding="1" Background="#10FFFFFF">
          <Image Width="24" Height="24" Margin="%s" Opacity="0.95" Source="{Binding [%s_UpgIcon%d]}">
            <Image.Effect>
              <DropShadowEffect Color="#000000" BlurRadius="2" ShadowDepth="1" Opacity="0.3"/>
            </Image.Effect>
            <Image.ToolTip>
              <ToolTip Content="{Binding [%s_UpgTip%d]}" Background="#D0181820" Foreground="#FFFFFF" BorderBrush="#30FFD700" BorderThickness="1"/>
            </Image.ToolTip>
          </Image>
        </Border>]], margin, key, i, key, i)
  end

  parts[#parts+1] = [[
      </StackPanel>
    </Border>]]
  return table.concat(parts, "\n")
end


-- Full-width Buildings strip row (spans all 5 columns) - Shows building icons with counts
local function buildings_row_xaml(key, row_index)
  local parts = {}
  parts[#parts+1] = string.format([[
    <!-- ===== Buildings row (fixed width, transparent) ===== -->
    <Border Grid.Row="%d" Grid.Column="0" Grid.ColumnSpan="5"
            Margin="4,-2,4,4" Padding="8,5,8,5"
            CornerRadius="6" HorizontalAlignment="Left" ClipToBounds="True"
            UseLayoutRounding="True" SnapsToDevicePixels="True">
      <Border.Background>
        <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
          <GradientStop Color="#0CFF6B00" Offset="0.0"/>
          <GradientStop Color="#06FF6B00" Offset="0.5"/>
          <GradientStop Color="#0CFF6B00" Offset="1.0"/>
        </LinearGradientBrush>
      </Border.Background>
      <Border.BorderBrush>
        <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
          <GradientStop Color="#18FF6B00" Offset="0.0"/>
          <GradientStop Color="#0CFFFFFF" Offset="0.5"/>
          <GradientStop Color="#18FF6B00" Offset="1.0"/>
        </LinearGradientBrush>
      </Border.BorderBrush>
      <Border.BorderThickness>0,1,0,0</Border.BorderThickness>
      <StackPanel Orientation="Horizontal" VerticalAlignment="Center">]], row_index)

  -- emit N building slots with icon + count
  for i = 1, BLD_MAX_SLOTS do
    local margin = (i == BLD_MAX_SLOTS) and "0,0,0,0" or "0,0,6,0"
    parts[#parts+1] = string.format([[
        <StackPanel Orientation="Horizontal" Margin="%s">
          <Border CornerRadius="3" Padding="1" Background="#10FFFFFF">
            <Image Width="22" Height="22" Opacity="0.95" Source="{Binding [%s_BldIcon%d]}">
              <Image.Effect>
                <DropShadowEffect Color="#000000" BlurRadius="2" ShadowDepth="1" Opacity="0.3"/>
              </Image.Effect>
              <Image.ToolTip>
                <ToolTip Content="{Binding [%s_BldTip%d]}" Background="#D0181820" Foreground="#FFFFFF" BorderBrush="#30FF6B00" BorderThickness="1"/>
              </Image.ToolTip>
            </Image>
          </Border>
          <TextBlock Text="{Binding [%s_BldCount%d]}" Foreground="#FFFFFFFF" FontSize="11" FontWeight="Bold"
                     VerticalAlignment="Center" Margin="2,0,0,0">
            <TextBlock.Effect>
              <DropShadowEffect Color="#000000" BlurRadius="2" ShadowDepth="1" Opacity="0.5"/>
            </TextBlock.Effect>
          </TextBlock>
        </StackPanel>]], margin, key, i, key, i, key, i)
  end

  parts[#parts+1] = [[
      </StackPanel>
    </Border>]]
  return table.concat(parts, "\n")
end


-- Full-width Units strip row (spans all 5 columns) - Shows unit icons with counts
local function units_row_xaml(key, row_index)
  local parts = {}
  parts[#parts+1] = string.format([[
    <!-- ===== Units row (fixed width, transparent) ===== -->
    <Border Grid.Row="%d" Grid.Column="0" Grid.ColumnSpan="5"
            Margin="4,-2,4,4" Padding="8,5,8,5"
            CornerRadius="6" HorizontalAlignment="Left" ClipToBounds="True"
            UseLayoutRounding="True" SnapsToDevicePixels="True">
      <Border.Background>
        <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
          <GradientStop Color="#0C00BFFF" Offset="0.0"/>
          <GradientStop Color="#0600BFFF" Offset="0.5"/>
          <GradientStop Color="#0C00BFFF" Offset="1.0"/>
        </LinearGradientBrush>
      </Border.Background>
      <Border.BorderBrush>
        <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
          <GradientStop Color="#1800BFFF" Offset="0.0"/>
          <GradientStop Color="#0CFFFFFF" Offset="0.5"/>
          <GradientStop Color="#1800BFFF" Offset="1.0"/>
        </LinearGradientBrush>
      </Border.BorderBrush>
      <Border.BorderThickness>0,1,0,0</Border.BorderThickness>
      <StackPanel Orientation="Horizontal" VerticalAlignment="Center">]], row_index)

  -- emit N unit slots with icon + count
  for i = 1, UNIT_MAX_SLOTS do
    local margin = (i == UNIT_MAX_SLOTS) and "0,0,0,0" or "0,0,6,0"
    parts[#parts+1] = string.format([[
        <StackPanel Orientation="Horizontal" Margin="%s">
          <Border CornerRadius="3" Padding="1" Background="#10FFFFFF">
            <Image Width="22" Height="22" Opacity="0.95" Source="{Binding [%s_UnitIcon%d]}">
              <Image.Effect>
                <DropShadowEffect Color="#000000" BlurRadius="2" ShadowDepth="1" Opacity="0.3"/>
              </Image.Effect>
              <Image.ToolTip>
                <ToolTip Content="{Binding [%s_UnitTip%d]}" Background="#D0181820" Foreground="#FFFFFF" BorderBrush="#3000BFFF" BorderThickness="1"/>
              </Image.ToolTip>
            </Image>
          </Border>
          <TextBlock Text="{Binding [%s_UnitCount%d]}" Foreground="#FFFFFFFF" FontSize="11" FontWeight="Bold"
                     VerticalAlignment="Center" Margin="2,0,0,0">
            <TextBlock.Effect>
              <DropShadowEffect Color="#000000" BlurRadius="2" ShadowDepth="1" Opacity="0.5"/>
            </TextBlock.Effect>
          </TextBlock>
        </StackPanel>]], margin, key, i, key, i, key, i)
  end

  parts[#parts+1] = [[
      </StackPanel>
    </Border>]]
  return table.concat(parts, "\n")
end


-- Landmark row (single line under buildings) - shows: Landmarks: [Name] | Time: [XX] | [Progress%]
-- Only shows the label "Landmarks:" when there's actual content
local function landmark_row_xaml(key, row_index)
  return string.format([[
    <!-- ===== Landmark row (single line) ===== -->
    <Border Grid.Row="%d" Grid.Column="0" Grid.ColumnSpan="5"
            Margin="4,-2,4,4" Padding="8,4,8,4"
            CornerRadius="6" HorizontalAlignment="Left"
            UseLayoutRounding="True" SnapsToDevicePixels="True">
      <Border.Background>
        <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
          <GradientStop Color="#15FFD700" Offset="0.0"/>
          <GradientStop Color="#0AFFD700" Offset="0.5"/>
          <GradientStop Color="#15FFD700" Offset="1.0"/>
        </LinearGradientBrush>
      </Border.Background>
      <Border.BorderBrush>
        <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
          <GradientStop Color="#25FFD700" Offset="0.0"/>
          <GradientStop Color="#10FFFFFF" Offset="0.5"/>
          <GradientStop Color="#25FFD700" Offset="1.0"/>
        </LinearGradientBrush>
      </Border.BorderBrush>
      <Border.BorderThickness>0,1,0,0</Border.BorderThickness>
      <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
        <TextBlock Text="{Binding [%s_LandmarkLabel]}" Foreground="#CCCCCC" FontSize="11" FontWeight="SemiBold" Margin="0,0,6,0">
          <TextBlock.Effect>
            <DropShadowEffect Color="#000000" BlurRadius="2" ShadowDepth="1" Opacity="0.5"/>
          </TextBlock.Effect>
        </TextBlock>
        <TextBlock Text="{Binding [%s_LandmarkName]}" Foreground="#FFD700" FontSize="11" FontWeight="Bold" Margin="0,0,10,0">
          <TextBlock.Effect>
            <DropShadowEffect Color="#000000" BlurRadius="2" ShadowDepth="1" Opacity="0.6"/>
          </TextBlock.Effect>
        </TextBlock>
        <TextBlock Text="{Binding [%s_LandmarkTimeLabel]}" Foreground="#AAAAAA" FontSize="10" Margin="0,0,4,0"/>
        <TextBlock Text="{Binding [%s_LandmarkTime]}" Foreground="#00FF88" FontSize="11" FontWeight="Bold" Margin="0,0,10,0">
          <TextBlock.Effect>
            <DropShadowEffect Color="#000000" BlurRadius="2" ShadowDepth="1" Opacity="0.5"/>
          </TextBlock.Effect>
        </TextBlock>
        <TextBlock Text="{Binding [%s_LandmarkPct]}" Foreground="#FFAA00" FontSize="11" FontWeight="Bold">
          <TextBlock.Effect>
            <DropShadowEffect Color="#000000" BlurRadius="2" ShadowDepth="1" Opacity="0.5"/>
          </TextBlock.Effect>
        </TextBlock>
      </StackPanel>
    </Border>]], row_index, key, key, key, key, key)
end


-- Build a single player panel (either P1 on left or P2 on right)
local function build_player_panel(key, align, margin, tickerName, noExtras)
  -- noExtras = true means only main info row (for local player P3)
  -- noExtras = false/nil means include upgrades/buildings/units (for enemies P1/P2)
  local showUpgrades = g_1v1ShowUpgrades and not noExtras
  local showBuildings = g_1v1ShowBuildings and not noExtras
  local showUnits = g_1v1ShowUnits and not noExtras

  -- Build row definitions dynamically
  local rowDefs = { '<RowDefinition Height="Auto"/>   <!-- main -->' }
  local currentRow = 1

  local upgradesRowIdx = nil
  if showUpgrades then
    rowDefs[#rowDefs+1] = '<RowDefinition Height="Auto"/>   <!-- upgrades -->'
    upgradesRowIdx = currentRow
    currentRow = currentRow + 1
  end

  local buildingsRowIdx = nil
  local landmarkRowIdx = nil
  if showBuildings then
    rowDefs[#rowDefs+1] = '<RowDefinition Height="Auto"/>   <!-- buildings -->'
    buildingsRowIdx = currentRow
    currentRow = currentRow + 1
    rowDefs[#rowDefs+1] = '<RowDefinition Height="Auto"/>   <!-- landmarks -->'
    landmarkRowIdx = currentRow
    currentRow = currentRow + 1
  end

  local unitsRowIdx = nil
  if showUnits then
    rowDefs[#rowDefs+1] = '<RowDefinition Height="Auto"/>   <!-- units -->'
    unitsRowIdx = currentRow
    currentRow = currentRow + 1
  end

  -- Build row content
  local rowContent = { row_cells_xaml(key, 0) }
  if showUpgrades then
    rowContent[#rowContent+1] = upgrades_row_xaml(key, upgradesRowIdx)
  end
  if showBuildings then
    rowContent[#rowContent+1] = buildings_row_xaml(key, buildingsRowIdx)
    rowContent[#rowContent+1] = landmark_row_xaml(key, landmarkRowIdx)
  end
  if showUnits then
    rowContent[#rowContent+1] = units_row_xaml(key, unitsRowIdx)
  end

  local xaml = string.format([[
<Border xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        HorizontalAlignment="%s" VerticalAlignment="Top"
        Margin="%s" CornerRadius="0,0,12,12"
        ClipToBounds="True" MinWidth="0"
        Visibility="{Binding [%s_Visible]}"
        >

  <!-- Transparent glass background -->
  <Border.Background>
    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
      <GradientStop Color="#B0080810" Offset="0.0"/>
      <GradientStop Color="#90050508" Offset="0.5"/>
      <GradientStop Color="#70030305" Offset="1.0"/>
    </LinearGradientBrush>
  </Border.Background>

  <!-- Subtle border glow -->
  <Border.BorderBrush>
    <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
      <GradientStop Color="#30FFD700" Offset="0.0"/>
      <GradientStop Color="#18FFFFFF" Offset="0.3"/>
      <GradientStop Color="#10FFFFFF" Offset="0.7"/>
      <GradientStop Color="#30FFD700" Offset="1.0"/>
    </LinearGradientBrush>
  </Border.BorderBrush>
  <Border.BorderThickness>1</Border.BorderThickness>

  <!-- Subtle shadow -->
  <Border.Effect>
    <DropShadowEffect Color="#000000" BlurRadius="15" ShadowDepth="0" Opacity="0.4"/>
  </Border.Effect>

  <Grid Margin="8,4,8,6">
    <!-- Define the 5 columns once -->
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="84"/>   <!-- Score -->
      <ColumnDefinition Width="Auto"/> <!-- Name -->
      <ColumnDefinition Width="Auto"/> <!-- Pop -->
      <ColumnDefinition Width="Auto"/> <!-- Army -->
      <ColumnDefinition Width="*"/>    <!-- Resources -->
    </Grid.ColumnDefinitions>

    <!-- Dynamic rows based on visibility flags -->
    <Grid.RowDefinitions>
      %s
    </Grid.RowDefinitions>

    %s

    <!-- ======= CREDIT OVERLAY (Canvas → zero desired size, no gap) ======= -->
    <Canvas Grid.Row="1" Grid.ColumnSpan="5" Panel.ZIndex="99"
            IsHitTestVisible="False" ClipToBounds="False">
      <TextBlock x:Name="%s"
                 Text="HUD developed by Billy"
                 Foreground="#FFFF3B30" FontSize="12" FontWeight="Bold"
                 Opacity="0.0" Canvas.Right="-260" Canvas.Top="-2">
        <TextBlock.RenderTransform>
          <TranslateTransform X="360" Y="0"/>
        </TextBlock.RenderTransform>
      </TextBlock>

      <Canvas.Triggers>
        <EventTrigger RoutedEvent="Canvas.Loaded">
          <BeginStoryboard>
            <Storyboard FillBehavior="Stop">
              <!-- slide right -> left -->
              <DoubleAnimation Storyboard.TargetName="%s"
                               Storyboard.TargetProperty="(TextBlock.RenderTransform).(TranslateTransform.X)"
                               From="360" To="-640" Duration="0:0:5.0"
                               AccelerationRatio="0.15" DecelerationRatio="0.25"/>
              <!-- fade in/out -->
              <DoubleAnimation Storyboard.TargetName="%s"
                               Storyboard.TargetProperty="Opacity"
                               From="0.0" To="0.95" Duration="0:0:0.20"/>
              <DoubleAnimation Storyboard.TargetName="%s"
                               Storyboard.TargetProperty="Opacity"
                               From="0.95" To="0.0" BeginTime="0:0:4.7" Duration="0:0:0.30"/>
              <!-- collapse after -->
              <ObjectAnimationUsingKeyFrames Storyboard.TargetName="%s"
                                             Storyboard.TargetProperty="Visibility" BeginTime="0:0:5.0">
                <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Visibility.Collapsed}"/>
              </ObjectAnimationUsingKeyFrames>
            </Storyboard>
          </BeginStoryboard>
        </EventTrigger>
      </Canvas.Triggers>
    </Canvas>

  </Grid>
</Border>]],
    align,
    margin,
    key,
    table.concat(rowDefs, "\n      "),
    table.concat(rowContent, "\n    "),
    tickerName, tickerName, tickerName, tickerName, tickerName
  )

  return xaml
end

-- Build panels: P1/P2 for enemies (with extras), P3 for local player (main info only)
-- Layout:  | __P3 (You)__ |  __P1 (Enemy)__  |  Game Time  | __P2 (Enemy)__ |
local function build_panel()
  -- P1 panel: Enemy 1 - with full extras (upgrades/buildings/units)
  local xaml_p1 = build_player_panel("P1", "Left", "80,0,0,0", "CreditTickerP1", false)
  UI_AddChild("ScarDefault", "XamlPresenter", "HUD_1v1_P1", {
    IsHitTestVisible = false,
    Xaml = xaml_p1,
    DataContext = UI_CreateDataContext(HUD)
  })

  -- P2 panel: Enemy 2 - with full extras (upgrades/buildings/units)
  local xaml_p2 = build_player_panel("P2", "Right", "0,0,80,0", "CreditTickerP2", false)
  UI_AddChild("ScarDefault", "XamlPresenter", "HUD_1v1_P2", {
    IsHitTestVisible = false,
    Xaml = xaml_p2,
    DataContext = UI_CreateDataContext(HUD)
  })

  -- P3 panel: Local player (You) - main info only, no extras
  local xaml_p3 = build_player_panel("P3", "Left", "80,0,0,0", "CreditTickerP3", true)
  UI_AddChild("ScarDefault", "XamlPresenter", "HUD_1v1_P3", {
    IsHitTestVisible = false,
    Xaml = xaml_p3,
    DataContext = UI_CreateDataContext(HUD)
  })
end


-- formatter like your ResourceHUD
local function fmt_num(v)
  if v > 9999 then return string.format("%.1fk", v/1000) end
  return tostring(v)
end

-- Clear all upgrade icon slots for a player key
local function clear_upgrade_icons(key)
  -- Prefer the upgrader's clearer if present
  if type(Upg_ClearIcons) == "function" then
    Upg_ClearIcons(key)
    return
  end

  -- Fallback: wipe 10 slots locally
  for i = 1, 10 do
    HUD[string.format("%s_UpgIcon%d", key, i)] = nil
    HUD[string.format("%s_UpgTip%d",  key, i)] = nil
  end
  HUD[key.."_UpgCount"] = 0
end


-- update one slot key with player p and label tag
local function fill_player(key, p, tag)
  local entry = Core_GetPlayersTableEntry(p)
  local total = total_for(entry)

  -- resources
  local food   = math.floor(Player_GetResource(p, RT_Food))
  local wood   = math.floor(Player_GetResource(p, RT_Wood))
  local stone  = math.floor(Player_GetResource(p, RT_Stone))
  local gold   = math.floor(Player_GetResource(p, RT_Gold))

  -- villager split per resource
  local vFood  = Player_GetNumGatheringSquads(p, RT_Food)  or 0
  local vWood  = Player_GetNumGatheringSquads(p, RT_Wood)  or 0
  local vGold  = Player_GetNumGatheringSquads(p, RT_Gold)  or 0
  local vStone = Player_GetNumGatheringSquads(p, RT_Stone) or 0
  local vTotal = vFood + vWood + vGold + vStone

  -- idle villager calc using improved squad state checks
  local villTotal = GetTotalVillagers(p, vFood, vWood, vGold, vStone)
  local idle, idleTotal = CountIdleWorkers(p)
  -- If new method worked, use it; otherwise fallback to simple subtraction
  if idleTotal > 0 then
    villTotal = idleTotal  -- use accurate total from squad iteration
  else
    idle = math.max(0, villTotal - vTotal)  -- fallback to old method
  end

  -- population (used / cap)
  local pop    = Player_GetCurrentPopulation(p, CT_Personnel)
  local popMax = Player_GetCurrentPopulationCap(p, CT_Personnel)
  local popStr = string.format("%d/%d", pop, popMax)

  -- ===== Army (Inf + Cav + Siege total) + breakdown (Inf/Cav/Ranged/Siege) =====
  local function safe_count(unitType)
    if not Player_GetEntityCountByUnitType then return 0 end
    local ok, n = pcall(Player_GetEntityCountByUnitType, p, unitType)
    if ok and type(n) == "number" then return n end
    return 0
  end

  -- raw counts
  local armyInfRaw = safe_count("infantry")   -- includes ranged infantry
  local armyCav    = safe_count("cavalry")
  local armyRng    = safe_count("ranged")     -- ranged infantry; may be 0 for some civs
  local armySie    = safe_count("siege")

  -- Show Infantry as melee-only (remove ranged overlap). Clamp at 0.
  local armyInfDisplay = math.max(0, armyInfRaw - armyRng)

  -- Total army counts all infantry (incl. ranged) + cav + siege (no double count)
  local armyTotal = armyInfRaw + armyCav + armySie

  -- name + age tag
  local disp       = entry and Player_GetDisplayName(entry.id)
  local nameFull   = (disp and disp.LocString) or Loc_ToAnsi(Player_GetDisplayName(p)) or "Player"
  local ageTag     = GetAgeRoman(p)
  local nameWithAge = string.format("%s (%s)", nameFull, ageTag)

  -- shorten name if too long
  local name = nameWithAge
  local maxName = COMPACT and 24 or 22
  if #name > maxName then
    name = string.sub(name, 1, maxName - 3) .. "..."
  end

  -- player colors
  local c      = Player_GetUIColour(p)
  local header = hex(c)
  local accent = hex(c)
  local border = hexA(0x99, c)

  -- sheep (cumulative: never decreases)
  local currentSheep = CountSheepForPlayer(p) or 0
  local pid          = Player_GetID and Player_GetID(p) or 0
  local prev         = _PlayerSheepMax[pid] or 0
  if currentSheep > prev then
    _PlayerSheepMax[pid] = currentSheep
  end
  local sheepCumulative = _PlayerSheepMax[pid] or currentSheep

  -- HUD bindings
  HUD[key.."_Tag"]               = tag
  HUD[key.."_Name"]              = name
  HUD[key.."_NameFull"]          = nameWithAge

  HUD[key.."_Food"]              = fmt_num(food)
  HUD[key.."_Wood"]              = fmt_num(wood)
  HUD[key.."_Gold"]              = fmt_num(gold)
  HUD[key.."_Stone"]             = fmt_num(stone)

  HUD[key.."_FoodWorkers"]       = vFood
  HUD[key.."_WoodWorkers"]       = vWood
  HUD[key.."_GoldWorkers"]       = vGold
  HUD[key.."_StoneWorkers"]      = vStone

  HUD[key.."_TotalVillager"]     = villTotal
  HUD[key.."_IdleVillager"]      = idle
  HUD[key.."_IdleVillagerColour"]= (idle > 0) and "#FF5555" or "#FFFFFF"

  HUD[key.."_Pop"]               = popStr
  HUD[key.."_Army"]       = tostring(armyTotal)
  HUD[key.."_ArmyInf"]    = tostring(armyInfDisplay)  -- melee-only
  HUD[key.."_ArmyCav"]    = tostring(armyCav)
  HUD[key.."_ArmyRanged"] = tostring(armyRng)
  HUD[key.."_ArmySiege"]  = tostring(armySie)

  HUD[key.."_SheepTotal"] = tostring(sheepCumulative)

  HUD[key.."_Score"]             = tostring(total)
  HUD[key.."_HeaderBg"]          = header
  HUD[key.."_Accent"]            = accent
  HUD[key.."_BorderColor"]       = border
  HUD[key.."_Background"]        = "#0AFFFFFF"
end

function Update1v1HUD()
  ----------------------------------------------------------------
  -- 2v2 MODE: detect enemies using ResourceHUD-style classifier
  ----------------------------------------------------------------
  local enemies = {}
  do
    local _, es = build_order_simple()
    enemies = es or {}
  end

  if #enemies >= 2 then
    local e1, e2 = enemies[1], enemies[2]

    -- P1 = ENEMY 1 (or clear if missing)
    if e1 then
      fill_player("P1", e1, "ENEMY 1")
      clear_upgrade_icons("P1")
      if type(bind_blacksmith_upgrades) == "function" then
        bind_blacksmith_upgrades("P1", e1, 1)
      end
      -- Populate enemy buildings and landmarks (P1)
      if g_1v1ShowBuildings then
        Bld_PopulateEnemy("P1", e1)
        Landmark_PopulateEnemy("P1", e1)
      else
        Bld_ClearIcons("P1")
        Landmark_PopulateEnemy("P1", nil)
      end
      -- Populate enemy units (P1)
      if g_1v1ShowUnits then
        Unit_PopulateEnemy("P1", e1)
      else
        Unit_ClearIcons("P1")
      end
    else
      HUD["P1_Name"]               = ""
      HUD["P1_TotalVillager"]      = "0"
      HUD["P1_IdleVillager"]       = "0"
      HUD["P1_IdleVillagerColour"] = "#FFFFFF"
      HUD["P1_Pop"]                = ""
      HUD["P1_Army"]               = "0"
      HUD["P1_ArmyInf"]            = "0"
      HUD["P1_ArmyCav"]            = "0"
      HUD["P1_ArmyRanged"]         = "0"
      HUD["P1_ArmySiege"]          = "0"
      HUD["P1_SheepTotal"]         = "0"
      HUD["P1_Food"]               = ""
      HUD["P1_Wood"]               = ""
      HUD["P1_Gold"]               = ""
      HUD["P1_Stone"]              = ""
      HUD["P1_FoodWorkers"]        = "0"
      HUD["P1_WoodWorkers"]        = "0"
      HUD["P1_GoldWorkers"]        = "0"
      HUD["P1_StoneWorkers"]       = "0"
      HUD["P1_Score"]              = ""
      HUD["P1_HeaderBg"]           = "#00000000"
      HUD["P1_Accent"]             = "#FFFFFF"
      HUD["P1_BorderColor"]        = "#00000000"
      HUD["P1_Background"]         = "#00000000"
      clear_upgrade_icons("P1")
      Bld_ClearIcons("P1")
      Unit_ClearIcons("P1")
      Landmark_PopulateEnemy("P1", nil)
      if type(Relic_ClearHUD) == "function" then Relic_ClearHUD(1) end
    end

    -- P2 = ENEMY 2 (or clear if missing)
    if e2 then
      fill_player("P2", e2, "ENEMY 2")
      clear_upgrade_icons("P2")
      if type(bind_blacksmith_upgrades) == "function" then
        bind_blacksmith_upgrades("P2", e2, 1)
      end
      -- Populate enemy buildings and landmarks (P2 only)
      if g_1v1ShowBuildings then
        Bld_PopulateEnemy("P2", e2)
        Landmark_PopulateEnemy("P2", e2)
      else
        Bld_ClearIcons("P2")
        Landmark_PopulateEnemy("P2", nil)
      end
      -- Populate enemy units (P2 only)
      if g_1v1ShowUnits then
        Unit_PopulateEnemy("P2", e2)
      else
        Unit_ClearIcons("P2")
      end
    else
      HUD["P2_Name"]               = ""
      HUD["P2_TotalVillager"]      = "0"
      HUD["P2_IdleVillager"]       = "0"
      HUD["P2_IdleVillagerColour"] = "#FFFFFF"
      HUD["P2_Pop"]                = ""
      HUD["P2_Army"]               = "0"
      HUD["P2_ArmyInf"]            = "0"
      HUD["P2_ArmyCav"]            = "0"
      HUD["P2_ArmyRanged"]         = "0"
      HUD["P2_ArmySiege"]          = "0"
      HUD["P2_SheepTotal"]         = "0"
      HUD["P2_Food"]               = ""
      HUD["P2_Wood"]               = ""
      HUD["P2_Gold"]               = ""
      HUD["P2_Stone"]              = ""
      HUD["P2_FoodWorkers"]        = "0"
      HUD["P2_WoodWorkers"]        = "0"
      HUD["P2_GoldWorkers"]        = "0"
      HUD["P2_StoneWorkers"]       = "0"
      HUD["P2_Score"]              = ""
      HUD["P2_HeaderBg"]           = "#00000000"
      HUD["P2_Accent"]             = "#FFFFFF"
      HUD["P2_BorderColor"]        = "#00000000"
      HUD["P2_Background"]         = "#00000000"
      clear_upgrade_icons("P2")
      Bld_ClearIcons("P2")
      Unit_ClearIcons("P2")
      Landmark_PopulateEnemy("P2", nil)  -- Clear landmarks
      if type(Relic_ClearHUD) == "function" then Relic_ClearHUD(2) end
    end

    -- Deer tracker: hide in 2v2 (no local row)
    if type(UpdateGaiaDeerTracker) == "function" then
      UpdateGaiaDeerTracker(false)
    end

    -- ✅ Relic counters for both rows in this mode
    if type(UpdateRelicRows) == "function" then
      UpdateRelicRows({ [1]=e1, [2]=e2 })
    end

    -- Set visibility: 2v2 mode shows P1 and P2, hides P3
    HUD["P1_Visible"] = "Visible"
    HUD["P2_Visible"] = "Visible"
    HUD["P3_Visible"] = "Collapsed"

    UpdateGaiaSheepBaselines()
    UI_SetDataContext("HUD_1v1_P1", HUD)
    UI_SetDataContext("HUD_1v1_P2", HUD)
    UI_SetDataContext("HUD_1v1_P3", HUD)
    return
  end

  ----------------------------------------------------------------
  -- 1v1 MODE: P3=You (LEFT), P2=Enemy (RIGHT), P1=hidden
  ----------------------------------------------------------------
  local you, opp = build_1v1()
  if not you then return end

  -- P3 = YOU (local player) - LEFT side, main info only
  fill_player("P3", you, "YOU")

  -- P2 = ENEMY (RIGHT side, with full extras)
  if opp then
    fill_player("P2", opp, "OPP")
    clear_upgrade_icons("P2")
    if type(bind_blacksmith_upgrades) == "function" then
      bind_blacksmith_upgrades("P2", opp, 1)
    end
    -- Populate enemy buildings and landmarks
    if g_1v1ShowBuildings then
      Bld_PopulateEnemy("P2", opp)
      Landmark_PopulateEnemy("P2", opp)
    else
      Bld_ClearIcons("P2")
      Landmark_PopulateEnemy("P2", nil)
    end
    -- Populate enemy units
    if g_1v1ShowUnits then
      Unit_PopulateEnemy("P2", opp)
    else
      Unit_ClearIcons("P2")
    end
  else
    -- No enemy found, clear P2
    HUD["P2_Name"]               = ""
    HUD["P2_TotalVillager"]      = "0"
    HUD["P2_IdleVillager"]       = "0"
    HUD["P2_IdleVillagerColour"] = "#FFFFFF"
    HUD["P2_Pop"]                = ""
    HUD["P2_Army"]               = "0"
    HUD["P2_ArmyInf"]            = "0"
    HUD["P2_ArmyCav"]            = "0"
    HUD["P2_ArmyRanged"]         = "0"
    HUD["P2_ArmySiege"]          = "0"
    HUD["P2_SheepTotal"]         = "0"
    HUD["P2_Food"]               = ""
    HUD["P2_Wood"]               = ""
    HUD["P2_Gold"]               = ""
    HUD["P2_Stone"]              = ""
    HUD["P2_FoodWorkers"]        = "0"
    HUD["P2_WoodWorkers"]        = "0"
    HUD["P2_GoldWorkers"]        = "0"
    HUD["P2_StoneWorkers"]       = "0"
    HUD["P2_Score"]              = ""
    HUD["P2_HeaderBg"]           = "#00000000"
    HUD["P2_Accent"]             = "#FFFFFF"
    HUD["P2_BorderColor"]        = "#00000000"
    HUD["P2_Background"]         = "#00000000"
    clear_upgrade_icons("P2")
    Bld_ClearIcons("P2")
    Unit_ClearIcons("P2")
    Landmark_PopulateEnemy("P2", nil)
    if type(Relic_ClearHUD) == "function" then Relic_ClearHUD(2) end
  end

  -- Set visibility: 1v1 mode shows P3 (you) and P2 (enemy), hides P1
  HUD["P1_Visible"] = "Collapsed"
  HUD["P2_Visible"] = "Visible"
  HUD["P3_Visible"] = "Visible"

  -- Deer tracker: show in 1v1 (local row = P3)
  if type(UpdateGaiaDeerTracker) == "function" then
    UpdateGaiaDeerTracker(true)
  end

  -- ✅ Relic counters for 1v1
  if type(UpdateRelicRows) == "function" then
    UpdateRelicRows({ [1]=nil, [2]=opp })
  end

  UpdateGaiaSheepBaselines()
  UI_SetDataContext("HUD_1v1_P1", HUD)
  UI_SetDataContext("HUD_1v1_P2", HUD)
  UI_SetDataContext("HUD_1v1_P3", HUD)
end


function Show1v1HUD()
  -- Stop old interval if already running
  pcall(function()
    if Rule_Remove and Update1v1HUD then
      Rule_Remove(Update1v1HUD)
    end
  end)

  -- Remove old HUD instances (P1, P2, P3 panels)
  if UI_Remove then
    UI_Remove("HUD_1v1_P1")
    UI_Remove("HUD_1v1_P2")
    UI_Remove("HUD_1v1_P3")
  end

  -- Build fresh XAML UI panels (P1 left, P2 right)
  build_panel()

  -- ✅ Reset trackers BEFORE first update
  if ResetDeerTracker then ResetDeerTracker() end
  if ResetRelicTrackerAll then ResetRelicTrackerAll() end  -- <— add this

  -- Resolve upgrade blueprints once
  Upg_InitLocal()

  -- ✅ Render 1 frame instantly so baselines are correct
  if Update1v1HUD then pcall(Update1v1HUD) end

  -- Start updating every 1 second
  Rule_AddInterval(Update1v1HUD, 1)
end




function Hide1v1HUD()
  pcall(function() if Rule_Remove and Update1v1HUD then Rule_Remove(Update1v1HUD) end end)
  if UI_Remove then
    UI_Remove("HUD_1v1_P1")
    UI_Remove("HUD_1v1_P2")
    UI_Remove("HUD_1v1_P3")
  end
end

-- Auto-show once after injection (like your ResourceHUD)
Rule_AddOneShot(Show1v1HUD, 1)