diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv
--- a/crates/ui-egui/src/i18n/it.tsv
+++ b/crates/ui-egui/src/i18n/it.tsv
@@ -81,6 +81,11 @@ menu	Modify	Edita
 	Array	Serie
 	Attach	Associa
 	Attribute	Attributo
+	All layouts	Tutti i layout
+	Only this one	Solo questo
+	Plot several sheets	Stampa più fogli
+	{n} sheets in one PDF, each with its own page setup (change them in the Page Setup Manager):	{n} fogli in un solo PDF, ciascuno con la propria impostazione pagina (si modificano nel Gestore impostazioni pagina):
+	{n} sheets	{n} fogli
 	Attribute Definition	Definizione di attributo
 	Attribute Display	Visualizzazione attributi
 	Audit	Controlla
diff --git a/crates/ui-egui/src/managers.rs b/crates/ui-egui/src/managers.rs
--- a/crates/ui-egui/src/managers.rs
+++ b/crates/ui-egui/src/managers.rs
@@ -808,6 +808,9 @@ struct PlotState {
     generation: u32,
     /// "Model" or a layout name.
     target: String,
+    /// The sheets OK plots, in tab order (`target` alone, until others are ticked). With several,
+    /// they go into one PDF, each with its own page setup.
+    sheets: Vec<String>,
     page: cadcraft_doc::PageSetup,
     /// Plot area "window" corners in drawing units: [x1, y1, x2, y2].
     window: [f64; 4],
@@ -835,6 +838,45 @@ fn plot_page(d: &cadcraft_doc::Drawing, target: &str) -> cadcraft_doc::PageSetup
     page
 }
 
+/// Tick or untick `name` among the sheets to plot. `targets` lists every sheet in tab order, which
+/// is the order of the result. At least one sheet always stays.
+fn toggle_sheet(sheets: &mut Vec<String>, targets: &[String], name: &str, on: bool) {
+    let next: Vec<String> = targets.iter().filter(|t| if t.as_str() == name { on } else { sheets.contains(t) }).cloned().collect();
+    if !next.is_empty() {
+        *sheets = next;
+    }
+}
+
+/// Drop the ticked sheets that no longer exist (`targets` lists those that do). With none left,
+/// `target` is the one; with exactly one left, that one is the target, which the dialog then edits
+/// and OK plots. Returns whether the target changed.
+fn sync_sheets(sheets: &mut Vec<String>, target: &mut String, targets: &[String]) -> bool {
+    sheets.retain(|n| targets.contains(n));
+    if sheets.is_empty() {
+        sheets.push(target.clone());
+    }
+    match sheets.as_slice() {
+        [only] if only != target => {
+            *target = only.clone();
+            true
+        }
+        _ => false,
+    }
+}
+
+/// The `plot` parameters for several sheets in one PDF: no device or page overrides, so each
+/// sheet plots with its own page setup.
+fn sheets_params(sheets: &[String]) -> Value {
+    json!({ "layouts": sheets, "layout": sheets.first(), "device": "None" })
+}
+
+/// One line about a sheet's page setup for the summary shown with several sheets ticked.
+fn sheet_summary(d: &cadcraft_doc::Drawing, name: &str) -> String {
+    let page = plot_page(d, name);
+    let orientation = if page.landscape { "landscape" } else { "portrait" };
+    format!("{name}: {} · {orientation} · {}", page.paper, style_label(&page))
+}
+
 /// The `plot` parameters for the dialog's settings (without the output path). `preview_dpi`
 /// asks for a PNG at that resolution instead of the device's output, on the same sheet.
 fn plot_params(st: &PlotState, preview_dpi: Option<f64>) -> Value {
@@ -918,12 +960,17 @@ fn plot(app: &mut CadApp, ctx: &egui::Context, open: &mut bool) -> Action {
         PlotState {
             generation,
             page: plot_page(d, &target),
+            sheets: vec![target.clone()],
             target,
             window: [ext.min.x, ext.min.y, ext.max.x, ext.max.y],
             center: true,
             offset_mm: [0.0, 0.0],
         }
     });
+    // Layouts deleted since the dialog was last open drop out of the ticked sheets.
+    if sync_sheets(&mut st.sheets, &mut st.target, &targets) {
+        st.page = plot_page(d, &st.target);
+    }
     let model = st.target == "Model";
     let choices = PageChoices::new(ctx, d);
     let offset_supported =
@@ -938,52 +985,98 @@ fn plot(app: &mut CadApp, ctx: &egui::Context, open: &mut bool) -> Action {
             ui.label("Layout");
             egui::ComboBox::from_id_salt("plot_layout").selected_text(st.target.as_str()).width(190.0).show_ui(ui, |ui| {
                 for n in &targets {
-                    if ui.selectable_label(*n == st.target, n).clicked() && *n != st.target {
+                    if ui.selectable_label(*n == st.target, n).clicked() && (*n != st.target || st.sheets.len() > 1) {
                         st.page = plot_page(d, n);
                         st.target = n.clone();
+                        st.sheets = vec![n.clone()];
                     }
                 }
             });
             ui.end_row();
         });
-        ui.separator();
-        ui.horizontal_top(|ui| {
-            egui::Grid::new("plot_grid").num_columns(2).spacing(vec2(10.0, 6.0)).show(ui, |ui| {
-                let rows = if model {
-                    PageRows { areas: MODEL_AREAS, scale: true, center: false }
-                } else {
-                    PageRows { areas: &["layout"], scale: false, center: false }
-                };
-                let placement = model.then_some((&mut st.center, &mut st.offset_mm, offset_supported));
-                if let Some((k, v)) = page_rows(ui, egui::Id::new(("plot", st.target.as_str())), &st.page, &rows, &choices, placement) {
-                    apply_page_change(&mut st.page, k, &v);
+        // Several sheets in one PDF: tick them here. Each plots with its own page setup.
+        egui::CollapsingHeader::new("Plot several sheets").default_open(st.sheets.len() > 1).show(ui, |ui| {
+            ui.horizontal(|ui| {
+                if ui.small_button("All layouts").on_hover_text("Every layout, in tab order (not the Model tab)").clicked() {
+                    st.sheets = targets.iter().filter(|n| *n != "Model").cloned().collect();
+                    if let Some(first) = st.sheets.first().cloned() {
+                        st.page = plot_page(d, &first);
+                        st.target = first;
+                    }
                 }
-                if model && st.page.plot_area == "window" {
-                    for (i, label) in [(0, "First corner"), (2, "Opposite corner")] {
-                        ui.label(label);
-                        ui.horizontal(|ui| {
-                            for (j, axis) in ["X", "Y"].iter().enumerate() {
-                                ui.label(*axis);
-                                let k = i + j;
-                                if let Some(slot) = st.window.get_mut(k)
-                                    && let Some(v) = num_field(ui, ("plot_window", k), *slot)
-                                {
-                                    *slot = v;
-                                }
-                            }
-                        });
-                        ui.end_row();
+                if ui.small_button("Only this one").clicked() {
+                    st.sheets = vec![st.target.clone()];
+                }
+            });
+            egui::ScrollArea::vertical().id_salt("plot_sheets").max_height(120.0).auto_shrink([true, true]).show(ui, |ui| {
+                for n in &targets {
+                    let mut on = st.sheets.contains(n);
+                    if ui.checkbox(&mut on, n).changed() {
+                        toggle_sheet(&mut st.sheets, &targets, n, on);
+                        // One sheet left: it is the one whose settings the dialog edits.
+                        if let [only] = st.sheets.as_slice()
+                            && *only != st.target
+                        {
+                            st.page = plot_page(d, only);
+                            st.target = only.clone();
+                        }
                     }
-                    ui.label("");
-                    pick = ui.button("Window <").on_hover_text("Pick the window's corners in the drawing").clicked();
-                    ui.end_row();
                 }
             });
-            sheet_sketch(ui, &st.page, &t);
         });
+        let several = st.sheets.len() > 1;
+        ui.separator();
+        if several {
+            ui.label(
+                RichText::new(crate::tf!(
+                    "{n} sheets in one PDF, each with its own page setup (change them in the Page Setup Manager):",
+                    n = st.sheets.len()
+                ))
+                .color(t.text_dim),
+            );
+            for n in &st.sheets {
+                ui.label(sheet_summary(d, n));
+            }
+        }
+        if !several {
+            ui.horizontal_top(|ui| {
+                egui::Grid::new("plot_grid").num_columns(2).spacing(vec2(10.0, 6.0)).show(ui, |ui| {
+                    let rows = if model {
+                        PageRows { areas: MODEL_AREAS, scale: true, center: false }
+                    } else {
+                        PageRows { areas: &["layout"], scale: false, center: false }
+                    };
+                    let placement = model.then_some((&mut st.center, &mut st.offset_mm, offset_supported));
+                    if let Some((k, v)) = page_rows(ui, egui::Id::new(("plot", st.target.as_str())), &st.page, &rows, &choices, placement) {
+                        apply_page_change(&mut st.page, k, &v);
+                    }
+                    if model && st.page.plot_area == "window" {
+                        for (i, label) in [(0, "First corner"), (2, "Opposite corner")] {
+                            ui.label(label);
+                            ui.horizontal(|ui| {
+                                for (j, axis) in ["X", "Y"].iter().enumerate() {
+                                    ui.label(*axis);
+                                    let k = i + j;
+                                    if let Some(slot) = st.window.get_mut(k)
+                                        && let Some(v) = num_field(ui, ("plot_window", k), *slot)
+                                    {
+                                        *slot = v;
+                                    }
+                                }
+                            });
+                            ui.end_row();
+                        }
+                        ui.label("");
+                        pick = ui.button("Window <").on_hover_text("Pick the window's corners in the drawing").clicked();
+                        ui.end_row();
+                    }
+                });
+                sheet_sketch(ui, &st.page, &t);
+            });
+        }
         ui.separator();
         ui.horizontal(|ui| {
-            if !model && ui.button("Apply to Layout").on_hover_text("Save these settings in the layout's page setup").clicked() {
+            if !model && !several && ui.button("Apply to Layout").on_hover_text("Save these settings in the layout's page setup").clicked() {
                 let page = &st.page;
                 action = Some((
                     "pagesetup",
@@ -1028,11 +1121,15 @@ fn plot(app: &mut CadApp, ctx: &egui::Context, open: &mut bool) -> Action {
         return action;
     }
     if ok {
-        let mut params = plot_params(&st, None);
+        let several = st.sheets.len() > 1;
+        let mut params = if several { sheets_params(&st.sheets) } else { plot_params(&st, None) };
+        // Several sheets always make a PDF; one sheet's file type follows its device.
+        let ext = if several { "pdf" } else { ext };
+        let what = if several { crate::tf!("{n} sheets", n = st.sheets.len()) } else { st.target.clone() };
         if let Some(pick) = app.services.pick_save.as_ref() {
             let title = app.session.state().map(|s| s.title.clone()).unwrap_or_default();
             let stem = std::path::Path::new(&title).file_stem().map(|s| s.to_string_lossy().to_string()).filter(|s| !s.is_empty());
-            let suggested = format!("{}-{}.{ext}", stem.unwrap_or_else(|| "Drawing".into()), st.target);
+            let suggested = format!("{}-{}.{ext}", stem.unwrap_or_else(|| "Drawing".into()), if several { "sheets" } else { st.target.as_str() });
             // Cancelling the file dialog keeps the Plot dialog open with its settings.
             let Some(path) = pick(&suggested) else { return action };
             if let Some(o) = params.as_object_mut() {
@@ -1043,8 +1140,8 @@ fn plot(app: &mut CadApp, ctx: &egui::Context, open: &mut bool) -> Action {
         match app.run("plot", params) {
             Ok(r) => {
                 let msg = match r.get("path").and_then(Value::as_str) {
-                    Some(path) => format!("Plotted {} to {path}", st.target),
-                    None => format!("Plotted {} ({} bytes of PDF; there is no file dialog here)", st.target, r["bytes"]),
+                    Some(path) => format!("Plotted {what} to {path}"),
+                    None => format!("Plotted {what} ({} bytes of PDF; there is no file dialog here)", r["bytes"]),
                 };
                 app.session.echo(msg);
             }
@@ -1149,6 +1246,7 @@ mod plot_dialog_tests {
         PlotState {
             generation: 0,
             page: plot_page(&d, target),
+            sheets: vec![target.into()],
             target: target.into(),
             window: [0.0, 0.0, 10.0, 10.0],
             center: true,
@@ -1256,3 +1354,74 @@ mod plot_dialog_tests {
         assert_eq!(temp::<PlotState>(&ctx, "plot_state").unwrap().window, [2.0, 3.0, 12.0, 9.0]);
     }
 }
+
+#[cfg(test)]
+mod plot_sheets_tests {
+    use super::*;
+
+    /// Model and three layouts, in tab order.
+    fn targets() -> Vec<String> {
+        ["Model", "Layout1", "Layout2", "Sheet 3"].map(String::from).to_vec()
+    }
+
+    /// Sheets of deleted layouts drop out; one left becomes the target.
+    #[test]
+    fn deleted_sheets_drop_out_and_a_single_sheet_left_is_the_target() {
+        let t = targets();
+        let names = |v: &[&str]| v.iter().map(|s| s.to_string()).collect::<Vec<_>>();
+        // Nothing deleted: nothing changes, whatever the target (it may have been unticked).
+        let (mut sheets, mut target) = (names(&["Layout1", "Layout2"]), "Sheet 3".to_string());
+        assert!(!sync_sheets(&mut sheets, &mut target, &t));
+        assert_eq!((sheets.as_slice(), target.as_str()), (names(&["Layout1", "Layout2"]).as_slice(), "Sheet 3"));
+        // One of three ticked layouts is deleted while the target was not among them: two are left.
+        let (mut sheets, mut target) = (names(&["Layout1", "Gone", "Layout2"]), "Sheet 3".to_string());
+        assert!(!sync_sheets(&mut sheets, &mut target, &t));
+        assert_eq!(sheets, ["Layout1", "Layout2"]);
+        // One left: it becomes the target, so the dialog's single sheet is what OK plots.
+        let (mut sheets, mut target) = (names(&["Gone", "Layout2"]), "Layout1".to_string());
+        assert!(sync_sheets(&mut sheets, &mut target, &t));
+        assert_eq!((sheets, target.as_str()), (names(&["Layout2"]), "Layout2"));
+        // None left: the target alone.
+        let (mut sheets, mut target) = (names(&["Gone", "Also gone"]), "Layout1".to_string());
+        assert!(!sync_sheets(&mut sheets, &mut target, &t));
+        assert_eq!((sheets, target.as_str()), (names(&["Layout1"]), "Layout1"));
+    }
+    /// Ticking keeps tab order and leaves at least one sheet.
+    #[test]
+    fn ticking_sheets_keeps_tab_order_and_at_least_one() {
+        let t = targets();
+        let mut sheets = vec!["Layout2".to_string()];
+        toggle_sheet(&mut sheets, &t, "Model", true);
+        toggle_sheet(&mut sheets, &t, "Sheet 3", true);
+        assert_eq!(sheets, ["Model", "Layout2", "Sheet 3"], "tab order, not the order they were ticked");
+        toggle_sheet(&mut sheets, &t, "Layout2", false);
+        assert_eq!(sheets, ["Model", "Sheet 3"]);
+        toggle_sheet(&mut sheets, &t, "Model", false);
+        toggle_sheet(&mut sheets, &t, "Sheet 3", false);
+        assert_eq!(sheets, ["Sheet 3"], "the last sheet can't be unticked");
+        toggle_sheet(&mut sheets, &t, "Nope", true);
+        assert_eq!(sheets, ["Sheet 3"], "an unknown name changes nothing");
+    }
+
+    /// Several sheets plot with no page overrides, so each uses its own page setup.
+    #[test]
+    fn several_sheets_plot_with_their_own_page_setups_into_one_pdf() {
+        let p = sheets_params(&["Layout1".to_string(), "Layout2".to_string()]);
+        assert_eq!(p, json!({ "layouts": ["Layout1", "Layout2"], "layout": "Layout1", "device": "None" }));
+        // No page overrides: each sheet keeps its paper, orientation, style table and lineweights.
+        for k in ["paper", "width", "height", "landscape", "plotStyleTable", "lineweights", "plotTransparency", "fit"] {
+            assert!(p.get(k).is_none(), "{k} would apply to every sheet");
+        }
+    }
+
+    /// The summary names each sheet's paper and style table.
+    #[test]
+    fn the_summary_names_each_sheets_paper_and_style() {
+        let mut app = crate::CadApp::new(cadcraft_engine::Session::default(), crate::Services::default());
+        app.run("pagesetup", json!({ "layout": "Layout1", "paper": "A3", "plotStyleTable": "monochrome.ctb" })).unwrap();
+        let d = app.session.doc().unwrap();
+        let line = sheet_summary(d, "Layout1");
+        assert!(line.starts_with("Layout1: ") && line.contains("A3") && line.ends_with("monochrome.ctb"), "{line}");
+        assert!(sheet_summary(d, "Model").starts_with("Model: "));
+    }
+}
diff --git a/docs/gaps.md b/docs/gaps.md
--- a/docs/gaps.md
+++ b/docs/gaps.md
@@ -1,6 +1,6 @@
 # Where CADCraft falls short of AutoCAD
 
-> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-16 · **Change:** minor (Command Aliases dialog for ALIASEDIT: U12 and E5 narrowed) · **Target:** Autodesk AutoCAD 2027
+> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-24 · **Change:** minor (The Plot dialog plots several sheets into one PDF) · **Target:** Autodesk AutoCAD 2027
 
 Every known shortfall, one row each, ranked within each section by user impact. This is the work
 list: agents pick from here (top of a section first), and remove or update a row in the same PR
@@ -150,6 +150,7 @@ Detail: [localization-parity.md](localization-parity.md).
 
 | Date | Change | Summary |
 |---|---|---|
+| 2026-10-24 | minor | The Plot dialog has "Plot several sheets": the ticked layouts (or all of them) go into one PDF, one page each, every sheet with its own page setup (the engine's `layouts` list) |
 | 2026-10-22 | minor | APPLOAD typed or from Tools ▸ Load Application opens a `.lsp` file browser starting in the last-used folder (kept in the preferences); a picked file loads interactively like a typed name (routines that ask for input wait), the web says to drop the file (no file system), and the JSON form `{path}` reads the path as given and returns the commands the file defined, never opening a dialog (#1820): E1 narrowed |
 | 2026-10-16 | minor | ALIASEDIT typed or from Tools ▸ Customize opens the Command Aliases dialog (user aliases next to the built-in ones; Add, Edit, Remove; OK, Cancel, Apply), #1381: U12 and E5 narrowed |
 | 2026-10-16 | minor | MOCORO (Tools ▸ Express Tools ▸ Move/Copy/Rotate): select objects and a base point once, then Move, Copy, Rotate, Scale, Base and Undo until eXit, one undo step; JSON form `{base, ops}`: E3 narrowed |
