diff --git a/crates/ui-egui/locales/bn.toml b/crates/ui-egui/locales/bn.toml
--- a/crates/ui-egui/locales/bn.toml
+++ b/crates/ui-egui/locales/bn.toml
@@ -1163,7 +1163,6 @@ status = "draft — needs native review"
 "Resolve" = "সমাধান করুন"
 "Resolve change" = "পরিবর্তন সমাধান করুন"
 "Resolved" = "সমাধান করা হয়েছে"
-"Resources" = "রিসোর্সসমূহ"
 "Responsive" = "রেসপনসিভ"
 "Restart" = "পুনরায় শুরু করুন"
 "Restore" = "পুনরুদ্ধার করুন"
diff --git a/crates/ui-egui/locales/cs.toml b/crates/ui-egui/locales/cs.toml
--- a/crates/ui-egui/locales/cs.toml
+++ b/crates/ui-egui/locales/cs.toml
@@ -1141,7 +1141,6 @@ status = "draft — needs native review"
 "Resolve" = "Vyřešit"
 "Resolve change" = "Vyřešit změnu"
 "Resolved" = "Vyřešené"
-"Resources" = "Zdroje"
 "Responsive" = "Responzivní"
 "Restart" = "Znovu spustit"
 "Restore" = "Obnovit"
diff --git a/crates/ui-egui/locales/de.toml b/crates/ui-egui/locales/de.toml
--- a/crates/ui-egui/locales/de.toml
+++ b/crates/ui-egui/locales/de.toml
@@ -1111,7 +1111,6 @@ status = "draft — needs native review"
 "Resolve" = "Erledigen"
 "Resolve change" = "Änderung erledigen"
 "Resolved" = "Erledigt"
-"Resources" = "Ressourcen"
 "Responsive" = "Responsiv"
 "Restart" = "Neu starten"
 "Restore" = "Wiederherstellen"
diff --git a/crates/ui-egui/locales/en.toml b/crates/ui-egui/locales/en.toml
--- a/crates/ui-egui/locales/en.toml
+++ b/crates/ui-egui/locales/en.toml
@@ -1198,7 +1198,6 @@ name = "English"
 "Resolve" = "Resolve"
 "Resolve change" = "Resolve change"
 "Resolved" = "Resolved"
-"Resources" = "Resources"
 "Responsive" = "Responsive"
 "Restart" = "Restart"
 "Restore" = "Restore"
diff --git a/crates/ui-egui/locales/es.toml b/crates/ui-egui/locales/es.toml
--- a/crates/ui-egui/locales/es.toml
+++ b/crates/ui-egui/locales/es.toml
@@ -1194,7 +1194,6 @@ status = "draft — needs native review"
 "Resolve" = "Resolver"
 "Resolve change" = "Resolver cambio"
 "Resolved" = "Resuelto"
-"Resources" = "Recursos"
 "Responsive" = "Adaptable"
 "Restart" = "Reiniciar"
 "Restore" = "Restaurar"
diff --git a/crates/ui-egui/locales/fi.toml b/crates/ui-egui/locales/fi.toml
--- a/crates/ui-egui/locales/fi.toml
+++ b/crates/ui-egui/locales/fi.toml
@@ -1167,7 +1167,6 @@ status = "draft — needs native review"
 "Resolve" = "Ratkaise"
 "Resolve change" = "Ratkaise muutos"
 "Resolved" = "Ratkaistu"
-"Resources" = "Resurssit"
 "Responsive" = "Mukautuva"
 "Restart" = "Aloita uudelleen"
 "Restore" = "Palauta"
diff --git a/crates/ui-egui/locales/fr.toml b/crates/ui-egui/locales/fr.toml
--- a/crates/ui-egui/locales/fr.toml
+++ b/crates/ui-egui/locales/fr.toml
@@ -1168,7 +1168,6 @@ status = "draft — needs native review"
 "Resolve" = "Résoudre"
 "Resolve change" = "Résoudre la modification"
 "Resolved" = "Résolu"
-"Resources" = "Ressources"
 "Responsive" = "Adaptatif"
 "Restart" = "Recommencer"
 "Restore" = "Restaurer"
diff --git a/crates/ui-egui/locales/id.toml b/crates/ui-egui/locales/id.toml
--- a/crates/ui-egui/locales/id.toml
+++ b/crates/ui-egui/locales/id.toml
@@ -1147,7 +1147,6 @@ status = "draft — needs native review"
 "Resolve" = "Selesaikan"
 "Resolve change" = "Selesaikan perubahan"
 "Resolved" = "Terselesaikan"
-"Resources" = "Sumber daya"
 "Responsive" = "Responsif"
 "Restart" = "Mulai ulang"
 "Restore" = "Pulihkan"
diff --git a/crates/ui-egui/locales/it.toml b/crates/ui-egui/locales/it.toml
--- a/crates/ui-egui/locales/it.toml
+++ b/crates/ui-egui/locales/it.toml
@@ -1128,7 +1128,6 @@ status = "draft — needs native review"
 "Resolve" = "Risolvi"
 "Resolve change" = "Risolvi la modifica"
 "Resolved" = "Risolto"
-"Resources" = "Risorse"
 "Responsive" = "Responsive"
 "Restart" = "Riavvia"
 "Restore" = "Ripristina"
diff --git a/crates/ui-egui/locales/ja.toml b/crates/ui-egui/locales/ja.toml
--- a/crates/ui-egui/locales/ja.toml
+++ b/crates/ui-egui/locales/ja.toml
@@ -1155,7 +1155,6 @@ status = "draft — needs native review"
 "Resolve" = "解決"
 "Resolve change" = "変更を解決"
 "Resolved" = "解決済み"
-"Resources" = "リソース"
 "Responsive" = "レスポンシブ"
 "Restart" = "再起動"
 "Restore" = "復元"
diff --git a/crates/ui-egui/locales/ka.toml b/crates/ui-egui/locales/ka.toml
--- a/crates/ui-egui/locales/ka.toml
+++ b/crates/ui-egui/locales/ka.toml
@@ -1165,7 +1165,6 @@ status = "draft — reviewed by a native speaker, awaiting a maintainer's approv
 "Resolve" = "გადაწყვეტა"
 "Resolve change" = "ცვლილების გადაწყვეტა"
 "Resolved" = "გადაწყვეტილია"
-"Resources" = "რესურსები"
 "Responsive" = "ადაპტური"
 "Restart" = "გადატვირთვა"
 "Restore" = "აღდგენა"
diff --git a/crates/ui-egui/locales/ko.toml b/crates/ui-egui/locales/ko.toml
--- a/crates/ui-egui/locales/ko.toml
+++ b/crates/ui-egui/locales/ko.toml
@@ -1105,7 +1105,6 @@ status = "draft — needs native review"
 "Resolve" = "해결"
 "Resolve change" = "변경 사항 해결"
 "Resolved" = "해결됨"
-"Resources" = "리소스"
 "Responsive" = "반응형"
 "Restart" = "다시 시작"
 "Restore" = "복원"
diff --git a/crates/ui-egui/locales/nl.toml b/crates/ui-egui/locales/nl.toml
--- a/crates/ui-egui/locales/nl.toml
+++ b/crates/ui-egui/locales/nl.toml
@@ -1167,7 +1167,6 @@ status = "draft — needs native review"
 "Resolve" = "Oplossen"
 "Resolve change" = "Wijziging oplossen"
 "Resolved" = "Opgelost"
-"Resources" = "Bronnen"
 "Responsive" = "Responsive"
 "Restart" = "Opnieuw starten"
 "Restore" = "Terugzetten"
diff --git a/crates/ui-egui/locales/pl.toml b/crates/ui-egui/locales/pl.toml
--- a/crates/ui-egui/locales/pl.toml
+++ b/crates/ui-egui/locales/pl.toml
@@ -1171,7 +1171,6 @@ name = "Polski"
 "Resolve" = "Rozwiąż"
 "Resolve change" = "Rozwiąż zmianę"
 "Resolved" = "Rozwiązany"
-"Resources" = "Zasoby"
 "Responsive" = "Responsywny"
 "Restart" = "Uruchom ponownie"
 "Restore" = "Przywróć"
diff --git a/crates/ui-egui/locales/pt-BR.toml b/crates/ui-egui/locales/pt-BR.toml
--- a/crates/ui-egui/locales/pt-BR.toml
+++ b/crates/ui-egui/locales/pt-BR.toml
@@ -1104,7 +1104,6 @@ status = "draft — needs native review"
 "Resolve" = "Resolver"
 "Resolve change" = "Resolver alteração"
 "Resolved" = "Resolvido"
-"Resources" = "Recursos"
 "Responsive" = "Responsivo"
 "Restart" = "Reiniciar"
 "Restore" = "Restaurar"
diff --git a/crates/ui-egui/locales/ru.toml b/crates/ui-egui/locales/ru.toml
--- a/crates/ui-egui/locales/ru.toml
+++ b/crates/ui-egui/locales/ru.toml
@@ -1171,7 +1171,6 @@ name = "Русский"
 "Resolve" = "Решить"
 "Resolve change" = "Разрешить изменение"
 "Resolved" = "Решённые"
-"Resources" = "Ресурсы"
 "Responsive" = "Адаптивно"
 "Restart" = "Начать заново"
 "Restore" = "Восстановить"
diff --git a/crates/ui-egui/locales/sv.toml b/crates/ui-egui/locales/sv.toml
--- a/crates/ui-egui/locales/sv.toml
+++ b/crates/ui-egui/locales/sv.toml
@@ -1173,7 +1173,6 @@ name = "Svenska"
 "Resolve" = "Lös"
 "Resolve change" = "Lös ändring"
 "Resolved" = "Löst"
-"Resources" = "Resurser"
 "Responsive" = "Responsiv"
 "Restart" = "Starta om"
 "Restore" = "Återställ"
diff --git a/crates/ui-egui/locales/th.toml b/crates/ui-egui/locales/th.toml
--- a/crates/ui-egui/locales/th.toml
+++ b/crates/ui-egui/locales/th.toml
@@ -1161,7 +1161,6 @@ status = "draft — needs native review"
 "Resolve" = "แก้ไขแล้ว"
 "Resolve change" = "แก้ไขการเปลี่ยนแปลง"
 "Resolved" = "แก้ไขแล้ว"
-"Resources" = "แหล่งข้อมูล"
 "Responsive" = "รีสปอนซีฟ"
 "Restart" = "เริ่มใหม่"
 "Restore" = "กู้คืน"
diff --git a/crates/ui-egui/locales/tr.toml b/crates/ui-egui/locales/tr.toml
--- a/crates/ui-egui/locales/tr.toml
+++ b/crates/ui-egui/locales/tr.toml
@@ -1145,7 +1145,6 @@ name = "Türkçe"
 "Resolve" = "Çöz"
 "Resolve change" = "Değişikliği çöz"
 "Resolved" = "Çözüldü"
-"Resources" = "Kaynaklar"
 "Responsive" = "Duyarlı"
 "Restart" = "Yeniden başlat"
 "Restore" = "Geri yükle"
diff --git a/crates/ui-egui/locales/vi.toml b/crates/ui-egui/locales/vi.toml
--- a/crates/ui-egui/locales/vi.toml
+++ b/crates/ui-egui/locales/vi.toml
@@ -1129,7 +1129,6 @@ status = "draft — needs native review"
 "Resolve" = "Giải quyết"
 "Resolve change" = "Giải quyết thay đổi"
 "Resolved" = "Đã giải quyết"
-"Resources" = "Tài nguyên"
 "Responsive" = "Đáp ứng"
 "Restart" = "Bắt đầu lại"
 "Restore" = "Khôi phục"
diff --git a/crates/ui-egui/locales/zh-Hans.toml b/crates/ui-egui/locales/zh-Hans.toml
--- a/crates/ui-egui/locales/zh-Hans.toml
+++ b/crates/ui-egui/locales/zh-Hans.toml
@@ -1113,7 +1113,6 @@ status = "draft — needs native review"
 "Resolve" = "解决"
 "Resolve change" = "解决更改"
 "Resolved" = "已解决"
-"Resources" = "资源"
 "Responsive" = "响应式"
 "Restart" = "重新开始"
 "Restore" = "恢复"
diff --git a/crates/ui-egui/src/board_picker_tests.rs b/crates/ui-egui/src/board_picker_tests.rs
--- a/crates/ui-egui/src/board_picker_tests.rs
+++ b/crates/ui-egui/src/board_picker_tests.rs
@@ -27,7 +27,7 @@ fn board() -> Ui {
 /// The templates button: left of the Design panel's button at the board toolbar's right end.
 fn templates_button(u: &Ui) -> Pos2 {
     let p = crate::toolbar::pill_rect(u.app.canvas.rect, crate::toolbar::bar_width(&u.app));
-    pos2(p.max.x - 8.0 - 32.0 - 4.0 - 16.0, p.center().y)
+    pos2(p.max.x - 8.0 - 32.0 - 8.0 - 16.0, p.center().y)
 }
 
 fn count(u: &Ui, f: impl Fn(&BoardItem) -> bool) -> usize {
diff --git a/crates/ui-egui/src/board_ui_tests.rs b/crates/ui-egui/src/board_ui_tests.rs
--- a/crates/ui-egui/src/board_ui_tests.rs
+++ b/crates/ui-egui/src/board_ui_tests.rs
@@ -39,10 +39,10 @@ fn pill(u: &Ui) -> Rect {
 }
 
 /// The left edge of board toolbar group `i` (button 32 wide, a 16-wide caret 2 after it on the
-/// groups with flyouts, 4 between): move, ink, sticky, shapes, connector, text, section, stamp,
+/// groups with flyouts, 8 between): move, ink, sticky, shapes, connector, text, section, stamp,
 /// comment.
 fn group_x(u: &Ui, i: usize) -> f32 {
-    const WIDTHS: [f32; 9] = [54.0, 54.0, 54.0, 54.0, 36.0, 36.0, 36.0, 54.0, 36.0];
+    const WIDTHS: [f32; 9] = [58.0, 58.0, 58.0, 58.0, 40.0, 40.0, 40.0, 58.0, 40.0];
     pill(u).min.x + 8.0 + WIDTHS.iter().take(i).sum::<f32>()
 }
 
diff --git a/crates/ui-egui/src/resources_tests.rs b/crates/ui-egui/src/resources_tests.rs
--- a/crates/ui-egui/src/resources_tests.rs
+++ b/crates/ui-egui/src/resources_tests.rs
@@ -1,19 +1,14 @@
-//! The toolbar's Resources button and Shift+I open the Assets panel (#710), from a collapsed
-//! sidebar and from the version history too.
+//! Shift+I opens the Assets panel (#710), from a collapsed sidebar and from the version history
+//! too — and the toolbar has no Resources button for it any more (the left rail's Assets tab,
+//! Shift+I, Alt+2 and View ▸ Panels ▸ Assets are the ways in).
 
 use egui::accesskit::Role;
-use egui::{Event, Key, Modifiers, Pos2, pos2};
+use egui::{Event, Key, Modifiers};
 use figcraft_engine::Session;
 
 use crate::LeftTab;
 use crate::layers_drag_tests::Ui;
 
-fn click(u: &mut Ui, p: Pos2) {
-    u.button(p, true);
-    u.button(p, false);
-    u.frame(vec![]);
-}
-
 fn shift_i(u: &mut Ui) {
     let modifiers = Modifiers::SHIFT;
     u.frame(vec![
@@ -23,22 +18,11 @@ fn shift_i(u: &mut Ui) {
     u.frame(vec![]);
 }
 
-/// The middle of the toolbar's Resources button, found in the AccessKit tree.
-fn resources_button(u: &mut Ui) -> Pos2 {
-    u.ctx.enable_accesskit();
-    u.frame(vec![]);
-    let out = u.frame_output(vec![]);
-    let nodes = out.platform_output.accesskit_update.expect("an AccessKit update").nodes;
-    let n = nodes.iter().map(|(_, n)| n).find(|n| n.role() == Role::Button && n.label() == Some("Resources")).expect("Resources");
-    let b = n.bounds().expect("bounds");
-    pos2(((b.x0 + b.x1) / 2.0) as f32, ((b.y0 + b.y1) / 2.0) as f32)
-}
-
 fn assets_shown(u: &Ui) -> bool {
     u.app.ui.left_tab == LeftTab::Assets && !u.app.ui.left_collapsed && !u.app.ui.versions.open
 }
 
-/// The three states the sidebar can be in when Resources is asked for.
+/// The three states the sidebar can be in when Shift+I is pressed.
 fn starts() -> Vec<(&'static str, Ui)> {
     let shown = Ui::new(Session::new());
     let mut collapsed = Ui::new(Session::new());
@@ -48,15 +32,6 @@ fn starts() -> Vec<(&'static str, Ui)> {
     vec![("layers", shown), ("collapsed", collapsed), ("version history", versions)]
 }
 
-#[test]
-fn the_resources_button_opens_the_assets_panel() {
-    for (name, mut u) in starts() {
-        let at = resources_button(&mut u);
-        click(&mut u, at);
-        assert!(assets_shown(&u), "{name}: the Assets panel is open");
-    }
-}
-
 #[test]
 fn shift_i_opens_the_assets_panel() {
     for (name, mut u) in starts() {
@@ -65,3 +40,27 @@ fn shift_i_opens_the_assets_panel() {
         assert!(assets_shown(&u), "{name}: the Assets panel is open");
     }
 }
+
+/// The toolbar's buttons, as screen readers get them.
+fn toolbar_labels(u: &mut Ui) -> Vec<String> {
+    u.ctx.enable_accesskit();
+    u.frame(vec![]);
+    let out = u.frame_output(vec![]);
+    out.platform_output
+        .accesskit_update
+        .expect("an AccessKit update")
+        .nodes
+        .iter()
+        .filter(|(_, n)| n.role() == Role::Button)
+        .filter_map(|(_, n)| n.label().map(str::to_string))
+        .collect()
+}
+
+#[test]
+fn the_toolbar_has_no_resources_button() {
+    let mut u = Ui::new(Session::new());
+    let labels = toolbar_labels(&mut u);
+    // Actions stays (the palette's button); Resources went (#1210).
+    assert!(labels.iter().any(|l| l == "Actions"), "Actions is in the toolbar: {labels:?}");
+    assert!(!labels.iter().any(|l| l == "Resources"), "no Resources button: {labels:?}");
+}
diff --git a/crates/ui-egui/src/shortcuts.rs b/crates/ui-egui/src/shortcuts.rs
--- a/crates/ui-egui/src/shortcuts.rs
+++ b/crates/ui-egui/src/shortcuts.rs
@@ -73,11 +73,6 @@ pub fn tip(label: &'static str, id: &str) -> &'static str {
     cached_tip(label, shortcut(id))
 }
 
-/// A tooltip naming a key ("Resources  ⇧I").
-pub fn key_tip(label: &'static str, key: &'static str) -> &'static str {
-    cached_tip(label, Some(key))
-}
-
 /// Panels ask for their tooltips every frame: each (label, key, platform) is built once.
 fn cached_tip(label: &'static str, key: Option<&'static str>) -> &'static str {
     type Entry = (Platform, &'static str, &'static str, &'static str);
diff --git a/crates/ui-egui/src/toolbar.rs b/crates/ui-egui/src/toolbar.rs
--- a/crates/ui-egui/src/toolbar.rs
+++ b/crates/ui-egui/src/toolbar.rs
@@ -21,6 +21,19 @@ const CARET: f32 = 14.0;
 const FLYOUT_W: f32 = 16.0;
 const FLYOUT_GAP: f32 = 2.0;
 
+// The pill's grid: every state of it (the tools, the vector and shape builder ones) is laid out on
+// one 32 pt cell, PAD in from its ends and GAP between its top-level items, the divider included.
+/// A toolbar button's size: the cell everything in the pill is placed on.
+const TOOL_CELL: f32 = 32.0;
+/// The pill's left and right padding.
+const PAD: f32 = 8.0;
+/// Between the pill's top-level items — the divider included.
+const GAP: f32 = 8.0;
+/// The divider's width.
+const DIVIDER: f32 = 1.0;
+/// The mode switcher's marks: smaller than the tools' (13–14 pt).
+const MODE_GLYPH: f32 = 14.0;
+
 /// `GLYPH`'s box centred in `r`.
 fn glyph_rect(r: Rect) -> Rect {
     Rect::from_center_size(r.center(), vec2(GLYPH, GLYPH))
@@ -129,17 +142,18 @@ pub(crate) fn bar_width(app: &FigApp) -> f32 {
     main_width(if board { BOARD_GROUPS } else { GROUPS }, board)
 }
 
-/// The main toolbar pill's width: the tool groups with their flyout carets, Resources and
-/// Actions, and the mode switcher (boards: the Design panel's button instead).
+/// The main toolbar pill's width: the tool groups with their flyout carets, Actions, the divider
+/// and the mode switcher (boards: the templates and the Design panel's buttons instead).
 fn main_width(groups: &[Group], board: bool) -> f32 {
-    let (cell, gap) = (TOOL_CELL, 4.0);
-    let carets = groups.iter().filter(|g| g.has_flyout()).count() as f32 * (FLYOUT_GAP + FLYOUT_W);
-    let end = if board { 2.0 * cell + gap + 16.0 } else { 4.0 * 34.0 + 24.0 };
-    groups.len() as f32 * (cell + gap) + carets + 2.0 * (cell + gap) + 16.0 + end
+    let tools: f32 = groups.iter().map(|g| TOOL_CELL + if g.has_flyout() { FLYOUT_GAP + FLYOUT_W } else { 0.0 }).sum();
+    // The end, and the gaps that aren't a group's own: after Actions and after the divider (and
+    // between a board's two buttons, which the end counts as one item).
+    let (end, gaps) = if board { (2.0 * TOOL_CELL, 3.0) } else { (4.0 * TOOL_CELL, 2.0) };
+    PAD * 2.0 + tools + (groups.len() as f32 + gaps) * GAP + TOOL_CELL + DIVIDER + end
 }
 
-/// The main toolbar pill: the tool groups with their flyouts, Resources and Actions, then the
-/// Draw · Design · Prototype · Inspect mode switcher (on a board, the Design panel's button).
+/// The main toolbar pill: the tool groups with their flyouts, Actions, then the
+/// Draw · Design · Prototype · Inspect mode switcher (on a board, its two buttons).
 fn main_toolbar(app: &mut FigApp, ui: &mut Ui, canvas: Rect, t: &Tokens, board: bool) {
     let groups = if board { BOARD_GROUPS } else { GROUPS };
     let width = main_width(groups, board);
@@ -148,16 +162,16 @@ fn main_toolbar(app: &mut FigApp, ui: &mut Ui, canvas: Rect, t: &Tokens, board:
     let scroll = pill_scroll(ui, rect, width);
     let outer_clip = ui.clip_rect();
     ui.set_clip_rect(rect.intersect(outer_clip));
-    let mut x = rect.min.x + 8.0 - scroll;
+    let mut x = rect.min.x + PAD - scroll;
     for g in groups {
         x = group_cell(app, ui, g, pos2(x, cy), rect.min.y, t);
     }
-    x = side_buttons(app, ui, x, cy, t);
+    x = actions_button(app, ui, x, cy, t);
     divider(ui, t, x, cy);
-    x += 8.0;
+    x += DIVIDER + GAP;
     if board {
         // Boards have no prototypes or Dev Mode; the Design panel is hidden until asked for.
-        x = crate::board_picker::button(app, ui, pos2(x, cy), rect.min.y, t);
+        x = crate::board_picker::button(app, ui, pos2(x, cy), rect.min.y, t) + GAP;
         let r = Rect::from_center_size(pos2(x + TOOL_CELL / 2.0, cy), vec2(TOOL_CELL, TOOL_CELL));
         let tip = crate::shortcuts::tip(tr("Design panel"), "view.design");
         if tool_cell(ui, t, r, "board_panel", Icon::Panels, app.ui.board_panel, tip).clicked() {
@@ -185,9 +199,6 @@ fn pill_scroll(ui: &Ui, rect: Rect, width: f32) -> f32 {
     scroll
 }
 
-/// A toolbar button's size.
-const TOOL_CELL: f32 = 32.0;
-
 /// Tool group `g` at `at` (its left edge, the pill's middle): the shown tool's button, its flyout
 /// caret and, while open, its flyout above the pill (whose top is `top`). Where the next starts.
 fn group_cell(app: &mut FigApp, ui: &mut Ui, g: &Group, at: egui::Pos2, top: f32, t: &Tokens) -> f32 {
@@ -223,7 +234,7 @@ fn group_cell(app: &mut FigApp, ui: &mut Ui, g: &Group, at: egui::Pos2, top: f32
             options_flyout(app, ui, g, pos2(r.min.x, top - 8.0));
         }
     }
-    x + 4.0
+    x + GAP
 }
 
 /// The caret after group `g`'s button (at `at`, its left edge and middle): opens or closes its
@@ -245,36 +256,23 @@ fn flyout_caret(app: &mut FigApp, ui: &mut Ui, g: &Group, at: egui::Pos2, t: &To
     }
 }
 
-/// Resources (components, plugins: the Assets panel) and the Actions palette's button, from `x`.
-/// Where the next part starts.
-fn side_buttons(app: &mut FigApp, ui: &mut Ui, mut x: f32, cy: f32, t: &Tokens) -> f32 {
-    for (icon, tip, id) in [
-        (Icon::Assets, crate::shortcuts::key_tip(tr("Resources"), "Shift+I"), "resources"),
-        (Icon::Actions, crate::shortcuts::tip(tr("Actions"), "app.actions"), "actions"),
-    ] {
-        let r = Rect::from_center_size(pos2(x + TOOL_CELL / 2.0, cy), vec2(TOOL_CELL, TOOL_CELL));
-        let resp = ui.interact(r, ui.scope_id().with(id), Sense::click()).tip(tip);
-        if id == "actions" {
-            app.ui.actions_rect = Some(r);
-        }
-        // The Actions button reads on while its panel is open; clicking it again closes it.
-        let actions_open = id == "actions" && app.ui.palette.is_some();
-        if actions_open {
-            ui.painter().rect_filled(r, CornerRadius::same(5), t.accent);
-        } else if resp.hovered() {
-            ui.painter().rect_filled(r, CornerRadius::same(5), t.hover);
-        }
-        icons::paint(ui.painter(), glyph_rect(r), icon, if actions_open { t.accent_on } else { t.icon });
-        if resp.clicked() {
-            if id == "actions" {
-                crate::menus::toggle_palette(&mut app.ui);
-            } else {
-                app.run("view.assets", json!({}));
-            }
-        }
-        x += TOOL_CELL + 4.0;
+/// The Actions palette's button, from `x`. Where the next part starts.
+fn actions_button(app: &mut FigApp, ui: &mut Ui, x: f32, cy: f32, t: &Tokens) -> f32 {
+    let r = Rect::from_center_size(pos2(x + TOOL_CELL / 2.0, cy), vec2(TOOL_CELL, TOOL_CELL));
+    let resp = ui.interact(r, ui.scope_id().with("actions"), Sense::click()).tip(crate::shortcuts::tip(tr("Actions"), "app.actions"));
+    app.ui.actions_rect = Some(r);
+    // The Actions button reads on while its panel is open; clicking it again closes it.
+    let open = app.ui.palette.is_some();
+    if open {
+        ui.painter().rect_filled(r, CornerRadius::same(5), t.accent);
+    } else if resp.hovered() {
+        ui.painter().rect_filled(r, CornerRadius::same(5), t.hover);
+    }
+    icons::paint(ui.painter(), glyph_rect(r), Icon::Actions, if open { t.accent_on } else { t.icon });
+    if resp.clicked() {
+        crate::menus::toggle_palette(&mut app.ui);
     }
-    x
+    x + TOOL_CELL + GAP
 }
 
 /// The mode switcher from `x`: Draw · Design · Prototype · Inspect (code).
@@ -286,19 +284,24 @@ fn mode_switcher(app: &mut FigApp, ui: &mut Ui, x: f32, cy: f32, t: &Tokens) {
         crate::RightTab::Prototype => 2,
         crate::RightTab::Inspect => 3,
     };
-    let seg = Rect::from_min_size(pos2(x, cy - 18.0), vec2(4.0 * 34.0 + 4.0, 36.0));
-    ui.painter().rect_filled(seg, CornerRadius::same(8), t.field);
+    let seg = Rect::from_min_size(pos2(x, cy - TOOL_CELL / 2.0), vec2(4.0 * TOOL_CELL, TOOL_CELL));
+    ui.painter().rect_filled(seg, CornerRadius::same(5), t.field);
     for (i, (icon, tip)) in modes.iter().enumerate() {
-        let r = Rect::from_min_size(pos2(seg.min.x + 2.0 + i as f32 * 34.0, seg.min.y + 2.0), vec2(34.0, 32.0));
+        let r = Rect::from_min_size(pos2(seg.min.x + i as f32 * TOOL_CELL, seg.min.y), vec2(TOOL_CELL, TOOL_CELL));
         let resp = ui.interact(r, ui.scope_id().with(("mode", i)), Sense::click()).on_hover_text(*tip);
         a11y::toggle(&resp, tip, i == active);
         if i == active {
-            ui.painter().rect_filled(r, CornerRadius::same(6), t.panel);
-            ui.painter().rect_stroke(r, CornerRadius::same(6), Stroke::new(1.0, t.border), StrokeKind::Inside);
+            // The current mode lifts with a plain inset, no outline.
+            ui.painter().rect_filled(r.shrink(2.0), CornerRadius::same(3), t.panel);
         } else if resp.hovered() {
-            ui.painter().rect_filled(r, CornerRadius::same(6), t.hover);
+            ui.painter().rect_filled(r, CornerRadius::same(5), t.hover);
         }
-        icons::paint(ui.painter(), glyph_rect(r), *icon, if i == active { t.accent_text } else { t.icon_secondary });
+        icons::paint(
+            ui.painter(),
+            Rect::from_center_size(r.center(), vec2(MODE_GLYPH, MODE_GLYPH)),
+            *icon,
+            if i == active { t.accent_text } else { t.icon_secondary },
+        );
         if resp.clicked() {
             pick_mode(app, i);
         }
@@ -359,15 +362,14 @@ fn view_only_toolbar(app: &mut FigApp, ui: &mut Ui, why: &'static str) {
         app.run("tool.set", json!({"tool": "move"}));
     }
     let label = if why == figcraft_engine::live::PUBLISHING { tr("Publishing…") } else { tr("View only") };
-    let cell = 32.0;
     let text = ui.painter().layout_no_wrap(label.to_string(), crate::theme::semibold(11.5), t.text);
     let badge_w = 12.0 + 14.0 + 6.0 + text.size().x + 12.0;
-    let width = 2.0 * (cell + 4.0) + 8.0 + 12.0 + badge_w + 8.0;
+    let width = PAD * 2.0 + 2.0 * (TOOL_CELL + GAP) + DIVIDER + GAP + badge_w;
     let rect = pill(ui, &mut app.ui.nav, app.canvas.rect, width, &t);
     let cy = rect.center().y;
-    let mut x = rect.min.x + 8.0;
+    let mut x = rect.min.x + PAD;
     for (tool, icon) in [(ToolId::Move, Icon::Move), (ToolId::Hand, Icon::Hand)] {
-        let r = Rect::from_center_size(pos2(x + cell / 2.0, cy), vec2(cell, cell));
+        let r = Rect::from_center_size(pos2(x + TOOL_CELL / 2.0, cy), vec2(TOOL_CELL, TOOL_CELL));
         let active = app.session.tool.tool == tool;
         let resp = tool_cell(ui, &t, r, ("tool", tool.id()), icon, active, &format!("{}  {}", tr(tool.label()), pretty_shortcut(tool.shortcut())));
         if active {
@@ -376,10 +378,10 @@ fn view_only_toolbar(app: &mut FigApp, ui: &mut Ui, why: &'static str) {
         if resp.clicked() {
             app.run("tool.set", json!({"tool": tool.id()}));
         }
-        x += cell + 4.0;
+        x += TOOL_CELL + GAP;
     }
     divider(ui, &t, x, cy);
-    let badge = Rect::from_min_size(pos2(x + 12.0, cy - 13.0), vec2(badge_w, 26.0));
+    let badge = Rect::from_min_size(pos2(x + DIVIDER + GAP, cy - 13.0), vec2(badge_w, 26.0));
     let resp = ui.interact(badge, ui.scope_id().with("view_only"), Sense::hover()).on_hover_text(why);
     a11y::status(&resp, label, why);
     ui.painter().rect_filled(badge, CornerRadius::same(13), t.field);
@@ -391,15 +393,14 @@ fn view_only_toolbar(app: &mut FigApp, ui: &mut Ui, why: &'static str) {
 fn edit_toolbar(app: &mut FigApp, ui: &mut Ui, current: VTool) {
     let t = Tokens::get(ui.ctx());
     let canvas = app.canvas.rect;
-    let cell = 32.0;
-    let width = VTool::ALL.len() as f32 * (cell + 4.0) + 8.0 + 12.0 + DONE_W + 8.0;
+    let width = VTool::ALL.len() as f32 * (TOOL_CELL + GAP) + PAD * 2.0 + DIVIDER + GAP + DONE_W;
     let rect = pill(ui, &mut app.ui.nav, canvas, width, &t);
     let cy = rect.center().y;
-    let mut x = rect.min.x + 8.0;
+    let mut x = rect.min.x + PAD;
     // Holding Cmd with the Move tool bends: show it.
     let held = current == VTool::Move && ui.input(|i| i.modifiers.command);
     for tool in VTool::ALL {
-        let r = Rect::from_center_size(pos2(x + cell / 2.0, cy), vec2(cell, cell));
+        let r = Rect::from_center_size(pos2(x + TOOL_CELL / 2.0, cy), vec2(TOOL_CELL, TOOL_CELL));
         let tip = if tool == VTool::Bend {
             trf("{tool}  hold {key}", &[("tool", &tr(tool.label())), ("key", &pretty_shortcut(tool.shortcut()))])
         } else {
@@ -423,7 +424,7 @@ fn edit_toolbar(app: &mut FigApp, ui: &mut Ui, current: VTool) {
         if resp.clicked() {
             app.run("vector.edit.tool", json!({"tool": tool.id()}));
         }
-        x += cell + 4.0;
+        x += TOOL_CELL + GAP;
     }
     if done_button(ui, &t, x, cy, &format!("{}  Esc", tr("Done editing"))) {
         app.run("vector.edit.end", json!({}));
@@ -433,7 +434,6 @@ fn edit_toolbar(app: &mut FigApp, ui: &mut Ui, current: VTool) {
 /// The pill shown while the shape builder is active: Merge and Remove modes, a hint and Done.
 fn builder_toolbar(app: &mut FigApp, ui: &mut Ui) {
     let t = Tokens::get(ui.ctx());
-    let cell = 32.0;
     let remove = app.session.tool.builder_remove;
     let has_regions = figcraft_engine::shapebuilder::current(&app.session).is_some_and(|a| !a.regions.is_empty());
     let alt = pretty_shortcut("Alt");
@@ -445,12 +445,12 @@ fn builder_toolbar(app: &mut FigApp, ui: &mut Ui) {
         trf("Click or drag to merge · {alt} removes", &[("alt", &alt)])
     };
     let galley = ui.painter().layout_no_wrap(hint, regular(12.0), t.text_secondary);
-    let width = 2.0 * (cell + 4.0) + 8.0 + 12.0 + galley.size().x + 12.0 + 12.0 + DONE_W + 8.0;
+    let width = PAD * 2.0 + 2.0 * (TOOL_CELL + GAP) + 2.0 * (DIVIDER + GAP) + galley.size().x + GAP + DONE_W;
     let rect = pill(ui, &mut app.ui.nav, app.canvas.rect, width, &t);
     let cy = rect.center().y;
-    let mut x = rect.min.x + 8.0;
+    let mut x = rect.min.x + PAD;
     for (mode, icon, tip) in [("merge", Icon::ShapeBuilder, tr("Merge regions")), ("remove", Icon::ShapeBuilderRemove, tr("Remove regions"))] {
-        let r = Rect::from_center_size(pos2(x + cell / 2.0, cy), vec2(cell, cell));
+        let r = Rect::from_center_size(pos2(x + TOOL_CELL / 2.0, cy), vec2(TOOL_CELL, TOOL_CELL));
         let active = (mode == "remove") == remove;
         let resp = tool_cell(ui, &t, r, ("sbmode", mode), icon, active, tip);
         if active {
@@ -459,13 +459,13 @@ fn builder_toolbar(app: &mut FigApp, ui: &mut Ui) {
         if resp.clicked() {
             app.run("shapeBuilder.mode", json!({"mode": mode}));
         }
-        x += cell + 4.0;
+        x += TOOL_CELL + GAP;
     }
     divider(ui, &t, x, cy);
-    x += 12.0;
+    x += DIVIDER + GAP;
     let w = galley.size().x;
     ui.painter().galley(pos2(x, cy - galley.size().y / 2.0), galley, t.text_secondary);
-    x += w + 12.0;
+    x += w + GAP;
     if done_button(ui, &t, x, cy, &format!("{}  {}", tr("Done"), pretty_shortcut("Enter"))) {
         app.run("tool.set", json!({"tool": "move"}));
     }
@@ -515,9 +515,10 @@ pub(crate) fn tool_cell(ui: &mut Ui, t: &Tokens, r: Rect, id: impl egui::AsIdSal
     resp
 }
 
-/// A vertical separator just after `x`.
+/// A vertical separator just after `x`: DIVIDER wide, as tall as the pill's content.
 fn divider(ui: &Ui, t: &Tokens, x: f32, cy: f32) {
-    ui.painter().line_segment([pos2(x + 2.0, cy - 14.0), pos2(x + 2.0, cy + 14.0)], Stroke::new(1.0, t.border));
+    let half = TOOL_CELL / 2.0;
+    ui.painter().line_segment([pos2(x + DIVIDER / 2.0, cy - half), pos2(x + DIVIDER / 2.0, cy + half)], Stroke::new(DIVIDER, t.border));
 }
 
 const DONE_W: f32 = 56.0;
@@ -525,7 +526,7 @@ const DONE_W: f32 = 56.0;
 /// A divider at `x` and the accent Done button after it; true when clicked.
 fn done_button(ui: &mut Ui, t: &Tokens, x: f32, cy: f32, tip: &str) -> bool {
     divider(ui, t, x, cy);
-    let done = Rect::from_min_size(pos2(x + 12.0, cy - 14.0), vec2(DONE_W, 28.0));
+    let done = Rect::from_min_size(pos2(x + DIVIDER + GAP, cy - 14.0), vec2(DONE_W, 28.0));
     let resp = ui.interact(done, ui.scope_id().with("tool_done"), Sense::click()).tip(tip);
     ui.painter().rect_filled(done, CornerRadius::same(6), if resp.hovered() { t.accent_hover } else { t.accent });
     ui.painter().text(done.center(), Align2::CENTER_CENTER, tr("Done"), crate::theme::semibold(11.5), t.accent_on);
@@ -642,12 +643,33 @@ mod tests {
         let phone = Rect::from_min_size(pos2(0.0, 0.0), vec2(375.0, 700.0));
         let pill = pill_rect(phone, full);
         assert!(pill.width() <= phone.width() - 16.0 && pill.min.x >= 8.0 && pill.max.x <= phone.max.x - 8.0, "{pill:?}");
+        // Narrower than its items, so it scrolls under a drag or the wheel (#1209).
+        assert!(full > pill.width(), "{full} in {}", pill.width());
         // A desktop canvas keeps the full width, and a degenerate one doesn't go negative.
         let wide = Rect::from_min_size(pos2(0.0, 0.0), vec2(1400.0, 800.0));
         assert_eq!(pill_rect(wide, full).width(), full);
         assert_eq!(pill_rect(Rect::from_min_size(pos2(0.0, 0.0), vec2(4.0, 4.0)), full).width(), 0.0);
     }
 
+    /// The width the pill asks for is the width its items take — the groups, Actions, the divider
+    /// and the end (the mode switcher, or a board's two buttons), PAD in from either end and one
+    /// GAP apart — so its last item ends exactly PAD from its right edge.
+    #[test]
+    fn the_pill_is_as_wide_as_its_items() {
+        let canvas = Rect::from_min_size(pos2(0.0, 0.0), vec2(1400.0, 800.0));
+        for (groups, board) in [(GROUPS, false), (BOARD_GROUPS, true)] {
+            let pill = pill_rect(canvas, main_width(groups, board));
+            let mut x = pill.min.x + PAD;
+            for g in groups {
+                x += TOOL_CELL + if g.has_flyout() { FLYOUT_GAP + FLYOUT_W } else { 0.0 } + GAP;
+            }
+            x += TOOL_CELL + GAP; // Actions
+            x += DIVIDER + GAP; // the divider
+            let end = if board { 2.0 * TOOL_CELL + GAP } else { 4.0 * TOOL_CELL };
+            assert!((x + end - (pill.max.x - PAD)).abs() < 0.01, "board {board}: {} vs {}", x + end, pill.max.x - PAD);
+        }
+    }
+
     /// The stroke widths `icon` paints with in a `size` box at `ppp` pixels per point.
     fn widths(ppp: f32, icon: Icon, size: f32) -> Vec<f32> {
         let ctx = egui::Context::default();
diff --git a/crates/ui-egui/src/widgets/board_picker.rs b/crates/ui-egui/src/widgets/board_picker.rs
--- a/crates/ui-egui/src/widgets/board_picker.rs
+++ b/crates/ui-egui/src/widgets/board_picker.rs
@@ -25,7 +25,8 @@ const GAP: f32 = 8.0;
 const TITLE_H: f32 = 26.0;
 
 /// The templates button at `at` (its left edge, the toolbar's middle) and, while open, its picker
-/// above the toolbar (whose top is `top`). Where the next button starts.
+/// above the toolbar (whose top is `top`). The button's right edge: the toolbar puts the next
+/// button a GAP after it.
 pub(crate) fn button(app: &mut FigApp, ui: &mut Ui, at: Pos2, top: f32, t: &Tokens) -> f32 {
     let r = Rect::from_min_size(pos2(at.x, at.y - 16.0), vec2(32.0, 32.0));
     let open = app.ui.tool_flyout == Some(KEY);
@@ -39,7 +40,7 @@ pub(crate) fn button(app: &mut FigApp, ui: &mut Ui, at: Pos2, top: f32, t: &Toke
     if app.ui.tool_flyout == Some(KEY) {
         picker(app, ui, r, top - 8.0, t);
     }
-    r.max.x + 4.0
+    r.max.x
 }
 
 fn area_id() -> egui::Id {
diff --git a/docs/boards-parity.md b/docs/boards-parity.md
--- a/docs/boards-parity.md
+++ b/docs/boards-parity.md
@@ -27,7 +27,7 @@ all end with the same geometry.
 | Feature | Status | Notes |
 |---|---|---|
 | Board files: File ▸ New board, the home screen's New board card, a light page | done | `file.newBoard`; the kind shows in `inspect_document`, tabs and file rows (a note mark; design files a pen nib) |
-| Board toolbar at the bottom | done | Move/Hand, Marker/Highlighter/Eraser, Sticky (colour flyout), Shapes (flyout), Connector, Text, Section, Stamp (flyout), Comment, Resources, Actions, the Design panel's button |
+| Board toolbar at the bottom | done | Move/Hand, Marker/Highlighter/Eraser, Sticky (colour flyout), Shapes (flyout), Connector, Text, Section, Stamp (flyout), Comment, Actions, then the templates and the Design panel's buttons — on the same 32 pt grid as a design file's (no Resources button, as there) |
 | Design panel hidden by default; a contextual bar above the selection | done | colour, shape, text size (S/M/L/XL), connector straight/elbow/curved and its arrow; the Design panel opens with its button or View ▸ Design panel |
 | Prototyping and Dev Mode refuse on boards | done | `proto.*`, `dev.*`, `view.prototype`, `view.devMode` say why |
 | Sticky notes | done | a square frame (200 wide, growing taller through auto layout), 8 soft colours, a soft shadow, the note's text size shrinking as it grows (24 → 11, by characters and lines), the author's name at the bottom; a click places one and starts typing; Enter or a double-click edits; Shift+Cmd/Ctrl+Enter adds the next one beside it |
diff --git a/docs/ui-parity.md b/docs/ui-parity.md
--- a/docs/ui-parity.md
+++ b/docs/ui-parity.md
@@ -43,7 +43,7 @@ interaction feel not yet compared.
 | Rename: double click, F2 / Cmd+R, Tab to the next row; several layers open Rename layers (match, regular expressions, current name and number codes, start value, preview) | done | numbered in stacking order, pinned |
 | Find layers (filter by name) | done | not covered by the documentation |
 | Pages: add, rename, drag to reorder, duplicate ("Copy of"), delete, dividers (a blank page named with a hyphen) | done | the line between Pages and Layers drags to size the pages list, kept in the preferences; a long list scrolls in its own area and a page dropped outside the visible list stays put (#272) (`layers_behaviour_tests`) |
-| Assets panel: folders from page, frames and slash names (fold away), search, thumbnails, drag onto the canvas or into frames and auto layout, Alt-drop swap, enabled libraries | done | Shift+I (Quick insert) and the toolbar's Resources open it (they didn't, #710) |
+| Assets panel: folders from page, frames and slash names (fold away), search, thumbnails, drag onto the canvas or into frames and auto layout, Alt-drop swap, enabled libraries | done | Shift+I (Quick insert), Alt+2 and View ▸ Panels ▸ Assets open it; the toolbar's Resources button was removed (the left rail's Assets tab and those shortcuts are the ways in) |
 | Hide when publishing from the Assets panel's context menu | done | a right-click on one of this file's components (also Libraries ▸ Hide when publishing for the selection, `component.hideWhenPublishing {ids?, on?}`): the model's sign of it is the name, so hiding puts a `.` before the name and showing takes the leading `.` and `_` away (one undo step, saved and synced like a rename; a variant or a layer inside hides its whole component or set); a hidden component shows a crossed eye on its tile and a dimmer name, the menu item its check mark; the next publish leaves it out and brings it back when shown again (`hide_publishing_tests`). Library components (read-only here) have no such menu |
 
 ## Selection and transform
