import { lazy, Suspense, useEffect, useMemo, useState } from 'react'
import {
  loadCpuPvpLeaderboard,
  type CpuPvpLeaderboardEntry,
  type PvpContentType,
} from '../../pvpRoguelike'
import {
  loadAuthSession,
  logoutAccount,
  type Account,
  type AuthSession,
  type CharacterProfile,
} from '../../profile'
import {
  applyCloudSaveSync,
  getCloudSyncStatus,
  getGameMode,
  previewCloudSaveSync,
  type CloudSyncChoice,
  type CloudSyncComparison,
  type GameMode,
} from '../../gameRepository'
import { focusFirstControl, useGameAction } from '../../input.tsx'
import {
  useDualScreen,
  useDualScreenSetupPublisher,
  type DualScreenSetupState,
} from '../../dualScreen'
import { barFillStyle } from '../../components/barStyles'

const CombatScreen = lazy(() => import('../../components/CombatScreen').then((module) => ({ default: module.CombatScreen })))
const CustomizeScreen = lazy(() => import('../../components/CustomizeScreen').then((module) => ({ default: module.CustomizeScreen })))
const EquipmentScreen = lazy(() => import('../../components/EquipmentScreen').then((module) => ({ default: module.EquipmentScreen })))
const HunterProfileScreen = lazy(() => import('../../components/HunterProfileScreen').then((module) => ({ default: module.HunterProfileScreen })))
const PvPRoguelikeScreen = lazy(() => import('../../components/PvpRoguelikeScreen').then((module) => ({ default: module.PvPRoguelikeScreen })))
const PvpStadiumScreen = lazy(() => import('../../components/PvpStadiumScreen').then((module) => ({ default: module.PvpStadiumScreen })))
const TalentScreen = lazy(() => import('../../components/TalentScreen').then((module) => ({ default: module.TalentScreen })))
const SettingsScreen = lazy(() => import('../../components/SettingsScreen').then((module) => ({ default: module.SettingsScreen })))

type Screen =
  | 'menu'
  | 'dungeons'
  | 'combat'
  | 'raids'
  | 'roguelike'
  | 'pvp'
  | 'customize'
  | 'equipment'
  | 'hunter-profile'
  | 'talents'
  | 'settings'

const MENU_ITEMS: Array<{
  screen: Screen
  label: string
  glyph: string
  description: string
}> = [
  { screen: 'dungeons', label: 'Dungeons', glyph: 'D', description: 'Guide a six-player party through dangerous encounters.' },
  { screen: 'raids', label: 'Raids', glyph: 'R', description: 'Guide an eighteen-player party through three-phase challenges.' },
  { screen: 'roguelike', label: 'Roguelike', glyph: 'L', description: 'Draft upgrades through escalating random encounters.' },
  { screen: 'pvp', label: 'PvP', glyph: 'P', description: 'Race another healer through roguelike encounters with buffs and sabotage.' },
  { screen: 'hunter-profile', label: 'Hunter Profile', glyph: 'H', description: 'Review boss kills, PvP record, drops, and pets.' },
  { screen: 'customize', label: 'Customize Character', glyph: 'C', description: 'Choose your class and prepare a six-ability loadout.' },
  { screen: 'settings', label: 'Settings', glyph: 'S', description: 'Remap PC and controller inputs.' },
]

const LAST_DIFFICULTY_KEY = 'i-want-to-heal:last-difficulty'
const SHOW_LEADERBOARDS = false
const ACTIVITY_PAGE_SIZE = 4
const HOME_MENU_COLUMNS = 2
const DUNGEON_NAV_COLUMNS = 2

type DungeonNavEntry =
  | { kind: 'spacer'; disabled: true }
  | { kind: 'back'; disabled?: boolean }
  | { kind: 'pagePrev'; disabled?: boolean }
  | { kind: 'pageNext'; disabled?: boolean }
  | { kind: 'activity'; index: number; disabled?: boolean }
  | { kind: 'tier'; index: number; disabled?: boolean }
  | { kind: 'start'; disabled?: boolean }
  | { kind: 'marathon'; disabled?: boolean }
  | { kind: 'loot'; disabled?: boolean }
  | { kind: 'lootSort'; disabled?: boolean }

type RoguelikeNavEntry =
  | { kind: 'back' }
  | { kind: 'variantPve' }
  | { kind: 'variantPvp' }
  | { kind: 'pveDungeon' }
  | { kind: 'pveRaid' }
  | { kind: 'pveStart' }
  | { kind: 'pvpDungeon' }
  | { kind: 'pvpRaid' }
  | { kind: 'pvpStadium' }
  | { kind: 'pvpStart' }

type RoguelikeNavPosition = {
  row: number
  column: number
}

function formatSaveTimestamp(updatedAt: number | null) {
  if (!updatedAt) return 'Unknown legacy timestamp'
  return new Intl.DateTimeFormat(undefined, {
    dateStyle: 'medium',
    timeStyle: 'short',
  }).format(new Date(updatedAt))
}

function cloudSyncRelationText(comparison: CloudSyncComparison) {
  if (comparison.relation === 'server-newer') return 'Server save is newer than this device.'
  if (comparison.relation === 'local-newer') return 'This device save is newer than the server.'
  if (comparison.relation === 'same') return 'Server and this device have the same save time.'
  return 'One save has no timestamp yet. Choose the copy you want to keep.'
}

function activityInitials(name: string) {
  return name
    .split(/\s+/)
    .filter((word) => /^[A-Za-z0-9]/.test(word))
    .slice(0, 2)
    .map((word) => word[0].toUpperCase())
    .join('')
}

function ScreenLoading() {
  return (
    <main className="game-shell">
      <section className="message-panel">
        <p className="eyebrow">Opening Chronicle</p>
        <h1>Loading...</h1>
      </section>
    </main>
  )
}

type RoguelikeVariant = 'pve' | 'pvp'

type IWantToHeal1AppProps = {
  initialSession: AuthSession
  onAuthenticationCleared: () => void
  onBackToGameSelect: () => void
}

function IWantToHeal1App({
  initialSession,
  onAuthenticationCleared,
  onBackToGameSelect,
}: IWantToHeal1AppProps) {
  const { enabled: dualScreenEnabled } = useDualScreen()
  const [screen, setScreen] = useState<Screen>('menu')
  const [account, setAccount] = useState<Account | null>(initialSession.account)
  const [profile, setProfile] = useState<CharacterProfile | null>(initialSession.profile)
  const [authChecked, setAuthChecked] = useState(false)
  const [gameMode, setGameMode] = useState<GameMode>(getGameMode())
  const [serverMessage, setServerMessage] = useState('')
  const [selectedDifficultyId, setSelectedDifficultyId] = useState(() => {
    const saved = Number(window.localStorage.getItem(LAST_DIFFICULTY_KEY))
    return Number.isFinite(saved) && saved > 0 ? saved : 1
  })
  const [selectedDungeonId, setSelectedDungeonId] = useState(1)
  const [selectedRaidId, setSelectedRaidId] = useState(20)
  const [roguelikeKind, setRoguelikeKind] = useState<'dungeon' | 'raid'>('dungeon')
  const [roguelikeVariant, setRoguelikeVariant] = useState<RoguelikeVariant>('pve')
  const [pvpContentType, setPvpContentType] = useState<PvpContentType>('dungeon')
  const [selectedMarathonMode, setSelectedMarathonMode] = useState(false)
  const [activityPage, setActivityPage] = useState(0)
  const [combatContentId, setCombatContentId] = useState(1)
  const [leaderboardCategory, setLeaderboardCategory] = useState<'part_1' | 'part_2' | 'part_3' | 'full_run'>('part_1')
  const [showLoot, setShowLoot] = useState(false)
  const [lootSort, setLootSort] = useState<'sequence' | 'boss'>('sequence')
  const [showLeaderboard, setShowLeaderboard] = useState(false)
  const [error, setError] = useState('')
  const [syncingCloud, setSyncingCloud] = useState(false)
  const [syncMessage, setSyncMessage] = useState('')
  const [syncComparison, setSyncComparison] = useState<CloudSyncComparison | null>(null)
  const [homeSelectedIndex, setHomeSelectedIndex] = useState(0)
  const [dungeonSelectedIndex, setDungeonSelectedIndex] = useState(0)
  const [roguelikeSelectedIndex, setRoguelikeSelectedIndex] = useState(1)

  useEffect(() => {
    loadAuthSession()
      .then((session) => {
        setAccount(session.account)
        setProfile(session.profile)
      })
      .catch((reason: unknown) => {
        setServerMessage(
          reason instanceof Error
            ? `${reason.message} Offline play is still available.`
            : 'Unable to reach the server. Offline play is still available.',
        )
      })
      .finally(() => setAuthChecked(true))
  }, [])

  useEffect(() => {
    if (authChecked && (!account || !profile)) onAuthenticationCleared()
  }, [account, authChecked, onAuthenticationCleared, profile])

  useEffect(() => {
    const handleModeChange = (event: Event) => {
      const nextMode = (event as CustomEvent<GameMode>).detail
      setGameMode(nextMode)
    }
    window.addEventListener('chronicle:mode-changed', handleModeChange as EventListener)
    return () => {
      window.removeEventListener('chronicle:mode-changed', handleModeChange as EventListener)
    }
  }, [])

  useEffect(() => {
    if (screen === 'combat') return
    window.requestAnimationFrame(() => {
      focusFirstControl()
    })
  }, [screen])

  useEffect(() => {
    if (!authChecked || !account || !profile || screen === 'combat') return
    window.requestAnimationFrame(() => {
      focusFirstControl()
    })
  }, [account, authChecked, profile, screen])

  useEffect(() => {
    window.localStorage.setItem(LAST_DIFFICULTY_KEY, String(selectedDifficultyId))
  }, [selectedDifficultyId])

  const [cpuLeaderboard, setCpuLeaderboard] = useState<CpuPvpLeaderboardEntry[]>([])

  useEffect(() => {
    const frame = window.requestAnimationFrame(() => {
      setCpuLeaderboard(loadCpuPvpLeaderboard(pvpContentType))
    })
    return () => window.cancelAnimationFrame(frame)
  }, [pvpContentType, screen, roguelikeVariant])

  const profileDungeons = profile?.dungeons
  const dungeonOptions = useMemo(
    () => profileDungeons?.filter((candidate) => candidate.contentType === 'dungeon') ?? [],
    [profileDungeons],
  )
  const raidOptions = useMemo(
    () => profileDungeons?.filter((candidate) => candidate.contentType === 'raid') ?? [],
    [profileDungeons],
  )
  const selectedDungeonOption = useMemo(
    () => dungeonOptions.find((candidate) => candidate.id === selectedDungeonId) ?? dungeonOptions[0],
    [dungeonOptions, selectedDungeonId],
  )
  const selectedRaidOption = useMemo(
    () => raidOptions.find((candidate) => candidate.id === selectedRaidId) ?? raidOptions[0],
    [raidOptions, selectedRaidId],
  )
  const activityOptions = useMemo(
    () => screen === 'raids' ? raidOptions : dungeonOptions,
    [dungeonOptions, raidOptions, screen],
  )
  const combatDungeonOption = useMemo(
    () => {
      if (!profileDungeons) return undefined
      return combatContentId < 0
        ? profileDungeons.find((candidate) => candidate.contentType === roguelikeKind) ?? profileDungeons[0]
        : profileDungeons.find((candidate) => candidate.id === combatContentId) ?? profileDungeons[0]
    },
    [combatContentId, profileDungeons, roguelikeKind],
  )
  const combatDifficultyOption = useMemo(
    () => combatDungeonOption?.difficulties.find(
      (candidate) => candidate.id === selectedDifficultyId,
    ) ?? combatDungeonOption?.difficulties[0],
    [combatDungeonOption, selectedDifficultyId],
  )
  const roguelikePool = useMemo(
    () => profileDungeons
      ?.filter((candidate) => candidate.contentType === roguelikeKind)
      .flatMap((candidate) => candidate.encounters) ?? [],
    [profileDungeons, roguelikeKind],
  )
  const pvpPool = useMemo(
    () => profileDungeons
      ?.filter((candidate) => candidate.contentType === pvpContentType)
      .flatMap((candidate) => candidate.encounters) ?? [],
    [profileDungeons, pvpContentType],
  )
  const tierOptions = useMemo(
    () => activityOptions
      .flatMap((option) => option.difficulties)
      .filter((difficulty, index, all) => (
        all.findIndex((candidate) => candidate.droppedItemLevel === difficulty.droppedItemLevel) === index
      ))
      .sort((a, b) => a.droppedItemLevel - b.droppedItemLevel),
    [activityOptions],
  )
  const savedDifficulty = useMemo(
    () => profileDungeons
      ?.flatMap((option) => option.difficulties)
      .find((candidate) => candidate.id === selectedDifficultyId),
    [profileDungeons, selectedDifficultyId],
  )
  const selectedTier = useMemo(
    () => {
      const characterLevel = profile?.character.level ?? 0
      const savedTier = tierOptions.find((candidate) => (
        candidate.droppedItemLevel === savedDifficulty?.droppedItemLevel
      ))
      if (savedTier) return savedTier
      for (let index = tierOptions.length - 1; index >= 0; index -= 1) {
        if (characterLevel >= tierOptions[index].unlockLevel) return tierOptions[index]
      }
      return tierOptions[0]
    },
    [profile?.character.level, savedDifficulty?.droppedItemLevel, tierOptions],
  )
  const selectedTierItemLevel = selectedTier?.droppedItemLevel ?? 0
  const activityPageCount = Math.max(1, Math.ceil(activityOptions.length / ACTIVITY_PAGE_SIZE))
  const currentActivityPage = Math.min(activityPage, activityPageCount - 1)
  const pagedActivityOptions = useMemo(
    () => activityOptions.slice(
      currentActivityPage * ACTIVITY_PAGE_SIZE,
      currentActivityPage * ACTIVITY_PAGE_SIZE + ACTIVITY_PAGE_SIZE,
    ),
    [activityOptions, currentActivityPage],
  )
  const selectedActivityOption = useMemo(
    () => {
      const fallback = screen === 'raids' && selectedRaidOption ? selectedRaidOption : selectedDungeonOption
      const selectedActivityId = screen === 'raids' && selectedRaidOption ? selectedRaidOption.id : selectedDungeonOption?.id
      return activityOptions.find((candidate) => candidate.id === selectedActivityId)
        ?? activityOptions[0]
        ?? fallback
    },
    [activityOptions, screen, selectedDungeonOption, selectedRaidOption],
  )
  const selectedDifficultyOption = useMemo(
    () => selectedActivityOption?.difficulties.find(
      (candidate) => candidate.droppedItemLevel === selectedTierItemLevel,
    ) ?? selectedActivityOption?.difficulties[0],
    [selectedActivityOption, selectedTierItemLevel],
  )
  const lootPreviewEncounters = useMemo(
    () => [...(selectedActivityOption?.encounters ?? [])]
      .filter((encounter) => encounter.isBoss)
      .sort((a, b) => lootSort === 'boss'
        ? a.enemyName.localeCompare(b.enemyName) || a.sequence - b.sequence
        : a.sequence - b.sequence),
    [lootSort, selectedActivityOption?.encounters],
  )
  const lootPreviewByEncounterId = useMemo(
    () => new Map(lootPreviewEncounters.map((encounter) => [
      encounter.id,
      encounter.lootTables.filter((entry) => entry.difficultyId === selectedDifficultyOption?.id),
    ])),
    [lootPreviewEncounters, selectedDifficultyOption?.id],
  )
  const leaderboardEntries = useMemo(
    () => selectedActivityOption?.leaderboards[leaderboardCategory].filter(
      (entry) => entry.difficultyId === selectedDifficultyOption?.id,
    ) ?? [],
    [leaderboardCategory, selectedActivityOption?.leaderboards, selectedDifficultyOption?.id],
  )

  async function signOut() {
    try {
      await logoutAccount()
      setAccount(null)
      setProfile(null)
      setGameMode(getGameMode())
      setScreen('menu')
      setSyncMessage('')
      setSyncComparison(null)
      onAuthenticationCleared()
    } catch (reason) {
      setError(reason instanceof Error ? reason.message : 'Unable to sign out.')
    }
  }

  async function syncSaveNow() {
    setSyncingCloud(true)
    setSyncMessage('')
    setSyncComparison(null)
    try {
      const comparison = await previewCloudSaveSync()
      setSyncComparison(comparison)
      setSyncMessage('')
    } catch (reason) {
      setSyncMessage(reason instanceof Error ? reason.message : 'Unable to sync cloud save.')
    } finally {
      setSyncingCloud(false)
    }
  }

  async function keepCloudSave(choice: CloudSyncChoice) {
    setSyncingCloud(true)
    setSyncMessage(choice === 'local' ? 'Uploading this device save...' : 'Downloading server save...')
    try {
      const updated = await applyCloudSaveSync(choice)
      setProfile(updated)
      setGameMode(getGameMode())
      setSyncComparison(null)
      setSyncMessage(choice === 'local'
        ? 'Server now uses this device save.'
        : 'This device now uses the server save.')
    } catch (reason) {
      setSyncMessage(reason instanceof Error ? reason.message : 'Unable to sync cloud save.')
    } finally {
      setSyncingCloud(false)
    }
  }

  const cloudSync = getCloudSyncStatus()
  const canShowCloudSync = Boolean(account && account.id !== -1 && cloudSync.available)
  const cloudSyncEntryCount = canShowCloudSync ? (syncComparison ? 3 : 1) : 0
  const homeMenuOffset = cloudSyncEntryCount
  const homeMenuEntryCount = MENU_ITEMS.length + homeMenuOffset
  const homeActiveIndex = Math.min(homeSelectedIndex, homeMenuEntryCount - 1)

  function openHomeMenuIndex(index: number) {
    if (canShowCloudSync && index === 0) {
      if (!syncingCloud) void syncSaveNow()
      return
    }
    if (canShowCloudSync && syncComparison && index === 1) {
      if (!syncingCloud) void keepCloudSave('local')
      return
    }
    if (canShowCloudSync && syncComparison && index === 2) {
      if (!syncingCloud) void keepCloudSave('server')
      return
    }
    const item = MENU_ITEMS[index - homeMenuOffset]
    if (!item) return
    if (item.screen === 'pvp') {
      setRoguelikeVariant('pvp')
      setRoguelikeSelectedIndex(2)
      setScreen('roguelike')
      return
    }
    if (item.screen === 'roguelike') {
      setRoguelikeSelectedIndex(1)
    }
    if (item.screen === 'dungeons' || item.screen === 'raids') {
      const nextOptions = item.screen === 'raids' ? raidOptions : dungeonOptions
      setActivityPage(0)
      setDungeonSelectedIndex(firstActivityDungeonEntryIndexForPageCount(
        Math.max(1, Math.ceil(nextOptions.length / ACTIVITY_PAGE_SIZE)),
      ))
    }
    setScreen(item.screen)
  }

  function moveHomeSelection(action: string) {
    setHomeSelectedIndex((current) => {
      const bounded = Math.min(current, homeMenuEntryCount - 1)
      if (canShowCloudSync && syncComparison && bounded <= 2) {
        if (bounded === 0) {
          return action === 'navigateDown' || action === 'navigateRight' ? 1 : 0
        }
        if (bounded === 1) {
          if (action === 'navigateRight') return 2
          if (action === 'navigateDown') return Math.min(homeMenuOffset, homeMenuEntryCount - 1)
          return 0
        }
        if (action === 'navigateLeft') return 1
        if (action === 'navigateDown') return Math.min(homeMenuOffset, homeMenuEntryCount - 1)
        return 0
      }
      if (canShowCloudSync && !syncComparison && bounded === 0) {
        return action === 'navigateDown' || action === 'navigateRight'
          ? Math.min(homeMenuOffset, homeMenuEntryCount - 1)
          : 0
      }

      const menuIndex = bounded - homeMenuOffset
      if (menuIndex < 0) return bounded
      const column = menuIndex % HOME_MENU_COLUMNS
      if (action === 'navigateLeft') {
        if (column > 0) return bounded - 1
        if (canShowCloudSync && syncComparison && menuIndex < HOME_MENU_COLUMNS) return 2
        return bounded
      }
      if (action === 'navigateRight') {
        const nextMenuIndex = menuIndex + 1
        return column < HOME_MENU_COLUMNS - 1 && nextMenuIndex < MENU_ITEMS.length ? bounded + 1 : bounded
      }
      if (action === 'navigateUp') {
        const previousMenuIndex = menuIndex - HOME_MENU_COLUMNS
        if (previousMenuIndex >= 0) return homeMenuOffset + previousMenuIndex
        if (canShowCloudSync && syncComparison) return column === 0 ? 1 : 2
        if (canShowCloudSync) return 0
        return bounded
      }
      const nextMenuIndex = menuIndex + HOME_MENU_COLUMNS
      return nextMenuIndex < MENU_ITEMS.length ? homeMenuOffset + nextMenuIndex : bounded
    })
  }

  function dungeonEntries() {
    const difficulty = selectedDifficultyOption ?? selectedActivityOption?.difficulties[0]
    const locked = !profile || !difficulty
    const entries: DungeonNavEntry[] = [
      { kind: 'back' },
    ]
    if (activityPageCount > 1) {
      entries.push(
        { kind: 'pagePrev', disabled: currentActivityPage === 0 },
        { kind: 'pageNext', disabled: currentActivityPage >= activityPageCount - 1 },
      )
    }
    if (entries.length % DUNGEON_NAV_COLUMNS !== 0) {
      entries.push({ kind: 'spacer', disabled: true })
    }
    pagedActivityOptions.forEach((candidate, index) => {
      const candidateDifficulty = difficulty
        ? candidate.difficulties.find(
          (option) => option.droppedItemLevel === difficulty.droppedItemLevel,
        ) ?? candidate.difficulties[0]
        : candidate.difficulties[0]
      entries.push({
        kind: 'activity',
        index,
        disabled: !profile || !candidateDifficulty,
      })
    })
    entries.push(
      { kind: 'start', disabled: locked },
      { kind: 'marathon', disabled: locked },
    )
    tierOptions.forEach((difficultyOption, index) => {
      entries.push({
        kind: 'tier',
        index,
        disabled: !profile || !difficultyOption,
      })
    })
    entries.push({ kind: 'loot' })
    if (showLoot) entries.push({ kind: 'lootSort' })
    return entries
  }

  function firstEnabledDungeonEntry(entries: DungeonNavEntry[]) {
    return Math.max(0, entries.findIndex((entry) => !entry.disabled))
  }

  function firstActivityDungeonEntryIndexForPageCount(pageCount: number) {
    const entryCountBeforeActivities = pageCount > 1 ? 3 : 1
    return entryCountBeforeActivities % DUNGEON_NAV_COLUMNS === 0
      ? entryCountBeforeActivities
      : entryCountBeforeActivities + 1
  }

  function activeDungeonEntry(entries = dungeonEntries()) {
    if (entries[dungeonSelectedIndex] && !entries[dungeonSelectedIndex].disabled) {
      return entries[dungeonSelectedIndex]
    }
    const firstEnabled = firstEnabledDungeonEntry(entries)
    return entries[firstEnabled] ?? entries[0]
  }

  function dungeonEntrySelected(entry: DungeonNavEntry['kind'], index?: number) {
    const active = activeDungeonEntry()
    return active?.kind === entry && ('index' in active ? active.index === index : index === undefined)
  }

  function moveDungeonSelection(action: string) {
    const entries = dungeonEntries()
    if (entries.length === 0) return
    setDungeonSelectedIndex((current) => {
      const bounded = entries[current] && !entries[current].disabled
        ? current
        : firstEnabledDungeonEntry(entries)
      const column = bounded % DUNGEON_NAV_COLUMNS
      const direction = action === 'navigateLeft' || action === 'navigateUp' ? -1 : 1
      const step = action === 'navigateUp' || action === 'navigateDown' ? DUNGEON_NAV_COLUMNS : 1
      if (action === 'navigateLeft' && column === 0) return bounded
      if (action === 'navigateRight' && column === DUNGEON_NAV_COLUMNS - 1) return bounded
      for (let next = bounded + direction * step; next >= 0 && next < entries.length; next += direction * step) {
        if (!entries[next].disabled) return next
      }
      return bounded
    })
  }

  function selectActivityByPageIndex(index: number) {
    const candidate = pagedActivityOptions[index]
    if (!candidate) return
    const difficulty = selectedDifficultyOption
      ? candidate.difficulties.find(
        (option) => option.droppedItemLevel === selectedDifficultyOption.droppedItemLevel,
      ) ?? candidate.difficulties[0]
      : candidate.difficulties[0]
    if (screen === 'raids') setSelectedRaidId(candidate.id)
    else setSelectedDungeonId(candidate.id)
    setSelectedDifficultyId(difficulty.id)
  }

  function selectTierByIndex(index: number) {
    const difficulty = tierOptions[index]
    const activity = selectedActivityOption ?? activityOptions[0]
    if (!difficulty || !activity) return
    setActivityPage(0)
    const nextActivity = activity.difficulties.some(
      (candidate) => candidate.droppedItemLevel === difficulty.droppedItemLevel,
    )
      ? activity
      : activityOptions.find((option) =>
        option.difficulties.some((candidate) => candidate.droppedItemLevel === difficulty.droppedItemLevel),
      )
    if (!nextActivity) return
    if (screen === 'raids') setSelectedRaidId(nextActivity.id)
    else setSelectedDungeonId(nextActivity.id)
    const nextDifficulty = nextActivity.difficulties.find(
      (candidate) => candidate.droppedItemLevel === difficulty.droppedItemLevel,
    )
    if (nextDifficulty) setSelectedDifficultyId(nextDifficulty.id)
  }

  function startSelectedRun(marathon: boolean) {
    const activity = selectedActivityOption ?? activityOptions[0]
    const difficulty = selectedDifficultyOption ?? activity?.difficulties[0]
    if (!activity || !difficulty) return
    setSelectedMarathonMode(marathon)
    setCombatContentId(activity.id)
    setSelectedDifficultyId(difficulty.id)
    setScreen('combat')
  }

  function openDungeonEntry(entry: DungeonNavEntry | undefined) {
    if (!entry || entry.disabled) return
    if (entry.kind === 'back') setScreen('menu')
    else if (entry.kind === 'pagePrev') setActivityPage((page) => Math.max(0, page - 1))
    else if (entry.kind === 'pageNext') setActivityPage((page) => Math.min(activityPageCount - 1, page + 1))
    else if (entry.kind === 'activity') selectActivityByPageIndex(entry.index)
    else if (entry.kind === 'tier') selectTierByIndex(entry.index)
    else if (entry.kind === 'start') startSelectedRun(false)
    else if (entry.kind === 'marathon') startSelectedRun(true)
    else if (entry.kind === 'loot') setShowLoot((current) => !current)
    else if (entry.kind === 'lootSort') setLootSort((current) => current === 'sequence' ? 'boss' : 'sequence')
  }

  function startPveRoguelike() {
    const baseDungeon = dungeonOptions[0]
    const baseRaid = raidOptions[0]
    if (roguelikeKind === 'raid') {
      setCombatContentId(-2)
      setSelectedDifficultyId(baseRaid?.difficulties[0]?.id ?? 101)
    } else {
      setCombatContentId(-1)
      setSelectedDifficultyId(baseDungeon?.difficulties[0]?.id ?? 1)
    }
    setSelectedMarathonMode(false)
    setScreen('combat')
  }

  function roguelikeEntries() {
    const entries: RoguelikeNavEntry[] = [
      { kind: 'back' },
      { kind: 'variantPve' },
      { kind: 'variantPvp' },
    ]
    if (roguelikeVariant === 'pve') {
      entries.push(
        { kind: 'pveDungeon' },
        { kind: 'pveRaid' },
        { kind: 'pveStart' },
      )
    } else {
      entries.push(
        { kind: 'pvpDungeon' },
        { kind: 'pvpRaid' },
        { kind: 'pvpStadium' },
        { kind: 'pvpStart' },
      )
    }
    return entries
  }

  function activeRoguelikeEntry(entries = roguelikeEntries()) {
    return entries[Math.min(roguelikeSelectedIndex, entries.length - 1)] ?? entries[0]
  }

  function roguelikeEntrySelected(kind: RoguelikeNavEntry['kind']) {
    return activeRoguelikeEntry()?.kind === kind
  }

  function roguelikeEntryPosition(entry: RoguelikeNavEntry): RoguelikeNavPosition {
    if (entry.kind === 'back') return { row: 0, column: 0 }
    if (entry.kind === 'variantPve') return { row: 1, column: 0 }
    if (entry.kind === 'variantPvp') return { row: 1, column: 1 }
    if (entry.kind === 'pveDungeon' || entry.kind === 'pvpDungeon') return { row: 2, column: 0 }
    if (entry.kind === 'pveRaid' || entry.kind === 'pvpRaid') return { row: 2, column: 1 }
    if (entry.kind === 'pvpStart') return { row: 3, column: 1 }
    return { row: 3, column: 0 }
  }

  function moveRoguelikeSelection(action: string) {
    const entries = roguelikeEntries()
    setRoguelikeSelectedIndex((current) => {
      const bounded = Math.min(current, entries.length - 1)
      const active = entries[bounded]
      if (!active) return bounded
      const activePosition = roguelikeEntryPosition(active)
      const candidates = entries
        .map((entry, index) => ({ entry, index, position: roguelikeEntryPosition(entry) }))
        .filter(({ index, position }) => {
          if (index === bounded) return false
          if (action === 'navigateLeft') return position.row === activePosition.row && position.column < activePosition.column
          if (action === 'navigateRight') return position.row === activePosition.row && position.column > activePosition.column
          if (action === 'navigateUp') return position.row < activePosition.row
          return position.row > activePosition.row
        })
      if (candidates.length === 0) return bounded
      candidates.sort((a, b) => {
        const aPrimary = Math.abs(a.position.row - activePosition.row)
          + Math.abs(a.position.column - activePosition.column)
        const bPrimary = Math.abs(b.position.row - activePosition.row)
          + Math.abs(b.position.column - activePosition.column)
        const aSecondary = action === 'navigateLeft' || action === 'navigateRight'
          ? Math.abs(a.position.column - activePosition.column)
          : Math.abs(a.position.row - activePosition.row)
        const bSecondary = action === 'navigateLeft' || action === 'navigateRight'
          ? Math.abs(b.position.column - activePosition.column)
          : Math.abs(b.position.row - activePosition.row)
        return aPrimary - bPrimary || aSecondary - bSecondary || a.index - b.index
      })
      return candidates[0]?.index ?? bounded
    })
  }

  function openRoguelikeEntry(entry: RoguelikeNavEntry | undefined) {
    if (!entry) return
    if (entry.kind === 'back') setScreen('menu')
    else if (entry.kind === 'variantPve') setRoguelikeVariant('pve')
    else if (entry.kind === 'variantPvp') setRoguelikeVariant('pvp')
    else if (entry.kind === 'pveDungeon') setRoguelikeKind('dungeon')
    else if (entry.kind === 'pveRaid') setRoguelikeKind('raid')
    else if (entry.kind === 'pveStart') startPveRoguelike()
    else if (entry.kind === 'pvpDungeon') setPvpContentType('dungeon')
    else if (entry.kind === 'pvpRaid') setPvpContentType('raid')
    else if (entry.kind === 'pvpStadium') setPvpContentType('stadium')
    else if (entry.kind === 'pvpStart') setScreen('pvp')
  }

  useGameAction((action, device) => {
    if (device !== 'controller') return
    if (screen === 'menu') {
      if (action === 'confirm') {
        openHomeMenuIndex(homeActiveIndex)
        return
      }
      if (!action.startsWith('navigate')) return
      moveHomeSelection(action)
      return
    }
    if (screen === 'dungeons' || screen === 'raids') {
      if (action === 'back') {
        setScreen('menu')
        return
      }
      if (action === 'confirm') {
        openDungeonEntry(activeDungeonEntry())
        return
      }
      if (action.startsWith('navigate')) moveDungeonSelection(action)
    }
    if (screen === 'roguelike') {
      if (action === 'back') {
        setScreen('menu')
        return
      }
      if (action === 'confirm') {
        openRoguelikeEntry(activeRoguelikeEntry())
        return
      }
      if (action.startsWith('navigate')) moveRoguelikeSelection(action)
    }
  })

  const setupDualScreenState = useMemo<DualScreenSetupState | null>(() => {
    if (
      !(screen === 'dungeons' || screen === 'raids')
      || !profile
      || !selectedActivityOption
      || !selectedDifficultyOption
    ) return null
    return {
      contentType: selectedActivityOption.contentType,
      title: selectedActivityOption.name,
      subtitle: `${selectedActivityOption.locationName} | Level ${selectedActivityOption.recommendedLevel} | ${selectedActivityOption.partySize} Players`,
      description: selectedActivityOption.description,
      initials: activityInitials(selectedActivityOption.name),
      difficultyName: selectedDifficultyOption.name,
      itemLevel: selectedDifficultyOption.droppedItemLevel,
      experience: Math.round(selectedActivityOption.experienceReward * selectedDifficultyOption.experienceMultiplier),
      lockedReason: undefined,
      stats: {
        health: `${selectedDifficultyOption.healthMultiplier.toFixed(2)}x`,
        damage: `${selectedDifficultyOption.damageMultiplier.toFixed(2)}x`,
        xp: `${selectedDifficultyOption.experienceMultiplier.toFixed(1)}x`,
        loot: `iLvl ${selectedDifficultyOption.droppedItemLevel}`,
      },
    }
  }, [profile, screen, selectedActivityOption, selectedDifficultyOption])

  useDualScreenSetupPublisher(setupDualScreenState, dualScreenEnabled)

  if (error) {
    return (
      <main className="game-shell">
        <section className="message-panel">
          <p className="eyebrow">Database Error</p>
          <h1>Character Unavailable</h1>
          <p>{error}</p>
        </section>
      </main>
    )
  }

  if (!authChecked) {
    return (
      <main className="game-shell">
        <section className="message-panel">
          <p className="eyebrow">Opening Chronicle</p>
          <h1>Loading...</h1>
        </section>
      </main>
    )
  }

  if (!account || !profile) {
    return (
      <main className="game-shell">
        <section className="message-panel">
          <p className="eyebrow">Opening Chronicle</p>
          <h1>Returning to Sign In...</h1>
          {serverMessage && <p>{serverMessage}</p>}
        </section>
      </main>
    )
  }

  if (screen === 'combat') {
    const dungeon = combatDungeonOption ?? profile.dungeons[0]
    const difficulty = combatDifficultyOption ?? dungeon.difficulties[0]
    return (
      <Suspense fallback={<ScreenLoading />}>
        <CombatScreen
          difficulty={difficulty}
          dungeon={dungeon}
          hardMode={false}
          marathonMode={selectedMarathonMode && combatContentId > 0}
          profile={profile}
          roguelikeMode={combatContentId < 0 ? roguelikeKind : undefined}
          roguelikeUpgradeTiming={combatContentId < 0 ? 'encounter' : undefined}
          roguelikeAbilityLabelMode={combatContentId < 0 ? 'ability' : undefined}
          roguelikeEncounterPool={combatContentId < 0 ? roguelikePool : undefined}
          startPart={1}
          onExit={() => {
            setScreen(combatContentId < 0 ? 'roguelike' : dungeon.contentType === 'raid' ? 'raids' : 'dungeons')
          }}
          onMainMenu={() => setScreen('menu')}
          onProfileUpdated={setProfile}
        />
      </Suspense>
    )
  }

  if (screen === 'pvp') {
    if (pvpContentType === 'stadium') {
      return (
        <Suspense fallback={<ScreenLoading />}>
          <PvpStadiumScreen
            gameMode={gameMode}
            onExit={() => {
              setRoguelikeVariant('pvp')
              setScreen('roguelike')
            }}
            onProfileUpdated={setProfile}
            profile={profile}
          />
        </Suspense>
      )
    }
    return (
      <Suspense fallback={<ScreenLoading />}>
        <PvPRoguelikeScreen
          contentType={pvpContentType}
          encounterPool={pvpPool}
          gameMode={gameMode}
          onExit={() => {
            setCpuLeaderboard(loadCpuPvpLeaderboard(pvpContentType))
            setRoguelikeVariant('pvp')
            setScreen('roguelike')
          }}
          onMainMenu={() => {
            setCpuLeaderboard(loadCpuPvpLeaderboard(pvpContentType))
            setScreen('menu')
          }}
          onProfileUpdated={setProfile}
          profile={profile}
        />
      </Suspense>
    )
  }

  const levelStart = profile.character.currentLevelExperience
  const levelEnd = profile.character.nextLevelExperience
  const experienceIntoLevel = profile.character.experience - levelStart
  const experienceForLevel = Math.max(1, levelEnd - levelStart)
  const experiencePercent = profile.character.level >= profile.maxLevel
    ? 100
    : Math.min(100, (experienceIntoLevel / experienceForLevel) * 100)
  const dungeon = selectedDungeonOption ?? profile.dungeons[0]!
  const activityPageStart = activityOptions.length === 0
    ? 0
    : currentActivityPage * ACTIVITY_PAGE_SIZE + 1
  const activityPageEnd = Math.min(activityOptions.length, (currentActivityPage + 1) * ACTIVITY_PAGE_SIZE)
  const activity = selectedActivityOption ?? dungeon
  const selectedDifficulty = selectedDifficultyOption ?? activity.difficulties[0]
  return (
    <main className={`game-shell ${screen === 'dungeons' || screen === 'raids' ? 'dungeon-shell' : ''} ${screen === 'customize' ? 'workshop-shell' : ''} ${screen === 'settings' ? 'settings-shell' : ''}`}>
      {screen !== 'hunter-profile' && (
        <header className="topbar app-header">
          <button
            className="brand-button"
            onClick={onBackToGameSelect}
            type="button"
          >
            <strong>Games</strong>
          </button>
          <button
            className="brand-button"
            onClick={() => setScreen('menu')}
            type="button"
          >
            <strong>Menu</strong>
          </button>
          <div className="character-summary">
            <strong>{profile.character.name}</strong>
            <small>Level {profile.character.level}</small>
            <small>Item Level {profile.gearStats.averageItemLevel.toFixed(1)}</small>
            <div className="header-xp" title={`${profile.character.experience} total experience`}>
              <span style={barFillStyle(experiencePercent)} />
            </div>
            <button className="logout-button" onClick={signOut} type="button">
              {gameMode === 'offline' ? 'Leave Offline' : `Sign Out ${account.username}`}
            </button>
          </div>
        </header>
      )}

      {screen === 'menu' && (
        <section className="menu-screen" data-game-nav-active="true">
          <div className={`main-menu-grid ${canShowCloudSync ? 'has-cloud-sync' : ''}`}>
            {canShowCloudSync && (
              <div
                className={`menu-card cloud-sync-card ${homeActiveIndex === 0 ? 'game-selected' : ''}`}
                data-game-selected={homeActiveIndex === 0 ? 'true' : undefined}
                onPointerDown={() => setHomeSelectedIndex(0)}
              >
                <span>{syncComparison ? '!' : cloudSync.dirty ? 'S' : 'C'}</span>
                <div>
                  <strong>Sync With Server</strong>
                  <small>
                    {syncComparison
                      ? cloudSyncRelationText(syncComparison)
                      : cloudSync.dirty
                      ? 'Local progress waiting. Upload when you want to refresh the server copy.'
                      : 'Server copy matches this device.'}
                  </small>
                  {syncComparison && (
                    <div className="cloud-sync-times">
                      <small>Device: {formatSaveTimestamp(syncComparison.local.updatedAt)}</small>
                      <small>Server: {formatSaveTimestamp(syncComparison.server.updatedAt)}</small>
                    </div>
                  )}
                  {syncMessage && <small className="cloud-sync-message">{syncMessage}</small>}
                </div>
                <div className="cloud-sync-control-stack">
                  <button
                    className="text-button"
                    data-controller-nav="skip"
                    disabled={syncingCloud}
                    onClick={syncSaveNow}
                    type="button"
                  >
                    {syncingCloud ? 'Checking...' : syncComparison ? 'Check Again' : 'Sync With Server'}
                  </button>
                  {syncComparison && (
                    <div className="cloud-sync-actions">
                      <button
                        className={`text-button ${homeActiveIndex === 1 ? 'game-selected' : ''}`}
                        data-controller-nav="skip"
                        data-game-selected={homeActiveIndex === 1 ? 'true' : undefined}
                        disabled={syncingCloud}
                        onClick={() => keepCloudSave('local')}
                        onPointerDown={() => setHomeSelectedIndex(1)}
                        type="button"
                      >
                        Keep Device Save
                      </button>
                      <button
                        className={`text-button ${homeActiveIndex === 2 ? 'game-selected' : ''}`}
                        data-controller-nav="skip"
                        data-game-selected={homeActiveIndex === 2 ? 'true' : undefined}
                        disabled={syncingCloud}
                        onClick={() => keepCloudSave('server')}
                        onPointerDown={() => setHomeSelectedIndex(2)}
                        type="button"
                      >
                        Keep Server Save
                      </button>
                    </div>
                  )}
                </div>
              </div>
            )}
            {MENU_ITEMS.map((item, index) => {
              const homeIndex = index + homeMenuOffset
              return (
                <button
                  className={`menu-card ${homeActiveIndex === homeIndex ? 'game-selected' : ''}`}
                  data-controller-nav="skip"
                  data-game-selected={homeActiveIndex === homeIndex ? 'true' : undefined}
                  key={item.screen}
                  onClick={() => openHomeMenuIndex(homeIndex)}
                  onPointerDown={() => setHomeSelectedIndex(homeIndex)}
                  type="button"
                >
                  <span>{item.glyph}</span>
                  <div>
                    <strong>{item.label}</strong>
                    <small>{item.description}</small>
                  </div>
                </button>
              )
            })}
          </div>
        </section>
      )}

      {screen === 'roguelike' && (
        <section className="content-screen roguelike-screen" data-game-nav-active="true">
          <ScreenHeading
            backClassName={roguelikeEntrySelected('back') ? 'game-selected' : ''}
            backControllerSkip
            eyebrow="Endless Draft"
            title="Roguelike"
            onBack={() => setScreen('menu')}
            onBackPointerDown={() => setRoguelikeSelectedIndex(roguelikeEntries().findIndex((entry) => entry.kind === 'back'))}
          />
          <div className="roguelike-variant-row">
            <button
              className={`text-button ${roguelikeVariant === 'pve' ? 'active' : ''} ${roguelikeEntrySelected('variantPve') ? 'game-selected' : ''}`}
              data-controller-nav="skip"
              onClick={() => setRoguelikeVariant('pve')}
              onPointerDown={() => setRoguelikeSelectedIndex(roguelikeEntries().findIndex((entry) => entry.kind === 'variantPve'))}
              type="button"
            >
              PvE
            </button>
            <button
              className={`text-button ${roguelikeVariant === 'pvp' ? 'active' : ''} ${roguelikeEntrySelected('variantPvp') ? 'game-selected' : ''}`}
              data-controller-nav="skip"
              onClick={() => setRoguelikeVariant('pvp')}
              onPointerDown={() => setRoguelikeSelectedIndex(roguelikeEntries().findIndex((entry) => entry.kind === 'variantPvp'))}
              type="button"
            >
              PvP
            </button>
          </div>
          {roguelikeVariant === 'pve' && (
            <>
              <div className="roguelike-option-panel">
                <div>
                  <p className="eyebrow">Run Type</p>
                  <h2>PvE Roguelike</h2>
                </div>
                <div className="roguelike-timing-row">
                  <button
                    className={`text-button ${roguelikeKind === 'dungeon' ? 'active' : ''} ${roguelikeEntrySelected('pveDungeon') ? 'game-selected' : ''}`}
                    data-controller-nav="skip"
                    onClick={() => setRoguelikeKind('dungeon')}
                    onPointerDown={() => setRoguelikeSelectedIndex(roguelikeEntries().findIndex((entry) => entry.kind === 'pveDungeon'))}
                    type="button"
                  >
                    Dungeon
                  </button>
                  <button
                    className={`text-button ${roguelikeKind === 'raid' ? 'active' : ''} ${roguelikeEntrySelected('pveRaid') ? 'game-selected' : ''}`}
                    data-controller-nav="skip"
                    onClick={() => setRoguelikeKind('raid')}
                    onPointerDown={() => setRoguelikeSelectedIndex(roguelikeEntries().findIndex((entry) => entry.kind === 'pveRaid'))}
                    type="button"
                  >
                    Raid
                  </button>
                </div>
              </div>
              <div className="menu-card pvp-queue-panel">
                <span>{roguelikeKind === 'raid' ? 'R' : 'D'}</span>
                <div>
                  <strong>{roguelikeKind === 'raid' ? 'Raid Roguelike' : 'Dungeon Roguelike'}</strong>
                  <small>
                    {roguelikeKind === 'raid'
                      ? 'Ten-player party. Raid pools, lighter early scaling, and the same upgrade draft.'
                      : 'Five-player party. Two random trash enemies and a boss with a lighter early ramp.'}
                  </small>
                </div>
                <button
                  className={`text-button ${roguelikeEntrySelected('pveStart') ? 'game-selected' : ''}`}
                  data-controller-nav="skip"
                  onClick={startPveRoguelike}
                  onPointerDown={() => setRoguelikeSelectedIndex(roguelikeEntries().findIndex((entry) => entry.kind === 'pveStart'))}
                  type="button"
                >
                  Start Run
                </button>
              </div>
            </>
          )}
          {roguelikeVariant === 'pvp' && (
            <>
              <div className="roguelike-option-panel">
                <div>
                  <p className="eyebrow">Match Type</p>
                  <h2>PvP Roguelike</h2>
                </div>
                <div className="roguelike-timing-row">
                  <button
                    className={`text-button ${pvpContentType === 'dungeon' ? 'active' : ''} ${roguelikeEntrySelected('pvpDungeon') ? 'game-selected' : ''}`}
                    data-controller-nav="skip"
                    onClick={() => setPvpContentType('dungeon')}
                    onPointerDown={() => setRoguelikeSelectedIndex(roguelikeEntries().findIndex((entry) => entry.kind === 'pvpDungeon'))}
                    type="button"
                  >
                    Dungeon
                  </button>
                  <button
                    className={`text-button ${pvpContentType === 'raid' ? 'active' : ''} ${roguelikeEntrySelected('pvpRaid') ? 'game-selected' : ''}`}
                    data-controller-nav="skip"
                    onClick={() => setPvpContentType('raid')}
                    onPointerDown={() => setRoguelikeSelectedIndex(roguelikeEntries().findIndex((entry) => entry.kind === 'pvpRaid'))}
                    type="button"
                  >
                    Raid
                  </button>
                  <button
                    className={`text-button ${pvpContentType === 'stadium' ? 'active' : ''} ${roguelikeEntrySelected('pvpStadium') ? 'game-selected' : ''}`}
                    data-controller-nav="skip"
                    onClick={() => setPvpContentType('stadium')}
                    onPointerDown={() => setRoguelikeSelectedIndex(roguelikeEntries().findIndex((entry) => entry.kind === 'pvpStadium'))}
                    type="button"
                  >
                    Stadium
                  </button>
                </div>
              </div>
              <div className="menu-card pvp-queue-panel">
                <span>{gameMode === 'offline' ? 'C' : 'Q'}</span>
                <div>
                  <strong>{gameMode === 'offline' ? 'Offline CPU Match' : 'Queue Then CPU Fallback'}</strong>
                  <small>
                    {pvpContentType === 'stadium'
                      ? 'Best-of-5 survival with dampening, equalized gear, and after-round buff buying.'
                      : gameMode === 'offline'
                        ? 'Offline mode always places you against a random CPU 1-5.'
                        : 'Online mode searches briefly. If nobody is queued, a random CPU 1-5 takes the slot.'}
                  </small>
                </div>
                <button
                  className={`text-button ${roguelikeEntrySelected('pvpStart') ? 'game-selected' : ''}`}
                  data-controller-nav="skip"
                  onClick={() => setScreen('pvp')}
                  onPointerDown={() => setRoguelikeSelectedIndex(roguelikeEntries().findIndex((entry) => entry.kind === 'pvpStart'))}
                  type="button"
                >
                  Start Match
                </button>
              </div>
              {SHOW_LEADERBOARDS && (
              <div className="leaderboard-section">
                <div className="equipment-heading toggle-heading">
                  <div>
                    <p className="eyebrow">CPU Leaderboard</p>
                    <h2>{pvpContentType === 'stadium' ? 'Stadium' : pvpContentType === 'raid' ? 'Raid Clash' : 'Dungeon Clash'}</h2>
                  </div>
                </div>
                <div className="leaderboard-table">
                  <div className="leaderboard-header pvp-leaderboard-row">
                    <strong>Rank</strong>
                    <strong>Player</strong>
                    <strong>CPU</strong>
                    <strong>Clears</strong>
                    <strong>Result</strong>
                  </div>
                  {cpuLeaderboard.map((entry, index) => (
                    <div className="leaderboard-row pvp-leaderboard-row" key={`${entry.characterName}-${entry.completedAt}`}>
                      <strong>#{index + 1}</strong>
                      <span>{entry.characterName}</span>
                      <span>CPU {entry.cpuDifficulty}</span>
                      <span>{entry.encountersCleared}</span>
                      <span>{entry.result}</span>
                    </div>
                  ))}
                  {cpuLeaderboard.length === 0 && (
                    <div className="leaderboard-empty">
                      No CPU runs recorded yet for this mode.
                    </div>
                  )}
                </div>
              </div>
              )}
            </>
          )}
        </section>
      )}

      {(screen === 'dungeons' || screen === 'raids') && (
        <section className="content-screen dungeon-run-screen" data-game-nav-active="true">
          <div className="dungeon-run-board">
            <div className="dungeon-run-main">
              <section className="run-setup-panel dungeon-choice-panel">
                <div className="run-setup-heading">
                  <div>
                    <p className="eyebrow">Pick Run</p>
                    <h2>{screen === 'raids' ? 'Raid' : 'Dungeon'}</h2>
                  </div>
                  <div className="dungeon-heading-actions">
                    <button
                      className={`back-button inline-back-button ${dungeonEntrySelected('back') ? 'game-selected' : ''}`}
                      data-controller-nav="skip"
                      onClick={() => setScreen('menu')}
                      onPointerDown={() => setDungeonSelectedIndex(dungeonEntries().findIndex((entry) => entry.kind === 'back'))}
                      type="button"
                    >
                      Back
                    </button>
                    {activityPageCount > 1 ? (
                      <div className="activity-pager" aria-label={`${screen === 'raids' ? 'Raid' : 'Dungeon'} pages`}>
                      <button
                        className={dungeonEntrySelected('pagePrev') ? 'game-selected' : ''}
                        data-controller-nav="skip"
                        disabled={currentActivityPage === 0}
                        onClick={() => setActivityPage((page) => Math.max(0, page - 1))}
                        onPointerDown={() => setDungeonSelectedIndex(dungeonEntries().findIndex((entry) => entry.kind === 'pagePrev'))}
                        type="button"
                      >
                        Prev
                      </button>
                      <span>{activityPageStart}-{activityPageEnd} of {activityOptions.length}</span>
                      <button
                        className={dungeonEntrySelected('pageNext') ? 'game-selected' : ''}
                        data-controller-nav="skip"
                        disabled={currentActivityPage >= activityPageCount - 1}
                        onClick={() => setActivityPage((page) => Math.min(activityPageCount - 1, page + 1))}
                        onPointerDown={() => setDungeonSelectedIndex(dungeonEntries().findIndex((entry) => entry.kind === 'pageNext'))}
                        type="button"
                      >
                        Next
                      </button>
                      </div>
                    ) : (
                      <small>{selectedDifficulty.name} rewards iLvl {selectedDifficulty.droppedItemLevel} components.</small>
                    )}
                  </div>
                </div>
                <div className="activity-card-grid dungeon-choice-grid">
                  {pagedActivityOptions.map((candidate, index) => {
                    const difficulty = candidate.difficulties.find(
                      (option) => option.droppedItemLevel === selectedDifficulty.droppedItemLevel,
                    ) ?? candidate.difficulties[0]
                    const selected = candidate.id === activity.id
                    return (
                      <button
                        className={`activity-card ${selected ? 'selected' : ''} ${dungeonEntrySelected('activity', index) ? 'game-selected' : ''}`}
                        data-controller-nav="skip"
                        key={candidate.id}
                        onClick={() => {
                          if (screen === 'raids') setSelectedRaidId(candidate.id)
                          else setSelectedDungeonId(candidate.id)
                          setSelectedDifficultyId(difficulty.id)
                        }}
                        onPointerDown={() => setDungeonSelectedIndex(dungeonEntries().findIndex((entry) => entry.kind === 'activity' && 'index' in entry && entry.index === index))}
                        type="button"
                      >
                        <span className={`dungeon-art ${candidate.contentType === 'raid' ? 'raid-art' : ''}`}>
                          {activityInitials(candidate.name)}
                        </span>
                        <strong>{candidate.name}</strong>
                        <small>{candidate.locationName}</small>
                        <i>
                          Level {candidate.recommendedLevel} | {candidate.partySize} Players
                        </i>
                      </button>
                    )
                  })}
                </div>
              </section>
            </div>

            <aside className="dungeon-setup-rail">
              <section className="run-setup-panel part-setup-panel">
                <div className="run-setup-heading">
                  <div>
                    <p className="eyebrow">Start</p>
                    <h2>Run</h2>
                  </div>
                  <small>
                    Pick a hunt or marathon.
                  </small>
                </div>
                <div className="part-picker">
                  <button
                    className={`primary-button selected-part ${dungeonEntrySelected('start') ? 'game-selected' : ''}`}
                    data-controller-nav="skip"
                    onClick={() => {
                      setSelectedMarathonMode(false)
                      setCombatContentId(activity.id)
                      setSelectedDifficultyId(selectedDifficulty.id)
                      setScreen('combat')
                    }}
                    onPointerDown={() => setDungeonSelectedIndex(dungeonEntries().findIndex((entry) => entry.kind === 'start'))}
                    type="button"
                  >
                    Start Hunt
                  </button>
                  <button
                    className={`primary-button ${selectedMarathonMode ? 'selected-part' : ''} ${dungeonEntrySelected('marathon') ? 'game-selected' : ''}`}
                    data-controller-nav="skip"
                    onClick={() => {
                      setSelectedMarathonMode(true)
                      setCombatContentId(activity.id)
                      setSelectedDifficultyId(selectedDifficulty.id)
                      setScreen('combat')
                    }}
                    onPointerDown={() => setDungeonSelectedIndex(dungeonEntries().findIndex((entry) => entry.kind === 'marathon'))}
                    type="button"
                  >
                    Marathon
                  </button>
                </div>
              </section>

              <section className="run-setup-panel tier-setup-panel">
                <div className="run-setup-heading">
                  <div>
                    <p className="eyebrow">Item Level</p>
                    <h2>Tier</h2>
                  </div>
                  <small>{screen === 'raids' ? 'Raid' : 'Dungeon'} tiers are available at every level.</small>
                </div>
                <div className="tier-grid">
                  {tierOptions.map((difficulty, index) => {
                    const selected = difficulty.droppedItemLevel === selectedDifficulty.droppedItemLevel
                    return (
                      <button
                        className={`${selected ? 'selected' : ''} ${dungeonEntrySelected('tier', index) ? 'game-selected' : ''}`}
                        data-controller-nav="skip"
                        key={difficulty.id}
                        onClick={() => {
                          setActivityPage(0)
                          const nextActivity = activity.difficulties.some(
                            (candidate) => candidate.droppedItemLevel === difficulty.droppedItemLevel,
                          )
                            ? activity
                            : activityOptions.find((option) =>
                              option.difficulties.some((candidate) => candidate.droppedItemLevel === difficulty.droppedItemLevel),
                            )
                          if (nextActivity) {
                            if (screen === 'raids') setSelectedRaidId(nextActivity.id)
                            else setSelectedDungeonId(nextActivity.id)
                            const nextDifficulty = nextActivity.difficulties.find(
                              (candidate) => candidate.droppedItemLevel === difficulty.droppedItemLevel,
                            )
                            if (nextDifficulty) setSelectedDifficultyId(nextDifficulty.id)
                          }
                        }}
                        onPointerDown={() => setDungeonSelectedIndex(dungeonEntries().findIndex((entry) => entry.kind === 'tier' && 'index' in entry && entry.index === index))}
                        type="button"
                      >
                        <strong>iLvl {difficulty.droppedItemLevel}</strong>
                        <span>{difficulty.name}</span>
                      </button>
                    )
                  })}
                </div>
              </section>

              <div className="loot-preview-section">
            <div className="equipment-heading toggle-heading">
              <div>
                <p className="eyebrow">Encounter Rewards</p>
                <h2>{selectedDifficulty.name} Loot Tables</h2>
              </div>
              <button
                className={`text-button ${dungeonEntrySelected('loot') ? 'game-selected' : ''}`}
                data-controller-nav="skip"
                onClick={() => setShowLoot((current) => !current)}
                onPointerDown={() => setDungeonSelectedIndex(dungeonEntries().findIndex((entry) => entry.kind === 'loot'))}
                type="button"
              >
                {showLoot ? 'Hide Loot' : 'View Loot'}
              </button>
            </div>
            {showLoot && (
              <>
                <div className="loot-toolbar">
                  <label className={dungeonEntrySelected('lootSort') ? 'game-selected' : ''}>
                    <span>Sort</span>
                    <select
                      data-controller-nav="skip"
                      onChange={(event) => setLootSort(event.target.value as 'sequence' | 'boss')}
                      onPointerDown={() => setDungeonSelectedIndex(dungeonEntries().findIndex((entry) => entry.kind === 'lootSort'))}
                      value={lootSort}
                    >
                      <option value="sequence">Encounter order</option>
                      <option value="boss">Boss name</option>
                    </select>
                  </label>
                </div>
                <p className="section-note">
                  Bosses drop 1-3 boss coins from one loot roll
                  {activity.completionItemLevel
                    ? ` - full clear guarantees iLvl ${activity.completionItemLevel}`
                    : ''}
                </p>
                <div className="loot-preview-grid">
                  {lootPreviewEncounters.map((encounter) => {
                    const loot = lootPreviewByEncounterId.get(encounter.id) ?? []
                    return (
                      <article key={encounter.id}>
                        <div className="loot-encounter-title">
                          {encounter.isBoss ? (
                            <img className="loot-boss-icon" src={encounter.imageUrl} alt={`${encounter.enemyName} icon`} />
                          ) : (
                            <span>{encounter.sequence}</span>
                          )}
                          <div>
                            <strong>{encounter.enemyName}</strong>
                            <small>{loot.length > 0 ? '1 loot roll, 1-3 coins' : 'No coin table'}</small>
                          </div>
                        </div>
                        <div className="loot-items">
                          {loot.map((item) => (
                            <div className={`rarity-${item.rarity}`} key={item.id}>
                              <span>{item.glyph}</span>
                              <div>
                                <strong>{item.name}</strong>
                                <small>{item.slot} - iLvl {item.itemLevel}</small>
                              </div>
                              <i>{item.dropWeight}% weight</i>
                            </div>
                          ))}
                        </div>
                      </article>
                    )
                  })}
                </div>
              </>
            )}
              </div>
              {SHOW_LEADERBOARDS && (
              <div className="leaderboard-section">
            <div className="equipment-heading toggle-heading">
              <div>
                <p className="eyebrow">Efficiency Rankings</p>
                <h2>{selectedDifficulty.name} Leaderboard</h2>
              </div>
              <button
                className="text-button"
                onClick={() => setShowLeaderboard((current) => !current)}
                type="button"
              >
                {showLeaderboard ? 'Hide Leaderboard' : 'View Leaderboard'}
              </button>
            </div>
            {showLeaderboard && (
              <>
                <p className="section-note">
                  {gameMode === 'offline'
                    ? 'Offline runs are not submitted'
                    : canShowCloudSync
                      ? 'Manual save sync updates your cloud profile.'
                      : 'Lowest resource spent ranks first'}
                </p>
                <div className="leaderboard-tabs">
                  {([
                    { key: 'part_1', label: 'Run' },
                    { key: 'part_2', label: 'Legacy 2' },
                    { key: 'part_3', label: 'Legacy 3' },
                    { key: 'full_run', label: 'Legacy Full' },
                  ] as const).map((tab) => (
                    <button
                      key={tab.key}
                      className={`leaderboard-tab ${leaderboardCategory === tab.key ? 'active' : ''}`}
                      onClick={() => setLeaderboardCategory(tab.key)}
                      type="button"
                    >
                      {tab.label}
                    </button>
                  ))}
                </div>
                <div className="leaderboard-table">
                  <div className="leaderboard-header">
                    <span>Rank</span>
                    <span>Healer</span>
                    <span>Class</span>
                    <span>Level</span>
                    <span>Item Level</span>
                    <span>Resource</span>
                    <span>Time</span>
                  </div>
                  {leaderboardEntries
                    .map((entry) => (
                      <div className="leaderboard-row" key={`${entry.rank}-${entry.completedAt}`}>
                        <strong>#{entry.rank}</strong>
                        <span>{entry.characterName}</span>
                        <span>{entry.className}</span>
                        <span>{entry.characterLevel}</span>
                        <span>{entry.averageItemLevel.toFixed(1)}</span>
                        <strong>{entry.resourceSpent}</strong>
                        <span>{entry.durationSeconds}s</span>
                      </div>
                    ))}
                  {leaderboardEntries.length === 0 && (
                    <div className="leaderboard-empty">
                      {gameMode === 'offline'
                        ? 'Connect with an online character to compete in rankings.'
                        : canShowCloudSync
                          ? 'No leaderboard entries yet.'
                          : 'Complete this difficulty to claim the first ranking.'}
                    </div>
                  )}
                </div>
              </>
            )}
              </div>
              )}
            </aside>
          </div>
        </section>
      )}

      <Suspense fallback={<ScreenLoading />}>
        {screen === 'customize' && (
          <CustomizeScreen
            profile={profile}
            onBack={() => setScreen('menu')}
            onSaved={setProfile}
          />
        )}

        {screen === 'talents' && (
          <TalentScreen
            profile={profile}
            onBack={() => setScreen('menu')}
            onUpdated={setProfile}
          />
        )}

        {screen === 'equipment' && (
          <EquipmentScreen
            profile={profile}
            onBack={() => setScreen('menu')}
            onUpdated={setProfile}
          />
        )}

        {screen === 'hunter-profile' && (
          <HunterProfileScreen
            profile={profile}
            onBack={() => setScreen('menu')}
          />
        )}

        {screen === 'settings' && (
          <SettingsScreen onBack={() => setScreen('menu')} />
        )}
      </Suspense>

    </main>
  )
}

function ScreenHeading({
  backClassName = '',
  backControllerSkip = false,
  eyebrow,
  title,
  onBack,
  onBackPointerDown,
}: {
  backClassName?: string
  backControllerSkip?: boolean
  eyebrow: string
  title: string
  onBack: () => void
  onBackPointerDown?: () => void
}) {
  return (
    <div className="screen-heading">
      <div>
        <p className="eyebrow">{eyebrow}</p>
        <h1>{title}</h1>
      </div>
      <button
        className={`back-button ${backClassName}`}
        data-controller-nav={backControllerSkip ? 'skip' : undefined}
        onClick={onBack}
        onPointerDown={onBackPointerDown}
        type="button"
      >
        Back
      </button>
    </div>
  )
}

export default IWantToHeal1App
