diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-from-accounts-id-page-1-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-from-accounts-id-page-1-chromium-linux.png
index 1aeaa14f98c0ff96efcef64c1c7140790a70c2b9..ce13427af8795856513e8632eae1a58bbaf84b51 100644
Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-from-accounts-id-page-1-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-from-accounts-id-page-1-chromium-linux.png differ
diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-from-accounts-id-page-2-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-from-accounts-id-page-2-chromium-linux.png
index 76c562bd18862ad1d2d184f238c63e7b8637d512..4b22f0e2f6e3adb5c0540b60bacb0671b6f6234f 100644
Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-from-accounts-id-page-2-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-from-accounts-id-page-2-chromium-linux.png differ
diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-1-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-1-chromium-linux.png
index 39f7c042e38adbc962d73ff15431208691c065a5..85905bc0ee1f8c18b55c53d7593a2d689f1d611b 100644
Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-1-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-1-chromium-linux.png differ
diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-2-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-2-chromium-linux.png
index 8320db5365131c4ee874af81ed3de17debb9d0db..efc32336eed6f6bad4ba415c8afe1432f6d9a628 100644
Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-2-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-2-chromium-linux.png differ
diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-3-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-3-chromium-linux.png
index 591897b5a477a03f07a5eb5bd71c3f579149e409..42cb054ab58c74803ee0ac0725c0239056e3ec0c 100644
Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-3-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-3-chromium-linux.png differ
diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-4-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-4-chromium-linux.png
index 3d879590fd4e43c2ed1047d53c5cbffec3df7211..034509229c4b9a828780a71321629b83f118e725 100644
Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-4-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-4-chromium-linux.png differ
diff --git a/packages/desktop-client/src/components/Modals.tsx b/packages/desktop-client/src/components/Modals.tsx
index 95e0713602f7b90a0f5da55b77169b8decf0d230..824a59460bf84a4343f4cac9dea6c594ac43d598 100644
--- a/packages/desktop-client/src/components/Modals.tsx
+++ b/packages/desktop-client/src/components/Modals.tsx
@@ -14,7 +14,7 @@ import { useSyncServerStatus } from '../hooks/useSyncServerStatus';
 import { ModalTitle } from './common/Modal';
 import { AccountAutocompleteModal } from './modals/AccountAutocompleteModal';
 import { AccountMenuModal } from './modals/AccountMenuModal';
-import { BudgetMenuModal } from './modals/BudgetMenuModal';
+import { BudgetMonthMenuModal } from './modals/BudgetMonthMenuModal';
 import { CategoryAutocompleteModal } from './modals/CategoryAutocompleteModal';
 import { CategoryGroupMenuModal } from './modals/CategoryGroupMenuModal';
 import { CategoryMenuModal } from './modals/CategoryMenuModal';
@@ -40,8 +40,10 @@ import { Notes } from './modals/Notes';
 import { PayeeAutocompleteModal } from './modals/PayeeAutocompleteModal';
 import { PlaidExternalMsg } from './modals/PlaidExternalMsg';
 import { ReportBalanceMenuModal } from './modals/ReportBalanceMenuModal';
+import { ReportBudgetMenuModal } from './modals/ReportBudgetMenuModal';
 import { ReportBudgetSummaryModal } from './modals/ReportBudgetSummaryModal';
 import { RolloverBalanceMenuModal } from './modals/RolloverBalanceMenuModal';
+import { RolloverBudgetMenuModal } from './modals/RolloverBudgetMenuModal';
 import { RolloverBudgetSummaryModal } from './modals/RolloverBudgetSummaryModal';
 import { RolloverToBudgetMenuModal } from './modals/RolloverToBudgetMenuModal';
 import { ScheduledTransactionMenuModal } from './modals/ScheduledTransactionMenuModal';
@@ -429,7 +431,6 @@ export function Modals() {
               key={name}
               modalProps={modalProps}
               categoryId={options.categoryId}
-              categoryGroup={options.categoryGroup}
               onSave={options.onSave}
               onEditNotes={options.onEditNotes}
               onDelete={options.onDelete}
@@ -437,6 +438,40 @@ export function Modals() {
             />
           );
 
+        case 'rollover-budget-menu':
+          return (
+            <NamespaceContext.Provider
+              key={name}
+              value={monthUtils.sheetForMonth(options.month)}
+            >
+              <RolloverBudgetMenuModal
+                modalProps={modalProps}
+                categoryId={options.categoryId}
+                onUpdateBudget={options.onUpdateBudget}
+                onCopyLastMonthAverage={options.onCopyLastMonthAverage}
+                onSetMonthsAverage={options.onSetMonthsAverage}
+                onApplyBudgetTemplate={options.onApplyBudgetTemplate}
+              />
+            </NamespaceContext.Provider>
+          );
+
+        case 'report-budget-menu':
+          return (
+            <NamespaceContext.Provider
+              key={name}
+              value={monthUtils.sheetForMonth(options.month)}
+            >
+              <ReportBudgetMenuModal
+                modalProps={modalProps}
+                categoryId={options.categoryId}
+                onUpdateBudget={options.onUpdateBudget}
+                onCopyLastMonthAverage={options.onCopyLastMonthAverage}
+                onSetMonthsAverage={options.onSetMonthsAverage}
+                onApplyBudgetTemplate={options.onApplyBudgetTemplate}
+              />
+            </NamespaceContext.Provider>
+          );
+
         case 'category-group-menu':
           return (
             <CategoryGroupMenuModal
@@ -479,7 +514,7 @@ export function Modals() {
             </NamespaceContext.Provider>
           );
 
-        case 'rollover-to-budget-menu':
+        case 'rollover-summary-to-budget-menu':
           return (
             <NamespaceContext.Provider
               key={name}
@@ -552,13 +587,13 @@ export function Modals() {
             />
           );
 
-        case 'budget-menu':
+        case 'budget-month-menu':
           return (
             <NamespaceContext.Provider
               key={name}
               value={monthUtils.sheetForMonth(options.month)}
             >
-              <BudgetMenuModal
+              <BudgetMonthMenuModal
                 modalProps={modalProps}
                 month={options.month}
                 onToggleHiddenCategories={options.onToggleHiddenCategories}
diff --git a/packages/desktop-client/src/components/budget/BudgetTable.jsx b/packages/desktop-client/src/components/budget/BudgetTable.jsx
index cc61c3d8b5ee522f43ecfe0f785f868c7492f8df..f4d45208dce1279046207f58526fcce41911d98d 100644
--- a/packages/desktop-client/src/components/budget/BudgetTable.jsx
+++ b/packages/desktop-client/src/components/budget/BudgetTable.jsx
@@ -1,7 +1,5 @@
 import React, { useRef, useState } from 'react';
 
-import * as monthUtils from 'loot-core/src/shared/months';
-
 import { useCategories } from '../../hooks/useCategories';
 import { useLocalPref } from '../../hooks/useLocalPref';
 import { theme, styles } from '../../style';
@@ -40,8 +38,8 @@ export function BudgetTable(props) {
   );
   const [editing, setEditing] = useState(null);
 
-  const onEditMonth = (id, monthIndex) => {
-    setEditing(id ? { id, cell: monthIndex } : null);
+  const onEditMonth = (id, month) => {
+    setEditing(id ? { id, cell: month } : null);
   };
 
   const onEditName = id => {
@@ -134,18 +132,6 @@ export function BudgetTable(props) {
     }
   };
 
-  const resolveMonth = monthIndex => {
-    return monthUtils.addMonths(startMonth, monthIndex);
-  };
-
-  const _onShowActivity = (catId, monthIndex) => {
-    onShowActivity(catId, resolveMonth(monthIndex));
-  };
-
-  const _onBudgetAction = (monthIndex, type, args) => {
-    onBudgetAction(resolveMonth(monthIndex), type, args);
-  };
-
   const onCollapse = collapsedIds => {
     setCollapsedPref(collapsedIds);
   };
@@ -244,8 +230,8 @@ export function BudgetTable(props) {
                 onDeleteGroup={onDeleteGroup}
                 onReorderCategory={_onReorderCategory}
                 onReorderGroup={_onReorderGroup}
-                onBudgetAction={_onBudgetAction}
-                onShowActivity={_onShowActivity}
+                onBudgetAction={onBudgetAction}
+                onShowActivity={onShowActivity}
               />
             </View>
           </View>
diff --git a/packages/desktop-client/src/components/budget/ExpenseCategory.tsx b/packages/desktop-client/src/components/budget/ExpenseCategory.tsx
index 5007b81c8d12d51d609ae28724b2b5b17dadf0e2..bb9da828c58ab549cd78f3322195ddac400b5a06 100644
--- a/packages/desktop-client/src/components/budget/ExpenseCategory.tsx
+++ b/packages/desktop-client/src/components/budget/ExpenseCategory.tsx
@@ -28,12 +28,12 @@ type ExpenseCategoryProps = {
   dragState: DragState<CategoryEntity>;
   MonthComponent: ComponentProps<typeof RenderMonths>['component'];
   onEditName?: ComponentProps<typeof SidebarCategory>['onEditName'];
-  onEditMonth?: (id: string, monthIndex: number) => void;
+  onEditMonth?: (id: string, month: string) => void;
   onSave?: ComponentProps<typeof SidebarCategory>['onSave'];
   onDelete?: ComponentProps<typeof SidebarCategory>['onDelete'];
   onDragChange: OnDragChangeCallback<CategoryEntity>;
-  onBudgetAction: (idx: number, action: string, arg: unknown) => void;
-  onShowActivity: (id: string, idx: number) => void;
+  onBudgetAction: (month: number, action: string, arg: unknown) => void;
+  onShowActivity: (id: string, month: string) => void;
   onReorder: OnDropCallback;
 };
 
@@ -101,7 +101,7 @@ export function ExpenseCategory({
 
         <RenderMonths
           component={MonthComponent}
-          editingIndex={
+          editingMonth={
             editingCell && editingCell.id === cat.id && editingCell.cell
           }
           args={{
diff --git a/packages/desktop-client/src/components/budget/IncomeCategory.tsx b/packages/desktop-client/src/components/budget/IncomeCategory.tsx
index f240d72b3d614b0baec5c4b6fa6827bf59a5e13e..97f8dec2b987bc12da56503dd913ba8d0add5858 100644
--- a/packages/desktop-client/src/components/budget/IncomeCategory.tsx
+++ b/packages/desktop-client/src/components/budget/IncomeCategory.tsx
@@ -21,13 +21,13 @@ type IncomeCategoryProps = {
   editingCell: { id: string; cell: string } | null;
   MonthComponent: ComponentProps<typeof RenderMonths>['component'];
   onEditName: ComponentProps<typeof SidebarCategory>['onEditName'];
-  onEditMonth?: (id: string, monthIndex: number) => void;
+  onEditMonth?: (id: string, month: string) => void;
   onSave: ComponentProps<typeof SidebarCategory>['onSave'];
   onDelete: ComponentProps<typeof SidebarCategory>['onDelete'];
   onDragChange: OnDragChangeCallback<CategoryEntity>;
-  onBudgetAction: (idx: number, action: string, arg: unknown) => void;
+  onBudgetAction: (month: string, action: string, arg: unknown) => void;
   onReorder: OnDropCallback;
-  onShowActivity: (id: string, idx: number) => void;
+  onShowActivity: (id: string, month: string) => void;
 };
 
 export function IncomeCategory({
@@ -76,7 +76,7 @@ export function IncomeCategory({
       />
       <RenderMonths
         component={MonthComponent}
-        editingIndex={
+        editingMonth={
           editingCell && editingCell.id === cat.id && editingCell.cell
         }
         args={{
diff --git a/packages/desktop-client/src/components/budget/RenderMonths.tsx b/packages/desktop-client/src/components/budget/RenderMonths.tsx
index 20aceb19922c953dfaf9d2c47af22003e41efe3d..f2968d3f714138dceddc7e5e8de7c66f1265d456 100644
--- a/packages/desktop-client/src/components/budget/RenderMonths.tsx
+++ b/packages/desktop-client/src/components/budget/RenderMonths.tsx
@@ -14,22 +14,22 @@ import { NamespaceContext } from '../spreadsheet/NamespaceContext';
 import { MonthsContext } from './MonthsContext';
 
 type RenderMonthsProps = {
-  component?: ComponentType<{ monthIndex: number; editing: boolean }>;
-  editingIndex?: string | number;
+  component?: ComponentType<{ month: string; editing: boolean }>;
+  editingMonth?: string;
   args?: object;
   style?: CSSProperties;
 };
 
 export function RenderMonths({
   component: Component,
-  editingIndex,
+  editingMonth,
   args,
   style,
 }: RenderMonthsProps) {
   const { months } = useContext(MonthsContext);
 
   return months.map((month, index) => {
-    const editing = editingIndex === index;
+    const editing = editingMonth === month;
 
     return (
       <NamespaceContext.Provider
@@ -43,7 +43,7 @@ export function RenderMonths({
             ...style,
           }}
         >
-          <Component monthIndex={index} editing={editing} {...args} />
+          <Component month={month} editing={editing} {...args} />
         </View>
       </NamespaceContext.Provider>
     );
diff --git a/packages/desktop-client/src/components/budget/report/BalanceTooltip.tsx b/packages/desktop-client/src/components/budget/report/BalanceTooltip.tsx
index 66999627bf7800736b0683af2adeabb17325721d..91aa1ade6d195009ca33a92fc92ee09d4ac59694 100644
--- a/packages/desktop-client/src/components/budget/report/BalanceTooltip.tsx
+++ b/packages/desktop-client/src/components/budget/report/BalanceTooltip.tsx
@@ -7,15 +7,15 @@ import { BalanceMenu } from './BalanceMenu';
 type BalanceTooltipProps = {
   categoryId: string;
   tooltip: { close: () => void };
-  monthIndex: number;
-  onBudgetAction: (idx: number, action: string, arg: unknown) => void;
+  month: string;
+  onBudgetAction: (month: string, action: string, arg: unknown) => void;
   onClose?: () => void;
 };
 
 export function BalanceTooltip({
   categoryId,
   tooltip,
-  monthIndex,
+  month,
   onBudgetAction,
   onClose,
   ...tooltipProps
@@ -36,7 +36,7 @@ export function BalanceTooltip({
       <BalanceMenu
         categoryId={categoryId}
         onCarryover={carryover => {
-          onBudgetAction?.(monthIndex, 'carryover', {
+          onBudgetAction?.(month, 'carryover', {
             category: categoryId,
             flag: carryover,
           });
diff --git a/packages/desktop-client/src/components/budget/report/BudgetMenu.tsx b/packages/desktop-client/src/components/budget/report/BudgetMenu.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..aeed3cddd88c1f867b6b5b7bb41ff4b664eeb49a
--- /dev/null
+++ b/packages/desktop-client/src/components/budget/report/BudgetMenu.tsx
@@ -0,0 +1,75 @@
+import React, { type ComponentPropsWithoutRef } from 'react';
+
+import { useFeatureFlag } from '../../../hooks/useFeatureFlag';
+import { Menu } from '../../common/Menu';
+
+type BudgetMenuProps = Omit<
+  ComponentPropsWithoutRef<typeof Menu>,
+  'onMenuSelect' | 'items'
+> & {
+  onCopyLastMonthAverage: () => void;
+  onSetMonthsAverage: (numberOfMonths: number) => void;
+  onApplyBudgetTemplate: () => void;
+};
+export function BudgetMenu({
+  onCopyLastMonthAverage,
+  onSetMonthsAverage,
+  onApplyBudgetTemplate,
+  ...props
+}: BudgetMenuProps) {
+  const isGoalTemplatesEnabled = useFeatureFlag('goalTemplatesEnabled');
+  const onMenuSelect = (name: string) => {
+    switch (name) {
+      case 'copy-single-last':
+        onCopyLastMonthAverage?.();
+        break;
+      case 'set-single-3-avg':
+        onSetMonthsAverage?.(3);
+        break;
+      case 'set-single-6-avg':
+        onSetMonthsAverage?.(6);
+        break;
+      case 'set-single-12-avg':
+        onSetMonthsAverage?.(12);
+        break;
+      case 'apply-single-category-template':
+        onApplyBudgetTemplate?.();
+        break;
+      default:
+        throw new Error(`Unrecognized menu item: ${name}`);
+    }
+  };
+
+  return (
+    <Menu
+      {...props}
+      onMenuSelect={onMenuSelect}
+      items={[
+        {
+          name: 'copy-single-last',
+          text: 'Copy last month’s budget',
+        },
+        {
+          name: 'set-single-3-avg',
+          text: 'Set to 3 month average',
+        },
+        {
+          name: 'set-single-6-avg',
+          text: 'Set to 6 month average',
+        },
+        {
+          name: 'set-single-12-avg',
+          text: 'Set to yearly average',
+        },
+        ...(isGoalTemplatesEnabled
+          ? [
+              {
+                name: 'apply-single-category-template',
+                text: 'Apply budget template',
+              },
+            ]
+          : []),
+      ]}
+    />
+  );
+}
diff --git a/packages/desktop-client/src/components/budget/report/ReportComponents.tsx b/packages/desktop-client/src/components/budget/report/ReportComponents.tsx
index 3181c8da7cb5554f7b7df4cb4f083ae6967d2729..5d09472c8ffc755b9d8aaa5312eb25293cae213c 100644
--- a/packages/desktop-client/src/components/budget/report/ReportComponents.tsx
+++ b/packages/desktop-client/src/components/budget/report/ReportComponents.tsx
@@ -5,11 +5,9 @@ import { reportBudget } from 'loot-core/src/client/queries';
 import { evalArithmetic } from 'loot-core/src/shared/arithmetic';
 import { integerToCurrency, amountToInteger } from 'loot-core/src/shared/util';
 
-import { useFeatureFlag } from '../../../hooks/useFeatureFlag';
 import { SvgCheveronDown } from '../../../icons/v1';
 import { styles, theme, type CSSProperties } from '../../../style';
 import { Button } from '../../common/Button';
-import { Menu } from '../../common/Menu';
 import { Text } from '../../common/Text';
 import { View } from '../../common/View';
 import { CellValue } from '../../spreadsheet/CellValue';
@@ -20,6 +18,7 @@ import { BalanceWithCarryover } from '../BalanceWithCarryover';
 import { makeAmountGrey } from '../util';
 
 import { BalanceTooltip } from './BalanceTooltip';
+import { BudgetMenu } from './BudgetMenu';
 
 const headerLabelStyle: CSSProperties = {
   flex: 1,
@@ -142,15 +141,15 @@ export const GroupMonth = memo(function GroupMonth({ group }: GroupMonthProps) {
 });
 
 type CategoryMonthProps = {
-  monthIndex: number;
+  month: string;
   category: { id: string; name: string; is_income: boolean };
   editing: boolean;
-  onEdit: (id: string | null, idx?: number) => void;
-  onBudgetAction: (idx: number, action: string, arg: unknown) => void;
-  onShowActivity: (id: string, idx: number) => void;
+  onEdit: (id: string | null, month?: string) => void;
+  onBudgetAction: (month: string, action: string, arg: unknown) => void;
+  onShowActivity: (id: string, month: string) => void;
 };
 export const CategoryMonth = memo(function CategoryMonth({
-  monthIndex,
+  month,
   category,
   editing,
   onEdit,
@@ -160,7 +159,6 @@ export const CategoryMonth = memo(function CategoryMonth({
   const balanceTooltip = useTooltip();
   const [menuOpen, setMenuOpen] = useState(false);
   const [hover, setHover] = useState(false);
-  const isGoalTemplatesEnabled = useFeatureFlag('goalTemplatesEnabled');
 
   return (
     <View
@@ -221,33 +219,34 @@ export const CategoryMonth = memo(function CategoryMonth({
                 style={{ padding: 0 }}
                 onClose={() => setMenuOpen(false)}
               >
-                <Menu
-                  onMenuSelect={type => {
-                    onBudgetAction(monthIndex, type, { category: category.id });
-                    setMenuOpen(false);
+                <BudgetMenu
+                  onCopyLastMonthAverage={() => {
+                    onBudgetAction?.(month, 'copy-single-last', {
+                      category: category.id,
+                    });
+                  }}
+                  onSetMonthsAverage={numberOfMonths => {
+                    if (
+                      numberOfMonths !== 3 &&
+                      numberOfMonths !== 6 &&
+                      numberOfMonths !== 12
+                    ) {
+                      return;
+                    }
+
+                    onBudgetAction?.(
+                      month,
+                      `set-single-${numberOfMonths}-avg`,
+                      {
+                        category: category.id,
+                      },
+                    );
+                  }}
+                  onApplyBudgetTemplate={() => {
+                    onBudgetAction?.(month, 'apply-single-category-template', {
+                      category: category.id,
+                    });
                   }}
-                  items={[
-                    {
-                      name: 'copy-single-last',
-                      text: 'Copy last month’s budget',
-                    },
-                    {
-                      name: 'set-single-3-avg',
-                      text: 'Set to 3 month average',
-                    },
-                    {
-                      name: 'set-single-6-avg',
-                      text: 'Set to 6 month average',
-                    },
-                    {
-                      name: 'set-single-12-avg',
-                      text: 'Set to yearly average',
-                    },
-                    isGoalTemplatesEnabled && {
-                      name: 'apply-single-category-template',
-                      text: 'Apply budget template',
-                    },
-                  ]}
                 />
               </Tooltip>
             )}
@@ -258,7 +257,7 @@ export const CategoryMonth = memo(function CategoryMonth({
           exposed={editing}
           focused={editing}
           width="flex"
-          onExpose={() => onEdit(category.id, monthIndex)}
+          onExpose={() => onEdit(category.id, month)}
           style={{ ...(editing && { zIndex: 100 }), ...styles.tnum }}
           textAlign="right"
           valueStyle={{
@@ -291,7 +290,7 @@ export const CategoryMonth = memo(function CategoryMonth({
             },
           }}
           onSave={amount => {
-            onBudgetAction(monthIndex, 'budget-amount', {
+            onBudgetAction(month, 'budget-amount', {
               category: category.id,
               amount,
             });
@@ -301,7 +300,7 @@ export const CategoryMonth = memo(function CategoryMonth({
       <Field name="spent" width="flex" style={{ textAlign: 'right' }}>
         <span
           data-testid="category-month-spent"
-          onClick={() => onShowActivity(category.id, monthIndex)}
+          onClick={() => onShowActivity(category.id, month)}
         >
           <CellValue
             binding={reportBudget.catSumAmount(category.id)}
@@ -337,7 +336,7 @@ export const CategoryMonth = memo(function CategoryMonth({
             <BalanceTooltip
               categoryId={category.id}
               tooltip={balanceTooltip}
-              monthIndex={monthIndex}
+              month={month}
               onBudgetAction={onBudgetAction}
             />
           )}
diff --git a/packages/desktop-client/src/components/budget/report/ReportContext.tsx b/packages/desktop-client/src/components/budget/report/ReportContext.tsx
index c1c90e93d725f0947bc7f73fc9e13a954c40fa5e..d93d8cbe66be4321d48a1cae5e758fc4372f5381 100644
--- a/packages/desktop-client/src/components/budget/report/ReportContext.tsx
+++ b/packages/desktop-client/src/components/budget/report/ReportContext.tsx
@@ -7,7 +7,7 @@ const Context = createContext(null);
 
 type ReportProviderProps = {
   summaryCollapsed: boolean;
-  onBudgetAction: (idx: number, action: string, arg: unknown) => void;
+  onBudgetAction: (month: string, action: string, arg: unknown) => void;
   onToggleSummaryCollapse: () => void;
   children: ReactNode;
 };
diff --git a/packages/desktop-client/src/components/budget/rollover/BalanceTooltip.tsx b/packages/desktop-client/src/components/budget/rollover/BalanceTooltip.tsx
index 66b8f86ba30538d0de14b7dd7e1c597b4bb744ed..1432556c096ce5f58dbc5ed931cb397345e2bf6e 100644
--- a/packages/desktop-client/src/components/budget/rollover/BalanceTooltip.tsx
+++ b/packages/desktop-client/src/components/budget/rollover/BalanceTooltip.tsx
@@ -12,15 +12,15 @@ import { TransferTooltip } from './TransferTooltip';
 type BalanceTooltipProps = {
   categoryId: string;
   tooltip: { close: () => void };
-  monthIndex: number;
-  onBudgetAction: (idx: number, action: string, arg?: unknown) => void;
+  month: string;
+  onBudgetAction: (month: string, action: string, arg?: unknown) => void;
   onClose?: () => void;
 };
 
 export function BalanceTooltip({
   categoryId,
   tooltip,
-  monthIndex,
+  month,
   onBudgetAction,
   onClose,
   ...tooltipProps
@@ -46,7 +46,7 @@ export function BalanceTooltip({
           <BalanceMenu
             categoryId={categoryId}
             onCarryover={carryover => {
-              onBudgetAction(monthIndex, 'carryover', {
+              onBudgetAction(month, 'carryover', {
                 category: categoryId,
                 flag: carryover,
               });
@@ -64,7 +64,7 @@ export function BalanceTooltip({
           showToBeBudgeted={true}
           onClose={_onClose}
           onSubmit={(amount, toCategoryId) => {
-            onBudgetAction(monthIndex, 'transfer-category', {
+            onBudgetAction(month, 'transfer-category', {
               amount,
               from: categoryId,
               to: toCategoryId,
@@ -77,7 +77,7 @@ export function BalanceTooltip({
         <CoverTooltip
           onClose={_onClose}
           onSubmit={fromCategoryId => {
-            onBudgetAction(monthIndex, 'cover', {
+            onBudgetAction(month, 'cover', {
               to: categoryId,
               from: fromCategoryId,
             });
diff --git a/packages/desktop-client/src/components/budget/rollover/BudgetMenu.tsx b/packages/desktop-client/src/components/budget/rollover/BudgetMenu.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..aeed3cddd88c1f867b6b5b7bb41ff4b664eeb49a
--- /dev/null
+++ b/packages/desktop-client/src/components/budget/rollover/BudgetMenu.tsx
@@ -0,0 +1,75 @@
+import React, { type ComponentPropsWithoutRef } from 'react';
+
+import { useFeatureFlag } from '../../../hooks/useFeatureFlag';
+import { Menu } from '../../common/Menu';
+
+type BudgetMenuProps = Omit<
+  ComponentPropsWithoutRef<typeof Menu>,
+  'onMenuSelect' | 'items'
+> & {
+  onCopyLastMonthAverage: () => void;
+  onSetMonthsAverage: (numberOfMonths: number) => void;
+  onApplyBudgetTemplate: () => void;
+};
+export function BudgetMenu({
+  onCopyLastMonthAverage,
+  onSetMonthsAverage,
+  onApplyBudgetTemplate,
+  ...props
+}: BudgetMenuProps) {
+  const isGoalTemplatesEnabled = useFeatureFlag('goalTemplatesEnabled');
+  const onMenuSelect = (name: string) => {
+    switch (name) {
+      case 'copy-single-last':
+        onCopyLastMonthAverage?.();
+        break;
+      case 'set-single-3-avg':
+        onSetMonthsAverage?.(3);
+        break;
+      case 'set-single-6-avg':
+        onSetMonthsAverage?.(6);
+        break;
+      case 'set-single-12-avg':
+        onSetMonthsAverage?.(12);
+        break;
+      case 'apply-single-category-template':
+        onApplyBudgetTemplate?.();
+        break;
+      default:
+        throw new Error(`Unrecognized menu item: ${name}`);
+    }
+  };
+
+  return (
+    <Menu
+      {...props}
+      onMenuSelect={onMenuSelect}
+      items={[
+        {
+          name: 'copy-single-last',
+          text: 'Copy last month’s budget',
+        },
+        {
+          name: 'set-single-3-avg',
+          text: 'Set to 3 month average',
+        },
+        {
+          name: 'set-single-6-avg',
+          text: 'Set to 6 month average',
+        },
+        {
+          name: 'set-single-12-avg',
+          text: 'Set to yearly average',
+        },
+        ...(isGoalTemplatesEnabled
+          ? [
+              {
+                name: 'apply-single-category-template',
+                text: 'Apply budget template',
+              },
+            ]
+          : []),
+      ]}
+    />
+  );
+}
diff --git a/packages/desktop-client/src/components/budget/rollover/RolloverComponents.tsx b/packages/desktop-client/src/components/budget/rollover/RolloverComponents.tsx
index a8e326f960180b5d7069b64572aa1fc592eb5bd8..b1ba0ff719eb6072fcb3657e83a5787ff435ca79 100644
--- a/packages/desktop-client/src/components/budget/rollover/RolloverComponents.tsx
+++ b/packages/desktop-client/src/components/budget/rollover/RolloverComponents.tsx
@@ -1,14 +1,13 @@
-import React, { memo, useState } from 'react';
+import type React from 'react';
+import { memo, useState } from 'react';
 
 import { rolloverBudget } from 'loot-core/src/client/queries';
 import { evalArithmetic } from 'loot-core/src/shared/arithmetic';
 import { integerToCurrency, amountToInteger } from 'loot-core/src/shared/util';
 
-import { useFeatureFlag } from '../../../hooks/useFeatureFlag';
 import { SvgCheveronDown } from '../../../icons/v1';
 import { styles, theme, type CSSProperties } from '../../../style';
 import { Button } from '../../common/Button';
-import { Menu } from '../../common/Menu';
 import { Text } from '../../common/Text';
 import { View } from '../../common/View';
 import { CellValue } from '../../spreadsheet/CellValue';
@@ -19,6 +18,7 @@ import { BalanceWithCarryover } from '../BalanceWithCarryover';
 import { makeAmountGrey } from '../util';
 
 import { BalanceTooltip } from './BalanceTooltip';
+import { BudgetMenu } from './BudgetMenu';
 
 const headerLabelStyle: CSSProperties = {
   flex: 1,
@@ -137,15 +137,15 @@ export const ExpenseGroupMonth = memo(function ExpenseGroupMonth({
 });
 
 type ExpenseCategoryMonthProps = {
-  monthIndex: number;
+  month: string;
   category: { id: string; name: string; is_income: boolean };
   editing: boolean;
-  onEdit: (id: string | null, idx?: number) => void;
-  onBudgetAction: (idx: number, action: string, arg?: unknown) => void;
-  onShowActivity: (id: string, idx: number) => void;
+  onEdit: (id: string | null, month?: string) => void;
+  onBudgetAction: (month: string, action: string, arg?: unknown) => void;
+  onShowActivity: (id: string, month: string) => void;
 };
 export const ExpenseCategoryMonth = memo(function ExpenseCategoryMonth({
-  monthIndex,
+  month,
   category,
   editing,
   onEdit,
@@ -155,7 +155,6 @@ export const ExpenseCategoryMonth = memo(function ExpenseCategoryMonth({
   const balanceTooltip = useTooltip();
   const [menuOpen, setMenuOpen] = useState(false);
   const [hover, setHover] = useState(false);
-  const isGoalTemplatesEnabled = useFeatureFlag('goalTemplatesEnabled');
 
   return (
     <View
@@ -216,37 +215,34 @@ export const ExpenseCategoryMonth = memo(function ExpenseCategoryMonth({
                 style={{ padding: 0 }}
                 onClose={() => setMenuOpen(false)}
               >
-                <Menu
-                  onMenuSelect={type => {
-                    onBudgetAction(monthIndex, type, { category: category.id });
-                    setMenuOpen(false);
+                <BudgetMenu
+                  onCopyLastMonthAverage={() => {
+                    onBudgetAction?.(month, 'copy-single-last', {
+                      category: category.id,
+                    });
+                  }}
+                  onSetMonthsAverage={numberOfMonths => {
+                    if (
+                      numberOfMonths !== 3 &&
+                      numberOfMonths !== 6 &&
+                      numberOfMonths !== 12
+                    ) {
+                      return;
+                    }
+
+                    onBudgetAction?.(
+                      month,
+                      `set-single-${numberOfMonths}-avg`,
+                      {
+                        category: category.id,
+                      },
+                    );
+                  }}
+                  onApplyBudgetTemplate={() => {
+                    onBudgetAction?.(month, 'apply-single-category-template', {
+                      category: category.id,
+                    });
                   }}
-                  items={[
-                    {
-                      name: 'copy-single-last',
-                      text: 'Copy last month’s budget',
-                    },
-                    {
-                      name: 'set-single-3-avg',
-                      text: 'Set to 3 month average',
-                    },
-                    {
-                      name: 'set-single-6-avg',
-                      text: 'Set to 6 month average',
-                    },
-                    {
-                      name: 'set-single-12-avg',
-                      text: 'Set to yearly average',
-                    },
-                    ...(isGoalTemplatesEnabled
-                      ? [
-                          {
-                            name: 'apply-single-category-template',
-                            text: 'Apply budget template',
-                          },
-                        ]
-                      : []),
-                  ]}
                 />
               </Tooltip>
             )}
@@ -257,7 +253,7 @@ export const ExpenseCategoryMonth = memo(function ExpenseCategoryMonth({
           exposed={editing}
           focused={editing}
           width="flex"
-          onExpose={() => onEdit(category.id, monthIndex)}
+          onExpose={() => onEdit(category.id, month)}
           style={{ ...(editing && { zIndex: 100 }), ...styles.tnum }}
           textAlign="right"
           valueStyle={{
@@ -290,7 +286,7 @@ export const ExpenseCategoryMonth = memo(function ExpenseCategoryMonth({
             },
           }}
           onSave={amount => {
-            onBudgetAction(monthIndex, 'budget-amount', {
+            onBudgetAction(month, 'budget-amount', {
               category: category.id,
               amount,
             });
@@ -300,7 +296,7 @@ export const ExpenseCategoryMonth = memo(function ExpenseCategoryMonth({
       <Field name="spent" width="flex" style={{ textAlign: 'right' }}>
         <span
           data-testid="category-month-spent"
-          onClick={() => onShowActivity(category.id, monthIndex)}
+          onClick={() => onShowActivity(category.id, month)}
         >
           <CellValue
             binding={rolloverBudget.catSumAmount(category.id)}
@@ -331,7 +327,7 @@ export const ExpenseCategoryMonth = memo(function ExpenseCategoryMonth({
           <BalanceTooltip
             categoryId={category.id}
             tooltip={balanceTooltip}
-            monthIndex={monthIndex}
+            month={month}
             onBudgetAction={onBudgetAction}
           />
         )}
@@ -369,13 +365,13 @@ export function IncomeGroupMonth() {
 type IncomeCategoryMonthProps = {
   category: { id: string; name: string };
   isLast: boolean;
-  monthIndex: number;
-  onShowActivity: (id: string, idx: number) => void;
+  month: string;
+  onShowActivity: (id: string, month: string) => void;
 };
 export function IncomeCategoryMonth({
   category,
   isLast,
-  monthIndex,
+  month,
   onShowActivity,
 }: IncomeCategoryMonthProps) {
   return (
@@ -389,7 +385,7 @@ export function IncomeCategoryMonth({
           ...(isLast && { borderBottomWidth: 0 }),
         }}
       >
-        <span onClick={() => onShowActivity(category.id, monthIndex)}>
+        <span onClick={() => onShowActivity(category.id, month)}>
           <CellValue
             binding={rolloverBudget.catSumAmount(category.id)}
             type="financial"
diff --git a/packages/desktop-client/src/components/budget/rollover/RolloverContext.tsx b/packages/desktop-client/src/components/budget/rollover/RolloverContext.tsx
index bf68c07afaa6cdad2afee0ba0c8a2320a55d6697..81e9f18cbe986cb73bd90890eb55cc7631f0e66e 100644
--- a/packages/desktop-client/src/components/budget/rollover/RolloverContext.tsx
+++ b/packages/desktop-client/src/components/budget/rollover/RolloverContext.tsx
@@ -4,7 +4,7 @@ import * as monthUtils from 'loot-core/src/shared/months';
 
 type RolloverContextDefinition = {
   summaryCollapsed: boolean;
-  onBudgetAction: (idx: string, action: string, arg?: unknown) => void;
+  onBudgetAction: (month: string, action: string, arg?: unknown) => void;
   onToggleSummaryCollapse: () => void;
   currentMonth: string;
 };
diff --git a/packages/desktop-client/src/components/budget/rollover/budgetsummary/ToBudget.tsx b/packages/desktop-client/src/components/budget/rollover/budgetsummary/ToBudget.tsx
index 75051811aa0ec9729935bfa66519f0ecbc5ec953..5630ad92bfec8c4b9f7778793b43a4427858402d 100644
--- a/packages/desktop-client/src/components/budget/rollover/budgetsummary/ToBudget.tsx
+++ b/packages/desktop-client/src/components/budget/rollover/budgetsummary/ToBudget.tsx
@@ -13,7 +13,7 @@ import { ToBudgetMenu } from './ToBudgetMenu';
 
 type ToBudgetProps = {
   month: string;
-  onBudgetAction: (idx: string, action: string, arg?: unknown) => void;
+  onBudgetAction: (month: string, action: string, arg?: unknown) => void;
   prevMonthName: string;
   showTotalsTooltipOnHover?: boolean;
   style?: CSSProperties;
diff --git a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx
index 4eb5cad56261600eaf4b083dd73d153776783bf2..c7fcdd68fdbc9298c0e180176ab5ac0b5b62ac1f 100644
--- a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx
+++ b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx
@@ -1,4 +1,4 @@
-import React, { memo, useRef, useState } from 'react';
+import React, { memo, useRef } from 'react';
 import { useDispatch } from 'react-redux';
 
 import memoizeOne from 'memoize-one';
@@ -9,10 +9,6 @@ import * as monthUtils from 'loot-core/src/shared/months';
 
 import { useLocalPref } from '../../../hooks/useLocalPref';
 import { useNavigate } from '../../../hooks/useNavigate';
-import {
-  SingleActiveEditFormProvider,
-  useSingleActiveEditForm,
-} from '../../../hooks/useSingleActiveEditForm';
 import { SvgLogo } from '../../../icons/logo';
 import { SvgAdd, SvgArrowThinLeft, SvgArrowThinRight } from '../../../icons/v1';
 import { useResponsive } from '../../../ResponsiveProvider';
@@ -28,7 +24,6 @@ import { Page } from '../../Page';
 import { CellValue } from '../../spreadsheet/CellValue';
 import { useFormat } from '../../spreadsheet/useFormat';
 import { useSheetValue } from '../../spreadsheet/useSheetValue';
-import { AmountInput } from '../../util/AmountInput';
 import { MOBILE_NAV_HEIGHT } from '../MobileNavTabs';
 import { PullToRefresh } from '../PullToRefresh';
 
@@ -123,66 +118,67 @@ function BudgetCell({
   name,
   binding,
   style,
-  textStyle,
   categoryId,
   month,
   onBudgetAction,
-  onEdit,
-  onBlur,
-  isEditing,
 }) {
-  const sheetValue = useSheetValue(binding);
+  const dispatch = useDispatch();
+  const [budgetType = 'rollover'] = useLocalPref('budgetType');
 
-  function updateBudgetAmount(amount) {
-    onBudgetAction?.(month, 'budget-amount', {
-      category: categoryId,
-      amount,
-    });
-  }
+  const categoryBudgetMenuModal = `${budgetType}-budget-menu`;
 
-  function onAmountClick() {
-    onEdit?.();
-  }
+  const onOpenCategoryBudgetMenu = () => {
+    dispatch(
+      pushModal(categoryBudgetMenuModal, {
+        categoryId,
+        month,
+        onUpdateBudget: amount => {
+          onBudgetAction(month, 'budget-amount', {
+            category: categoryId,
+            amount,
+          });
+        },
+        onCopyLastMonthAverage: () => {
+          onBudgetAction(month, 'copy-single-last', {
+            category: categoryId,
+          });
+        },
+        onSetMonthsAverage: numberOfMonths => {
+          if (
+            numberOfMonths !== 3 &&
+            numberOfMonths !== 6 &&
+            numberOfMonths !== 12
+          ) {
+            return;
+          }
+
+          onBudgetAction(month, `set-single-${numberOfMonths}-avg`, {
+            category: categoryId,
+          });
+        },
+        onApplyBudgetTemplate: () => {
+          onBudgetAction(month, 'apply-single-category-template', {
+            category: categoryId,
+          });
+        },
+      }),
+    );
+  };
 
   return (
-    <View style={style}>
-      <AmountInput
-        value={sheetValue}
-        zeroSign="+"
-        style={{
-          ...(!isEditing && { display: 'none' }),
-          height: ROW_HEIGHT,
-          transform: 'translateX(6px)',
-        }}
-        focused={isEditing}
-        textStyle={{ ...styles.smallText, ...textStyle }}
-        onUpdate={updateBudgetAmount}
-        onBlur={onBlur}
-      />
-      <View
-        role="button"
-        style={{
-          ...(isEditing && { display: 'none' }),
-          justifyContent: 'center',
-          alignItems: 'flex-end',
-          height: ROW_HEIGHT,
-        }}
-      >
-        <CellValue
-          binding={binding}
-          type="financial"
-          style={{
-            ...styles.smallText,
-            ...textStyle,
-            ...styles.underlinedText,
-          }}
-          getStyle={makeAmountGrey}
-          data-testid={name}
-          onPointerUp={onAmountClick}
-          onPointerDown={e => e.preventDefault()}
-        />
-      </View>
-    </View>
+    <CellValue
+      binding={binding}
+      type="financial"
+      style={{
+        textAlign: 'right',
+        ...styles.smallText,
+        ...style,
+        ...styles.underlinedText,
+      }}
+      getStyle={makeAmountGrey}
+      data-testid={name}
+      onClick={onOpenCategoryBudgetMenu}
+    />
   );
 }
 
@@ -247,17 +243,8 @@ const ExpenseCategory = memo(function ExpenseCategory({
   const opacity = blank ? 0 : 1;
 
   const [budgetType = 'rollover'] = useLocalPref('budgetType');
-  const [isEditingBudget, setIsEditingBudget] = useState(false);
-  const { onRequestActiveEdit, onClearActiveEdit } = useSingleActiveEditForm();
   const dispatch = useDispatch();
 
-  const onEditBudget = () => {
-    onRequestActiveEdit(`${category.id}-budget`, () => {
-      setIsEditingBudget(true);
-      return () => setIsEditingBudget(false);
-    });
-  };
-
   const onCarryover = carryover => {
     onBudgetAction(month, 'carryover', {
       category: category.id,
@@ -305,7 +292,7 @@ const ExpenseCategory = memo(function ExpenseCategory({
     );
   };
 
-  const onOpenBalanceActionMenu = () => {
+  const onOpenBalanceMenu = () => {
     dispatch(
       pushModal(`${budgetType}-balance-menu`, {
         categoryId: category.id,
@@ -367,16 +354,12 @@ const ExpenseCategory = memo(function ExpenseCategory({
           name="budgeted"
           binding={budgeted}
           style={{
-            ...(!show3Cols && !showBudgetedCol && { display: 'none' }),
             width: 90,
+            ...(!show3Cols && !showBudgetedCol && { display: 'none' }),
           }}
-          textStyle={{ ...styles.smallText, textAlign: 'right' }}
           categoryId={category.id}
           month={month}
           onBudgetAction={onBudgetAction}
-          isEditing={isEditingBudget}
-          onEdit={onEditBudget}
-          onBlur={onClearActiveEdit}
         />
         <View
           style={{
@@ -409,7 +392,7 @@ const ExpenseCategory = memo(function ExpenseCategory({
             height: ROW_HEIGHT,
           }}
         >
-          <span role="button" onClick={() => onOpenBalanceActionMenu?.()}>
+          <span role="button" onClick={() => onOpenBalanceMenu?.()}>
             <BalanceWithCarryover
               carryover={carryover}
               balance={balance}
@@ -693,15 +676,6 @@ const IncomeCategory = memo(function IncomeCategory({
   onBudgetAction,
 }) {
   const listItemRef = useRef();
-  const [isEditingBudget, setIsEditingBudget] = useState(false);
-  const { onRequestActiveEdit, onClearActiveEdit } = useSingleActiveEditForm();
-
-  const onEditBudget = () => {
-    onRequestActiveEdit(`${category.id}-budget`, () => {
-      setIsEditingBudget(true);
-      return () => setIsEditingBudget(false);
-    });
-  };
 
   return (
     <ListItem
@@ -739,29 +713,14 @@ const IncomeCategory = memo(function IncomeCategory({
         </Text>
       </View>
       {budgeted && (
-        <View
-          style={{
-            justifyContent: 'center',
-            alignItems: 'flex-end',
-            width: 90,
-            height: ROW_HEIGHT,
-          }}
-        >
-          <BudgetCell
-            name="budgeted"
-            binding={budgeted}
-            style={{
-              width: 90,
-            }}
-            textStyle={{ ...styles.smallText, textAlign: 'right' }}
-            categoryId={category.id}
-            month={month}
-            onBudgetAction={onBudgetAction}
-            isEditing={isEditingBudget}
-            onEdit={onEditBudget}
-            onBlur={onClearActiveEdit}
-          />
-        </View>
+        <BudgetCell
+          name="budgeted"
+          binding={budgeted}
+          style={{ width: 90 }}
+          categoryId={category.id}
+          month={month}
+          onBudgetAction={onBudgetAction}
+        />
       )}
       <View
         style={{
@@ -1061,54 +1020,52 @@ function BudgetGroups({
   const { incomeGroup, expenseGroups } = separateGroups(categoryGroups);
 
   return (
-    <SingleActiveEditFormProvider formName="mobile-budget-table">
-      <View
-        data-testid="budget-groups"
-        style={{ flex: '1 0 auto', overflowY: 'auto', paddingBottom: 15 }}
-      >
-        {expenseGroups
-          .filter(group => !group.hidden || showHiddenCategories)
-          .map(group => {
-            return (
-              <ExpenseGroup
-                key={group.id}
-                type={type}
-                group={group}
-                showBudgetedCol={showBudgetedCol}
-                gestures={gestures}
-                month={month}
-                editMode={editMode}
-                onEditGroup={onEditGroup}
-                onEditCategory={onEditCategory}
-                onSaveCategory={onSaveCategory}
-                onDeleteCategory={onDeleteCategory}
-                onAddCategory={onAddCategory}
-                onReorderCategory={onReorderCategory}
-                onReorderGroup={onReorderGroup}
-                onBudgetAction={onBudgetAction}
-                show3Cols={show3Cols}
-                showHiddenCategories={showHiddenCategories}
-              />
-            );
-          })}
+    <View
+      data-testid="budget-groups"
+      style={{ flex: '1 0 auto', overflowY: 'auto', paddingBottom: 15 }}
+    >
+      {expenseGroups
+        .filter(group => !group.hidden || showHiddenCategories)
+        .map(group => {
+          return (
+            <ExpenseGroup
+              key={group.id}
+              type={type}
+              group={group}
+              showBudgetedCol={showBudgetedCol}
+              gestures={gestures}
+              month={month}
+              editMode={editMode}
+              onEditGroup={onEditGroup}
+              onEditCategory={onEditCategory}
+              onSaveCategory={onSaveCategory}
+              onDeleteCategory={onDeleteCategory}
+              onAddCategory={onAddCategory}
+              onReorderCategory={onReorderCategory}
+              onReorderGroup={onReorderGroup}
+              onBudgetAction={onBudgetAction}
+              show3Cols={show3Cols}
+              showHiddenCategories={showHiddenCategories}
+            />
+          );
+        })}
 
-        {incomeGroup && (
-          <IncomeGroup
-            type={type}
-            group={incomeGroup}
-            month={month}
-            onAddCategory={onAddCategory}
-            onSaveCategory={onSaveCategory}
-            onDeleteCategory={onDeleteCategory}
-            showHiddenCategories={showHiddenCategories}
-            editMode={editMode}
-            onEditGroup={onEditGroup}
-            onEditCategory={onEditCategory}
-            onBudgetAction={onBudgetAction}
-          />
-        )}
-      </View>
-    </SingleActiveEditFormProvider>
+      {incomeGroup && (
+        <IncomeGroup
+          type={type}
+          group={incomeGroup}
+          month={month}
+          onAddCategory={onAddCategory}
+          onSaveCategory={onSaveCategory}
+          onDeleteCategory={onDeleteCategory}
+          showHiddenCategories={showHiddenCategories}
+          editMode={editMode}
+          onEditGroup={onEditGroup}
+          onEditCategory={onEditCategory}
+          onBudgetAction={onBudgetAction}
+        />
+      )}
+    </View>
   );
 }
 
@@ -1118,7 +1075,6 @@ export function BudgetTable({
   month,
   monthBounds,
   // editMode,
-  // refreshControl,
   onPrevMonth,
   onNextMonth,
   onSaveGroup,
@@ -1134,7 +1090,7 @@ export function BudgetTable({
   onRefresh,
   onEditGroup,
   onEditCategory,
-  onOpenBudgetActionMenu,
+  onOpenBudgetMonthMenu,
 }) {
   const { width } = useResponsive();
   const show3Cols = width >= 360;
@@ -1185,7 +1141,7 @@ export function BudgetTable({
           }}
           hoveredStyle={noBackgroundColorStyle}
           activeStyle={noBackgroundColorStyle}
-          onClick={() => onOpenBudgetActionMenu?.(month)}
+          onClick={() => onOpenBudgetMonthMenu?.(month)}
         >
           <SvgLogo width="20" height="20" />
         </Button>
diff --git a/packages/desktop-client/src/components/mobile/budget/index.tsx b/packages/desktop-client/src/components/mobile/budget/index.tsx
index d3c2bd2a78806f3edb1ac7a3c6ff1f757912a32a..0e244fb1f7b1a283555d482e676b9b69a0c784ff 100644
--- a/packages/desktop-client/src/components/mobile/budget/index.tsx
+++ b/packages/desktop-client/src/components/mobile/budget/index.tsx
@@ -343,14 +343,13 @@ function BudgetInner(props: BudgetInnerProps) {
 
   const onEditCategory = id => {
     const category = categories.find(c => c.id === id);
-    const categoryGroup = categoryGroups.find(g => g.id === category.cat_group);
     dispatch(
       pushModal('category-menu', {
         categoryId: category.id,
-        categoryGroup,
         onSave: onSaveCategory,
         onEditNotes: onEditCategoryNotes,
         onDelete: onDeleteCategory,
+        onBudgetAction,
       }),
     );
   };
@@ -360,7 +359,7 @@ function BudgetInner(props: BudgetInnerProps) {
       pushModal('switch-budget-type', {
         onSwitch: () => {
           onSwitchBudgetType?.();
-          dispatch(collapseModals('budget-menu'));
+          dispatch(collapseModals('budget-month-menu'));
         },
       }),
     );
@@ -372,12 +371,12 @@ function BudgetInner(props: BudgetInnerProps) {
 
   const onToggleHiddenCategories = () => {
     setShowHiddenCategoriesPref(!showHiddenCategories);
-    dispatch(collapseModals('budget-menu'));
+    dispatch(collapseModals('budget-month-menu'));
   };
 
-  const onOpenBudgetActionMenu = month => {
+  const onOpenBudgetMonthMenu = month => {
     dispatch(
-      pushModal('budget-menu', {
+      pushModal('budget-month-menu', {
         month,
         onToggleHiddenCategories,
         onSwitchBudgetType: _onSwitchBudgetType,
@@ -433,7 +432,7 @@ function BudgetInner(props: BudgetInnerProps) {
             onRefresh={onRefresh}
             onEditGroup={onEditGroup}
             onEditCategory={onEditCategory}
-            onOpenBudgetActionMenu={onOpenBudgetActionMenu}
+            onOpenBudgetMonthMenu={onOpenBudgetMonthMenu}
           />
         )}
       </SyncRefresh>
diff --git a/packages/desktop-client/src/components/mobile/transactions/FocusableAmountInput.jsx b/packages/desktop-client/src/components/mobile/transactions/FocusableAmountInput.tsx
similarity index 67%
rename from packages/desktop-client/src/components/mobile/transactions/FocusableAmountInput.jsx
rename to packages/desktop-client/src/components/mobile/transactions/FocusableAmountInput.tsx
index 482759d5f76067b87d2266cda5c73be31eabfad3..17b0d0d3281d6efc488ef3b99099d75f2147707c 100644
--- a/packages/desktop-client/src/components/mobile/transactions/FocusableAmountInput.jsx
+++ b/packages/desktop-client/src/components/mobile/transactions/FocusableAmountInput.tsx
@@ -1,4 +1,12 @@
-import { memo, useEffect, useRef, useState } from 'react';
+import React, {
+  type Ref,
+  type ComponentPropsWithRef,
+  type HTMLProps,
+  memo,
+  useEffect,
+  useRef,
+  useState,
+} from 'react';
 
 import {
   toRelaxedNumber,
@@ -8,23 +16,41 @@ import {
 
 import { useLocalPref } from '../../../hooks/useLocalPref';
 import { useMergedRefs } from '../../../hooks/useMergedRefs';
-import { theme } from '../../../style';
+import { type CSSProperties, theme } from '../../../style';
 import { Button } from '../../common/Button';
 import { Text } from '../../common/Text';
 import { View } from '../../common/View';
 
+type AmountInputProps = {
+  value: number;
+  focused?: boolean;
+  style?: CSSProperties;
+  textStyle?: CSSProperties;
+  inputRef?: Ref<HTMLInputElement>;
+  onFocus?: HTMLProps<HTMLInputElement>['onFocus'];
+  onBlur?: HTMLProps<HTMLInputElement>['onBlur'];
+  onEnter?: HTMLProps<HTMLInputElement>['onKeyUp'];
+  onChangeValue?: (value: string) => void;
+  onUpdate?: (value: string) => void;
+  onUpdateAmount?: (value: number) => void;
+};
+
 const AmountInput = memo(function AmountInput({
   focused,
   style,
   textStyle,
   ...props
-}) {
+}: AmountInputProps) {
   const [editing, setEditing] = useState(false);
   const [text, setText] = useState('');
   const [value, setValue] = useState(0);
-  const inputRef = useRef();
+  const inputRef = useRef<HTMLInputElement>();
   const [hideFraction = false] = useLocalPref('hideFraction');
-  const mergedInputRef = useMergedRefs(props.inputRef, inputRef);
+
+  const mergedInputRef = useMergedRefs<HTMLInputElement>(
+    props.inputRef,
+    inputRef,
+  );
 
   const initialValue = Math.abs(props.value);
 
@@ -44,11 +70,17 @@ const AmountInput = memo(function AmountInput({
     return toRelaxedNumber(text.replace(/[,.]/, getNumberFormat().separator));
   };
 
-  const onKeyPress = e => {
+  const onKeyUp: HTMLProps<HTMLInputElement>['onKeyUp'] = e => {
     if (e.key === 'Backspace' && text === '') {
       setEditing(true);
+    } else if (e.key === 'Enter') {
+      props.onEnter?.(e);
+      if (!e.defaultPrevented) {
+        onUpdate(e.currentTarget.value);
+      }
     }
   };
+
   const applyText = () => {
     const parsed = parseText();
     const newValue = editing ? parsed : value;
@@ -60,17 +92,24 @@ const AmountInput = memo(function AmountInput({
     return newValue;
   };
 
-  const onFocus = e => {
+  const onFocus: HTMLProps<HTMLInputElement>['onFocus'] = e => {
     props.onFocus?.(e);
   };
 
-  const onBlur = e => {
-    const value = applyText();
+  const onUpdate = (value: string) => {
     props.onUpdate?.(value);
+    const amount = applyText();
+    props.onUpdateAmount?.(amount);
+  };
+
+  const onBlur: HTMLProps<HTMLInputElement>['onBlur'] = e => {
     props.onBlur?.(e);
+    if (!e.defaultPrevented) {
+      onUpdate(e.target.value);
+    }
   };
 
-  const onChangeText = text => {
+  const onChangeText = (text: string) => {
     if (text.slice(-1) === '.') {
       text = text.slice(0, -1);
     }
@@ -83,7 +122,7 @@ const AmountInput = memo(function AmountInput({
 
     setEditing(true);
     setText(text);
-    props.onChange?.(text);
+    props.onChangeValue?.(text);
   };
 
   const input = (
@@ -96,7 +135,7 @@ const AmountInput = memo(function AmountInput({
       onChange={e => onChangeText(e.target.value)}
       onFocus={onFocus}
       onBlur={onBlur}
-      onKeyUp={onKeyPress}
+      onKeyUp={onKeyUp}
       data-testid="amount-input"
       style={{ flex: 1, textAlign: 'center', position: 'absolute' }}
     />
@@ -111,14 +150,17 @@ const AmountInput = memo(function AmountInput({
         borderRadius: 4,
         padding: 5,
         backgroundColor: theme.tableBackground,
+        maxWidth: 'calc(100% - 40px)',
         ...style,
       }}
     >
       <View style={{ overflowY: 'auto', overflowX: 'hidden' }}>{input}</View>
       <Text
-        style={textStyle}
+        style={{
+          pointerEvents: 'none',
+          ...textStyle,
+        }}
         data-testid="amount-fake-input"
-        pointerEvents="none"
       >
         {editing ? amountToCurrency(text) : amountToCurrency(value)}
       </Text>
@@ -126,11 +168,22 @@ const AmountInput = memo(function AmountInput({
   );
 });
 
+type FocusableAmountInputProps = Omit<AmountInputProps, 'onFocus'> & {
+  sign?: '+' | '-';
+  zeroSign?: '+' | '-';
+  focused?: boolean;
+  disabled?: boolean;
+  focusedStyle?: CSSProperties;
+  buttonProps?: ComponentPropsWithRef<typeof Button>;
+  onFocus?: () => void;
+};
+
 export const FocusableAmountInput = memo(function FocusableAmountInput({
   value,
-  sign, // + or -
-  zeroSign, // + or -
+  sign,
+  zeroSign,
   focused,
+  disabled,
   textStyle,
   style,
   focusedStyle,
@@ -138,9 +191,18 @@ export const FocusableAmountInput = memo(function FocusableAmountInput({
   onFocus,
   onBlur,
   ...props
-}) {
+}: FocusableAmountInputProps) {
   const [isNegative, setIsNegative] = useState(true);
 
+  const maybeApplyNegative = (amount: number, negative: boolean) => {
+    const absValue = Math.abs(amount);
+    return negative ? -absValue : absValue;
+  };
+
+  const onUpdateAmount = (amount: number, negative: boolean) => {
+    props.onUpdateAmount?.(maybeApplyNegative(amount, negative));
+  };
+
   useEffect(() => {
     if (sign) {
       setIsNegative(sign === '-');
@@ -150,17 +212,12 @@ export const FocusableAmountInput = memo(function FocusableAmountInput({
   }, [sign, value, zeroSign]);
 
   const toggleIsNegative = () => {
-    setIsNegative(!isNegative);
-    props.onUpdate?.(maybeApplyNegative(value, !isNegative));
-  };
-
-  const maybeApplyNegative = (val, negative) => {
-    const absValue = Math.abs(val);
-    return negative ? -absValue : absValue;
-  };
+    if (disabled) {
+      return;
+    }
 
-  const onUpdate = val => {
-    props.onUpdate?.(maybeApplyNegative(val, isNegative));
+    onUpdateAmount(value, !isNegative);
+    setIsNegative(!isNegative);
   };
 
   return (
@@ -170,8 +227,8 @@ export const FocusableAmountInput = memo(function FocusableAmountInput({
         value={value}
         onFocus={onFocus}
         onBlur={onBlur}
-        onUpdate={onUpdate}
-        focused={focused}
+        onUpdateAmount={amount => onUpdateAmount(amount, isNegative)}
+        focused={focused && !disabled}
         style={{
           width: 80,
           justifyContent: 'center',
@@ -216,7 +273,6 @@ export const FocusableAmountInput = memo(function FocusableAmountInput({
               borderBottomWidth: 1,
               borderColor: '#e0e0e0',
               justifyContent: 'center',
-              transform: [{ translateY: 0.5 }],
               ...style,
             }}
           >
diff --git a/packages/desktop-client/src/components/mobile/transactions/TransactionEdit.jsx b/packages/desktop-client/src/components/mobile/transactions/TransactionEdit.jsx
index b800f9595ccabeb9b1ef33122e1ea515d8bbab62..02864a9b5b870ea14654f47814eb7a945d32d2c8 100644
--- a/packages/desktop-client/src/components/mobile/transactions/TransactionEdit.jsx
+++ b/packages/desktop-client/src/components/mobile/transactions/TransactionEdit.jsx
@@ -720,16 +720,15 @@ const TransactionEditInner = memo(function TransactionEditInner({
             zeroSign="-"
             focused={totalAmountFocused}
             onFocus={onTotalAmountEdit}
-            onUpdate={onTotalAmountUpdate}
+            onUpdateAmount={onTotalAmountUpdate}
             focusedStyle={{
               width: 'auto',
               padding: '5px',
               paddingLeft: '20px',
               paddingRight: '20px',
-              minWidth: 120,
-              transform: [{ translateY: -0.5 }],
+              minWidth: '100%',
             }}
-            textStyle={{ fontSize: 30, textAlign: 'center' }}
+            textStyle={{ ...styles.veryLargeText, textAlign: 'center' }}
           />
         </View>
 
diff --git a/packages/desktop-client/src/components/modals/AccountMenuModal.tsx b/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
index 33c456f873954c372473c641514afd018f4d0fcb..f798c50327a1114621554fe92a0cec74ee66f74c 100644
--- a/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
+++ b/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { type ComponentProps, useState } from 'react';
 
 import { useLiveQuery } from 'loot-core/src/client/query-hooks';
 import { q } from 'loot-core/src/shared/query';
@@ -146,7 +146,8 @@ export function AccountMenuModal({
             flexWrap: 'wrap',
             justifyContent: 'space-between',
             alignContent: 'space-between',
-            margin: '10px 0',
+            paddingTop: 10,
+            paddingBottom: 10,
           }}
         >
           <Button style={buttonStyle} onClick={_onEditNotes}>
@@ -176,6 +177,11 @@ function AdditionalAccountMenu({
     height: styles.mobileMinHeight,
   };
 
+  const getItemStyle: ComponentProps<typeof Menu>['getItemStyle'] = item => ({
+    ...itemStyle,
+    ...(item.name === 'close' && { color: theme.errorTextMenu }),
+  });
+
   return (
     <View>
       <Button
@@ -199,7 +205,7 @@ function AdditionalAccountMenu({
             }}
           >
             <Menu
-              getItemStyle={() => itemStyle}
+              getItemStyle={getItemStyle}
               items={[
                 account.closed
                   ? {
diff --git a/packages/desktop-client/src/components/modals/BudgetMenuModal.tsx b/packages/desktop-client/src/components/modals/BudgetMonthMenuModal.tsx
similarity index 87%
rename from packages/desktop-client/src/components/modals/BudgetMenuModal.tsx
rename to packages/desktop-client/src/components/modals/BudgetMonthMenuModal.tsx
index 59a7d867c7e3b2fd5a5ab384da04b939ed236813..05c7dd0352f5ac82346ff92fc4540f01a0ea2054 100644
--- a/packages/desktop-client/src/components/modals/BudgetMenuModal.tsx
+++ b/packages/desktop-client/src/components/modals/BudgetMonthMenuModal.tsx
@@ -6,16 +6,18 @@ import { Menu } from '../common/Menu';
 import { Modal } from '../common/Modal';
 import { type CommonModalProps } from '../Modals';
 
-type BudgetMenuModalProps = ComponentPropsWithoutRef<typeof BudgetMenu> & {
+type BudgetMonthMenuModalProps = ComponentPropsWithoutRef<
+  typeof BudgetMonthMenu
+> & {
   modalProps: CommonModalProps;
 };
 
-export function BudgetMenuModal({
+export function BudgetMonthMenuModal({
   modalProps,
   month,
   onToggleHiddenCategories,
   onSwitchBudgetType,
-}: BudgetMenuModalProps) {
+}: BudgetMonthMenuModalProps) {
   const defaultMenuItemStyle: CSSProperties = {
     ...styles.mobileMenuItem,
     color: theme.menuItemText,
@@ -36,9 +38,9 @@ export function BudgetMenuModal({
         borderRadius: '6px',
       }}
     >
-      <BudgetMenu
-        getItemStyle={() => defaultMenuItemStyle}
+      <BudgetMonthMenu
         month={month}
+        getItemStyle={() => defaultMenuItemStyle}
         onToggleHiddenCategories={onToggleHiddenCategories}
         onSwitchBudgetType={onSwitchBudgetType}
       />
@@ -46,7 +48,7 @@ export function BudgetMenuModal({
   );
 }
 
-type BudgetMenuProps = Omit<
+type BudgetMonthMenuProps = Omit<
   ComponentPropsWithoutRef<typeof Menu>,
   'onMenuSelect' | 'items'
 > & {
@@ -55,13 +57,13 @@ type BudgetMenuProps = Omit<
   onSwitchBudgetType: () => void;
 };
 
-function BudgetMenu({
+function BudgetMonthMenu({
   // onEditMode,
   month,
   onToggleHiddenCategories,
   onSwitchBudgetType,
   ...props
-}: BudgetMenuProps) {
+}: BudgetMonthMenuProps) {
   const isReportBudgetEnabled = useFeatureFlag('reportBudget');
 
   const onMenuSelect = (name: string) => {
@@ -76,7 +78,7 @@ function BudgetMenu({
         onSwitchBudgetType?.();
         break;
       default:
-        throw new Error(`Unrecognized menu option: ${name}`);
+        throw new Error(`Unrecognized menu item: ${name}`);
     }
   };
 
diff --git a/packages/desktop-client/src/components/modals/CategoryGroupMenuModal.tsx b/packages/desktop-client/src/components/modals/CategoryGroupMenuModal.tsx
index 3f99d8e2bfd6c24e2f51a641e57f9392fd84310b..e9a2c1ffd1d6292eed1d4200df9abd1e421cd4c7 100644
--- a/packages/desktop-client/src/components/modals/CategoryGroupMenuModal.tsx
+++ b/packages/desktop-client/src/components/modals/CategoryGroupMenuModal.tsx
@@ -174,6 +174,11 @@ function AdditionalCategoryGroupMenu({ group, onDelete, onToggleVisibility }) {
     height: styles.mobileMinHeight,
   };
 
+  const getItemStyle = item => ({
+    ...itemStyle,
+    ...(item.name === 'delete' && { color: theme.errorTextMenu }),
+  });
+
   return (
     <View>
       {!group.is_income && (
@@ -202,7 +207,7 @@ function AdditionalCategoryGroupMenu({ group, onDelete, onToggleVisibility }) {
                   ...styles.mediumText,
                   color: theme.formLabelText,
                 }}
-                getItemStyle={() => itemStyle}
+                getItemStyle={getItemStyle}
                 items={
                   [
                     {
diff --git a/packages/desktop-client/src/components/modals/CategoryMenuModal.tsx b/packages/desktop-client/src/components/modals/CategoryMenuModal.tsx
index 75ffcaa1ec42a1be87ef2a6f4f32e81a0a665059..686e99dd40f6ec3d1b426ebd65b48ec3a27b8b53 100644
--- a/packages/desktop-client/src/components/modals/CategoryMenuModal.tsx
+++ b/packages/desktop-client/src/components/modals/CategoryMenuModal.tsx
@@ -4,12 +4,12 @@ import React, { useState } from 'react';
 import { useLiveQuery } from 'loot-core/src/client/query-hooks';
 import { q } from 'loot-core/src/shared/query';
 import {
-  type CategoryGroupEntity,
   type CategoryEntity,
   type NoteEntity,
 } from 'loot-core/src/types/models';
 
-import { useCategories } from '../../hooks/useCategories';
+import { useCategory } from '../../hooks/useCategory';
+import { useCategoryGroup } from '../../hooks/useCategoryGroup';
 import { SvgDotsHorizontalTriple, SvgTrash } from '../../icons/v1';
 import { SvgNotesPaper, SvgViewHide, SvgViewShow } from '../../icons/v2';
 import { type CSSProperties, styles, theme } from '../../style';
@@ -24,7 +24,6 @@ import { Tooltip } from '../tooltips';
 type CategoryMenuModalProps = {
   modalProps: CommonModalProps;
   categoryId: string;
-  categoryGroup?: CategoryGroupEntity;
   onSave: (category: CategoryEntity) => void;
   onEditNotes: (id: string) => void;
   onDelete: (categoryId: string) => void;
@@ -34,14 +33,13 @@ type CategoryMenuModalProps = {
 export function CategoryMenuModal({
   modalProps,
   categoryId,
-  categoryGroup,
   onSave,
   onEditNotes,
   onDelete,
   onClose,
 }: CategoryMenuModalProps) {
-  const { list: categories } = useCategories();
-  const category = categories.find(c => c.id === categoryId);
+  const category = useCategory(categoryId);
+  const categoryGroup = useCategoryGroup(category?.cat_group);
   const data = useLiveQuery<NoteEntity[]>(
     () => q('notes').filter({ id: category.id }).select('*'),
     [category.id],
@@ -143,7 +141,8 @@ export function CategoryMenuModal({
             flexWrap: 'wrap',
             justifyContent: 'space-between',
             alignContent: 'space-between',
-            margin: '10px 0',
+            paddingTop: 10,
+            paddingBottom: 10,
           }}
         >
           <Button style={buttonStyle} onClick={_onEditNotes}>
@@ -168,6 +167,11 @@ function AdditionalCategoryMenu({
     height: styles.mobileMinHeight,
   };
 
+  const getItemStyle = item => ({
+    ...itemStyle,
+    ...(item.name === 'delete' && { color: theme.errorTextMenu }),
+  });
+
   return (
     <View>
       <Button
@@ -191,7 +195,7 @@ function AdditionalCategoryMenu({
             }}
           >
             <Menu
-              getItemStyle={() => itemStyle}
+              getItemStyle={getItemStyle}
               items={[
                 !categoryGroup?.hidden && {
                   name: 'toggleVisibility',
diff --git a/packages/desktop-client/src/components/modals/ReportBudgetMenuModal.tsx b/packages/desktop-client/src/components/modals/ReportBudgetMenuModal.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..1d994f67c07222b117b31c85970322e0e54e03c5
--- /dev/null
+++ b/packages/desktop-client/src/components/modals/ReportBudgetMenuModal.tsx
@@ -0,0 +1,101 @@
+import React, {
+  useState,
+  type ComponentPropsWithoutRef,
+  useEffect,
+} from 'react';
+
+import { reportBudget } from 'loot-core/client/queries';
+import { amountToInteger, integerToAmount } from 'loot-core/shared/util';
+
+import { useCategory } from '../../hooks/useCategory';
+import { type CSSProperties, theme, styles } from '../../style';
+import { BudgetMenu } from '../budget/report/BudgetMenu';
+import { Modal } from '../common/Modal';
+import { View } from '../common/View';
+import { FocusableAmountInput } from '../mobile/transactions/FocusableAmountInput';
+import { type CommonModalProps } from '../Modals';
+import { useSheetValue } from '../spreadsheet/useSheetValue';
+
+type ReportBudgetMenuModalProps = ComponentPropsWithoutRef<
+  typeof BudgetMenu
+> & {
+  modalProps: CommonModalProps;
+  categoryId: string;
+  onUpdateBudget: (amount: number) => void;
+};
+
+export function ReportBudgetMenuModal({
+  modalProps,
+  categoryId,
+  onUpdateBudget,
+  onCopyLastMonthAverage,
+  onSetMonthsAverage,
+  onApplyBudgetTemplate,
+}: ReportBudgetMenuModalProps) {
+  const defaultMenuItemStyle: CSSProperties = {
+    ...styles.mobileMenuItem,
+    color: theme.menuItemText,
+    borderRadius: 0,
+    borderTop: `1px solid ${theme.pillBorder}`,
+  };
+
+  const budgeted = useSheetValue(reportBudget.catBudgeted(categoryId));
+  const category = useCategory(categoryId);
+  const [amountFocused, setAmountFocused] = useState(false);
+
+  const _onUpdateBudget = (amount: number) => {
+    onUpdateBudget?.(amountToInteger(amount));
+  };
+
+  useEffect(() => {
+    setAmountFocused(true);
+  }, []);
+
+  return (
+    <Modal
+      title={`Budget: ${category?.name}`}
+      showHeader
+      focusAfterClose={false}
+      {...modalProps}
+      padding={0}
+      style={{
+        flex: 1,
+        padding: '0 10px',
+        paddingBottom: 10,
+        borderRadius: '6px',
+      }}
+    >
+      <View
+        style={{
+          justifyContent: 'center',
+          alignItems: 'center',
+          marginBottom: 20,
+        }}
+      >
+        <FocusableAmountInput
+          value={integerToAmount(budgeted || 0)}
+          focused={amountFocused}
+          onFocus={() => setAmountFocused(true)}
+          onBlur={() => setAmountFocused(false)}
+          onEnter={() => modalProps.onClose()}
+          zeroSign="+"
+          focusedStyle={{
+            width: 'auto',
+            padding: '5px',
+            paddingLeft: '20px',
+            paddingRight: '20px',
+            minWidth: '100%',
+          }}
+          textStyle={{ ...styles.veryLargeText, textAlign: 'center' }}
+          onUpdateAmount={_onUpdateBudget}
+        />
+      </View>
+      <BudgetMenu
+        getItemStyle={() => defaultMenuItemStyle}
+        onCopyLastMonthAverage={onCopyLastMonthAverage}
+        onSetMonthsAverage={onSetMonthsAverage}
+        onApplyBudgetTemplate={onApplyBudgetTemplate}
+      />
+    </Modal>
+  );
+}
diff --git a/packages/desktop-client/src/components/modals/RolloverBudgetMenuModal.tsx b/packages/desktop-client/src/components/modals/RolloverBudgetMenuModal.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..c06e91781473e89665a35d16ce1d2be71512d2c7
--- /dev/null
+++ b/packages/desktop-client/src/components/modals/RolloverBudgetMenuModal.tsx
@@ -0,0 +1,101 @@
+import React, {
+  useState,
+  type ComponentPropsWithoutRef,
+  useEffect,
+} from 'react';
+
+import { rolloverBudget } from 'loot-core/client/queries';
+import { amountToInteger, integerToAmount } from 'loot-core/shared/util';
+
+import { useCategory } from '../../hooks/useCategory';
+import { type CSSProperties, theme, styles } from '../../style';
+import { BudgetMenu } from '../budget/rollover/BudgetMenu';
+import { Modal } from '../common/Modal';
+import { View } from '../common/View';
+import { FocusableAmountInput } from '../mobile/transactions/FocusableAmountInput';
+import { type CommonModalProps } from '../Modals';
+import { useSheetValue } from '../spreadsheet/useSheetValue';
+
+type RolloverBudgetMenuModalProps = ComponentPropsWithoutRef<
+  typeof BudgetMenu
+> & {
+  modalProps: CommonModalProps;
+  categoryId: string;
+  onUpdateBudget: (amount: number) => void;
+};
+
+export function RolloverBudgetMenuModal({
+  modalProps,
+  categoryId,
+  onUpdateBudget,
+  onCopyLastMonthAverage,
+  onSetMonthsAverage,
+  onApplyBudgetTemplate,
+}: RolloverBudgetMenuModalProps) {
+  const defaultMenuItemStyle: CSSProperties = {
+    ...styles.mobileMenuItem,
+    color: theme.menuItemText,
+    borderRadius: 0,
+    borderTop: `1px solid ${theme.pillBorder}`,
+  };
+
+  const budgeted = useSheetValue(rolloverBudget.catBudgeted(categoryId));
+  const category = useCategory(categoryId);
+  const [amountFocused, setAmountFocused] = useState(false);
+
+  const _onUpdateBudget = (amount: number) => {
+    onUpdateBudget?.(amountToInteger(amount));
+  };
+
+  useEffect(() => {
+    setAmountFocused(true);
+  }, []);
+
+  return (
+    <Modal
+      title={`Budget: ${category?.name}`}
+      showHeader
+      focusAfterClose={false}
+      {...modalProps}
+      padding={0}
+      style={{
+        flex: 1,
+        padding: '0 10px',
+        paddingBottom: 10,
+        borderRadius: '6px',
+      }}
+    >
+      <View
+        style={{
+          justifyContent: 'center',
+          alignItems: 'center',
+          marginBottom: 20,
+        }}
+      >
+        <FocusableAmountInput
+          value={integerToAmount(budgeted || 0)}
+          focused={amountFocused}
+          onFocus={() => setAmountFocused(true)}
+          onBlur={() => setAmountFocused(false)}
+          onEnter={() => modalProps.onClose()}
+          zeroSign="+"
+          focusedStyle={{
+            width: 'auto',
+            padding: '5px',
+            paddingLeft: '20px',
+            paddingRight: '20px',
+            minWidth: '100%',
+          }}
+          textStyle={{ ...styles.veryLargeText, textAlign: 'center' }}
+          onUpdateAmount={_onUpdateBudget}
+        />
+      </View>
+      <BudgetMenu
+        getItemStyle={() => defaultMenuItemStyle}
+        onCopyLastMonthAverage={onCopyLastMonthAverage}
+        onSetMonthsAverage={onSetMonthsAverage}
+        onApplyBudgetTemplate={onApplyBudgetTemplate}
+      />
+    </Modal>
+  );
+}
diff --git a/packages/desktop-client/src/components/modals/RolloverBudgetSummaryModal.tsx b/packages/desktop-client/src/components/modals/RolloverBudgetSummaryModal.tsx
index 2839c3ee666583842099e18797f7884c18033604..f0d55b7abcbedc96575ea6becf1e8ba6ccbf42f6 100644
--- a/packages/desktop-client/src/components/modals/RolloverBudgetSummaryModal.tsx
+++ b/packages/desktop-client/src/components/modals/RolloverBudgetSummaryModal.tsx
@@ -15,7 +15,7 @@ import { useSheetValue } from '../spreadsheet/useSheetValue';
 
 type RolloverBudgetSummaryModalProps = {
   modalProps: CommonModalProps;
-  onBudgetAction: (idx: string | number, action: string, arg?: unknown) => void;
+  onBudgetAction: (month: string, action: string, arg?: unknown) => void;
   month: string;
 };
 
@@ -67,7 +67,7 @@ export function RolloverBudgetSummaryModal({
 
   const onClick = () => {
     dispatch(
-      pushModal('rollover-to-budget-menu', {
+      pushModal('rollover-summary-to-budget-menu', {
         month,
         onTransfer: openTransferModal,
         onResetHoldBuffer,
diff --git a/packages/desktop-client/src/hooks/useCategory.ts b/packages/desktop-client/src/hooks/useCategory.ts
new file mode 100644
index 0000000000000000000000000000000000000000..028d504090615badd857993e347611cd031af3d8
--- /dev/null
+++ b/packages/desktop-client/src/hooks/useCategory.ts
@@ -0,0 +1,8 @@
+import { useMemo } from 'react';
+
+import { useCategories } from './useCategories';
+
+export function useCategory(id: string) {
+  const { list: categories } = useCategories();
+  return useMemo(() => categories.find(c => c.id === id), [id, categories]);
+}
diff --git a/packages/desktop-client/src/hooks/useCategoryGroup.ts b/packages/desktop-client/src/hooks/useCategoryGroup.ts
new file mode 100644
index 0000000000000000000000000000000000000000..53806fe3cff466b9ea56aa9034f76e17c3d46a02
--- /dev/null
+++ b/packages/desktop-client/src/hooks/useCategoryGroup.ts
@@ -0,0 +1,11 @@
+import { useMemo } from 'react';
+
+import { useCategories } from './useCategories';
+
+export function useCategoryGroup(id: string) {
+  const { grouped: categoryGroups } = useCategories();
+  return useMemo(
+    () => categoryGroups.find(g => g.id === id),
+    [id, categoryGroups],
+  );
+}
diff --git a/packages/desktop-client/src/hooks/useMergedRefs.ts b/packages/desktop-client/src/hooks/useMergedRefs.ts
index b30cbef5fa151f5c9179d5decc61e1bd7c645bd1..498b588416b06ec00827fe8d038654d793c21d3d 100644
--- a/packages/desktop-client/src/hooks/useMergedRefs.ts
+++ b/packages/desktop-client/src/hooks/useMergedRefs.ts
@@ -2,7 +2,13 @@ import { useCallback } from 'react';
 import type { MutableRefObject, Ref, RefCallback } from 'react';
 
 export function useMergedRefs<T>(
-  ...refs: (RefCallback<T> | MutableRefObject<T> | Ref<T> | null | undefined)[]
+  ...refs: (
+    | RefCallback<T | null | undefined>
+    | MutableRefObject<T | null | undefined>
+    | Ref<T | null | undefined>
+    | null
+    | undefined
+  )[]
 ): Ref<T> {
   return useCallback(
     (value: T) => {
diff --git a/packages/loot-core/src/client/state-types/modals.d.ts b/packages/loot-core/src/client/state-types/modals.d.ts
index eef5b20f8074e2704ca1fd6753078122a8b4b966..96c3b65cc3ab966a43ee19b096b9e875069ff6f0 100644
--- a/packages/loot-core/src/client/state-types/modals.d.ts
+++ b/packages/loot-core/src/client/state-types/modals.d.ts
@@ -143,12 +143,28 @@ type FinanceModals = {
   };
   'category-menu': {
     categoryId: string;
-    categoryGroup?: CategoryGroupEntity;
     onSave: (category: CategoryEntity) => void;
     onEditNotes: (id: string) => void;
     onDelete: (categoryId: string) => void;
+    onBudgetAction: (month: string, action: string, args?: unknown) => void;
     onClose?: () => void;
   };
+  'rollover-budget-menu': {
+    categoryId: string;
+    month: string;
+    onUpdateBudget: (amount: number) => void;
+    onCopyLastMonthAverage: () => void;
+    onSetMonthsAverage: (numberOfMonths: number) => void;
+    onApplyBudgetTemplate: () => void;
+  };
+  'report-budget-menu': {
+    categoryId: string;
+    month: string;
+    onUpdateBudget: (amount: number) => void;
+    onCopyLastMonthAverage: () => void;
+    onSetMonthsAverage: (numberOfMonths: number) => void;
+    onApplyBudgetTemplate: () => void;
+  };
   'category-group-menu': {
     groupId: string;
     onSave: (group: CategoryGroupEntity) => void;
@@ -186,7 +202,7 @@ type FinanceModals = {
     onTransfer: () => void;
     onCover: () => void;
   };
-  'rollover-to-budget-menu': {
+  'rollover-summary-to-budget-menu': {
     month: string;
     onTransfer: () => void;
     onHoldBuffer: () => void;
@@ -216,7 +232,7 @@ type FinanceModals = {
     onPost: (transactionId: string) => void;
     onSkip: (transactionId: string) => void;
   };
-  'budget-menu': {
+  'budget-month-menu': {
     month: string;
     onToggleHiddenCategories: () => void;
     onSwitchBudgetType: () => void;
diff --git a/upcoming-release-notes/2501.md b/upcoming-release-notes/2501.md
new file mode 100644
index 0000000000000000000000000000000000000000..d667d63f7412e4e4c94180404ffd2d3c4b140492
--- /dev/null
+++ b/upcoming-release-notes/2501.md
@@ -0,0 +1,6 @@
+---
+category: Enhancements
+authors: [joel-jeremy]
+---
+
+Mobile budget menu modal to set budget amounts.