diff --git a/crates/ui-egui/src/formula_bar.rs b/crates/ui-egui/src/formula_bar.rs
--- a/crates/ui-egui/src/formula_bar.rs
+++ b/crates/ui-egui/src/formula_bar.rs
@@ -292,9 +292,17 @@ fn show_edit<'t>(
 /// `max_height` of text and scrolls beyond that, keeping the caret in view.
 pub fn editor_widget(app: &mut SheetApp, ui: &mut egui::Ui, id: egui::Id, font: FontId, multiline: bool, width: f32, max_height: f32) -> EditorInfo {
     let Some(mut ed) = app.editor.take() else { return EditorInfo::default() };
-    let mine = ed.from_formula_bar == (id == egui::Id::unique("gridcraft.formula_bar"));
+    let is_bar = id == egui::Id::unique("gridcraft.formula_bar");
+    // The in-cell editor is only drawn on the sheet the edit started on: while a formula points at
+    // another sheet, the formula bar's field takes the keys (Enter, arrows, Ctrl+Page Down).
+    let cell_editor_hidden = !ed.from_formula_bar && app.view_key() != Some((ed.doc, ed.sheet));
+    let mine = ed.from_formula_bar == is_bar || (is_bar && cell_editor_hidden);
     // Keys the editor handles itself (before the TextEdit sees them), only where it has focus.
     let focused = ui.ctx().memory(|m| m.focused());
+    // Back on the sheet the edit started on, the bar's field that took the keys there hands them back.
+    if mine && !is_bar && !ed.from_formula_bar && focused == Some(egui::Id::unique("gridcraft.formula_bar")) {
+        ed.request_focus = true;
+    }
     let has_focus = focused == Some(id) || (mine && (ed.request_focus || focused.is_none()));
     let mut action: Option<(i64, i64, bool, bool)> = None;
     let mut arrow = false;
@@ -388,6 +396,22 @@ pub fn editor_widget(app: &mut SheetApp, ui: &mut egui::Ui, id: egui::Id, font:
                 arrow = true;
             }
         }
+        // Ctrl+PageDown / Ctrl+PageUp while a formula waits for a reference: on to the next / previous sheet.
+        // Only where the arrows point too, so every switch leaves the arrows working on the new sheet.
+        if ed.enter_mode && !ed.from_formula_bar && ed.can_point() {
+            let step = ui.input_mut(|i| {
+                if i.consume_key(egui::Modifiers::CTRL, Key::PageDown) {
+                    Some("sheet.next")
+                } else if i.consume_key(egui::Modifiers::CTRL, Key::PageUp) {
+                    Some("sheet.previous")
+                } else {
+                    None
+                }
+            });
+            if let Some(command) = step {
+                crate::grid::point_sheet(app, &mut ed, command, json!({}));
+            }
+        }
         // F4 cycles $ anchors of the reference before the caret.
         if ui.input_mut(|i| i.consume_key(egui::Modifiers::NONE, Key::F4)) {
             cycle_anchor(&mut ed);
diff --git a/crates/ui-egui/src/grid.rs b/crates/ui-egui/src/grid.rs
--- a/crates/ui-egui/src/grid.rs
+++ b/crates/ui-egui/src/grid.rs
@@ -36,7 +36,7 @@ pub use editor::validation_arrow;
 #[cfg(test)]
 use editor::{editor_slack, editor_width, list_picker_height};
 pub use geo::Geo;
-pub use interact::{follow_link, freeze_handles, point_move};
+pub use interact::{follow_link, freeze_handles, point_move, point_sheet};
 pub(crate) use quadrant::paint_quadrant;
 pub(crate) use selection::paint_selection;
 pub use selection::selection_fill;
diff --git a/crates/ui-egui/src/grid/interact.rs b/crates/ui-egui/src/grid/interact.rs
--- a/crates/ui-egui/src/grid/interact.rs
+++ b/crates/ui-egui/src/grid/interact.rs
@@ -877,12 +877,13 @@ pub fn follow_link(app: &mut SheetApp, target: &str) {
 /// Used by `EditState` users to keep the active cell visible while pointing.
 pub fn point_move(app: &mut SheetApp, ed: &mut EditState, dr: i64, dc: i64, extend: bool) {
     let Some(d) = app.session.active() else { return };
-    let from = ed.point_cell.unwrap_or(ed.cell);
+    // Before any pointing on another sheet, the arrows start from the cell selected there.
+    let from = ed.point_cell.unwrap_or_else(|| if ed.sheet == d.wb.active_sheet() { ed.cell } else { d.selection.active });
     let to = from.offset_clamped(dr, dc);
     let anchor = if extend {
         // The anchor is the corner opposite the moving end: the written range is normalized, so
         // after extending up or left its first cell is the moving end, not the anchor.
-        let pointed = ed.point.and_then(|(s, e)| ed.text.get(s..e)).and_then(RangeRef::parse);
+        let pointed = ed.point.and_then(|(s, e)| ed.text.get(s..e)).and_then(|t| RangeRef::parse(t.rsplit('!').next().unwrap_or(t)));
         let inferred_anchor = pointed
             .map(|r| {
                 CellRef::new(
@@ -897,8 +898,31 @@ pub fn point_move(app: &mut SheetApp, ed: &mut EditState, dr: i64, dc: i64, exte
     };
     let text = if extend { RangeRef::new(anchor, to).a1() } else { to.a1() };
     let sheet = d.wb.active_sheet();
+    // Pointing on another sheet than the one the edit started on names it, as a click there does.
+    let text = match d.wb.active() {
+        Some(on) if ed.sheet != sheet => format!("{}!{text}", gridcraft_engine::formula::quote_sheet(&on.name)),
+        _ => text,
+    };
     ed.insert_ref(&text);
     ed.point_cell = Some(to);
     ed.point_anchor = Some((sheet, anchor));
     app.grid.ensure_visible = true;
 }
+
+/// Switches to another sheet (`command` with `params`: `sheet.next`, `sheet.previous` or
+/// `sheet.activate`) while a formula waits for a reference. The formula carries on there: the
+/// arrows point from the cell selected on that sheet, and a range starts at it. The edit stays on
+/// the sheet it started on, which Enter returns to. Nothing changes when the sheet does not (the
+/// first or last sheet, a failed command).
+pub fn point_sheet(app: &mut SheetApp, ed: &mut EditState, command: &str, params: serde_json::Value) {
+    let before = app.session.active().map(|d| d.wb.active_sheet());
+    if app.session.run(command, params).is_err() {
+        return;
+    }
+    let Some(d) = app.session.active().filter(|d| Some(d.wb.active_sheet()) != before) else { return };
+    ed.point_cell = Some(d.selection.active);
+    ed.point_anchor = Some((d.wb.active_sheet(), d.selection.active));
+    // The new sheet's grid took the focus: the editor needs it back for the next key.
+    ed.request_focus = true;
+    app.grid.ensure_visible = true;
+}
diff --git a/crates/ui-egui/src/tabs.rs b/crates/ui-egui/src/tabs.rs
--- a/crates/ui-egui/src/tabs.rs
+++ b/crates/ui-egui/src/tabs.rs
@@ -25,8 +25,11 @@ fn navigator_items(sheets: &[Arc<Sheet>]) -> Vec<(String, String)> {
 /// Switch to sheet `i` from the tab strip or the navigator. In point mode the formula being typed keeps going on the
 /// other sheet; otherwise the edit is committed first.
 fn activate_sheet(app: &mut SheetApp, i: usize) {
-    if app.editor.as_ref().is_some_and(|e| e.can_point()) {
-        let _ = app.session.run("sheet.activate", json!({"sheet": i}));
+    if app.editor.as_ref().is_some_and(|e| e.can_point())
+        && let Some(mut ed) = app.editor.take()
+    {
+        crate::grid::point_sheet(app, &mut ed, "sheet.activate", json!({"sheet": i}));
+        app.editor = Some(ed);
     } else {
         if app.editor.is_some() && !app.commit_edit(0, 0, false, false) {
             return;
diff --git a/crates/ui-egui/tests/it/formula_point_sheets.rs b/crates/ui-egui/tests/it/formula_point_sheets.rs
new file mode 100644
--- /dev/null
+++ b/crates/ui-egui/tests/it/formula_point_sheets.rs
@@ -0,0 +1,128 @@
+//! Keyboard-only references to another sheet: Ctrl+PageDown / Ctrl+PageUp while a formula waits
+//! for a reference switch sheets without ending the edit, and the arrows point on the sheet shown.
+
+use egui::{Event, Key, Modifiers};
+use gridcraft_engine::Session;
+use gridcraft_engine::core::CellRef;
+use gridcraft_ui_egui::SheetApp;
+use serde_json::json;
+
+type Harness = egui_kittest::Harness<'static, SheetApp>;
+
+/// Ctrl on Windows and Linux sets both `ctrl` and `command`.
+const CTRL: Modifiers = Modifiers { alt: false, ctrl: true, shift: false, mac_cmd: false, command: true };
+
+/// Two sheets with B2 selected on the first, `=` typed there.
+fn pointing(text: &str) -> Harness {
+    let mut session = Session::new();
+    session.new_workbook();
+    session.execute("home.insertSheet", json!({})).unwrap();
+    session.execute("sheet.activate", json!({"sheet": 0})).unwrap();
+    session.execute("selection.set", json!({"range": "B2"})).unwrap();
+    let mut h = crate::support::harness(session, 4);
+    h.state_mut().begin_edit(Some(text.into()), false);
+    h.run_steps(4);
+    h
+}
+
+fn press(h: &mut Harness, k: Key, m: Modifiers) {
+    h.input_mut().events.push(Event::Key { key: k, physical_key: Some(k), pressed: true, repeat: false, modifiers: m });
+    h.input_mut().events.push(Event::Key { key: k, physical_key: Some(k), pressed: false, repeat: false, modifiers: m });
+    h.run_steps(2);
+}
+
+/// A key pressed with Shift held, as the OS reports it: the modifier changes first.
+fn press_shift(h: &mut Harness, k: Key) {
+    h.input_mut().events.push(Event::ModifiersChanged(Modifiers::SHIFT));
+    press(h, k, Modifiers::SHIFT);
+    h.input_mut().events.push(Event::ModifiersChanged(Modifiers::NONE));
+    h.run_steps(1);
+}
+
+fn text(h: &Harness) -> String {
+    h.state().editor.as_ref().map(|e| e.text.clone()).unwrap_or_default()
+}
+
+fn shown(h: &Harness) -> usize {
+    h.state().session.active().unwrap().wb.active_sheet()
+}
+
+fn second_name(h: &Harness) -> String {
+    h.state().session.active().unwrap().wb.sheet(1).unwrap().name.clone()
+}
+
+#[test]
+fn ctrl_page_down_goes_on_pointing_on_the_next_sheet() {
+    let mut h = pointing("=");
+    press(&mut h, Key::PageDown, CTRL);
+    assert_eq!(shown(&h), 1, "the next sheet is shown");
+    assert!(h.state().editor.is_some(), "the edit goes on");
+    press(&mut h, Key::ArrowDown, Modifiers::NONE);
+    assert_eq!(text(&h), format!("={}!A2", second_name(&h)), "the arrow points from the cell selected on that sheet and names the sheet");
+}
+
+#[test]
+fn ctrl_page_up_comes_back_and_the_reference_loses_its_sheet() {
+    let mut h = pointing("=");
+    press(&mut h, Key::PageDown, CTRL);
+    press(&mut h, Key::ArrowDown, Modifiers::NONE);
+    press(&mut h, Key::PageUp, CTRL);
+    assert_eq!(shown(&h), 0);
+    press(&mut h, Key::ArrowRight, Modifiers::NONE);
+    assert_eq!(text(&h), "=C2", "the pointed reference is replaced by one on the sheet being edited, from its selected cell");
+}
+
+#[test]
+fn enter_commits_on_the_sheet_the_edit_started_on() {
+    let mut h = pointing("=");
+    press(&mut h, Key::PageDown, CTRL);
+    press(&mut h, Key::ArrowDown, Modifiers::NONE);
+    press(&mut h, Key::Enter, Modifiers::NONE);
+    assert!(h.state().editor.is_none(), "Enter ends the edit");
+    assert_eq!(shown(&h), 0, "the sheet the formula was typed on is shown again");
+    let wb = &h.state().session.active().unwrap().wb;
+    assert!(wb.sheet(0).unwrap().cell(CellRef::parse("B2").unwrap()).is_some_and(|c| c.formula.is_some()));
+}
+
+#[test]
+fn text_that_is_not_a_formula_leaves_ctrl_page_down_alone() {
+    let mut h = pointing("abc");
+    press(&mut h, Key::PageDown, CTRL);
+    assert_eq!(shown(&h), 0, "only a formula waiting for a reference switches sheets");
+}
+
+#[test]
+fn a_formula_edited_in_the_formula_bar_leaves_ctrl_page_down_alone() {
+    let mut h = pointing("=");
+    h.state_mut().cancel_edit();
+    h.state_mut().begin_edit(Some("=".into()), true);
+    h.run_steps(4);
+    press(&mut h, Key::PageDown, CTRL);
+    assert_eq!(shown(&h), 0, "the arrows do not point there, so the sheet does not switch");
+}
+
+#[test]
+fn on_the_last_sheet_ctrl_page_down_keeps_the_pointing_where_it_was() {
+    let mut h = pointing("=");
+    press(&mut h, Key::ArrowDown, Modifiers::NONE);
+    assert_eq!(text(&h), "=B3");
+    press(&mut h, Key::PageDown, CTRL);
+    press(&mut h, Key::PageDown, CTRL);
+    assert_eq!(shown(&h), 1, "the last sheet is as far as it goes");
+    press(&mut h, Key::ArrowDown, Modifiers::NONE);
+    assert_eq!(text(&h), format!("={}!A2", second_name(&h)));
+    press(&mut h, Key::PageDown, CTRL);
+    press(&mut h, Key::ArrowDown, Modifiers::NONE);
+    assert_eq!(text(&h), format!("={}!A3", second_name(&h)), "no sheet change, so the pointing goes on from A2");
+}
+
+#[test]
+fn shift_arrow_after_a_sheet_switch_starts_the_range_at_the_selected_cell() {
+    let mut h = pointing("=");
+    press(&mut h, Key::ArrowRight, Modifiers::NONE);
+    press_shift(&mut h, Key::ArrowRight);
+    assert_eq!(text(&h), "=C2:D2");
+    press(&mut h, Key::PageDown, CTRL);
+    press_shift(&mut h, Key::ArrowDown);
+    assert_eq!(text(&h), format!("={}!A1:A2", second_name(&h)));
+}
diff --git a/crates/ui-egui/tests/it/main.rs b/crates/ui-egui/tests/it/main.rs
--- a/crates/ui-egui/tests/it/main.rs
+++ b/crates/ui-egui/tests/it/main.rs
@@ -44,6 +44,7 @@ mod formula_bar;
 mod formula_bar_expand;
 mod formula_locale;
 mod formula_point_headers;
+mod formula_point_sheets;
 mod formula_popup;
 mod freeze_handles;
 mod go_to_dialog;
diff --git a/docs/ui-parity.md b/docs/ui-parity.md
--- a/docs/ui-parity.md
+++ b/docs/ui-parity.md
@@ -1,6 +1,6 @@
 # UI parity
 
-> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-25 · **Change:** minor (AutoSum over a selected row puts its total to the right) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
+> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-26 · **Change:** minor (Ctrl+Page Down / Ctrl+Page Up while pointing a formula switch sheets) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
 
 Interaction, controls and feel, against Excel. Part of [target-app-parity.md](target-app-parity.md).
 **UI/UX fidelity: ~55% ready, 50–80 h** (estimated). Ribbon command coverage is measured
@@ -187,6 +187,7 @@ scrollbar bugs 2–4 · pixel/feel pass against observed Excel 7–12.
 
 | Date | Change | Summary |
 |---|---|---|
+| 2026-10-26 | minor | Formula pointing from the keyboard: while a formula waits for a reference, Ctrl+Page Down / Ctrl+Page Up go to the next / previous sheet without ending the edit, and the arrows then point from the cell selected there and write the sheet name (`=Sheet2!A2`); Enter returns to the sheet the formula was typed on. Excel's Page Up / Page Down pointing by a screen is not done. Not observed in Excel: the keys cannot be driven through COM. |
 | 2026-10-25 | minor | AutoSum (Alt+=, Home › AutoSum) over a selected row puts its total to the right of the row, or in its last cell when that is empty, as in Excel; it wrote a one-cell total under each cell. A selection's empty last column takes each row's total and its empty last row each column's, with the block's total in the corner when both are empty |
 | 2026-10-25 | minor | Paste Name lays out only the names in view, so a workbook with thousands of names opens it at once; the arrow keys still scroll the chosen name into view. Go To matches a `Sheet2!Name` reference and hides a workbook name behind the active sheet's own name in any case and script (`Größe` = `GRÖSSE`), as formulas do. The two Page Layout view rows are one; the 0.7 in side margin used when Align with Page Margins is off is marked unverified |
 | 2026-10-24 | minor | Go To lists the named ranges on every sheet, as Excel's does ("The Go to popup window shows named ranges on every worksheet in your workbook"): a name scoped to another sheet appears as `Sheet2!Name`, and hidden names, constants and formulas are left out. It keeps the references entered (newest first, ten at most), and a double-click on an entry goes there. The Reference box and `edit.goTo` take several names or references separated by commas (`Price, Type`, `B14:C22,F19:G30,H21:H29`) and select them all; `edit.goTo {list: true}` returns the list |
