diff --git a/src/main/java/ru/dbotthepony/mc/otm/menu/BatteryBankMenu.java b/src/main/java/ru/dbotthepony/mc/otm/menu/BatteryBankMenu.java
index 80a82739d..a643a5ea6 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/menu/BatteryBankMenu.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/menu/BatteryBankMenu.java
@@ -20,9 +20,10 @@ public class BatteryBankMenu extends MatteryMenu {
 	protected BlockEntityBatteryBank tile;
 
 	public BatteryLevelWidget battery_widget;
+	public BatterySlot[] battery_slots = new BatterySlot[6 * 2];
 
 	public BatteryBankMenu(int p_38852_, Inventory inventory) {
-		this(p_38852_, inventory, null, new SimpleContainer(5 * 3));
+		this(p_38852_, inventory, null, new SimpleContainer(6 * 2));
 	}
 
 	public BatteryBankMenu(int p_38852_, Inventory inventory, BlockEntityBatteryBank tile, Container batteries) {
@@ -35,9 +36,12 @@ public class BatteryBankMenu extends MatteryMenu {
 			battery_widget = new BatteryLevelWidget(this, 13, 24, tile.getCapability(MatteryCapability.ENERGY).resolve().get());
 		}
 
-		for (int row = 0; row < 2; row++)
-			for (int column = 0; column < 6; column++)
-				addMainSlot(new BatterySlot(batteries, row * 6 + column, 44 + column * 18, 24 + row * 18));
+		for (int row = 0; row < 2; row++) {
+			for (int column = 0; column < 6; column++) {
+				battery_slots[row * 6 + column] = new BatterySlot(batteries, row * 6 + column, 44 + column * 18, 24 + row * 18);
+				addSlot(battery_slots[row * 6 + column]);
+			}
+		}
 
 		addInventorySlots();
 	}
@@ -49,6 +53,6 @@ public class BatteryBankMenu extends MatteryMenu {
 
 	@Override
 	protected int getWorkingSlotEnd() {
-		return 5 * 3;
+		return 6 * 2;
 	}
 }
diff --git a/src/main/java/ru/dbotthepony/mc/otm/menu/MatterCapacitorBankMenu.java b/src/main/java/ru/dbotthepony/mc/otm/menu/MatterCapacitorBankMenu.java
index 1a141de45..b8e068ef4 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/menu/MatterCapacitorBankMenu.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/menu/MatterCapacitorBankMenu.java
@@ -16,6 +16,8 @@ public class MatterCapacitorBankMenu extends MatteryMenu {
 
 	public MatterLevelWidget matter;
 
+	public MatterContainerInputSlot[] container_slots = new MatterContainerInputSlot[2 * 6];
+
 	public MatterCapacitorBankMenu(int p_38852_, Inventory inventory, BlockEntityMatterCapacitorBank tile) {
 		super(Registry.Menus.MATTER_CAPACITOR_BANK, p_38852_, inventory, tile);
 
@@ -25,11 +27,14 @@ public class MatterCapacitorBankMenu extends MatteryMenu {
 			matter = new MatterLevelWidget(this, 14, 20, tile.matter);
 		}
 
-		Container container = tile != null ? tile.matter_container : new SimpleContainer(5 * 3);
+		Container container = tile != null ? tile.matter_container : new SimpleContainer(2 * 6);
 
-		for (int row = 0; row < 2; row++)
-			for (int column = 0; column < 6; column++)
-				addMainSlot(new MatterContainerInputSlot(container, row * 6 + column, 44 + column * 18, 20 + row * 18, true, IMatterHandler.MatterDirection.BIDIRECTIONAL));
+		for (int row = 0; row < 2; row++){
+			for (int column = 0; column < 6; column++) {
+				container_slots[row * 6 + column] = new MatterContainerInputSlot(container, row * 6 + column, 44 + column * 18, 20 + row * 18, true, IMatterHandler.MatterDirection.BIDIRECTIONAL);
+				addSlot(container_slots[row * 6 + column]);
+			}
+		}
 
 		addInventorySlots();
 	}
@@ -41,6 +46,6 @@ public class MatterCapacitorBankMenu extends MatteryMenu {
 
 	@Override
 	protected int getWorkingSlotEnd() {
-		return 16;
+		return 2 * 6;
 	}
 }
diff --git a/src/main/java/ru/dbotthepony/mc/otm/menu/MatterDecomposerMenu.java b/src/main/java/ru/dbotthepony/mc/otm/menu/MatterDecomposerMenu.java
index 0863f9a24..6674deb7c 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/menu/MatterDecomposerMenu.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/menu/MatterDecomposerMenu.java
@@ -20,29 +20,38 @@ public class MatterDecomposerMenu extends PoweredMatteryMenu {
 		this(containerID, inventory, null);
 	}
 
+	public MatterySlot input;
+	public MachineOutputSlot output;
+	public ProgressGaugeWidget progress;
+
 	public MatterDecomposerMenu(int containerID, Inventory inventory, @Nullable BlockEntityMatterDecomposer tile) {
 		super(Registry.Menus.MATTER_DECOMPOSER, containerID, inventory, tile);
 		Container container = tile != null ? tile.item_container : new SimpleContainer(2);
 
 		// Вход
-		addMainSlot(new MatterySlot(container, 0, 61, 36) {
+		input = new MatterySlot(container, 0, 61, 36) {
 			@Override
 			public boolean mayPlace(ItemStack p_40231_) {
 				return MatterRegistry.hasMatterValue(p_40231_);
 			}
-		});
+		};
+
+		addSlot(input);
 
 		// Выход
-		addMainSlot(new MachineOutputSlot(container, 1, 61 + 18 + 10 + 3 + 22, 36, true, true));
+		output = new MachineOutputSlot(container, 1, 61 + 18 + 10 + 3 + 22, 36, true);
+		addSlot(output);
 
 		if (tile == null || tile.getCapability(MatteryCapability.MATTER).resolve().isEmpty()) {
 			new MatterLevelWidget(this, 22, 14);
-			new ProgressGaugeWidget(this, 61 + 18 + 3, 36);
+			progress = new ProgressGaugeWidget(this, 61 + 18 + 3, 36);
 		} else {
 			new MatterLevelWidget(this, 22, 14, tile.getCapability(MatteryCapability.MATTER).resolve().get());
-			new ProgressGaugeWidget(this, 61 + 18 + 3, 36, () -> (float) tile.getWorkProgress(), tile::cantProcessJob);
+			progress = new ProgressGaugeWidget(this, 61 + 18 + 3, 36, () -> (float) tile.getWorkProgress(), tile::cantProcessJob);
 		}
 
+		progress.noAutoParent();
+
 		addBatterySlot(14);
 		addInventorySlots();
 	}
diff --git a/src/main/java/ru/dbotthepony/mc/otm/menu/MatterReplicatorMenu.java b/src/main/java/ru/dbotthepony/mc/otm/menu/MatterReplicatorMenu.java
index 7c077b4ff..771ff9f6b 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/menu/MatterReplicatorMenu.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/menu/MatterReplicatorMenu.java
@@ -14,22 +14,29 @@ public class MatterReplicatorMenu extends PoweredMatteryMenu {
 		this(p_38852_, inventory, null);
 	}
 
+	public ProgressGaugeWidget progress;
+	public MachineOutputSlot[] output_slots = new MachineOutputSlot[3];
+
 	public MatterReplicatorMenu(int p_38852_, Inventory inventory, BlockEntityMatterReplicator tile) {
 		super(Registry.Menus.MATTER_REPLICATOR, p_38852_, inventory, tile);
 
 		Container container = tile != null ? tile.regular_slots : new SimpleContainer(3);
 
-		for (int i = 0; i < container.getContainerSize(); i++)
-			addMainSlot(new MachineOutputSlot(container, i, 64 + 18 * i, 38, true, false));
+		for (int i = 0; i < container.getContainerSize(); i++) {
+			output_slots[i] = new MachineOutputSlot(container, i, 64 + 18 * i, 38, true);
+			addSlot(output_slots[i]);
+		}
 
 		if (tile != null) {
 			new MatterLevelWidget(this, 22, 14, tile.matter);
-			new ProgressGaugeWidget(this, 38, 38, () -> (float) tile.getWorkProgress(), tile::cantProcessJob);
+			progress = new ProgressGaugeWidget(this, 38, 38, () -> (float) tile.getWorkProgress(), tile::cantProcessJob);
 		} else {
 			new MatterLevelWidget(this, 22, 14);
-			new ProgressGaugeWidget(this, 38, 38);
+			progress = new ProgressGaugeWidget(this, 38, 38);
 		}
 
+		progress.noAutoParent();
+
 		addBatterySlot(14);
 		addInventorySlots();
 	}
diff --git a/src/main/java/ru/dbotthepony/mc/otm/menu/MatterScannerMenu.java b/src/main/java/ru/dbotthepony/mc/otm/menu/MatterScannerMenu.java
index 03d7f4822..4f96a1a2f 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/menu/MatterScannerMenu.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/menu/MatterScannerMenu.java
@@ -15,24 +15,31 @@ public class MatterScannerMenu extends PoweredMatteryMenu {
 		this(p_38852_, inventory, null);
 	}
 
+	public MatterySlot input;
+	public ProgressGaugeWidget progress;
+
 	public MatterScannerMenu(int p_38852_, Inventory inventory, BlockEntityMatterScanner tile) {
 		super(Registry.Menus.MATTER_SCANNER, p_38852_, inventory, tile);
 
 		Container container = tile != null ? tile.input_slot : new SimpleContainer(1);
 
-		addMainSlot(new MatterySlot(container, 0, 64, 38) {
+		input = new MatterySlot(container, 0, 64, 38) {
 			@Override
 			public boolean mayPlace(ItemStack p_40231_) {
 				return MatterRegistry.hasMatterValue(p_40231_);
 			}
-		});
+		};
+
+		addSlot(input);
 
 		if (tile != null) {
-			new ProgressGaugeWidget(this, 88, 38, () -> (float) tile.getWorkProgress(), tile::cantProcessJob);
+			progress = new ProgressGaugeWidget(this, 88, 38, () -> (float) tile.getWorkProgress(), tile::cantProcessJob);
 		} else {
-			new ProgressGaugeWidget(this, 88, 38);
+			progress = new ProgressGaugeWidget(this, 88, 38);
 		}
 
+		progress.noAutoParent();
+
 		addBatterySlot();
 
 		addInventorySlots();
diff --git a/src/main/java/ru/dbotthepony/mc/otm/menu/PatternStorageMenu.java b/src/main/java/ru/dbotthepony/mc/otm/menu/PatternStorageMenu.java
index c9c0e7f9a..81da17864 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/menu/PatternStorageMenu.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/menu/PatternStorageMenu.java
@@ -12,14 +12,19 @@ public class PatternStorageMenu extends MatteryMenu {
 		this(p_38852_, inventory, null);
 	}
 
+	public PatternSlot[] pattern_slots = new PatternSlot[2 * 4];
+
 	public PatternStorageMenu(int p_38852_, Inventory inventory, BlockEntityPatternStorage tile) {
 		super(Registry.Menus.PATTERN_STORAGE, p_38852_, inventory, tile);
 
-		Container patterns = tile != null ? tile.patterns : new SimpleContainer(3 * 3);
+		Container patterns = tile != null ? tile.patterns : new SimpleContainer(2 * 4);
 
-		for (int row = 0; row < 2; row++)
-			for (int column = 0; column < 4; column++)
-				addMainSlot(new PatternSlot(patterns, row * 4 + column, 48 + column * 20, 27 + row * 24));
+		for (int row = 0; row < 2; row++) {
+			for (int column = 0; column < 4; column++) {
+				pattern_slots[row * 4 + column] = new PatternSlot(patterns, row * 4 + column, 48 + column * 20, 27 + row * 24);
+				addSlot(pattern_slots[row * 4 + column]);
+			}
+		}
 
 		addInventorySlots();
 	}
@@ -31,6 +36,6 @@ public class PatternStorageMenu extends MatteryMenu {
 
 	@Override
 	protected int getWorkingSlotEnd() {
-		return 3 * 3;
+		return 2 * 4;
 	}
 }
diff --git a/src/main/java/ru/dbotthepony/mc/otm/menu/slot/MachineOutputSlot.java b/src/main/java/ru/dbotthepony/mc/otm/menu/slot/MachineOutputSlot.java
index f7840f7da..e0fb8f561 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/menu/slot/MachineOutputSlot.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/menu/slot/MachineOutputSlot.java
@@ -6,16 +6,14 @@ import net.minecraft.world.item.ItemStack;
 
 public class MachineOutputSlot extends MatterySlot {
 	public boolean auto_bg;
-	public boolean is_main_output;
 
-	public MachineOutputSlot(Container p_40223_, int index, int x, int y, boolean auto_bg, boolean is_main_output) {
+	public MachineOutputSlot(Container p_40223_, int index, int x, int y, boolean auto_bg) {
 		super(p_40223_, index, x, y);
-		this.is_main_output = is_main_output;
 		this.auto_bg = auto_bg;
 	}
 
 	public MachineOutputSlot(Container p_40223_, int index, int x, int y) {
-		this(p_40223_, index, x, y, true, false);
+		this(p_40223_, index, x, y, true);
 	}
 
 	@Override
diff --git a/src/main/java/ru/dbotthepony/mc/otm/menu/widget/AbstractWidget.java b/src/main/java/ru/dbotthepony/mc/otm/menu/widget/AbstractWidget.java
index e3ed8c0e6..cee67cab5 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/menu/widget/AbstractWidget.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/menu/widget/AbstractWidget.java
@@ -29,6 +29,8 @@ public abstract class AbstractWidget {
 	public int y;
 	protected Consumer<ContainerData> register_data;
 
+	public boolean auto_parent = true;
+
 	public abstract int getImageWidth();
 	public abstract int getImageHeight();
 
@@ -42,6 +44,11 @@ public abstract class AbstractWidget {
 		menu.addWidget(this, (consumer) -> register_data = consumer);
 	}
 
+	public AbstractWidget noAutoParent() {
+		auto_parent = false;
+		return this;
+	}
+
 	protected void addDataSlots(ContainerData slots) {
 		register_data.accept(slots);
 	}
diff --git a/src/main/java/ru/dbotthepony/mc/otm/screen/AndroidStationScreen.java b/src/main/java/ru/dbotthepony/mc/otm/screen/AndroidStationScreen.java
index 11cc8b6b4..3b126fb07 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/screen/AndroidStationScreen.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/screen/AndroidStationScreen.java
@@ -113,7 +113,7 @@ public class AndroidStationScreen extends MatteryScreen<AndroidStationMenu> {
 			}
 		});
 
-		grid.setDocking(Dock.RIGHT);
+		grid.setDock(Dock.RIGHT);
 
 		return frame;
 	}
diff --git a/src/main/java/ru/dbotthepony/mc/otm/screen/BatteryBankScreen.java b/src/main/java/ru/dbotthepony/mc/otm/screen/BatteryBankScreen.java
index 48aa2ef12..ea1c1bf24 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/screen/BatteryBankScreen.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/screen/BatteryBankScreen.java
@@ -8,9 +8,30 @@ import ru.dbotthepony.mc.otm.OverdriveThatMatters;
 import ru.dbotthepony.mc.otm.menu.AndroidStationMenu;
 import ru.dbotthepony.mc.otm.menu.BatteryBankMenu;
 import ru.dbotthepony.mc.otm.menu.MatteryMenu;
+import ru.dbotthepony.mc.otm.screen.panels.Dock;
+import ru.dbotthepony.mc.otm.screen.panels.FlexGridPanel;
+import ru.dbotthepony.mc.otm.screen.panels.FramePanel;
+import ru.dbotthepony.mc.otm.screen.panels.SlotPanel;
+
+import javax.annotation.Nullable;
 
 public class BatteryBankScreen extends MatteryScreen<BatteryBankMenu> {
 	public BatteryBankScreen(BatteryBankMenu p_97741_, Inventory p_97742_, Component p_97743_) {
 		super(p_97741_, p_97742_, p_97743_);
 	}
+
+	@Nullable
+	@Override
+	protected FramePanel makeMainFrame() {
+		var frame = super.makeMainFrame();
+
+		var grid = new FlexGridPanel(this, frame, 0, 0, 0, 0);
+		grid.setDock(Dock.FILL);
+
+		for (var slot : menu.battery_slots) {
+			new SlotPanel(this, grid, 0, 0, slot);
+		}
+
+		return frame;
+	}
 }
diff --git a/src/main/java/ru/dbotthepony/mc/otm/screen/MatterCapacitorBankScreen.java b/src/main/java/ru/dbotthepony/mc/otm/screen/MatterCapacitorBankScreen.java
index 6e669f269..8cf313d31 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/screen/MatterCapacitorBankScreen.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/screen/MatterCapacitorBankScreen.java
@@ -3,9 +3,27 @@ package ru.dbotthepony.mc.otm.screen;
 import net.minecraft.network.chat.Component;
 import net.minecraft.world.entity.player.Inventory;
 import ru.dbotthepony.mc.otm.menu.MatterCapacitorBankMenu;
+import ru.dbotthepony.mc.otm.screen.panels.*;
+
+import javax.annotation.Nullable;
 
 public class MatterCapacitorBankScreen extends MatteryScreen<MatterCapacitorBankMenu> {
 	public MatterCapacitorBankScreen(MatterCapacitorBankMenu p_97741_, Inventory p_97742_, Component p_97743_) {
 		super(p_97741_, p_97742_, p_97743_);
 	}
+
+	@Nullable
+	@Override
+	protected FramePanel makeMainFrame() {
+		var frame = super.makeMainFrame();
+
+		var grid = new FlexGridPanel(this, frame, 0, 0, 0, 0);
+		grid.setDock(Dock.FILL);
+
+		for (var slot : menu.container_slots) {
+			new SlotPanel(this, grid, 0, 0, slot);
+		}
+
+		return frame;
+	}
 }
diff --git a/src/main/java/ru/dbotthepony/mc/otm/screen/MatterDecomposerScreen.java b/src/main/java/ru/dbotthepony/mc/otm/screen/MatterDecomposerScreen.java
index 19a406024..835a2b4cd 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/screen/MatterDecomposerScreen.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/screen/MatterDecomposerScreen.java
@@ -4,9 +4,28 @@ import com.mojang.blaze3d.vertex.PoseStack;
 import net.minecraft.network.chat.Component;
 import net.minecraft.world.entity.player.Inventory;
 import ru.dbotthepony.mc.otm.menu.MatterDecomposerMenu;
+import ru.dbotthepony.mc.otm.screen.panels.*;
+
+import javax.annotation.Nullable;
 
 public class MatterDecomposerScreen extends MatteryScreen<MatterDecomposerMenu> {
 	public MatterDecomposerScreen(MatterDecomposerMenu p_97741_, Inventory p_97742_, Component p_97743_) {
 		super(p_97741_, p_97742_, p_97743_);
 	}
+
+	@Nullable
+	@Override
+	protected FramePanel makeMainFrame() {
+		var frame = super.makeMainFrame();
+
+		var grid = new FlexGridPanel(this, frame, 0, 0, 0, 0);
+		grid.setDock(Dock.FILL);
+		grid.setDockPadding(0, 0, 40, 12);
+
+		new SlotPanel(this, grid, 0, 0, menu.input).setDockMargin(2, 0, 2, 0);
+		new MatteryWidgetPanel(this, grid, menu.progress).setDockMargin(2, 0, 2, 0);
+		new SlotPanel(this, grid, 0, 0, menu.output).setDockMargin(2, 0, 2, 0);
+
+		return frame;
+	}
 }
diff --git a/src/main/java/ru/dbotthepony/mc/otm/screen/MatterPanelScreen.java b/src/main/java/ru/dbotthepony/mc/otm/screen/MatterPanelScreen.java
index 52da0d43c..6d4663dc4 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/screen/MatterPanelScreen.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/screen/MatterPanelScreen.java
@@ -1,21 +1,13 @@
 package ru.dbotthepony.mc.otm.screen;
 
-import com.mojang.blaze3d.systems.RenderSystem;
-import com.mojang.blaze3d.vertex.PoseStack;
 import net.minecraft.ChatFormatting;
-import net.minecraft.client.Minecraft;
-import net.minecraft.client.gui.components.Button;
 import net.minecraft.client.gui.components.EditBox;
-import net.minecraft.client.renderer.GameRenderer;
-import net.minecraft.client.resources.sounds.SimpleSoundInstance;
 import net.minecraft.network.chat.Component;
 import net.minecraft.network.chat.TextComponent;
 import net.minecraft.network.chat.TranslatableComponent;
 import net.minecraft.resources.ResourceLocation;
-import net.minecraft.sounds.SoundEvents;
 import net.minecraft.world.entity.player.Inventory;
 import net.minecraft.world.item.ItemStack;
-import net.minecraft.world.item.Items;
 import ru.dbotthepony.mc.otm.OverdriveThatMatters;
 import ru.dbotthepony.mc.otm.capability.MatterTask;
 import ru.dbotthepony.mc.otm.capability.PatternState;
@@ -28,10 +20,6 @@ import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.UUID;
-
-import static org.lwjgl.opengl.GL11.GL_ALWAYS;
-import static org.lwjgl.opengl.GL11.GL_LESS;
 
 public class MatterPanelScreen extends MatteryScreen<MatterPanelMenu> {
 	private static final int MODAL_WIDTH = 213;
@@ -94,7 +82,7 @@ public class MatterPanelScreen extends MatteryScreen<MatterPanelMenu> {
 		});
 
 		scroll_panel.setParent(frame);
-		scroll_panel.setDocking(Dock.RIGHT);
+		scroll_panel.setDock(Dock.RIGHT);
 		scroll_panel.setupRowMultiplier(() -> {
 			if (tasks_tab.isActive()) {
 				return menu.tasks.size() / GRID_WIDTH;
@@ -110,7 +98,7 @@ public class MatterPanelScreen extends MatteryScreen<MatterPanelMenu> {
 			}
 		};
 
-		grid.setDocking(Dock.LEFT);
+		grid.setDock(Dock.LEFT);
 		grid.setDockMargin(4, 0, 0, 0);
 
 		for (int i = 0; i < GRID_WIDTH * GRID_HEIGHT; i++) {
@@ -261,15 +249,15 @@ public class MatterPanelScreen extends MatteryScreen<MatterPanelMenu> {
 			}
 		};
 
-		slot.setDocking(Dock.LEFT);
+		slot.setDock(Dock.LEFT);
 
 		var button = new ButtonPanel(this, task_frame, 0, 0, 40, 20, new TranslatableComponent("otm.container.matter_panel.close"));
-		button.setDocking(Dock.RIGHT);
+		button.setDock(Dock.RIGHT);
 		button.setDockMargin(2, 0, 0, 0);
 		button.bindOnPress(task_frame::remove);
 
 		button = new ButtonPanel(this, task_frame, 0, 0, 80, 20, new TranslatableComponent("otm.container.matter_panel.cancel_task"));
-		button.setDocking(Dock.RIGHT);
+		button.setDock(Dock.RIGHT);
 		button.setDockMargin(2, 0, 0, 0);
 		button.bindOnPress(() -> {
 			menu.requestTaskCancel(task.id());
@@ -297,10 +285,10 @@ public class MatterPanelScreen extends MatteryScreen<MatterPanelMenu> {
 		var row_3 = new EditablePanel(this, pattern_frame);
 		var row_4 = new EditablePanel(this, pattern_frame);
 
-		row_1.setDocking(Dock.TOP);
-		row_2.setDocking(Dock.TOP);
-		row_3.setDocking(Dock.TOP);
-		row_4.setDocking(Dock.TOP);
+		row_1.setDock(Dock.TOP);
+		row_2.setDock(Dock.TOP);
+		row_3.setDock(Dock.TOP);
+		row_4.setDock(Dock.TOP);
 
 		row_1.setHeight(20);
 		row_2.setHeight(20);
@@ -355,45 +343,45 @@ public class MatterPanelScreen extends MatteryScreen<MatterPanelMenu> {
 
 		var button = new ButtonPanel(this, row_1, 0, 0, 40, 20, new TranslatableComponent("otm.container.matter_panel.increase_by", 8));
 		button.bindOnPress(() -> input_amount.increase(8));
-		button.setDocking(Dock.RIGHT);
+		button.setDock(Dock.RIGHT);
 		button.setDockMargin(2, 0, 0, 0);
 
 		button = new ButtonPanel(this, row_1, 0, 0, 40, 20, new TranslatableComponent("otm.container.matter_panel.increase_by", 64));
-		button.setDocking(Dock.RIGHT);
+		button.setDock(Dock.RIGHT);
 		button.bindOnPress(() -> input_amount.increase(64));
 		button.setDockMargin(2, 0, 0, 0);
 
 		button = new ButtonPanel(this, row_1, 0, 0, 40, 20, new TranslatableComponent("otm.container.matter_panel.increase_by", 256));
-		button.setDocking(Dock.RIGHT);
+		button.setDock(Dock.RIGHT);
 		button.bindOnPress(() -> input_amount.increase(256));
 		button.setDockMargin(2, 0, 0, 0);
 
-		slot.setDocking(Dock.LEFT);
+		slot.setDock(Dock.LEFT);
 		slot.setDockMargin(0, 0, 4, 0);
-		input_amount.setDocking(Dock.FILL);
+		input_amount.setDock(Dock.FILL);
 
 		button = new ButtonPanel(this, row_3, 0, 0, 40, 20, new TranslatableComponent("otm.container.matter_panel.decrease_by", 8));
 		button.bindOnPress(() -> input_amount.increase(-8));
-		button.setDocking(Dock.RIGHT);
+		button.setDock(Dock.RIGHT);
 		button.setDockMargin(2, 0, 0, 0);
 
 		button = new ButtonPanel(this, row_3, 0, 0, 40, 20, new TranslatableComponent("otm.container.matter_panel.decrease_by", 64));
-		button.setDocking(Dock.RIGHT);
+		button.setDock(Dock.RIGHT);
 		button.bindOnPress(() -> input_amount.increase(-64));
 		button.setDockMargin(2, 0, 0, 0);
 
 		button = new ButtonPanel(this, row_3, 0, 0, 40, 20, new TranslatableComponent("otm.container.matter_panel.decrease_by", 256));
-		button.setDocking(Dock.RIGHT);
+		button.setDock(Dock.RIGHT);
 		button.bindOnPress(() -> input_amount.increase(-256));
 		button.setDockMargin(2, 0, 0, 0);
 
 		button = new ButtonPanel(this, row_4, 0, 0, 40, 20, new TranslatableComponent("otm.container.matter_panel.cancel"));
-		button.setDocking(Dock.RIGHT);
+		button.setDock(Dock.RIGHT);
 		button.bindOnPress(pattern_frame::remove);
 		button.setDockMargin(2, 0, 0, 0);
 
 		button = new ButtonPanel(this, row_4, 0, 0, 82, 20, new TranslatableComponent("otm.container.matter_panel.send"));
-		button.setDocking(Dock.RIGHT);
+		button.setDock(Dock.RIGHT);
 		button.bindOnPress(() -> {
 			int value = 1;
 
diff --git a/src/main/java/ru/dbotthepony/mc/otm/screen/MatterReplicatorScreen.java b/src/main/java/ru/dbotthepony/mc/otm/screen/MatterReplicatorScreen.java
index 1929d24fd..43ce9bad0 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/screen/MatterReplicatorScreen.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/screen/MatterReplicatorScreen.java
@@ -3,9 +3,30 @@ package ru.dbotthepony.mc.otm.screen;
 import net.minecraft.network.chat.Component;
 import net.minecraft.world.entity.player.Inventory;
 import ru.dbotthepony.mc.otm.menu.MatterReplicatorMenu;
+import ru.dbotthepony.mc.otm.screen.panels.*;
+
+import javax.annotation.Nullable;
 
 public class MatterReplicatorScreen extends MatteryScreen<MatterReplicatorMenu> {
 	public MatterReplicatorScreen(MatterReplicatorMenu p_97741_, Inventory p_97742_, Component p_97743_) {
 		super(p_97741_, p_97742_, p_97743_);
 	}
+
+	@Nullable
+	@Override
+	protected FramePanel makeMainFrame() {
+		var frame = super.makeMainFrame();
+
+		var grid = new FlexGridPanel(this, frame, 0, 0, 0, 0);
+		grid.setDock(Dock.FILL);
+		grid.setDockPadding(0, 0, 40, 12);
+
+		new MatteryWidgetPanel(this, grid, menu.progress).setDockMargin(2, 0, 2, 0);
+
+		for (var slot : menu.output_slots) {
+			new SlotPanel(this, grid, 0, 0, slot);
+		}
+
+		return frame;
+	}
 }
diff --git a/src/main/java/ru/dbotthepony/mc/otm/screen/MatterScannerScreen.java b/src/main/java/ru/dbotthepony/mc/otm/screen/MatterScannerScreen.java
index 259581340..9442e3cb6 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/screen/MatterScannerScreen.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/screen/MatterScannerScreen.java
@@ -3,9 +3,27 @@ package ru.dbotthepony.mc.otm.screen;
 import net.minecraft.network.chat.Component;
 import net.minecraft.world.entity.player.Inventory;
 import ru.dbotthepony.mc.otm.menu.MatterScannerMenu;
+import ru.dbotthepony.mc.otm.screen.panels.*;
+
+import javax.annotation.Nullable;
 
 public class MatterScannerScreen extends MatteryScreen<MatterScannerMenu> {
 	public MatterScannerScreen(MatterScannerMenu p_97741_, Inventory p_97742_, Component p_97743_) {
 		super(p_97741_, p_97742_, p_97743_);
 	}
+
+	@Nullable
+	@Override
+	protected FramePanel makeMainFrame() {
+		var frame = super.makeMainFrame();
+
+		var grid = new FlexGridPanel(this, frame, 0, 0, 0, 0);
+		grid.setDock(Dock.FILL);
+		grid.setDockPadding(0, 0, 40, 12);
+
+		new SlotPanel(this, grid, 0, 0, menu.input).setDockMargin(2, 0, 2, 0);
+		new MatteryWidgetPanel(this, grid, menu.progress).setDockMargin(2, 0, 2, 0);
+
+		return frame;
+	}
 }
diff --git a/src/main/java/ru/dbotthepony/mc/otm/screen/MatteryScreen.java b/src/main/java/ru/dbotthepony/mc/otm/screen/MatteryScreen.java
index 011e3c6f1..d68e44f21 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/screen/MatteryScreen.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/screen/MatteryScreen.java
@@ -21,11 +21,9 @@ import net.minecraft.world.inventory.Slot;
 import net.minecraft.world.item.ItemStack;
 import ru.dbotthepony.mc.otm.OverdriveThatMatters;
 import ru.dbotthepony.mc.otm.menu.MatteryMenu;
+import ru.dbotthepony.mc.otm.menu.PoweredMatteryMenu;
 import ru.dbotthepony.mc.otm.menu.slot.MatterySlot;
-import ru.dbotthepony.mc.otm.screen.panels.EditablePanel;
-import ru.dbotthepony.mc.otm.screen.panels.FramePanel;
-import ru.dbotthepony.mc.otm.screen.panels.MatteryWidgetPanel;
-import ru.dbotthepony.mc.otm.screen.panels.SlotPanel;
+import ru.dbotthepony.mc.otm.screen.panels.*;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -132,7 +130,15 @@ public abstract class MatteryScreen<T extends MatteryMenu> extends AbstractConta
 
 	@Nullable
 	protected FramePanel makeMainFrame() {
-		return new FramePanel(this, null, 0, 0, 18 * 9 + 16, 100, getTitle());
+		var frame = new FramePanel(this, null, 0, 0, 18 * 9 + 16, 100, getTitle());
+
+		if (menu instanceof PoweredMatteryMenu) {
+			// reserve 30 pixels on left
+			var reserve = new EditablePanel(this, frame, 0, 0, 30, 0);
+			reserve.setDock(Dock.LEFT);
+		}
+
+		return frame;
 	}
 
 	public ItemRenderer getItemRenderer() {
@@ -337,7 +343,8 @@ public abstract class MatteryScreen<T extends MatteryMenu> extends AbstractConta
 				addPanel(main_frame);
 
 				for (var widget : menu.mattery_widgets) {
-					new MatteryWidgetPanel(this, main_frame, widget);
+					if (widget.auto_parent)
+						new MatteryWidgetPanel(this, main_frame, widget);
 				}
 
 				for (var slot : menu.main_slots) {
diff --git a/src/main/java/ru/dbotthepony/mc/otm/screen/PatternStorageScreen.java b/src/main/java/ru/dbotthepony/mc/otm/screen/PatternStorageScreen.java
index 4fa7bb51c..76a6d733c 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/screen/PatternStorageScreen.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/screen/PatternStorageScreen.java
@@ -3,9 +3,30 @@ package ru.dbotthepony.mc.otm.screen;
 import net.minecraft.network.chat.Component;
 import net.minecraft.world.entity.player.Inventory;
 import ru.dbotthepony.mc.otm.menu.PatternStorageMenu;
+import ru.dbotthepony.mc.otm.screen.panels.Dock;
+import ru.dbotthepony.mc.otm.screen.panels.FlexGridPanel;
+import ru.dbotthepony.mc.otm.screen.panels.FramePanel;
+import ru.dbotthepony.mc.otm.screen.panels.SlotPanel;
+
+import javax.annotation.Nullable;
 
 public class PatternStorageScreen extends MatteryScreen<PatternStorageMenu> {
 	public PatternStorageScreen(PatternStorageMenu p_97741_, Inventory p_97742_, Component p_97743_) {
 		super(p_97741_, p_97742_, p_97743_);
 	}
+
+	@Nullable
+	@Override
+	protected FramePanel makeMainFrame() {
+		var frame = super.makeMainFrame();
+
+		var grid = new FlexGridPanel(this, frame, 0, 0, 0, 0);
+		grid.setDock(Dock.FILL);
+
+		for (var slot : menu.pattern_slots) {
+			new SlotPanel(this, grid, 0, 0, slot).setDockMargin(1, 1, 1, 1);
+		}
+
+		return frame;
+	}
 }
diff --git a/src/main/java/ru/dbotthepony/mc/otm/screen/panels/EditablePanel.java b/src/main/java/ru/dbotthepony/mc/otm/screen/panels/EditablePanel.java
index 18fc686c1..0415eb990 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/screen/panels/EditablePanel.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/screen/panels/EditablePanel.java
@@ -3,7 +3,6 @@ package ru.dbotthepony.mc.otm.screen.panels;
 import com.mojang.blaze3d.systems.RenderSystem;
 import com.mojang.blaze3d.vertex.PoseStack;
 import net.minecraft.client.gui.components.events.GuiEventListener;
-import ru.dbotthepony.mc.otm.OverdriveThatMatters;
 import ru.dbotthepony.mc.otm.screen.MatteryScreen;
 
 import javax.annotation.Nonnull;
@@ -17,9 +16,7 @@ import java.util.List;
  * which allows much simpler GUI composition
  */
 public class EditablePanel implements GuiEventListener {
-	public record ScreenPos(float x, float y) {
-
-	}
+	public record ScreenPos(float x, float y) {}
 
 	private EditablePanel parent;
 	public final MatteryScreen<?> screen;
@@ -83,7 +80,7 @@ public class EditablePanel implements GuiEventListener {
 
 	private final ArrayList<EditablePanel> children = new ArrayList<>();
 
-	private Dock docking = Dock.NONE;
+	private Dock dock = Dock.NONE;
 
 	public EditablePanel(@Nonnull MatteryScreen<?> screen, @Nullable EditablePanel parent, float x, float y, float width, float height) {
 		this.parent = parent;
@@ -212,15 +209,19 @@ public class EditablePanel implements GuiEventListener {
 		return false;
 	}
 
-	public void setDocking(Dock docking) {
-		if (docking != this.docking) {
-			this.docking = docking;
+	public void setDock(Dock dock) {
+		if (dock != this.dock) {
+			this.dock = dock;
 
 			if (parent != null)
 				parent.invalidateLayout();
 		}
 	}
 
+	public Dock getDock() {
+		return this.dock;
+	}
+
 	public static record DockProperty(float left, float top, float right, float bottom) {
 		public boolean equals(Object dock_margin) {
 			if (dock_margin instanceof DockProperty prop) {
@@ -248,7 +249,7 @@ public class EditablePanel implements GuiEventListener {
 		if (!dock.equals(dock_margin)) {
 			dock_margin = dock;
 
-			if (docking != Dock.NONE && parent != null)
+			if (this.dock != Dock.NONE && parent != null)
 				parent.invalidateLayout();
 
 			return true;
@@ -587,6 +588,26 @@ public class EditablePanel implements GuiEventListener {
 		return List.copyOf(children);
 	}
 
+	public List<EditablePanel> getUndockedChildren() {
+		int count = 0;
+
+		for (var child : children) {
+			if (child.dock == Dock.NONE) {
+				count++;
+			}
+		}
+
+		var list = new ArrayList<EditablePanel>(count);
+
+		for (var child : children) {
+			if (child.dock == Dock.NONE) {
+				list.add(child);
+			}
+		}
+
+		return list;
+	}
+
 	protected ArrayList<EditablePanel> getChildrenInternal() {
 		return children;
 	}
@@ -612,7 +633,7 @@ public class EditablePanel implements GuiEventListener {
 		// determine sizes
 		for (var child : children) {
 			if (child.getVisible()) {
-				switch (child.docking) {
+				switch (child.dock) {
 					case TOP -> dock_top += child.height + child.dock_margin.top;
 					case BOTTOM -> dock_bottom += child.height + child.dock_margin.bottom;
 					case LEFT -> dock_left += child.width + child.dock_margin.left;
@@ -635,7 +656,7 @@ public class EditablePanel implements GuiEventListener {
 		// apply values
 		for (var child : children) {
 			if (child.getVisible()) {
-				switch (child.docking) {
+				switch (child.dock) {
 					case TOP -> {
 						child.setPos(dock_left + child.dock_margin.left, dock_top2 + child.dock_margin.top);
 						dock_top2 += child.height + child.dock_margin.top + child.dock_margin.bottom;
@@ -665,7 +686,7 @@ public class EditablePanel implements GuiEventListener {
 
 		for (var child : children) {
 			if (child.getVisible()) {
-				if (child.docking == Dock.FILL) {
+				if (child.dock == Dock.FILL) {
 					child.setDimensions(
 						dock_left2 + child.dock_margin.left,
 						dock_top2 + child.dock_margin.top,
diff --git a/src/main/java/ru/dbotthepony/mc/otm/screen/panels/FlexGridPanel.java b/src/main/java/ru/dbotthepony/mc/otm/screen/panels/FlexGridPanel.java
new file mode 100644
index 000000000..e2157c127
--- /dev/null
+++ b/src/main/java/ru/dbotthepony/mc/otm/screen/panels/FlexGridPanel.java
@@ -0,0 +1,152 @@
+package ru.dbotthepony.mc.otm.screen.panels;
+
+import ru.dbotthepony.mc.otm.OverdriveThatMatters;
+import ru.dbotthepony.mc.otm.screen.MatteryScreen;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+public class FlexGridPanel extends EditablePanel {
+	public enum FlexAlign {
+		TOP_LEFT,
+		TOP_CENTER,
+		TOP_RIGHT,
+
+		MIDDLE_LEFT,
+		MIDDLE_CENTER,
+		MIDDLE_RIGHT,
+
+		BOTTOM_LEFT,
+		BOTTOM_CENTER,
+		BOTTOM_RIGHT
+	}
+
+	protected FlexAlign align;
+
+	public FlexGridPanel(@Nonnull MatteryScreen<?> screen, @Nullable EditablePanel parent, float x, float y, float width, float height) {
+		super(screen, parent, x, y, width, height);
+		align = FlexAlign.MIDDLE_CENTER;
+	}
+
+	public FlexAlign getAlign() {
+		return align;
+	}
+
+	public FlexGridPanel setAlign(FlexAlign align) {
+		this.align = align;
+		return this;
+	}
+
+	@Override
+	protected void performLayout() {
+		if (align == FlexAlign.MIDDLE_CENTER) {
+			// список потомков
+			var children = getUndockedChildren();
+
+			// хранит общую ширину всех потомков в ряд
+			// а так же ограничитель ширины ряда после
+			// определения количества рядов
+			float desired_width = 0;
+
+			// минимально допустимая ширина одного ряда
+			float min_width = getWidth();
+
+			// "финальная" ширина этой панели
+			float this_width = getWidth() - getDockPadding().left() - getDockPadding().right();
+
+			for (var child : children) {
+				min_width = Math.min(min_width, child.getWidth() + child.getDockMargin().left() + child.getDockMargin().right());
+				desired_width += child.getWidth() + child.getDockMargin().left() + child.getDockMargin().right();
+			}
+
+			int rows = 1;
+
+			// если общая ширина больше чем ширина панели, делим пополам пока не найдем нужную ширину и количество рядов
+			while (desired_width > this_width && desired_width > min_width) {
+				desired_width /= 2;
+				rows++;
+			}
+
+			if (desired_width < min_width) {
+				desired_width = min_width;
+			}
+
+			// ширину на середину для позиционирования по центру
+			this_width /= 2;
+
+			int index = 0;
+
+			// определение высоты всех рядов вместе
+			float total_height = 0;
+
+			for (int row = 0; row < rows; row++) {
+				float max_height = 0;
+				float total_width = 0;
+
+				for (int i = index; i < children.size(); i++) {
+					var child = children.get(i);
+					var gain_width = child.getWidth() + child.getDockMargin().left() + child.getDockMargin().right();
+
+					index = i;
+
+					if (gain_width + total_width > desired_width) {
+						break;
+					}
+
+					max_height = Math.max(max_height, child.getHeight() + child.getDockMargin().top() + child.getDockMargin().bottom());
+					total_width += gain_width;
+				}
+
+				total_height += max_height;
+			}
+
+			index = 0;
+
+			// определение точки по середине по высоте
+			float h_middle = (getHeight() - getDockPadding().bottom() - getDockPadding().top() - total_height) / 2;
+
+			OverdriveThatMatters.LOGGER.info("Общая высота {}, рядов {}, середина {}", total_height, rows, h_middle);
+
+			for (int row = 0; row < rows; row++) {
+				float max_height = 0;
+				float total_width = 0;
+
+				int until = index;
+
+				for (int i = index; i < children.size(); i++) {
+					var child = children.get(i);
+					var gain_width = child.getWidth() + child.getDockMargin().left() + child.getDockMargin().right();
+
+					until = i;
+
+					if (gain_width + total_width > desired_width) {
+						break;
+					}
+
+					max_height = Math.max(max_height, child.getHeight() + child.getDockMargin().top() + child.getDockMargin().bottom());
+					total_width += gain_width;
+				}
+
+				total_width /= 2;
+				max_height /= 2;
+
+				OverdriveThatMatters.LOGGER.info("Ряд {}, общая ширина {}, максимальная высота {}, позиция {}, c {} до {}", row, total_width * 2, max_height * 2, h_middle, index, until);
+
+				float accumulate_width = 0;
+
+				for (int i = index; i <= until; i++) {
+					var child = children.get(i);
+
+					child.setPos((int) (this_width - total_width + accumulate_width + child.getDockMargin().left()), (int) (h_middle + max_height - child.getHeight() / 2));
+					accumulate_width += child.getWidth() + child.getDockMargin().left() + child.getDockMargin().right();
+				}
+
+				h_middle += max_height * 2;
+
+				index = until;
+			}
+		}
+
+		super.performLayout();
+	}
+}
diff --git a/src/main/java/ru/dbotthepony/mc/otm/screen/panels/GridPanel.java b/src/main/java/ru/dbotthepony/mc/otm/screen/panels/GridPanel.java
index 9651ef921..6ffe4dc18 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/screen/panels/GridPanel.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/screen/panels/GridPanel.java
@@ -26,8 +26,6 @@ public class GridPanel extends EditablePanel {
 
 	@Override
 	protected void performLayout() {
-		super.performLayout();
-
 		float current_x = 0;
 		float current_y = 0;
 		float line_y = 0;
@@ -42,7 +40,7 @@ public class GridPanel extends EditablePanel {
 					break;
 				}
 
-				if (child.getVisible()) {
+				if (child.getVisible() && child.getDock() == Dock.NONE) {
 					line_y = Math.max(line_y, child.getHeight() + child.getDockMargin().top() + child.getDockMargin().bottom());
 					child.setPos(current_x + child.getDockMargin().left(), current_y + child.getDockMargin().top());
 					current_x += child.getWidth() + child.getDockMargin().left() + child.getDockMargin().right();
@@ -61,5 +59,7 @@ public class GridPanel extends EditablePanel {
 				break;
 			}
 		}
+
+		super.performLayout();
 	}
 }
diff --git a/src/main/java/ru/dbotthepony/mc/otm/screen/panels/MatteryWidgetPanel.java b/src/main/java/ru/dbotthepony/mc/otm/screen/panels/MatteryWidgetPanel.java
index bcd770eee..7c5b92a52 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/screen/panels/MatteryWidgetPanel.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/screen/panels/MatteryWidgetPanel.java
@@ -18,6 +18,13 @@ public class MatteryWidgetPanel extends EditablePanel {
 		this.widget = widget;
 	}
 
+	public MatteryWidgetPanel(@Nonnull MatteryScreen screen, @Nullable EditablePanel parent, float x, float y, AbstractWidget widget) {
+		super(screen, parent, x, y, widget.getImageWidth(), widget.getImageHeight());
+		widget.x = 0;
+		widget.y = 0;
+		this.widget = widget;
+	}
+
 	@Override
 	protected void innerRender(PoseStack stack, float mouse_x, float mouse_y, float flag) {
 		widget.renderBackground(stack, 0, 0);
diff --git a/src/main/java/ru/dbotthepony/mc/otm/screen/panels/SlotPanel.java b/src/main/java/ru/dbotthepony/mc/otm/screen/panels/SlotPanel.java
index a7f3f6165..df82b5319 100644
--- a/src/main/java/ru/dbotthepony/mc/otm/screen/panels/SlotPanel.java
+++ b/src/main/java/ru/dbotthepony/mc/otm/screen/panels/SlotPanel.java
@@ -31,6 +31,10 @@ public class SlotPanel<T extends MatterySlot> extends AbstractSlotPanel {
 		this(screen, parent, slot.x, slot.y, 18, 18, slot);
 	}
 
+	public SlotPanel(@Nonnull MatteryScreen<?> screen, @Nullable EditablePanel parent, float x, float y, T slot) {
+		this(screen, parent, x, y, 18, 18, slot);
+	}
+
 	@Override
 	protected boolean mouseClickedInner(double mouse_x, double mouse_y, int button) {
 		screen.onSlotClicked(slot, button);