diff --git a/crates/ui-egui/src/app/frame.rs b/crates/ui-egui/src/app/frame.rs
--- a/crates/ui-egui/src/app/frame.rs
+++ b/crates/ui-egui/src/app/frame.rs
@@ -371,6 +371,8 @@ impl LightcraftApp {
             ctx.send_viewport_cmd(egui::ViewportCommand::Close);
         }
         self.library_changed(&ctx);
+        // (a tool panel opened some other way still remembers the panel it took the place of)
+        self.ui.note_right();
         // panels set it again this frame while the pointer rests on a preset or profile
         self.hover_preview = None;
         if self.ui.fullscreen {
diff --git a/crates/ui-egui/src/control.rs b/crates/ui-egui/src/control.rs
--- a/crates/ui-egui/src/control.rs
+++ b/crates/ui-egui/src/control.rs
@@ -400,6 +400,11 @@ pub fn handle(app: &mut LightcraftApp, ctx: &egui::Context, req: &ControlRequest
                     }
                     u.collapsed_sidebar.clear();
                     u.toast = app.ui.toast.clone();
+                    // the panel a tool panel took the place of, to put back when it closes, survives a ui.set
+                    u.tool_return = app.ui.tool_return;
+                    u.right_seen = app.ui.right_seen;
+                    // (and what a panel toggle replaced, so pressing its key again still puts that back)
+                    u.toggle_return = app.ui.toggle_return;
                     u.dialog = app.ui.dialog.clone();
                     u.status = app.ui.status.clone();
                     crate::docking::cancel_replaced_gesture(app, u.docking.as_ref());
diff --git a/crates/ui-egui/src/menus/run.rs b/crates/ui-egui/src/menus/run.rs
--- a/crates/ui-egui/src/menus/run.rs
+++ b/crates/ui-egui/src/menus/run.rs
@@ -3,11 +3,12 @@ use super::ui_commands::{UI_COMMANDS, language_from_command};
 use super::*;
 
 fn panel(app: &mut LightcraftApp, ctx: &egui::Context, p: RightPanel, name: &str) {
-    if app.ui.right == p && crate::docking::exposed(app, crate::docking::Panel::Tools) {
-        app.ui.right = RightPanel::None;
+    // toggled off: back to exactly what the right column showed before (in the same frame: no flash, no jump),
+    // closed when it was closed (note 21)
+    let exposed = crate::docking::exposed(app, crate::docking::Panel::Tools);
+    if !app.ui.toggle_panel(p, exposed) {
         app.toast(ctx, crate::i18n::tr_format!("{name} Off", name = crate::i18n::tr(name)));
     } else {
-        app.ui.right = p;
         app.toast(ctx, crate::i18n::tr_format!("{name} On", name = crate::i18n::tr(name)));
         if p.is_edit_tool() && !matches!(app.ui.view, ViewMode::Detail) {
             app.ui.view = ViewMode::Detail;
@@ -16,6 +17,7 @@ fn panel(app: &mut LightcraftApp, ctx: &egui::Context, p: RightPanel, name: &str
     if p != RightPanel::Masking && app.ui.tool != "wbPicker" {
         app.ui.tool.clear();
     }
+    app.ui.note_right();
     let _ = app.session.end_interaction();
 }
 
@@ -1019,12 +1021,13 @@ pub fn run_ui_command(app: &mut LightcraftApp, id: &str, p: &Value) -> Option<Re
             Ok(Value::Null)
         }
         "tool.done" => {
-            // Return commits a tool panel (crop, remove, red eye, masking): back to Edit
+            // Return commits a tool panel (crop, remove, red eye, masking): back to the panel before it
             use RightPanel::*;
             if app.ui.dialog.is_none() && matches!(app.ui.right, Crop | Remove | RedEye | Masking) {
                 let _ = app.session.end_interaction();
                 app.ui.tool.clear();
-                app.ui.right = Edit;
+                // back to the panel shown before the tool (hidden if it was)
+                app.ui.right = app.ui.leave_tool();
             }
             Ok(Value::Null)
         }
diff --git a/crates/ui-egui/src/panels/right.rs b/crates/ui-egui/src/panels/right.rs
--- a/crates/ui-egui/src/panels/right.rs
+++ b/crates/ui-egui/src/panels/right.rs
@@ -28,6 +28,12 @@ use info::info;
 pub(crate) use info::mixed_meta;
 use keywords::keywords;
 
+/// The right column's scroll area for `panel`: one per panel, the same with or without a selected photo (the
+/// Keywords panel shows its list either way).
+fn scroll_salt(panel: RightPanel) -> (&'static str, RightPanel) {
+    ("right-scroll", panel)
+}
+
 pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) {
     let t = Tokens::get(ui.ctx());
     let frame = egui::Frame::NONE.fill(t.chrome).stroke(egui::Stroke::new(1.0, t.divider));
@@ -39,7 +45,7 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) {
             if app.ui.right == RightPanel::Keywords {
                 // (without a selection there is no Add keyword box for Add Keywords to put the cursor in)
                 app.ui.focus_keywords = None;
-                egui::ScrollArea::vertical().id_salt("right-scroll").auto_shrink([false, false]).show(ui, |ui| {
+                egui::ScrollArea::vertical().id_salt(scroll_salt(RightPanel::Keywords)).auto_shrink([false, false]).show(ui, |ui| {
                     ui.spacing_mut().item_spacing.y = 0.0;
                     if fold_header(app, ui, "keywording", "Keywords") {
                         padded(ui, |ui| {
@@ -59,7 +65,8 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) {
             }
             return;
         };
-        let mut area = egui::ScrollArea::vertical().id_salt("right-scroll").auto_shrink([false, false]);
+        // each panel keeps its own scroll position: Edit is where it was after Masking or Crop
+        let mut area = egui::ScrollArea::vertical().id_salt(scroll_salt(app.ui.right)).auto_shrink([false, false]);
         if let Some(offset) = held {
             area = area.vertical_scroll_offset(offset);
         }
diff --git a/crates/ui-egui/src/panels/right/tests.rs b/crates/ui-egui/src/panels/right/tests.rs
--- a/crates/ui-egui/src/panels/right/tests.rs
+++ b/crates/ui-egui/src/panels/right/tests.rs
@@ -30,3 +30,14 @@ fn short_times() {
     assert_eq!(super::short_time("2026-09-30T12:00:05"), "Sep 30, 2026, 12:00 PM");
     assert_eq!(super::short_time("2026-01-02"), "Jan 2, 2026");
 }
+
+// Feature: the Keywords panel keeps its scroll position when a photo is selected (CodeRabbit on #3031: the
+// no-selection list scrolled under another id)
+#[test]
+fn the_keywords_panel_scrolls_under_one_id_with_or_without_a_selection() {
+    use crate::state::RightPanel;
+    // (`show` names the no-selection list's scroll area by `scroll_salt(RightPanel::Keywords)`, as it names the
+    // panel's with a selection by `scroll_salt(app.ui.right)`)
+    assert_eq!(super::scroll_salt(RightPanel::Keywords), ("right-scroll", RightPanel::Keywords));
+    assert_ne!(super::scroll_salt(RightPanel::Keywords), super::scroll_salt(RightPanel::Edit), "each panel keeps its own");
+}
diff --git a/crates/ui-egui/src/state.rs b/crates/ui-egui/src/state.rs
--- a/crates/ui-egui/src/state.rs
+++ b/crates/ui-egui/src/state.rs
@@ -50,6 +50,18 @@ pub struct UiState {
     /// The right panel F8 (`view.rightPanel`) hid, which the next F8 shows again.
     #[serde(skip)]
     pub right_hidden: RightPanel,
+    /// The panel on show before a tool panel ([`RightPanel::is_tool`]) took its place, put back
+    /// when the tool closes ([`RightPanel::None`]: the panel was hidden, and stays hidden).
+    #[serde(skip)]
+    pub tool_return: Option<RightPanel>,
+    /// The panel a toggle (a panel key or button: I, M, R, Q…) opened, with what the right column showed before it
+    /// and that one's [`UiState::tool_return`]: toggling it off puts back exactly that, closed included (test notes
+    /// 2026-10-24 note 21). Forgotten once another panel is shown.
+    #[serde(skip)]
+    pub toggle_return: Option<(RightPanel, RightPanel, Option<RightPanel>)>,
+    /// The panel [`UiState::note_right`] last saw.
+    #[serde(skip)]
+    pub right_seen: RightPanel,
     /// Widths of the left sidebar and of the right panel in points (dragging their inner edge
     /// resizes them; within [`LEFT_WIDTH`] / [`RIGHT_WIDTH`]).
     pub left_width: f32,
@@ -354,6 +366,72 @@ pub struct UiState {
     pub print: crate::print_view::PrintUi,
 }
 
+impl UiState {
+    /// Notes the panel on show: a tool panel opened from another panel (or from none) remembers
+    /// that one, to put it back when the tool closes; any other panel forgets it.
+    pub fn note_right(&mut self) {
+        if self.right == self.right_seen {
+            return;
+        }
+        if self.toggle_return.is_some_and(|(opened, ..)| opened != self.right) {
+            self.toggle_return = None;
+        }
+        if self.right.is_tool() {
+            if !self.right_seen.is_tool() {
+                self.tool_return = Some(self.right_seen);
+            }
+        } else {
+            self.tool_return = None;
+        }
+        self.right_seen = self.right;
+    }
+
+    /// The panel to show when the tool panel on show closes: the one shown before it, or none
+    /// when the panel was hidden (Edit when nothing is known).
+    pub fn leave_tool(&mut self) -> RightPanel {
+        self.note_right();
+        if let Some(back) = self.untoggle() {
+            return back;
+        }
+        let back = self.tool_return.take().unwrap_or(RightPanel::Edit);
+        if back.is_tool() { RightPanel::Edit } else { back }
+    }
+
+    /// A panel toggle (`panel.info`, `panel.masking`…) for `p`: when `p` is on show (`exposed`: not hidden behind
+    /// another dock tab) it goes and the right column shows exactly what it showed before the toggle opened it, or
+    /// stays closed when it was (note 21); otherwise `p` comes up and what was there is remembered. Returns whether
+    /// `p` is now on.
+    pub fn toggle_panel(&mut self, p: RightPanel, exposed: bool) -> bool {
+        self.note_right();
+        if self.right == p && exposed {
+            self.right = match self.untoggle() {
+                Some(back) => back,
+                None if p.is_tool() => self.leave_tool(),
+                None => RightPanel::None,
+            };
+            self.note_right();
+            false
+        } else {
+            let saved = (p, if exposed || self.right != p { self.right } else { RightPanel::None }, self.tool_return);
+            self.right = p;
+            self.note_right();
+            self.toggle_return = Some(saved);
+            true
+        }
+    }
+
+    /// What the toggle that opened the panel on show replaced (see [`UiState::toggle_panel`]), with its own tool
+    /// memory put back, or `None` when that panel wasn't opened by a toggle.
+    fn untoggle(&mut self) -> Option<RightPanel> {
+        let (_, before, tool_return) = self.toggle_return.filter(|(opened, ..)| *opened == self.right)?;
+        self.toggle_return = None;
+        self.tool_return = tool_return;
+        // seen already: `note_right` must not take the panel being left for the one this came back from
+        self.right_seen = before;
+        Some(before)
+    }
+}
+
 impl Default for UiState {
     fn default() -> Self {
         UiState {
@@ -371,6 +449,9 @@ impl Default for UiState {
             right_width: RIGHT_WIDTH.default,
             right: RightPanel::Edit,
             right_hidden: RightPanel::None,
+            tool_return: None,
+            toggle_return: None,
+            right_seen: RightPanel::Edit,
             presets: false,
             preset_thumbs: false,
             filmstrip: true,
diff --git a/crates/ui-egui/src/state/view.rs b/crates/ui-egui/src/state/view.rs
--- a/crates/ui-egui/src/state/view.rs
+++ b/crates/ui-egui/src/state/view.rs
@@ -19,7 +19,7 @@ pub enum ViewMode {
 }
 
 /// The right-hand tool/panel shown next to the tool strip.
-#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub enum RightPanel {
     #[default]
@@ -38,6 +38,12 @@ pub enum RightPanel {
 }
 
 impl RightPanel {
+    /// A tool that takes the panel's place while it is used (Crop, Remove, Masking, Red Eye): on
+    /// closing, the panel shown before it comes back ([`UiState::leave_tool`]).
+    pub fn is_tool(self) -> bool {
+        matches!(self, RightPanel::Crop | RightPanel::Remove | RightPanel::Masking | RightPanel::RedEye)
+    }
+
     pub fn is_edit_tool(self) -> bool {
         matches!(self, RightPanel::Edit | RightPanel::Profiles | RightPanel::Crop | RightPanel::Remove | RightPanel::Masking | RightPanel::RedEye)
     }
diff --git a/crates/ui-egui/src/tests.rs b/crates/ui-egui/src/tests.rs
--- a/crates/ui-egui/src/tests.rs
+++ b/crates/ui-egui/src/tests.rs
@@ -53,6 +53,7 @@ mod merge_preview;
 mod model_setup;
 mod offline;
 mod open_lightroom;
+mod panel_restore;
 pub(super) mod panels;
 mod preset_favorites;
 mod preset_import;
diff --git a/crates/ui-egui/src/tests/docking_acceptance.rs b/crates/ui-egui/src/tests/docking_acceptance.rs
--- a/crates/ui-egui/src/tests/docking_acceptance.rs
+++ b/crates/ui-egui/src/tests/docking_acceptance.rs
@@ -198,6 +198,7 @@ fn dock_tab_keeps_activation_keys_and_global_command_remains_reachable() {
     let mut h = app();
     h.app.run("app.setShortcut", json!({"id":"panel.crop","shortcut":"K"})).unwrap();
     dock(&mut h, json!({"operation":"move","panel":"tools","target":"library","zone":"center"}));
+    let before = h.app.ui.right;
     h.app.run("panel.crop", json!({})).unwrap();
     h.step();
     // The test chooses the renderer's actual registered tab, rather than an arbitrary focus owner.
@@ -207,7 +208,9 @@ fn dock_tab_keeps_activation_keys_and_global_command_remains_reachable() {
     key(&mut h, egui::Key::Enter);
     assert_eq!(h.app.ui.right, RightPanel::Crop, "Enter activates the tab without tool.done");
     key(&mut h, egui::Key::K);
-    assert_eq!(h.app.ui.right, RightPanel::None, "the global crop toggle is reachable from tab focus");
+    // toggled off, Crop gives back the panel it took the place of (KEY-INFO / UiState::toggle_panel)
+    assert_eq!(h.app.ui.right, before, "the global crop toggle is reachable from tab focus");
+    assert_ne!(before, RightPanel::Crop);
 }
 
 #[test]
diff --git a/crates/ui-egui/src/tests/panel_restore.rs b/crates/ui-egui/src/tests/panel_restore.rs
new file mode 100644
--- /dev/null
+++ b/crates/ui-egui/src/tests/panel_restore.rs
@@ -0,0 +1,168 @@
+//! The panel a tool takes the place of comes back when the tool closes (item 12 of the evening
+//! retest): open Edit → Masking (or Crop) → close it → Edit again, at the same width and scroll
+//! position, with the photo where it was; a hidden panel stays hidden.
+
+use std::time::Duration;
+
+use serde_json::json;
+
+use crate::headless::Headless;
+use crate::state::RightPanel;
+use crate::{LightcraftApp, Services};
+
+const T: Duration = Duration::from_secs(20);
+const SETTLE: Duration = Duration::from_secs(120);
+
+fn detail() -> Headless {
+    let services = Services { png: None, ..Default::default() };
+    let app = LightcraftApp::new(lightcraft_engine::Session::with_demo(), services);
+    let mut h = Headless::new(app, [1400.0, 900.0], 1.0);
+    let r = h.request("ui.set", json!({"view": "detail", "right": "edit"}), T);
+    assert_eq!(r["ok"], true, "{r}");
+    h.settle(SETTLE);
+    h
+}
+
+fn exec(h: &mut Headless, command: &str) {
+    let r = h.request("engine.execute", json!({"command": command, "params": {}}), T);
+    assert_eq!(r["ok"], true, "{command}: {r}");
+    h.step();
+    h.step();
+}
+
+fn widget(h: &Headless, id: &str) -> Option<egui::Rect> {
+    h.app.widgets.iter().find(|(w, _)| w == id).map(|(_, r)| *r)
+}
+
+/// The Edit panel's first slider on show (its id and where it is: it moves when the panel scrolls).
+fn first_slider(h: &Headless) -> (String, egui::Rect) {
+    let panel = widget(h, "panel:right_panel").expect("the right panel");
+    h.app
+        .widgets
+        .iter()
+        .filter(|(w, r)| w.starts_with("slider:") && panel.contains_rect(*r))
+        .min_by(|a, b| a.1.top().total_cmp(&b.1.top()))
+        .map(|(w, r)| (w.clone(), *r))
+        .expect("a slider in the Edit panel")
+}
+
+#[test]
+fn the_edit_panel_comes_back_after_masking_and_crop_where_it_was() {
+    let mut h = detail();
+    // scroll the Edit panel down a little (the wheel scrolls what the pointer is over)
+    let panel = widget(&h, "panel:right_panel").expect("the right panel");
+    h.request("ui.move", json!({"x": panel.center().x, "y": panel.center().y}), T);
+    let r = h.request("ui.scroll", json!({"dy": -300.0}), T);
+    assert_eq!(r["ok"], true, "{r}");
+    for _ in 0..60 {
+        h.step();
+    }
+    let (slider, at) = first_slider(&h);
+    let photo = widget(&h, "canvas:image").expect("the photo");
+    for tool in ["panel.masking", "panel.crop", "panel.remove"] {
+        exec(&mut h, tool);
+        assert!(h.app.ui.right.is_tool(), "{tool}: {:?}", h.app.ui.right);
+        assert_eq!(widget(&h, "panel:right_panel"), Some(panel), "{tool}: the panel keeps its place and width");
+        // the same button again closes the tool: Edit is back, as it was
+        exec(&mut h, tool);
+        assert_eq!(h.app.ui.right, RightPanel::Edit, "{tool}: the Edit panel comes back");
+        assert_eq!(widget(&h, "panel:right_panel"), Some(panel), "{tool}: same place and width");
+        assert_eq!(widget(&h, "canvas:image"), Some(photo), "{tool}: the photo didn't move");
+        assert_eq!(widget(&h, &slider), Some(at), "{tool}: Edit kept its scroll position");
+    }
+    // Masking, then Crop from it, then Crop closed: exactly what was there before, Masking (note 21); then Edit
+    exec(&mut h, "panel.masking");
+    exec(&mut h, "panel.crop");
+    exec(&mut h, "panel.crop");
+    assert_eq!(h.app.ui.right, RightPanel::Masking);
+    exec(&mut h, "panel.masking");
+    assert_eq!(h.app.ui.right, RightPanel::Edit);
+    // Return (Done) in a tool also goes back
+    exec(&mut h, "panel.masking");
+    exec(&mut h, "tool.done");
+    assert_eq!(h.app.ui.right, RightPanel::Edit);
+}
+
+#[test]
+fn a_hidden_panel_stays_hidden_after_a_tool() {
+    let mut h = detail();
+    exec(&mut h, "panel.close");
+    assert_eq!(h.app.ui.right, RightPanel::None);
+    let photo = widget(&h, "canvas:image").expect("the photo");
+    for tool in ["panel.masking", "panel.crop"] {
+        exec(&mut h, tool);
+        assert!(h.app.ui.right.is_tool(), "{tool} shows");
+        exec(&mut h, tool);
+        assert_eq!(h.app.ui.right, RightPanel::None, "{tool}: nothing shows again");
+        h.settle(SETTLE);
+        assert_eq!(widget(&h, "canvas:image"), Some(photo), "{tool}: the photo is where it was");
+    }
+    exec(&mut h, "panel.masking");
+    exec(&mut h, "tool.done");
+    assert_eq!(h.app.ui.right, RightPanel::None, "Done: hidden as before");
+    // a panel picked meanwhile is what comes back: Info → Masking → closed = Info
+    exec(&mut h, "panel.info");
+    exec(&mut h, "panel.masking");
+    exec(&mut h, "panel.masking");
+    assert_eq!(h.app.ui.right, RightPanel::Info);
+}
+
+/// "I" toggles the Info panel; pressing it again brings back exactly what the right column showed before, closed
+/// included (test notes 2026-10-24 note 21: the Edit sliders stayed gone).
+#[test]
+fn info_toggled_off_brings_back_exactly_what_was_there() {
+    let mut h = detail();
+    assert_eq!(h.app.ui.right, RightPanel::Edit);
+    // the key, as the user pressed it
+    let key = |h: &mut Headless| {
+        let r = h.request("ui.key", json!({"key": "I"}), T);
+        assert_eq!(r["ok"], true, "{r}");
+        h.step();
+        h.step();
+    };
+    key(&mut h);
+    assert_eq!(h.app.ui.right, RightPanel::Info);
+    key(&mut h);
+    assert_eq!(h.app.ui.right, RightPanel::Edit, "Edit → I → I = Edit");
+    exec(&mut h, "panel.masking");
+    key(&mut h);
+    assert_eq!(h.app.ui.right, RightPanel::Info);
+    key(&mut h);
+    assert_eq!(h.app.ui.right, RightPanel::Masking, "Masks → I → I = Masks");
+    // and Masks still closes back to Edit
+    exec(&mut h, "panel.masking");
+    assert_eq!(h.app.ui.right, RightPanel::Edit);
+    for panel in ["panel.keywords", "panel.versions", "panel.activity"] {
+        exec(&mut h, panel);
+        exec(&mut h, panel);
+        assert_eq!(h.app.ui.right, RightPanel::Edit, "{panel} toggled off: Edit again");
+    }
+    exec(&mut h, "panel.close");
+    key(&mut h);
+    assert_eq!(h.app.ui.right, RightPanel::Info);
+    key(&mut h);
+    assert_eq!(h.app.ui.right, RightPanel::None, "closed → I → I = closed");
+}
+
+/// A `ui.set` that changes something else (here the panel width) keeps a pending toggle: I still puts Edit back
+/// (CodeRabbit on #3031).
+#[test]
+fn a_ui_set_between_the_two_presses_keeps_the_toggle() {
+    let mut h = detail();
+    exec(&mut h, "panel.info");
+    assert_eq!(h.app.ui.right, RightPanel::Info);
+    let r = h.request("ui.set", json!({"rightWidth": 320.0}), T);
+    assert_eq!(r["ok"], true, "{r}");
+    h.step();
+    exec(&mut h, "panel.info");
+    assert_eq!(h.app.ui.right, RightPanel::Edit, "Edit → I → ui.set → I = Edit");
+}
+
+/// A tool panel restored from the saved UI state (the panel before it unknown) closes to Edit, not to a hidden column.
+#[test]
+fn a_tool_restored_from_saved_state_closes_to_edit() {
+    let mut ui: crate::state::UiState = serde_json::from_str(r#"{"right": "crop"}"#).unwrap();
+    assert_eq!(ui.right, RightPanel::Crop);
+    ui.note_right();
+    assert_eq!(ui.leave_tool(), RightPanel::Edit);
+}
diff --git a/docs/parity-checklist.md b/docs/parity-checklist.md
--- a/docs/parity-checklist.md
+++ b/docs/parity-checklist.md
@@ -1,6 +1,6 @@
 # Lightroom parity checklist
 
-> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-25 · **Change:** minor (LR-BEHAV-TITLEBAR: a double-click follows the system setting, and a panel over the bar keeps the window still; KEYC-METADATA, LRC-LIB-KEYWORDS: ⌥0 / ⌥⇧0 go to the next / previous keyword set; LRC-DEV-HISTOGRAM-DRAG: the histogram's five tonal zones — hover for the value, drag to adjust the Basic control the press locked, double-click to reset; LR-VIEW-HISTOGRAM note updated; MENU-BAR: on macOS a menu item that yields its key to a focused text field or to shortcut recording really lets it go; LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings; KEYC-METADATA: ⌘K / Ctrl+K Add Keywords; LRC-DEV-HISTOGRAM-DRAG: the histogram's five tonal zones — hover for the value, drag to adjust the Basic control the press locked, double-click to reset; LR-VIEW-HISTOGRAM note updated; MENU-BAR: on macOS a menu item that yields its key to a focused text field or to shortcut recording really lets it go; LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6
+> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-26 · **Change:** minor (LR-BEHAV-PANEL-RESIZE, KEY-INFO: closing a tool panel or toggling a panel off puts back exactly what the right column showed before; LR-BEHAV-TITLEBAR: a double-click follows the system setting, and a panel over the bar keeps the window still; KEYC-METADATA, LRC-LIB-KEYWORDS: ⌥0 / ⌥⇧0 go to the next / previous keyword set; LRC-DEV-HISTOGRAM-DRAG: the histogram's five tonal zones — hover for the value, drag to adjust the Basic control the press locked, double-click to reset; LR-VIEW-HISTOGRAM note updated; MENU-BAR: on macOS a menu item that yields its key to a focused text field or to shortcut recording really lets it go; LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings; KEYC-METADATA: ⌘K / Ctrl+K Add Keywords; LRC-DEV-HISTOGRAM-DRAG: the histogram's five tonal zones — hover for the value, drag to adjust the Basic control the press locked, double-click to reset; LR-VIEW-HISTOGRAM note updated; MENU-BAR: on macOS a menu item that yields its key to a focused text field or to shortcut recording really lets it go; LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6
 
 The measured, row-by-row checklist: one row per Lightroom feature, menu item and shortcut, what we
 have and what is missing. It answers *does the feature exist?* (feature breadth). Whether it is good
@@ -474,7 +474,7 @@ differences and missing bindings are in [ui-parity.md](ui-parity.md#shortcuts).
 | LR-BEHAV-SIDEBAR-HIDE | Show and hide left-sidebar sections | P2 | ✅ | `cmd:view.sidebarSection`, `cmd:view.sidebarReset`, `crates/ui-egui/src/sidebar.rs`, `crates/ui-egui/src/panels/left.rs` (`sections_menu`), `crates/ui-egui/src/menus/menubar.rs`, `crates/ui-egui/src/tests/panels.rs` | View ▸ Sidebar Sections lists Albums, Local, By Date, Folders and Keywords in the sidebar's order, each checked while shown; unchecking one takes its header and rows off the sidebar, checking it puts it back where and how it was (still folded if it was). The same list opens with a right-click on any section header (with Hide This Section first; widgets `sidebarMenu:hide`, `sidebarMenu:section:<id>`, `sidebarMenu:reset`) and on the My Photos title (`sidebarTitle`), which is always there, so hidden sections can always come back. Reset Sidebar Sections (enabled once something changed) shows and opens every section in the usual order. `view.sidebarSection` `{section, show?, collapsed?}` flips shown / hidden when neither flag is given and refuses an unknown section or a flag that is not true / false. Hiding the section of what the grid shows (an album, a keyword filter) leaves the grid as it is; an album made while Albums is hidden or folded brings Albums back, open. Kept with the UI state (`sidebar`). My Photos itself can't be hidden |
 | LR-BEHAV-SIDEBAR-REORDER | Reorder left-sidebar sections | P2 | ✅ | `cmd:view.sidebarMoveSection`, `crates/ui-egui/src/sidebar.rs` (`move_before`, `move_by`), `crates/ui-egui/src/panels/left.rs` (`section_drag`, `section_drop_before`, `section_drag_feedback`), `crates/ui-egui/src/tests/panels.rs` | drag a section by its header: its name follows the pointer, a line (`sidebarSectionDrop`) shows the gap it would go to (above the section whose upper half the pointer is over, or below the last), releasing over the sidebar moves it there, Esc or releasing elsewhere leaves it; the sidebar scrolls when the drag nears its top or bottom edge, also over a pinned header (a section picked up by the pinned header scrolls once the pointer has left it). A press on a header's button is never a section drag. A click on the header still folds the section, and an album dragged onto the Albums header is still an album drag. The header's menu has Move Up / Move Down (`sidebarMenu:moveUp` / `moveDown`), one shown section at a time, stepping over hidden ones, disabled at the ends. `view.sidebarMoveSection` `{section, direction: up\|down}` or `{section, before: <section>\|null}` (null: last) answers `{moved, order}`. The order is kept with the UI state (`sidebar`). Recently Deleted is listed with My Photos (below Picks / Missing Photos), not after the sections. Not here: dragging My Photos, or rows from one section into another |
 | LR-BEHAV-SIDEBAR-STICKY | Pinned section header and section look | P2 | ✅ | `crates/ui-egui/src/panels/left.rs` (`pinned_header`, `section_header`, `empty_hint`), `crates/ui-egui/src/tests/panels.rs` | while the rows of an open section are in view but its header has scrolled away, a copy of the header (`sidebarSectionPinned:<id>`, with the section's buttons and menu) stays at the top of the sidebar; the next section's header pushes it out; folding from it brings the section's own header into view. Rows scrolled beneath the pinned header are covered by it: an album or photos dropped on the pinned Albums header go to the top level / nowhere, never to the row under it; folding from it keeps the sideways scroll. Sections are told apart by a hairline in the gap above each; the fold chevron sits in the gutter, in the column of the rows' triangles (`sidebarSectionChevron:<id>`); a header highlights under the pointer; a folded section shows how many top-level rows it holds (`sidebarSectionCount:<id>`, with View ▸ Photo Counts); a count of 0 is fainter. Not here: a height of its own per section (one scroll area serves the whole sidebar), a count for Recently Added, Local's Browse Folder… as a header button |
-| LR-BEHAV-PANEL-RESIZE | Resizable side panels | P1 | ✅ | `crates/ui-egui/src/panels.rs` (`resizable_side`), `crates/ui-egui/src/state.rs` (`LEFT_WIDTH`, `RIGHT_WIDTH`), `crates/ui-egui/src/tests/panels.rs` | drag the left sidebar's right edge (200–480 pt) or the right panel's left edge (250–520 pt); the photo area keeps ≥ 360 pt; widths are kept across panels, views and restarts (`leftWidth` / `rightWidth` in the UI state); the Presets column stays fixed |
+| LR-BEHAV-PANEL-RESIZE | Resizable side panels | P1 | ✅ | `crates/ui-egui/src/panels.rs` (`resizable_side`), `crates/ui-egui/src/state.rs` (`LEFT_WIDTH`, `RIGHT_WIDTH`), `crates/ui-egui/src/tests/panels.rs`, `crates/ui-egui/src/tests/panel_restore.rs` | closing a tool panel (Crop, Remove, Masking, Red Eye) puts back the panel shown before it in the same frame, at the same width and scroll position, or leaves it hidden if it was (`UiState::leave_tool`); drag the left sidebar's right edge (200–480 pt) or the right panel's left edge (250–520 pt); the photo area keeps ≥ 360 pt; widths are kept across panels, views and restarts (`leftWidth` / `rightWidth` in the UI state); the Presets column stays fixed |
 | LR-BEHAV-EMPTY-STATES | Empty states | P1 | ✅ | `crates/ui-egui/src/panels.rs` (`empty_message`, `empty_state`), `crates/ui-egui/src/panels/empty_library.rs`, `cmd:file.addPhotos`, `cmd:file.importLightroom`, `crates/ui-egui/src/tests/empty_library.rs` | each view says why it is empty (no photo selected, filters, an empty folder). A library with no photos (none, or only deleted or browsed ones) opens on the Photo Grid (Square Grid stays) with the sidebar, and the grid and Detail view (not while a folder is browsed under Local) offer Import Photos… (primary) and Import Lightroom Catalog… (desktop) as buttons, with a line saying photos and folders can be dropped into the window and the key Import Photos… is bound to now; the right-hand panel says to import photos first. The top bar has an Import button beside Export in every library. Files dragged over the window dim it and a card says what dropping will do; a drop of photos or folders opens the same import review as File ▸ Import |
 | LR-BEHAV-TOOLTIPS | Tooltips with shortcuts | P0 | ✅ | `crates/ui-egui/src/panels/bottombar.rs` | |
 | LR-BEHAV-TEXTFIELD | Text fields behave alike | P1 | 🟡 | `crates/ui-egui/src/widgets/text_field.rs`, `crates/ui-egui/src/panels/topbar.rs`, `crates/ui-egui/src/widgets.rs`, `crates/ui-egui/src/panels/right.rs` (`meta_field`), `crates/ui-egui/src/dialogs.rs`, `crates/ui-egui/src/panels/unnamed.rs` | one shared field: right-click Cut / Copy / Paste / Select All (no Paste in the browser), Esc gives back the text from before the edit, Return / leaving / Esc reported to the caller, optional select-on-focus; agents use `ui.clipboard` (MCP `clipboard`) and `ui.inspect` → `copied`. Used by Search Photos, slider values, the Info panel's metadata fields (multi-line captions), the one-field dialogs (New / Rename Album, New Smart Album, Rename / Merge Keywords, text prompts) and the unnamed faces' name field so far; the other text fields (most dialogs, import, settings, masking, keywords …) still use egui's own and have no menu |
@@ -605,7 +605,7 @@ From `06-shortcuts.md` part 1. Evidence is our binding; conflicts are explained
 | KEY-FULLSCREEN | Full-screen preview — F | P1 | ✅ | `cmd:view.fullScreenPreview` | |
 | KEY-LIGHTSOUT | Lights Out — Shift+L (Classic's L is Linear Gradient here) | P2 | ✅ | `cmd:view.lightsOut` | On → Dim (80 %) → Off; Esc turns the lights on |
 | KEY-GRID | Grid — G | P0 | ✅ | `cmd:view.gridToggle`, `cmd:view.photoGrid` | G toggles Photo Grid ↔ Square Grid; both View menu rows show its live shortcut (including remapping/unbinding), with direct grid bindings taking priority; menu clicks still choose the named grid (#2500) |
-| KEY-INFO | Info — I | P0 | ✅ | `cmd:panel.info` | |
+| KEY-INFO | Info — I | P0 | ✅ | `cmd:panel.info`, `crates/ui-egui/src/tests/panel_restore.rs` | pressing I again brings back exactly what the right column showed before (Edit, Masks…; closed stays closed); every panel key and button does the same (`UiState::toggle_panel`) |
 | KEY-KEYWORDS | Keywords — K | P0 | ✅ | `cmd:panel.keywords` | |
 | KEY-CLIPBOARD | Copy / paste edit settings — ⌘C / ⌘V | P0 | ✅ | `cmd:develop.copy`, `cmd:develop.paste`, `crates/ui-egui/src/shortcuts.rs` (`matches`) | ⌘X has nothing to cut outside text fields; on Windows and Linux, where the windowing layer sends Ctrl+C / Ctrl+V as clipboard events rather than key presses, those run the shortcuts too (⇧Insert pastes plainly); there Ctrl+V only arrives while the system clipboard holds text (egui-winit drops it otherwise), so with an empty clipboard Paste Settings needs the menu |
 | KEY-UNDOREDO | Undo / redo — ⌘Z / ⇧⌘Z | P0 | ✅ | `cmd:edit.undo`, `cmd:edit.redo` | |
@@ -681,7 +681,7 @@ From `06-shortcuts.md` part 1. Evidence is our binding; conflicts are explained
 | KEY-HELP | Help — F1 | P2 | ✅ | `cmd:app.help` | |
 | KEY-VIDEO-PLAY | Play/pause video — Space | P1 | ⬜ | | |
 | KEY-ESC | Leave tool / view — Esc | P0 | ✅ | `cmd:view.back`, `crates/ui-egui/src/icons.rs` (`navigation_arrowheads_have_both_halves`), `crates/ui-egui/src/tests/crop_tool.rs` | top-bar navigation arrowheads render both halves at 1× and 2× scale; in the crop tool Esc closes the tool first (the crop stays), the next Esc goes back to the grid; Esc that ends an edit in a text field does nothing else |
-| KEY-COMMIT | Commit tool — Return | P1 | ✅ | `cmd:tool.done` | Return closes Crop / Remove / Red Eye / Masking back to Edit (edits apply live, as in Lightroom) |
+| KEY-COMMIT | Commit tool — Return | P1 | ✅ | `cmd:tool.done` | Return closes Crop / Remove / Red Eye / Masking and goes back to the panel shown before the tool, the right column hidden again if it was (edits apply live, as in Lightroom) |
 | KEY-DELETE-PIN | Delete selected pin — ⌫ | P0 | ✅ | `cmd:mask.delete`, `cmd:spot.delete` | ⌫ deletes the active mask (Masking) or the selected spot (Remove), never the photo while retouching |
 | KEY-HIDEPINS | Hide pins — H | P2 | ✅ | `cmd:view.toolOverlay`, `crates/ui-egui/src/tests/tool_overlay.rs` | ⇧H cycles the Tool Overlay (Auto → Always → Selected → Never) for spot and mask pins, since H opens Remove; also View ▸ Tool Overlay and the Remove / Masking panels (#224) |
 
@@ -799,6 +799,7 @@ Latin stays upright. Advanced Japanese composition remains open. Regression cove
 
 | Date | Change | Summary |
 |---|---|---|
+| 2026-10-26 | minor | LR-BEHAV-PANEL-RESIZE, KEY-INFO: closing a tool panel (Crop, Remove, Masking, Red Eye) puts back the panel shown before it, hidden if it was hidden, at the same width and scroll position; a panel key or button (I, M, C, H…) pressed again puts back exactly what the right column showed before it (Edit → I → I = Edit, closed → I → I = closed); status unchanged (✅) |
 | 2026-10-25 | patch | LR-BEHAV-TITLEBAR: the top bar's title-bar gestures come from craft-window's `title_bar`: a double-click follows the system setting, and a press on a button, the search field or a panel floating over the bar never moves the window; status counts unchanged |
 | 2026-10-24 | minor | KEYC-METADATA, LRC-LIB-KEYWORDS: ⌥0 / ⌥⇧0 (`keyword.nextSet`, `keyword.previousSet`) go to the next / previous keyword set; status counts unchanged (KEYC-METADATA stays 🟡) |
 | 2026-10-24 | minor | KEYC-METADATA: ⌘K / Ctrl+K (`panel.addKeywords`, Add Keywords) shows the Keywords panel, opens Keywording and puts the cursor in the Add keyword box; status counts unchanged (KEYC-METADATA stays 🟡) |
diff --git a/docs/whats-new.md b/docs/whats-new.md
--- a/docs/whats-new.md
+++ b/docs/whats-new.md
@@ -1,5 +1,13 @@
 # What's new in LightCraft
 
+## 2026-10-26
+
+### Panels come back as they were
+- Close Masking, Crop, Remove or Red Eye and the panel you came from is there again, at the same width and scroll
+  position, and the photo doesn't move; if the panel was hidden, it stays hidden. Pressing a panel key a second time
+  (I for Info, M for Masking, C for Crop, H for Remove) or its tool-strip button puts back exactly what the right
+  column showed before: Edit → I → I is Edit again, Masks → I → I is Masks, and a closed panel stays closed.
+
 ## 2026-10-24
 
 ### Relative-date filters and smart albums follow the clock
