diff --git a/esoui/ingame/mainmenu/gamepad/zo_mainmenu_gamepad.lua b/esoui/ingame/mainmenu/gamepad/zo_mainmenu_gamepad.lua
index 8f1665d..7dec08a 100755
--- a/esoui/ingame/mainmenu/gamepad/zo_mainmenu_gamepad.lua
+++ b/esoui/ingame/mainmenu/gamepad/zo_mainmenu_gamepad.lua
@@ -1,259 +1,379 @@
-local CATEGORY_TO_SCENE =
+local MENU_ENTRIES = {}
+local CATEGORY_TO_ENTRY_DATA = {}
+
+do
+    local MENU_MAIN_ENTRIES =
+    {
+        MARKET          = 1,
+        NOTIFICATIONS   = 2,
+        COLLECTIONS     = 3,
+        INVENTORY       = 4,
+        CHARACTER       = 5,
+        SKILLS          = 6,
+        CHAMPION        = 7,
+        CAMPAIGN        = 8,
+        JOURNAL         = 9,
+        SOCIAL          = 10,
+        ACTIVITY_FINDER = 11,
+        HELP            = 12,
+        OPTIONS         = 13,
+        LOG_OUT         = 14,
+    }
+    local MENU_JOURNAL_ENTRIES =
     {
-    [MENU_CATEGORY_MARKET] =
+        QUESTS              = 1,
+        CADWELLS_JOURNAL    = 2,
+        LORE_LIBRARY        = 3,
+        ACHIEVEMENTS        = 4,
+        LEADERBOARDS        = 5,
+    }
+    local MENU_SOCIAL_ENTRIES =
+    {
+        VOICE_CHAT  = 1,
+        TEXT_CHAT   = 2,
+        EMOTES      = 3,
+        GROUP       = 4,
+        GUILDS      = 5,
+        FRIENDS     = 6,
+        IGNORED     = 7,
+        MAIL        = 8,
+    }
+
+    local MENU_ENTRY_DATA =
+    {
+        [MENU_MAIN_ENTRIES.NOTIFICATIONS] =
+        {
+            scene = "gamepad_notifications_root",
+            name =
+                function()
+                    local numNotifications = GAMEPAD_NOTIFICATIONS and GAMEPAD_NOTIFICATIONS:GetNumNotifications() or 0
+                    return zo_strformat(SI_GAMEPAD_MAIN_MENU_NOTIFICATIONS, numNotifications)
+                end,
+            icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_notifications.dds",
+            isNewCallback =
+                function()
+                    return true --new icon indicator should always display
+                end,
+            isVisibleCallback =
+                function()
+                    if GAMEPAD_NOTIFICATIONS then
+                        return GAMEPAD_NOTIFICATIONS:GetNumNotifications() > 0
+                    else
+                        return false
+                    end
+                end,
+        },
+        [MENU_MAIN_ENTRIES.MARKET] =
         {
             scene = "gamepad_market_pre_scene",
+            additionalScenes =
+                {
+                    "gamepad_market",
+                    "gamepad_market_preview",
+                    "gamepad_market_bundle_contents",
+                    "gamepad_market_purchase",
+                    "gamepad_market_locked",
                 },
-    [MENU_CATEGORY_INVENTORY] =
+            customTemplate = "ZO_GamepadMenuCrownStoreEntryTemplate",
+            name = GetString(SI_GAMEPAD_MAIN_MENU_MARKET_ENTRY),
+            icon = "EsoUI/Art/MenuBar/Gamepad/gp_PlayerMenu_icon_store.dds",
+            header = GetString(SI_ESO_PLUS_TITLE),
+            postPadding = 70,
+            showHeader = function() return IsESOPlusSubscriber() end
+        },
+        [MENU_MAIN_ENTRIES.COLLECTIONS] =
+        {
+            scene = "gamepadCollectionsBook",
+            name = GetString(SI_MAIN_MENU_COLLECTIONS),
+            icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_collections.dds",
+            isNewCallback =
+                function()
+                    return GAMEPAD_NOTIFICATIONS and GAMEPAD_NOTIFICATIONS:GetNumCollectionsNotifications() > 0 or false
+                end,
+        },
+        [MENU_MAIN_ENTRIES.INVENTORY] =
         {
             scene = "gamepad_inventory_root",
+            name = GetString(SI_MAIN_MENU_INVENTORY),
+            icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_inventory.dds",
+            isNewCallback =
+                function()
+                    return SHARED_INVENTORY:AreAnyItemsNew(nil, nil, BAG_BACKPACK)
+                end,
         },
-    [MENU_CATEGORY_CHARACTER] =
+        [MENU_MAIN_ENTRIES.CHARACTER] =
         {
             scene = "gamepad_stats_root",
+            name = GetString(SI_MAIN_MENU_CHARACTER),
+            icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_character.dds",
+            canLevel =
+                function()
+                    return GetAttributeUnspentPoints() > 0
+                end
         },
-    [MENU_CATEGORY_SKILLS] =
+        [MENU_MAIN_ENTRIES.SKILLS] =
         {
             scene = "gamepad_skills_root",
+            customTemplate = "ZO_GamepadNewAnimatingMenuEntryTemplate",
+            name = GetString(SI_MAIN_MENU_SKILLS),
+            icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_skills.dds",
+            canLevel =
+                function()
+                    return GetAvailableSkillPoints() > 0
+                end,
         },
-    [MENU_CATEGORY_CHAMPION] =
+        [MENU_MAIN_ENTRIES.CHAMPION] =
         {
             scene = "gamepad_championPerks_root",
+            name = GetString(SI_MAIN_MENU_CHAMPION),
+            icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_champion.dds",
+            isNewCallback =
+                function()
+                    if CHAMPION_PERKS then
+                        return CHAMPION_PERKS:IsChampionSystemNew()
+                    end
+                end,
+            isVisibleCallback =
+                function()
+                    return IsChampionSystemUnlocked()
+                end,
+            canLevel =
+                function()
+                    if CHAMPION_PERKS then
+                        return CHAMPION_PERKS:HasAnySpendableUnspentPoints()
+                    end
+                end,
         },
-    [MENU_CATEGORY_JOURNAL] =
+        [MENU_MAIN_ENTRIES.CAMPAIGN] =
+        {
+            scene = "gamepad_campaign_root",
+            name = GetString(SI_PLAYER_MENU_CAMPAIGNS),
+            icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_allianceWar.dds",
+            isNewCallback =
+                function()
+                    local tutorialId = GetTutorialId(TUTORIAL_TRIGGER_CAMPAIGN_BROWSER_OPENED)
+                    if CanTutorialBeSeen(tutorialId) then
+                        return not HasSeenTutorial(tutorialId)
+                    end
+                    return false
+                end,
+            isVisibleCallback =
+                function()
+                    local currentLevel = GetUnitLevel("player")
+                    return currentLevel >= GetMinLevelForCampaignTutorial()
+                end,
+        },
+        [MENU_MAIN_ENTRIES.JOURNAL] =
+        {
+            customTemplate = "ZO_GamepadMenuEntryTemplateWithArrow",
+            name = GetString(SI_MAIN_MENU_JOURNAL),
+            icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_quests.dds",
+            subMenu =
+            {
+                [MENU_JOURNAL_ENTRIES.QUESTS] =
                 {
                     scene = "gamepad_quest_journal",
+                    name = GetString(SI_GAMEPAD_MAIN_MENU_JOURNAL_QUESTS),
+                    icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_quests.dds",
+                    header = GetString(SI_MAIN_MENU_JOURNAL),
                 },
-    [MENU_CATEGORY_COLLECTIONS] =
+                [MENU_JOURNAL_ENTRIES.CADWELLS_JOURNAL] =
                 {
-        scene = "gamepadCollectionsBook",
+                    scene = "cadwellGamepad",
+                    name = GetString(SI_GAMEPAD_MAIN_MENU_JOURNAL_CADWELL),
+                    icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_cadwell.dds",
+                    isVisibleCallback =
+                        function()
+                            return GetPlayerDifficultyLevel() > PLAYER_DIFFICULTY_LEVEL_FIRST_ALLIANCE
+                        end,
                 },
-    [MENU_CATEGORY_MAP] =
+                [MENU_JOURNAL_ENTRIES.LORE_LIBRARY] =
                 {
-        scene = "gamepad_worldMap",
+                    scene = "loreLibraryGamepad",
+                    name = GetString(SI_GAMEPAD_MAIN_MENU_JOURNAL_LORE_LIBRARAY),
+                    icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_loreLibrary.dds",
                 },
-    [MENU_CATEGORY_GROUP] =
+                [MENU_JOURNAL_ENTRIES.ACHIEVEMENTS] =
                 {
-        scene = "gamepad_groupList",
+                    scene = "achievementsGamepad",
+                    name = GetString(SI_GAMEPAD_MAIN_MENU_JOURNAL_ACHIEVEMENTS),
+                    icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_achievements.dds",
                 },
-    [MENU_CATEGORY_CONTACTS] =
+                [MENU_JOURNAL_ENTRIES.LEADERBOARDS] =
                 {
-        scene = "gamepad_friends",
+                    scene = "gamepad_leaderboards",
+                    name = GetString(SI_JOURNAL_MENU_LEADERBOARDS),
+                    icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_leaderBoards.dds",
+                },
+            }
         },
-    [MENU_CATEGORY_GUILDS] =
+        [MENU_MAIN_ENTRIES.SOCIAL] =
+        {
+            customTemplate = "ZO_GamepadMenuEntryTemplateWithArrow",
+            name = GetString(SI_MAIN_MENU_SOCIAL),
+            icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_multiplayer.dds",
+            isNewCallback =
+                function()
+                    return HasUnreadMail()
+                end,
+            subMenu =
+            {
+                [MENU_SOCIAL_ENTRIES.VOICE_CHAT] =
+                {
+                    scene = "gamepad_voice_chat",
+                    name = GetString(SI_MAIN_MENU_GAMEPAD_VOICECHAT),
+                    icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_communications.dds",
+                    header = GetString(SI_MAIN_MENU_SOCIAL),
+                    isVisibleCallback =
+                        function()
+                            return IsConsoleUI()
+                        end,
+                },
+                [MENU_SOCIAL_ENTRIES.TEXT_CHAT] =
+                {
+                    scene = "gamepad_text_chat",
+                    name = GetString(SI_GAMEPAD_TEXT_CHAT),
+                    icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_communications.dds",
+                    isVisibleCallback =
+                        function()
+                            return IsConsoleUI() and IsChatSystemAvailableForCurrentPlatform()
+                        end,
+                },
+                [MENU_SOCIAL_ENTRIES.EMOTES] =
+                {
+                    scene = "gamepad_player_emote",
+                    name = GetString(SI_GAMEPAD_MAIN_MENU_EMOTES),
+                    icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_emotes.dds",
+                    header = not IsConsoleUI() and GetString(SI_MAIN_MENU_SOCIAL) or nil,
+                },
+                [MENU_SOCIAL_ENTRIES.GROUP] =
+                {
+                    scene = "gamepad_groupList",
+                    name = GetString(SI_PLAYER_MENU_GROUP),
+                    icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_groups.dds",
+                },
+                [MENU_SOCIAL_ENTRIES.GUILDS] =
                 {
                     scene = "gamepad_guild_hub",
+                    name = GetString(SI_MAIN_MENU_GUILDS),
+                    icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_guilds.dds",
                 },
-    [MENU_CATEGORY_ALLIANCE_WAR] =
+                [MENU_SOCIAL_ENTRIES.FRIENDS] =
                 {
-        scene = "gamepad_campaign_root",
+                    scene = "gamepad_friends",
+                    name = GetString(SI_GAMEPAD_CONTACTS_FRIENDS_LIST_TITLE),
+                    icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_contacts.dds",
                 },
-    [MENU_CATEGORY_MAIL] =
+                [MENU_SOCIAL_ENTRIES.IGNORED] =
+                {
+                    scene = "gamepad_ignored",
+                    name = GetString(SI_GAMEPAD_CONTACTS_IGNORED_LIST_TITLE),
+                    icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_contacts.dds",
+                    isVisibleCallback =
+                        function()
+                            return not IsConsoleUI()
+                        end,
+                },
+                [MENU_SOCIAL_ENTRIES.MAIL] =
                 {
                     scene = "mailManagerGamepad",
+                    name = GetString(SI_MAIN_MENU_MAIL),
+                    icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_mail.dds",
+                    isNewCallback =
+                        function()
+                            return HasUnreadMail()
+                        end,
+                    disableWhenDead = true,
+                    disableWhenInCombat = true,
+                    disableWhenReviving = true,
+                },
+            }
         },
-    [MENU_CATEGORY_NOTIFICATIONS] =
+        [MENU_MAIN_ENTRIES.ACTIVITY_FINDER] =
         {
-        scene = "gamepad_notifications_root",
+            scene = ZO_GAMEPAD_ACTIVITY_FINDER_ROOT_SCENE_NAME,
+            name = GetString(SI_MAIN_MENU_ACTIVITY_FINDER),
+            icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_activityFinder.dds",
         },
-    [MENU_CATEGORY_HELP] =
+        [MENU_MAIN_ENTRIES.HELP] =
         {
             scene = "helpRootGamepad",
+            name = GetString(SI_MAIN_MENU_HELP),
+            icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_help.dds",
         },
-}
-
-local function ShowLogoutDialog()
+        [MENU_MAIN_ENTRIES.OPTIONS] =
+        {
+            scene = "gamepad_options_root",
+            name = GetString(SI_GAMEPAD_OPTIONS_MENU),
+            icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_settings.dds",
+        },
+        [MENU_MAIN_ENTRIES.LOG_OUT] =
+        {
+            name = GetString(SI_GAME_MENU_LOGOUT),
+            icon = "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_logout.dds",
+            activatedCallback =
+                function()
                     ZO_Dialogs_ShowPlatformDialog("LOG_OUT")
-end
+                end,
+        },
+    }
 
---[[
-    menuTable = the table to add the entry to. MUST exist and be a table
-    entryName = the display name in the menu.
-    icon = the icon displayed to the left of the name. Can be nil.
-    isNew = function or boolean determining if the new icon indicator should display. Can be nil.
-    isVisibleCallback = function determining when to show the entry. If nil, the entry is always shown.
-    header = string to set as the header for the entry. Can be nil. If header is an empty string, the entry will have a blank header.
---]]
-local function AddEntryToMenu(menuTable, entryName, icon, sceneName, isNew, isVisibleCallback, header)
-    local entry = ZO_GamepadEntryData:New(entryName, icon, nil, nil, isNew)
-    entry.scene = sceneName
-    entry.isVisibleCallback = isVisibleCallback
-    if header then
-        entry:SetHeader(header)
-    end
+    CATEGORY_TO_ENTRY_DATA =
+    {
+        [MENU_CATEGORY_NOTIFICATIONS]   = MENU_ENTRY_DATA[MENU_MAIN_ENTRIES.NOTIFICATIONS],
+        [MENU_CATEGORY_MARKET]          = MENU_ENTRY_DATA[MENU_MAIN_ENTRIES.MARKET],
+        [MENU_CATEGORY_COLLECTIONS]     = MENU_ENTRY_DATA[MENU_MAIN_ENTRIES.COLLECTIONS],
+        [MENU_CATEGORY_INVENTORY]       = MENU_ENTRY_DATA[MENU_MAIN_ENTRIES.INVENTORY],
+        [MENU_CATEGORY_CHARACTER]       = MENU_ENTRY_DATA[MENU_MAIN_ENTRIES.CHARACTER],
+        [MENU_CATEGORY_SKILLS]          = MENU_ENTRY_DATA[MENU_MAIN_ENTRIES.SKILLS],
+        [MENU_CATEGORY_CHAMPION]        = MENU_ENTRY_DATA[MENU_MAIN_ENTRIES.CHAMPION],
+        [MENU_CATEGORY_ALLIANCE_WAR]    = MENU_ENTRY_DATA[MENU_MAIN_ENTRIES.CAMPAIGN],
+        [MENU_CATEGORY_JOURNAL]         = MENU_ENTRY_DATA[MENU_MAIN_ENTRIES.JOURNAL].subMenu[MENU_JOURNAL_ENTRIES.QUESTS],
+        [MENU_CATEGORY_GROUP]           = MENU_ENTRY_DATA[MENU_MAIN_ENTRIES.SOCIAL].subMenu[MENU_SOCIAL_ENTRIES.GROUP],
+        [MENU_CATEGORY_CONTACTS]        = MENU_ENTRY_DATA[MENU_MAIN_ENTRIES.SOCIAL].subMenu[MENU_SOCIAL_ENTRIES.FRIENDS],
+        [MENU_CATEGORY_GUILDS]          = MENU_ENTRY_DATA[MENU_MAIN_ENTRIES.SOCIAL].subMenu[MENU_SOCIAL_ENTRIES.GUILDS],
+        [MENU_CATEGORY_MAIL]            = MENU_ENTRY_DATA[MENU_MAIN_ENTRIES.SOCIAL].subMenu[MENU_SOCIAL_ENTRIES.MAIL],
+        [MENU_CATEGORY_ACTIVITY_FINDER] = MENU_ENTRY_DATA[MENU_MAIN_ENTRIES.ACTIVITY_FINDER],
+        [MENU_CATEGORY_HELP]            = MENU_ENTRY_DATA[MENU_MAIN_ENTRIES.HELP],
+        [MENU_CATEGORY_MAP]             = { scene = "gamepad_worldMap" }, --no gamepad menu entry for world map
+    }
 
-    table.insert(menuTable, entry)
-    return entry
+    local function CreateEntry(data)
+        local name = data.name
+        if type(name) == "function" then
+            name = "" --will be updated whenever the list is generated
         end
 
--- a table of layout information to display a list of scenes available to the user
--- Entries are addded to the list in order, so the first entry added to the table appears at the top of the list
--- If you disable a category in PlayerMenu.lua you should also disable it in MainMenu.lua
-local function GetLayoutData()
-    local layoutData = {}
+        local entry = ZO_GamepadEntryData:New(name, data.icon, nil, nil, data.isNewCallback)
+        entry:SetIconTintOnSelection(true)
+        entry:SetIconDisabledTintOnSelection(true)
 
-    -- Notifications --
-    local MARK_NOTIFICATIONS_NEW = true -- when we have notifications, we are always new
-    local notifications = AddEntryToMenu(layoutData, "", "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_notifications.dds", "gamepad_notifications_root", MARK_NOTIFICATIONS_NEW,
-                                            function()
-                                                if GAMEPAD_NOTIFICATIONS then
-                                                    return GAMEPAD_NOTIFICATIONS:GetNumNotifications() > 0
-                                                else
-                                                    return false
-                                                end
-                                            end)
-    notifications.refreshCallback = function()
-                                        local numNotifications = GAMEPAD_NOTIFICATIONS and GAMEPAD_NOTIFICATIONS:GetNumNotifications() or 0
-                                        local textString = zo_strformat(SI_GAMEPAD_MAIN_MENU_NOTIFICATIONS, numNotifications)
-                                        notifications:SetText(textString)
+        local header = data.header
+        if header then
+            entry:SetHeader(header)
         end
-    notifications:refreshCallback()
 
-    -- Crown Store --
-    local market = AddEntryToMenu(layoutData, GetString(SI_GAMEPAD_MAIN_MENU_MARKET_ENTRY), "EsoUI/Art/MenuBar/Gamepad/gp_PlayerMenu_icon_store.dds", "gamepad_market_pre_scene")
-    market.additionalScenes =   {
-                                    "gamepad_market",
-                                    "gamepad_market_preview",
-                                    "gamepad_market_bundle_contents",
-                                    "gamepad_market_purchase",
-                                    "gamepad_market_locked",
-                                }
-    market.disableWhenDead = true
-
-    -- Collections --
-    local collections = AddEntryToMenu(layoutData, GetString(SI_MAIN_MENU_COLLECTIONS), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_collections.dds", "gamepadCollectionsBook")
-    collections:SetNew(function() return GAMEPAD_NOTIFICATIONS and GAMEPAD_NOTIFICATIONS:GetNumCollectionsNotifications() > 0 or false end)
-
-    -- Inventory --
-    AddEntryToMenu(layoutData, GetString(SI_MAIN_MENU_INVENTORY), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_inventory.dds", "gamepad_inventory_root",
-                    function() return SHARED_INVENTORY:AreAnyItemsNew(nil, nil, BAG_BACKPACK) end)
+        entry.canLevel = data.canLevel
 
-    -- Character Sheet --
-    local character = AddEntryToMenu(layoutData, GetString(SI_MAIN_MENU_CHARACTER), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_character.dds", "gamepad_stats_root")
-    character.canLevel = function() return GetAttributeUnspentPoints() > 0 end
-
-    -- Skills --
-    local skills = AddEntryToMenu(layoutData, GetString(SI_MAIN_MENU_SKILLS), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_skills.dds", "gamepad_skills_root")
-    skills.canLevel = function() return GetAvailableSkillPoints() > 0 end
-    skills.canAnimate = true
-    
-    -- Champion --
-    local champion = AddEntryToMenu(layoutData, GetString(SI_MAIN_MENU_CHAMPION), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_champion.dds", "gamepad_championPerks_root",
-                                        function()
-                                            if CHAMPION_PERKS then
-                                                return CHAMPION_PERKS:IsChampionSystemNew()
-                                            end
-                                        end,
-                                        function() return IsChampionSystemUnlocked() end)
-    champion.canLevel = function()
-        if CHAMPION_PERKS then
-            return CHAMPION_PERKS:HasAnySpendableUnspentPoints()
-        end
-    end
-
-    -- Alliance War --
-    AddEntryToMenu(layoutData, GetString(SI_PLAYER_MENU_CAMPAIGNS), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_allianceWar.dds", "gamepad_campaign_root",
-                    function()
-                        local tutorialId = GetTutorialId(TUTORIAL_TRIGGER_CAMPAIGN_BROWSER_OPENED)
-                        if CanTutorialBeSeen(tutorialId) then
-                            return not HasSeenTutorial(tutorialId)
+        entry.data = data
+        return entry
     end
-                        return false
-                    end,
-                    function()
-                        local currentLevel = GetUnitLevel("player")
-                        return currentLevel >= GetMinLevelForCampaignTutorial()
-                    end)
-
-    -- Journal Menu --
-    local journal = AddEntryToMenu(layoutData, GetString(SI_MAIN_MENU_JOURNAL), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_quests.dds")
-    journal.subMenu = {}
-
-    -- Quests --
-    AddEntryToMenu(journal.subMenu, GetString(SI_GAMEPAD_MAIN_MENU_JOURNAL_QUESTS), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_quests.dds", "gamepad_quest_journal", nil, nil, GetString(SI_MAIN_MENU_JOURNAL))
-
-    -- Cadwell's Journal --
-    AddEntryToMenu(journal.subMenu, GetString(SI_GAMEPAD_MAIN_MENU_JOURNAL_CADWELL), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_cadwell.dds", "cadwellGamepad", nil,
-                    function()
-                        return GetPlayerDifficultyLevel() > PLAYER_DIFFICULTY_LEVEL_FIRST_ALLIANCE
-                    end)
-
-    -- Lore Library --
-    AddEntryToMenu(journal.subMenu, GetString(SI_GAMEPAD_MAIN_MENU_JOURNAL_LORE_LIBRARAY), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_loreLibrary.dds", "loreLibraryGamepad")
-
-    -- Achievements --
-    AddEntryToMenu(journal.subMenu, GetString(SI_GAMEPAD_MAIN_MENU_JOURNAL_ACHIEVEMENTS), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_achievements.dds", "achievementsGamepad")
-
-    -- Leaderboards --
-    AddEntryToMenu(journal.subMenu, GetString(SI_JOURNAL_MENU_LEADERBOARDS), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_leaderBoards.dds", "gamepad_leaderboards")
 
+    for _, data in ipairs(MENU_ENTRY_DATA) do
+        local newEntry = CreateEntry(data)
 
-    -- Social Menu --
-    local social = AddEntryToMenu(layoutData, GetString(SI_MAIN_MENU_SOCIAL), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_multiplayer.dds", nil,
-                                        function()
-                                            return HasUnreadMail()
-                                        end)
-    social.subMenu = {}
-
-    -- Voice Chat --
-    if IsConsoleUI() then
-        AddEntryToMenu(social.subMenu, GetString(SI_MAIN_MENU_GAMEPAD_VOICECHAT), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_communications.dds", "gamepad_voice_chat", nil, nil, GetString(SI_MAIN_MENU_SOCIAL))
-    
-            -- Text Chat --
-        if IsChatSystemAvailableForCurrentPlatform() then
-            AddEntryToMenu(social.subMenu, GetString(SI_GAMEPAD_TEXT_CHAT), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_communications.dds", "gamepad_text_chat")
+        if data.subMenu then
+            newEntry.subMenu = {}
+            for _, subMenuData in ipairs(data.subMenu) do
+                local newSubMenuEntry = CreateEntry(subMenuData)
+                table.insert(newEntry.subMenu, newSubMenuEntry)
             end
         end
 
-    -- Emotes --
-    local headerText
-    if not IsConsoleUI() then
-        headerText = GetString(SI_MAIN_MENU_SOCIAL)
+        table.insert(MENU_ENTRIES, newEntry)
     end
-    AddEntryToMenu(social.subMenu, GetString(SI_GAMEPAD_MAIN_MENU_EMOTES), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_emotes.dds", "gamepad_player_emote", nil, nil, headerText)
-
-    -- Group --
-    AddEntryToMenu(social.subMenu, GetString(SI_PLAYER_MENU_GROUP), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_groups.dds", "gamepad_groupList")
-
-    -- Guilds --
-    AddEntryToMenu(social.subMenu, GetString(SI_MAIN_MENU_GUILDS), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_guilds.dds", "gamepad_guild_hub")
-
-    -- Friends --
-    AddEntryToMenu(social.subMenu, GetString(SI_GAMEPAD_CONTACTS_FRIENDS_LIST_TITLE), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_contacts.dds", "gamepad_friends")
-
-    -- Ignored List --
-    if not IsConsoleUI() then
-        AddEntryToMenu(social.subMenu, GetString(SI_GAMEPAD_CONTACTS_IGNORED_LIST_TITLE), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_contacts.dds", "gamepad_ignored")
-    end
-
-    -- Mail --
-    local mailbox = AddEntryToMenu(social.subMenu, GetString(SI_MAIN_MENU_MAIL), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_mail.dds", "mailManagerGamepad",
-                                    function()
-                                        return HasUnreadMail()
-                                    end)
-    mailbox.disableWhenDead = true
-    mailbox.disableWhenInCombat = true
-    mailbox.disableWhenReviving = true
-
-    -- Help --
-    AddEntryToMenu(layoutData, GetString(SI_MAIN_MENU_HELP), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_help.dds", "helpRootGamepad")
-
-    -- Options --
-    AddEntryToMenu(layoutData, GetString(SI_GAMEPAD_OPTIONS_MENU), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_settings.dds", "gamepad_options_root")
-
-    -- Log Out --
-    local logOut = AddEntryToMenu(layoutData, GetString(SI_GAME_MENU_LOGOUT), "EsoUI/Art/MenuBar/Gamepad/gp_playerMenu_icon_logout.dds")
-    logOut.activatedCallback = ShowLogoutDialog
-
-    return layoutData
 end
 
-local CATEGORY_LAYOUT_INFO = GetLayoutData()
-
-local GENERIC_ENTRY_TEMPLATE = "ZO_GamepadNewMenuEntryTemplate"
-local ENTRY_WITH_SUB_MENU_TEMPLATE = "ZO_GamepadMenuEntryTemplateWithArrow"
-local ANIMATING_ENTRY_TEMPLATE = "ZO_GamepadNewAnimatingMenuEntryTemplate"
-
 local MODE_MAIN_LIST = 1
 local MODE_SUBLIST = 2
 --
@@ -281,12 +401,8 @@ function ZO_MainMenuManager_Gamepad:Initialize(control)
 
     self:SetListsUseTriggerKeybinds(true)
 
-    control:RegisterForEvent(EVENT_LEVEL_UPDATE,
-            function()
-                self:RefreshLists()
-            end)
+    control:RegisterForEvent(EVENT_LEVEL_UPDATE, function() self:RefreshLists() end)
     control:AddFilterForEvent(EVENT_LEVEL_UPDATE, REGISTER_FILTER_UNIT_TAG, "player")
-
     control:RegisterForEvent(EVENT_DIFFICULTY_LEVEL_CHANGED, function() self:RefreshLists() end)
 
     PLAYER_SUBMENU_SCENE:RegisterCallback("StateChange", function(oldState, newState)
@@ -322,6 +438,7 @@ do
     end
 end
 
+do
     local function AnimatingLabelEntrySetup(control, data, selected, reselectingDuringRebuild, enabled, active)
         ZO_SharedGamepadEntry_OnSetup(control, data, selected, reselectingDuringRebuild, enabled, active)
         local totalSpendablePoints = GetAttributeUnspentPoints()
@@ -354,59 +471,64 @@ local function EntryWithSubMenuSetup(control, data, selected, reselectingDuringR
         control:GetNamedChild("Arrow"):SetColor(color:UnpackRGBA())
     end
     
-function ZO_MainMenuManager_Gamepad:SetupList(list)
-    list:AddDataTemplate(GENERIC_ENTRY_TEMPLATE, ZO_SharedGamepadEntry_OnSetup, ZO_GamepadMenuEntryTemplateParametricListFunction)
-    list:AddDataTemplateWithHeader(GENERIC_ENTRY_TEMPLATE, ZO_SharedGamepadEntry_OnSetup, ZO_GamepadMenuEntryTemplateParametricListFunction, nil, "ZO_GamepadMenuEntryHeaderTemplate")
-    list:AddDataTemplate(ENTRY_WITH_SUB_MENU_TEMPLATE, EntryWithSubMenuSetup, ZO_GamepadMenuEntryTemplateParametricListFunction)
-    list:AddDataTemplateWithHeader(ENTRY_WITH_SUB_MENU_TEMPLATE, EntryWithSubMenuSetup, ZO_GamepadMenuEntryTemplateParametricListFunction, nil, "ZO_GamepadMenuEntryHeaderTemplate")
-    list:AddDataTemplate(ANIMATING_ENTRY_TEMPLATE, AnimatingLabelEntrySetup, ZO_GamepadMenuEntryTemplateParametricListFunction)
+    local function CrownStoreEntrySetup(control, data, selected, reselectingDuringRebuild, enabled, active)
+        ZO_SharedGamepadEntry_OnSetup(control, data, selected, reselectingDuringRebuild, enabled, active)
+    
+        local balanceLabel = control:GetNamedChild("Balance")
+        balanceLabel:SetText(GetString(SI_GAMEPAD_MAIN_MENU_MARKET_BALANCE_TITLE))
+    
+        local remainingCrownsLabel = control:GetNamedChild("RemainingCrowns")
+        local currencyString = ZO_CommaDelimitNumber(GetMarketCurrency())
+        remainingCrownsLabel:SetText(currencyString)
     end
     
-do
-    local function GetCategoryState(categoryInfo)
-        if MAIN_MENU_MANAGER:IsPlayerDead() and categoryInfo.disableWhenDead then
-            return MAIN_MENU_CATEGORY_DISABLED_WHILE_DEAD
-        elseif MAIN_MENU_MANAGER:IsPlayerInCombat() and categoryInfo.disableWhenInCombat then
-            return MAIN_MENU_CATEGORY_DISABLED_WHILE_IN_COMBAT
-        elseif MAIN_MENU_MANAGER:IsPlayerReviving() and categoryInfo.disableWhenReviving then
-            return MAIN_MENU_CATEGORY_DISABLED_WHILE_REVIVING
-        else
-            return MAIN_MENU_CATEGORY_ENABLED
+    function ZO_MainMenuManager_Gamepad:SetupList(list)
+        list:AddDataTemplate("ZO_GamepadNewMenuEntryTemplate", ZO_SharedGamepadEntry_OnSetup, ZO_GamepadMenuEntryTemplateParametricListFunction)
+        list:AddDataTemplateWithHeader("ZO_GamepadNewMenuEntryTemplate", ZO_SharedGamepadEntry_OnSetup, ZO_GamepadMenuEntryTemplateParametricListFunction, nil, "ZO_GamepadMenuEntryHeaderTemplate")
+    
+        list:AddDataTemplate("ZO_GamepadMenuEntryTemplateWithArrow", EntryWithSubMenuSetup, ZO_GamepadMenuEntryTemplateParametricListFunction)
+        list:AddDataTemplateWithHeader("ZO_GamepadMenuEntryTemplateWithArrow", EntryWithSubMenuSetup, ZO_GamepadMenuEntryTemplateParametricListFunction, nil, "ZO_GamepadMenuEntryHeaderTemplate")
+    
+        list:AddDataTemplate("ZO_GamepadNewAnimatingMenuEntryTemplate", AnimatingLabelEntrySetup, ZO_GamepadMenuEntryTemplateParametricListFunction)
+    
+        list:AddDataTemplateWithHeader("ZO_GamepadMenuCrownStoreEntryTemplate", CrownStoreEntrySetup, ZO_GamepadMenuEntryTemplateParametricListFunction, nil, "ZO_GamepadCrownStoreMenuEntryHeaderTemplate")
+        list:AddDataTemplate("ZO_GamepadMenuCrownStoreEntryTemplate", CrownStoreEntrySetup, ZO_GamepadMenuEntryTemplateParametricListFunction)
     end
 end
 
-    local function TryHideCategoryScene(categoryInfo)
-        local shouldHide = false
-        if SCENE_MANAGER:IsShowing(categoryInfo.scene) then
-            shouldHide = true
-        elseif categoryInfo.additionalScenes then
-            for _, sceneName in ipairs(categoryInfo.additionalScenes) do
-                if SCENE_MANAGER:IsShowing(sceneName) then
-                    shouldHide = true
-                    break
-                end
+do
+    local function ShouldDisableEntry(entry)
+        local data = entry.data
+
+        if MAIN_MENU_MANAGER:IsPlayerDead() and data.disableWhenDead then
+            return true
+        elseif MAIN_MENU_MANAGER:IsPlayerInCombat() and data.disableWhenInCombat then
+            return true
+        elseif MAIN_MENU_MANAGER:IsPlayerReviving() and data.disableWhenReviving then
+            return true
         end
+
+        return false
     end
 
-        if shouldHide then
+    function ZO_MainMenuManager_Gamepad:UpdateEntryEnabledStates()
+        local function UpdateState(entry)
+            if ShouldDisableEntry(entry) then
+                entry:SetEnabled(false)
+
+                if self:IsEntrySceneShowing(entry.data) then
                     SCENE_MANAGER:ShowBaseScene()
                 end
-    end
-
-    local function DetermineCategoryEnabled(categoryInfo)
-        local shouldBeEnabled = GetCategoryState(categoryInfo) == MAIN_MENU_CATEGORY_ENABLED
-        categoryInfo:SetEnabled(shouldBeEnabled)
-        if not shouldBeEnabled then
-            TryHideCategoryScene(categoryInfo)
+            else
+                entry:SetEnabled(true)
             end
         end
 
-    function ZO_MainMenuManager_Gamepad:UpdateCategories()
-        for _, categoryInfo in ipairs(CATEGORY_LAYOUT_INFO) do
-            DetermineCategoryEnabled(categoryInfo)
-            if categoryInfo.subMenu then
-                for _, subCategoryInfo in ipairs(categoryInfo.subMenu) do
-                    DetermineCategoryEnabled(subCategoryInfo)
+        for _, entry in ipairs(MENU_ENTRIES) do
+            UpdateState(entry)
+            if entry.subMenu then
+                for _, subMenuEntry in ipairs(entry.subMenu) do
+                    UpdateState(subMenuEntry)
                 end
             end
         end
@@ -419,8 +541,8 @@ function ZO_MainMenuManager_Gamepad:RefreshLists()
     if self.mode == MODE_MAIN_LIST then
         self:RefreshMainList()
     else
-        local targetData = self.mainList:GetTargetData()
-        self:RefreshSubList(targetData)
+        local entry = self.mainList:GetTargetData()
+        self:RefreshSubList(entry)
     end
 end
 
@@ -443,10 +565,10 @@ function ZO_MainMenuManager_Gamepad:OnDeferredInitialize()
     SHARED_INVENTORY:RegisterCallback("SingleSlotInventoryUpdate", MarkNewnessDirty)
     EVENT_MANAGER:RegisterForEvent("mainMenuGamepad", EVENT_LEVEL_UPDATE, MarkNewnessDirty)
     EVENT_MANAGER:RegisterForEvent("mainMenuGamepad", EVENT_MAIL_NUM_UNREAD_CHANGED, MarkNewnessDirty)
-    MAIN_MENU_MANAGER:RegisterCallback("OnPlayerStateUpdate", function() self:UpdateCategories() end)
+    MAIN_MENU_MANAGER:RegisterCallback("OnPlayerStateUpdate", function() self:UpdateEntryEnabledStates() end)
     MAIN_MENU_MANAGER:RegisterCallback("OnBlockingSceneCleared", OnBlockingSceneCleared)
 
-    self:UpdateCategories()
+    self:UpdateEntryEnabledStates()
 end
 
 function ZO_MainMenuManager_Gamepad:InitializeKeybindStripDescriptors()
@@ -467,8 +589,8 @@ function ZO_MainMenuManager_Gamepad:InitializeKeybindStripDescriptors()
 
     local  function IsForwardNavigationEnabled()
         local currentList = self:GetCurrentList() 
-        local categoryInfo = currentList and currentList:GetTargetData()
-        return categoryInfo and categoryInfo:IsEnabled()
+        local entry = currentList and currentList:GetTargetData()
+        return entry and entry:IsEnabled()
     end
 
     ZO_Gamepad_AddForwardNavigationKeybindDescriptors(self.keybindStripDescriptor, GAME_NAVIGATION_TYPE_BUTTON, function() self:SwitchToSelectedScene(self:GetCurrentList()) end, nil, nil, IsForwardNavigationEnabled)
@@ -482,15 +604,17 @@ function ZO_MainMenuManager_Gamepad:InitializeKeybindStripDescriptors()
 end
 
 function ZO_MainMenuManager_Gamepad:SwitchToSelectedScene(list)
-    local targetData = list:GetTargetData()
-    if targetData.enabled then
-        local scene = targetData.scene
-        local activatedCallback = targetData.activatedCallback
+    local entry = list:GetTargetData()
+    
+    if entry.enabled then
+        local entryData = entry.data
+        local scene = entryData.scene
+        local activatedCallback = entryData.activatedCallback
 
         if scene then
             list:SetActive(false)
             SCENE_MANAGER:Push(scene)
-        elseif targetData.subMenu then
+        elseif entryData.subMenu then
             list:SetActive(false)
             SCENE_MANAGER:Push("playerSubmenu")
         elseif activatedCallback then
@@ -507,26 +631,30 @@ function ZO_MainMenuManager_Gamepad:Exit()
 end
 
 local function AddEntryToList(list, entry)
-    if not entry.isVisibleCallback or entry.isVisibleCallback() then
-        if entry.refreshCallback then
-            entry.refreshCallback()
-        end
+    local entryData = entry.data
 
-        entry:SetIconTintOnSelection(true)
-        entry:SetIconDisabledTintOnSelection(true)
+    if not entryData.isVisibleCallback or entryData.isVisibleCallback() then
+        local customTemplate = entryData.customTemplate
+        local postPadding = entryData.postPadding or 0
+        local entryTemplate = customTemplate and customTemplate or "ZO_GamepadNewMenuEntryTemplate"
        
-        local entryTemplate = GENERIC_ENTRY_TEMPLATE
+        local showHeader = entryData.showHeader
+        local useHeader = entry.header
+        if type(showHeader) == "function" then
+            useHeader = showHeader()
+        elseif type(showHeader) == "boolean" then
+            useHeader = showHeader
+        end
 
-        if entry.canAnimate then
-            entryTemplate = ANIMATING_ENTRY_TEMPLATE
-        elseif entry.subMenu then
-            entryTemplate = ENTRY_WITH_SUB_MENU_TEMPLATE
+        local name = entryData.name
+        if type(name) == "function" then
+            entry:SetText(name())
         end
 
-        if entry.header then
-            list:AddEntryWithHeader(entryTemplate, entry)
+        if useHeader then
+            list:AddEntryWithHeader(entryTemplate, entry, 0, postPadding)
         else
-            list:AddEntry(entryTemplate, entry)
+            list:AddEntry(entryTemplate, entry, 0, postPadding)
         end
     end
 end
@@ -534,8 +662,8 @@ end
 function ZO_MainMenuManager_Gamepad:RefreshMainList()
     self.mainList:Clear()
 
-    for _, layout in ipairs(CATEGORY_LAYOUT_INFO) do
-        AddEntryToList(self.mainList, layout)
+    for _, entry in ipairs(MENU_ENTRIES) do
+        AddEntryToList(self.mainList, entry)
     end
 
     -- if we haven't yet initialized, set the default selection to be the inventory
@@ -550,11 +678,11 @@ function ZO_MainMenuManager_Gamepad:RefreshMainList()
     self.mainList:Commit()
 end
 
-function ZO_MainMenuManager_Gamepad:RefreshSubList(targetData)
+function ZO_MainMenuManager_Gamepad:RefreshSubList(mainListEntry)
     self.subList:Clear()
 
-    if targetData and targetData.subMenu then
-        for _, entry in ipairs(targetData.subMenu) do
+    if mainListEntry and mainListEntry.subMenu then
+        for _, entry in ipairs(mainListEntry.subMenu) do
             AddEntryToList(self.subList, entry)
         end
     end
@@ -580,12 +708,30 @@ function ZO_MainMenuManager_Gamepad:OnNumNotificationsChanged(numNotifications)
     self:MarkNewnessDirty()
 end
 
+function ZO_MainMenuManager_Gamepad:IsEntrySceneShowing(entryData)
+    if entryData.additionalScenes then
+        for _, scene in ipairs(entryData.additionalScenes) do
+            if SCENE_MANAGER:IsShowing(scene) then
+                return true
+            end
+        end
+    end
+
+    return SCENE_MANAGER:IsShowing(entryData.scene)
+end
+
 function ZO_MainMenuManager_Gamepad:ToggleCategory(category)
-    self:ToggleScene(CATEGORY_TO_SCENE[category].scene)
+    local entryData = CATEGORY_TO_ENTRY_DATA[category]
+
+    if self:IsEntrySceneShowing(entryData) then
+        SCENE_MANAGER:ShowBaseScene()
+    else
+        self:ToggleScene(entryData.scene)
+    end
 end
 
 function ZO_MainMenuManager_Gamepad:ShowCategory(category)
-    self:ShowScene(CATEGORY_TO_SCENE[category].scene)
+    self:ShowScene(CATEGORY_TO_ENTRY_DATA[category].scene)
 end
 
 function ZO_MainMenuManager_Gamepad:ShowScene(sceneName)