git.lucas.co / cce-system-interface
system settings
git clone https://git.lucas.co/cce-system-interface.git

commit5df4413bc6e3482883ec3a78f649820ea3107777
parent6f5b5d047d
authorLucas Galante <[email protected]>
date2026-06-05 20:13
refactor: rename colors page to interface, supporting general interface settings and dynamic scale factor

 src/app.rs                            |  14 +-
 src/main.rs                           | 340 ++++++++++-----
 src/pages/accounts.rs                 | 535 ++++++++++++-----------
 src/pages/audio.rs                    | 226 +++++-----
 src/pages/backup.rs                   | 125 +++---
 src/pages/display.rs                  | 145 +++---
 src/pages/hardware.rs                 | 381 ++++++++--------
 src/pages/input.rs                    | 203 +++++----
 src/pages/{colors.rs => interface.rs} | 329 ++++++++++----
 src/pages/layout.rs                   | 799 ++++++++--------------------------
 src/pages/mod.rs                      |   8 +-
 src/pages/network.rs                  | 328 ++++++++++----
 src/pages/notifications.rs            | 106 +++--
 src/pages/screensaver.rs              |  77 ++--
 src/pages/services.rs                 | 372 ++++++++--------
 src/pages/status.rs                   | 125 +++---
 src/pages/storage.rs                  | 102 ++---
 src/pages/system_info.rs              |  87 ++--
 src/pages/typeface.rs                 | 565 ++++++++++++------------
 19 files changed, 2513 insertions(+), 2354 deletions(-)

diff --git a/src/app.rs b/src/app.rs
index b2f764d..9a45c1e 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1,4 +1,4 @@
-use clear_ui::layout::RenderTarget;
+use clear_ui::layout::{RenderTarget, Radial};
 
 use crate::pages::audio;
 use crate::pages::display;
@@ -13,7 +13,7 @@ use crate::pages::system_info;
 use crate::pages::backup;
 use crate::pages::typeface;
 use crate::pages::services;
-use crate::pages::colors;
+use crate::pages::interface;
 use crate::pages::screensaver;
 use crate::pages::accounts;
 use crate::pages::Page;
@@ -33,7 +33,7 @@ pub struct AppState {
     pub backup: backup::BackupState,
     pub typeface: typeface::TypefaceState,
     pub services: services::ServicesState,
-    pub colors: colors::ColorsState,
+    pub interface: interface::InterfaceState,
     pub screensaver: screensaver::ScreensaverState,
     pub accounts: accounts::AccountsState,
 }
@@ -55,7 +55,7 @@ impl Default for AppState {
             backup: backup::BackupState::default(),
             typeface: typeface::TypefaceState::default(),
             services: services::ServicesState::default(),
-            colors: colors::ColorsState::default(),
+            interface: interface::InterfaceState::default(),
             screensaver: screensaver::read_screensaver_config(),
             accounts: accounts::AccountsState::default_mock(),
         }
@@ -77,12 +77,13 @@ pub enum AppAction {
     Backup(backup::BackupMessage),
     Typeface(typeface::TypefaceMessage),
     Services(services::ServicesMessage),
-    Colors(colors::ColorsMessage),
+    Interface(interface::InterfaceMessage),
     Screensaver(screensaver::ScreensaverMessage),
     Accounts(accounts::AccountsMessage),
 }
 
 
+#[derive(Default)]
 pub struct PageContent {
     pub rects: Vec<([f32; 4], f32, f32, f32, f32)>,
     pub texts: Vec<(String, f32, f32, f32, [f32; 4], Option<String>, Option<[f32; 4]>)>,
@@ -162,3 +163,6 @@ impl RenderTarget for PageContent {
         self.texts.push((content.to_string(), size, x, y, color, Some(font.to_string()), bounds));
     }
 }
+
+
+
diff --git a/src/main.rs b/src/main.rs
index af2af1a..c60d28c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -206,7 +206,7 @@ struct SystemInterface {
     rx_backup_state: std::sync::mpsc::Receiver<pages::backup::BackupState>,
     rx_typeface: std::sync::mpsc::Receiver<pages::typeface::TypefaceState>,
     rx_services: std::sync::mpsc::Receiver<Vec<pages::services::ServiceInfo>>,
-    rx_colors: std::sync::mpsc::Receiver<pages::colors::ColorsState>,
+    rx_interface: std::sync::mpsc::Receiver<pages::interface::InterfaceState>,
     rx_accounts: std::sync::mpsc::Receiver<Vec<pages::accounts::AccountInfo>>,
     tx_backup: std::sync::mpsc::Sender<pages::backup::BackupMessage>,
     rx_backup: std::sync::mpsc::Receiver<pages::backup::BackupMessage>,
@@ -233,6 +233,7 @@ impl clear_ui::engine::Application for SystemInterface {
     type Message = AppAction;
 
     fn new(_qh: &wayland_client::QueueHandle<clear_ui::engine::EngineState<Self>>, sender: calloop::channel::Sender<Self::Message>) -> Self {
+        clear_ui::scale::set_scale_factor(1.0);
         let app = AppState {
             layout: pages::layout::read_layout_config(),
             input: pages::input::read_input_config(),
@@ -365,11 +366,11 @@ impl clear_ui::engine::Application for SystemInterface {
         let rx_typeface = spawn_bg(30, || pages::typeface::fetch_typeface_state());
         let rx_services = spawn_bg(3, || pages::services::fetch_services());
         let rx_accounts = spawn_bg(3, || pages::accounts::fetch_accounts());
-        let rx_colors = {
-            let (tx, rx) = std::sync::mpsc::channel::<pages::colors::ColorsState>();
+        let rx_interface = {
+            let (tx, rx) = std::sync::mpsc::channel::<pages::interface::InterfaceState>();
             tokio::spawn(async move {
                 loop {
-                    let val = tokio::task::spawn_blocking(|| pages::colors::read_colors_config()).await;
+                    let val = tokio::task::spawn_blocking(|| pages::interface::read_interface_config()).await;
                     if let Ok(val) = val { if tx.send(val).is_err() { break; } }
                     tokio::time::sleep(std::time::Duration::from_secs(30)).await;
                 }
@@ -383,7 +384,8 @@ impl clear_ui::engine::Application for SystemInterface {
 
         let pages_names = Page::ALL.iter().map(|p| p.label().to_string()).collect::<Vec<_>>();
         let paginator = clear_ui::widget::Paginator::new(56.0, pages_names)
-            .with_tabs_rotated(true);
+            .with_tabs_rotated(true)
+            .with_sidebar_label("SYSTEM");
 
         let initial_page_idx = INITIAL_PAGE_INDEX.load(std::sync::atomic::Ordering::SeqCst);
         let mut app_state = app;
@@ -397,7 +399,7 @@ impl clear_ui::engine::Application for SystemInterface {
             widgets: Vec::new(),
             text_items: Vec::new(),
             page_buttons: Vec::new(),
-            sidebar_width: 56.0,
+            sidebar_width: paginator.sidebar_w(),
             header_height: 0.0,
             status_height: 0.0,
             cursor_x: 0.0,
@@ -418,7 +420,7 @@ impl clear_ui::engine::Application for SystemInterface {
             rx_backup_state,
             rx_typeface,
             rx_services,
-            rx_colors,
+            rx_interface,
             rx_accounts,
             tx_backup,
             rx_backup,
@@ -477,7 +479,7 @@ impl clear_ui::engine::Application for SystemInterface {
             self.width = width as u32;
             self.height = height as u32;
             self.scale_factor = scale;
-            self.paginator.set_scale_factor(scale as f32);
+            clear_ui::scale::set_scale_factor(scale as f32);
             self.rebuild_layout(width, height);
         }
         for w in &self.widgets {
@@ -491,9 +493,9 @@ impl clear_ui::engine::Application for SystemInterface {
 
     fn clear_color(&self) -> [f32; 4] {
         let mut color = [
-            self.app.colors.page_low_color[0] as f32 / 255.0,
-            self.app.colors.page_low_color[1] as f32 / 255.0,
-            self.app.colors.page_low_color[2] as f32 / 255.0,
+            self.app.interface.page_low_color[0] as f32 / 255.0,
+            self.app.interface.page_low_color[1] as f32 / 255.0,
+            self.app.interface.page_low_color[2] as f32 / 255.0,
             1.0,
         ];
         if let Some(opacity) = clear_ui::color::read_opacity_if_configured() {
@@ -545,6 +547,7 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Widget, popovers: &mut Vec<(f
 }
 
     fn rebuild_layout(&mut self, sw: f32, sh: f32) {
+        self.sidebar_width = self.paginator.sidebar_w();
         let s = 1.0f32;
         let mut widgets = Vec::new();
         let mut text_items = Vec::new();
@@ -643,10 +646,18 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Widget, popovers: &mut Vec<(f
         self.app.layout.transition_duration_spinbox.clear_children(); self.app.layout.transition_duration_spinbox.set_parent(None);
         self.app.layout.status_height_spinbox.clear_children(); self.app.layout.status_height_spinbox.set_parent(None);
 
-        for cs in &mut self.app.colors.color_selectors {
+        for cs in &mut self.app.interface.color_selectors {
             cs.clear_children();
             cs.set_parent(None);
         }
+        self.app.interface.tab_margin_spinbox_x.clear_children();
+        self.app.interface.tab_margin_spinbox_x.set_parent(None);
+        self.app.interface.tab_margin_spinbox_y.clear_children();
+        self.app.interface.tab_margin_spinbox_y.set_parent(None);
+        self.app.interface.tab_padding_spinbox_x.clear_children();
+        self.app.interface.tab_padding_spinbox_x.set_parent(None);
+        self.app.interface.tab_padding_spinbox_y.clear_children();
+        self.app.interface.tab_padding_spinbox_y.set_parent(None);
 
         self.app.notifications.duration_spinbox.clear_children(); self.app.notifications.duration_spinbox.set_parent(None);
         self.app.notifications.opacity_slider.clear_children(); self.app.notifications.opacity_slider.set_parent(None);
@@ -756,10 +767,14 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Widget, popovers: &mut Vec<(f
                     link_parent_child(&mut self.page_sec_containers[4], menu);
                 }
             }
-            Page::Colors => {
-                for cs in &mut self.app.colors.color_selectors {
+            Page::Interface => {
+                for cs in &mut self.app.interface.color_selectors {
                     link_parent_child(&mut self.page_root_container, cs);
                 }
+                link_parent_child(&mut self.page_root_container, &mut self.app.interface.tab_margin_spinbox_x);
+                link_parent_child(&mut self.page_root_container, &mut self.app.interface.tab_margin_spinbox_y);
+                link_parent_child(&mut self.page_root_container, &mut self.app.interface.tab_padding_spinbox_x);
+                link_parent_child(&mut self.page_root_container, &mut self.app.interface.tab_padding_spinbox_y);
             }
             Page::Notifications => {
                 link_parent_child(&mut self.page_root_container, &mut self.app.notifications.duration_spinbox);
@@ -1008,27 +1023,29 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Widget, popovers: &mut Vec<(f
 
     fn render_page_content(&mut self, cx: f32, cy: f32, cw: f32, ch: f32) -> PageContent {
         use pages::*;
+        use clear_ui::layout::{LayoutStrategy, GridLayout};
+        let mut layout = GridLayout::new(320.0, 20.0);
         let root_focused = clear_ui::widget::focus::is_focused(&self.page_root_container);
         let sec_focused: Vec<bool> = self.page_sec_containers.iter()
             .map(|c| clear_ui::widget::focus::is_focused(c))
             .collect();
         match self.app.current_page {
-            Page::Accounts => accounts::view(&mut self.app.accounts, cx, cy, cw, ch),
-            Page::Audio => audio::view(&mut self.app.audio, cx, cy, cw, ch, &sec_focused),
-            Page::Display => display::view(&mut self.app.display, cx, cy, cw, ch),
-            Page::Radios => network::view(&mut self.app.network, cx, cy, cw, ch, root_focused),
-            Page::Layout => layout::view(&mut self.app.layout, cx, cy, cw, ch, &sec_focused),
-            Page::Hardware => hardware::view(&mut self.app.hardware, cx, cy, cw, ch, root_focused),
-            Page::Input => input::view(&mut self.app.input, cx, cy, cw, ch, &sec_focused),
-            Page::System => system_info::view(&self.app.system_info, cx, cy, cw, ch),
-            Page::Status => status::view(&mut self.app.status, cx, cy, cw, ch),
-            Page::Storage => storage::view(&self.app.storage, cx, cy, cw, ch),
-            Page::Notifications => notifications::view(&mut self.app.notifications, cx, cy, cw, ch),
-            Page::Backup => backup::view(&self.app.backup, cx, cy, cw, ch),
-            Page::Screensaver => screensaver::view(&mut self.app.screensaver, cx, cy, cw, ch),
-            Page::Typefaces => typeface::view(&mut self.app.typeface, cx, cy, cw, ch, &sec_focused),
-            Page::Services => services::view(&mut self.app.services, cx, cy, cw, ch, root_focused),
-            Page::Colors => colors::view(&mut self.app.colors, cx, cy, cw, ch),
+            Page::Accounts => accounts::view(&mut self.app.accounts, cx, cy, cw, ch, &mut layout),
+            Page::Audio => audio::view(&mut self.app.audio, cx, cy, cw, ch, &sec_focused, &mut layout),
+            Page::Display => display::view(&mut self.app.display, cx, cy, cw, ch, &mut layout),
+            Page::Radios => network::view(&mut self.app.network, cx, cy, cw, ch, root_focused, &mut layout),
+            Page::Layout => layout::view(&mut self.app.layout, cx, cy, cw, ch, &sec_focused, &mut layout),
+            Page::Hardware => hardware::view(&mut self.app.hardware, cx, cy, cw, ch, root_focused, &mut layout),
+            Page::Input => input::view(&mut self.app.input, cx, cy, cw, ch, &sec_focused, &mut layout),
+            Page::System => system_info::view(&self.app.system_info, cx, cy, cw, ch, &mut layout),
+            Page::Status => status::view(&mut self.app.status, cx, cy, cw, ch, &mut layout),
+            Page::Storage => storage::view(&self.app.storage, cx, cy, cw, ch, &mut layout),
+            Page::Notifications => notifications::view(&mut self.app.notifications, cx, cy, cw, ch, &mut layout),
+            Page::Backup => backup::view(&self.app.backup, cx, cy, cw, ch, &mut layout),
+            Page::Screensaver => screensaver::view(&mut self.app.screensaver, cx, cy, cw, ch, &mut layout),
+            Page::Typefaces => typeface::view(&mut self.app.typeface, cx, cy, cw, ch, &sec_focused, &mut layout),
+            Page::Services => services::view(&mut self.app.services, cx, cy, cw, ch, root_focused, &mut layout),
+            Page::Interface => interface::view(&mut self.app.interface, cx, cy, cw, ch, &mut layout),
         }
     }
 
@@ -1055,41 +1072,45 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Widget, popovers: &mut Vec<(f
         // Asynchronously check color selector changes (e.g. Zenity process exit)
         let mut color_changed = false;
         let mut color_actions = Vec::new();
-        for (i, cp) in self.app.colors.color_selectors.iter_mut().enumerate() {
+        for (i, cp) in self.app.interface.color_selectors.iter_mut().enumerate() {
             if cp.tick(dt) {
                 needs_redraw = true;
                 self.needs_rebuild = true;
             }
             let state_color = match i {
-                0 => self.app.colors.page_low_color,
-                1 => self.app.colors.high_color,
-                2 => self.app.colors.visual_guides_color,
-                3 => self.app.colors.disabled_color,
-                4 => self.app.colors.separator_color,
-                5 => self.app.colors.slider_track_color,
-                6 => self.app.colors.color_borders_color,
-                7 => self.app.colors.low_color,
-                8 => self.app.colors.normal_color,
-                9 => self.app.colors.paginator_sidebar_color,
-                10 => self.app.colors.primary_highlight_color,
-                11 => self.app.colors.paginator_tab_label_color,
-                _ => self.app.colors.low_color,
+                0 => self.app.interface.page_low_color,
+                1 => self.app.interface.high_color,
+                2 => self.app.interface.visual_guides_color,
+                3 => self.app.interface.disabled_color,
+                4 => self.app.interface.separator_color,
+                5 => self.app.interface.slider_track_color,
+                6 => self.app.interface.color_borders_color,
+                7 => self.app.interface.low_color,
+                8 => self.app.interface.normal_color,
+                9 => self.app.interface.paginator_sidebar_color,
+                10 => self.app.interface.primary_highlight_color,
+                11 => self.app.interface.paginator_tab_label_color,
+                12 => self.app.interface.toggle_enabled_color,
+                13 => self.app.interface.toggle_disabled_color,
+                _ => self.app.interface.low_color,
             };
             if cp.color != state_color {
-                color_actions.push(AppAction::Colors(match i {
-                    0 => pages::colors::ColorsMessage::SetPageLowColor(cp.color),
-                    1 => pages::colors::ColorsMessage::SetHighColor(cp.color),
-                    2 => pages::colors::ColorsMessage::SetVisualGuidesColor(cp.color),
-                    3 => pages::colors::ColorsMessage::SetDisabledColor(cp.color),
-                    4 => pages::colors::ColorsMessage::SetSeparatorColor(cp.color),
-                    5 => pages::colors::ColorsMessage::SetSliderTrackColor(cp.color),
-                    6 => pages::colors::ColorsMessage::SetColorBordersColor(cp.color),
-                    7 => pages::colors::ColorsMessage::SetLowColor(cp.color),
-                    8 => pages::colors::ColorsMessage::SetNormalColor(cp.color),
-                    9 => pages::colors::ColorsMessage::SetPaginatorSidebarColor(cp.color),
-                    10 => pages::colors::ColorsMessage::SetPrimaryHighlightColor(cp.color),
-                    11 => pages::colors::ColorsMessage::SetPaginatorTabLabelColor(cp.color),
-                    _ => pages::colors::ColorsMessage::SetLowColor(cp.color),
+                color_actions.push(AppAction::Interface(match i {
+                    0 => pages::interface::InterfaceMessage::SetPageLowColor(cp.color),
+                    1 => pages::interface::InterfaceMessage::SetHighColor(cp.color),
+                    2 => pages::interface::InterfaceMessage::SetVisualGuidesColor(cp.color),
+                    3 => pages::interface::InterfaceMessage::SetDisabledColor(cp.color),
+                    4 => pages::interface::InterfaceMessage::SetSeparatorColor(cp.color),
+                    5 => pages::interface::InterfaceMessage::SetSliderTrackColor(cp.color),
+                    6 => pages::interface::InterfaceMessage::SetColorBordersColor(cp.color),
+                    7 => pages::interface::InterfaceMessage::SetLowColor(cp.color),
+                    8 => pages::interface::InterfaceMessage::SetNormalColor(cp.color),
+                    9 => pages::interface::InterfaceMessage::SetPaginatorSidebarColor(cp.color),
+                    10 => pages::interface::InterfaceMessage::SetPrimaryHighlightColor(cp.color),
+                    11 => pages::interface::InterfaceMessage::SetPaginatorTabLabelColor(cp.color),
+                    12 => pages::interface::InterfaceMessage::SetToggleEnabledColor(cp.color),
+                    13 => pages::interface::InterfaceMessage::SetToggleDisabledColor(cp.color),
+                    _ => pages::interface::InterfaceMessage::SetLowColor(cp.color),
                 }));
                 color_changed = true;
             }
@@ -1177,8 +1198,8 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Widget, popovers: &mut Vec<(f
             pages::services::update(&mut self.app.services, pages::services::ServicesMessage::Refreshed(s));
             self.needs_rebuild = true;
         }
-        while let Ok(s) = self.rx_colors.try_recv() {
-            colors::update(&mut self.app.colors, pages::colors::ColorsMessage::Refreshed(s));
+        while let Ok(s) = self.rx_interface.try_recv() {
+            interface::update(&mut self.app.interface, pages::interface::InterfaceMessage::Refreshed(s));
             self.needs_rebuild = true;
         }
         while let Ok(s) = self.rx_accounts.try_recv() {
@@ -1192,10 +1213,10 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Widget, popovers: &mut Vec<(f
         while let Ok(action) = self.rx_color_selector.try_recv() {
             match action {
                 ColorSelectorAction::Background(rgb) => {
-                    colors::update(&mut self.app.colors, pages::colors::ColorsMessage::SetLowColor(rgb));
+                    interface::update(&mut self.app.interface, pages::interface::InterfaceMessage::SetLowColor(rgb));
                 }
                 ColorSelectorAction::Border(rgb) => {
-                    colors::update(&mut self.app.colors, pages::colors::ColorsMessage::SetHighColor(rgb));
+                    interface::update(&mut self.app.interface, pages::interface::InterfaceMessage::SetHighColor(rgb));
                 }
             }
             self.needs_rebuild = true;
@@ -1222,7 +1243,7 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Widget, popovers: &mut Vec<(f
                 self.monospace_family = self.app.typeface.monospace.clone();
             }
             AppAction::Services(m) => services::update(&mut self.app.services, m.clone()),
-            AppAction::Colors(m) => colors::update(&mut self.app.colors, m.clone()),
+            AppAction::Interface(m) => interface::update(&mut self.app.interface, m.clone()),
             AppAction::Screensaver(m) => screensaver::update(&mut self.app.screensaver, m.clone()),
             AppAction::Backup(m) => match m {
                 pages::backup::BackupMessage::StartBackup => {
@@ -1309,12 +1330,24 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Widget, popovers: &mut Vec<(f
                 }
             }
         }
-        if self.app.current_page == Page::Colors {
-            for cp in &mut self.app.colors.color_selectors {
+        if self.app.current_page == Page::Interface {
+            for cp in &mut self.app.interface.color_selectors {
                 if cp.cursor_moved(lx, ly) {
                     changed = true;
                 }
             }
+            if self.app.interface.tab_margin_spinbox_x.cursor_moved(lx, ly) {
+                changed = true;
+            }
+            if self.app.interface.tab_margin_spinbox_y.cursor_moved(lx, ly) {
+                changed = true;
+            }
+            if self.app.interface.tab_padding_spinbox_x.cursor_moved(lx, ly) {
+                changed = true;
+            }
+            if self.app.interface.tab_padding_spinbox_y.cursor_moved(lx, ly) {
+                changed = true;
+            }
         }
         if self.app.current_page == Page::Input {
             if self.app.input.rate_spinbox.cursor_moved(lx, ly) {
@@ -1615,10 +1648,14 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Widget, popovers: &mut Vec<(f
                         if menu.hit_test(lx, ly) { clicked_any_focusable = true; }
                     }
                 }
-                Page::Colors => {
-                    for cp in &mut self.app.colors.color_selectors {
+                Page::Interface => {
+                    for cp in &mut self.app.interface.color_selectors {
                         if cp.hit_test(lx, ly) { clicked_any_focusable = true; }
                     }
+                    if self.app.interface.tab_margin_spinbox_x.hit_test(lx, ly) { clicked_any_focusable = true; }
+                    if self.app.interface.tab_margin_spinbox_y.hit_test(lx, ly) { clicked_any_focusable = true; }
+                    if self.app.interface.tab_padding_spinbox_x.hit_test(lx, ly) { clicked_any_focusable = true; }
+                    if self.app.interface.tab_padding_spinbox_y.hit_test(lx, ly) { clicked_any_focusable = true; }
                 }
                 Page::Input => {
                     if self.app.input.rate_spinbox.hit_test(lx, ly) { clicked_any_focusable = true; }
@@ -1773,46 +1810,74 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Widget, popovers: &mut Vec<(f
                 }
             }
         }
-        if state == clear_ui::widget::ElementState::Pressed && self.app.current_page == Page::Colors {
-            for (i, cp) in self.app.colors.color_selectors.iter_mut().enumerate() {
+        if state == clear_ui::widget::ElementState::Pressed && self.app.current_page == Page::Interface {
+            for (i, cp) in self.app.interface.color_selectors.iter_mut().enumerate() {
                 let old = cp.color;
                 if !cp.hit_test(lx, ly) { cp.unfocus(); }
                 cp.mouse_input(button, state, lx, ly);
                 if cp.take_click() {
-                    actions.push(AppAction::Colors(match i {
-                        0 => pages::colors::ColorsMessage::PickPageLowColor,
-                        1 => pages::colors::ColorsMessage::PickHighColor,
-                        2 => pages::colors::ColorsMessage::PickVisualGuides,
-                        3 => pages::colors::ColorsMessage::PickDisabledColor,
-                        4 => pages::colors::ColorsMessage::PickSeparatorColor,
-                        5 => pages::colors::ColorsMessage::PickSliderTrackColor,
-                        6 => pages::colors::ColorsMessage::PickColorBordersColor,
-                        7 => pages::colors::ColorsMessage::PickLowColor,
-                        8 => pages::colors::ColorsMessage::PickNormalColor,
-                        9 => pages::colors::ColorsMessage::PickPaginatorSidebarColor,
-                        10 => pages::colors::ColorsMessage::PickPrimaryHighlightColor,
-                        11 => pages::colors::ColorsMessage::PickPaginatorTabLabelColor,
-                        _ => pages::colors::ColorsMessage::PickLowColor,
+                    actions.push(AppAction::Interface(match i {
+                        0 => pages::interface::InterfaceMessage::PickPageLowColor,
+                        1 => pages::interface::InterfaceMessage::PickHighColor,
+                        2 => pages::interface::InterfaceMessage::PickVisualGuides,
+                        3 => pages::interface::InterfaceMessage::PickDisabledColor,
+                        4 => pages::interface::InterfaceMessage::PickSeparatorColor,
+                        5 => pages::interface::InterfaceMessage::PickSliderTrackColor,
+                        6 => pages::interface::InterfaceMessage::PickColorBordersColor,
+                        7 => pages::interface::InterfaceMessage::PickLowColor,
+                        8 => pages::interface::InterfaceMessage::PickNormalColor,
+                        9 => pages::interface::InterfaceMessage::PickPaginatorSidebarColor,
+                        10 => pages::interface::InterfaceMessage::PickPrimaryHighlightColor,
+                        11 => pages::interface::InterfaceMessage::PickPaginatorTabLabelColor,
+                        12 => pages::interface::InterfaceMessage::PickToggleEnabledColor,
+                        13 => pages::interface::InterfaceMessage::PickToggleDisabledColor,
+                        _ => pages::interface::InterfaceMessage::PickLowColor,
                     }));
                 }
                 if cp.color != old {
-                    actions.push(AppAction::Colors(match i {
-                        0 => pages::colors::ColorsMessage::SetPageLowColor(cp.color),
-                        1 => pages::colors::ColorsMessage::SetHighColor(cp.color),
-                        2 => pages::colors::ColorsMessage::SetVisualGuidesColor(cp.color),
-                        3 => pages::colors::ColorsMessage::SetDisabledColor(cp.color),
-                        4 => pages::colors::ColorsMessage::SetSeparatorColor(cp.color),
-                        5 => pages::colors::ColorsMessage::SetSliderTrackColor(cp.color),
-                        6 => pages::colors::ColorsMessage::SetColorBordersColor(cp.color),
-                        7 => pages::colors::ColorsMessage::SetLowColor(cp.color),
-                        8 => pages::colors::ColorsMessage::SetNormalColor(cp.color),
-                        9 => pages::colors::ColorsMessage::SetPaginatorSidebarColor(cp.color),
-                        10 => pages::colors::ColorsMessage::SetPrimaryHighlightColor(cp.color),
-                        11 => pages::colors::ColorsMessage::SetPaginatorTabLabelColor(cp.color),
-                        _ => pages::colors::ColorsMessage::SetLowColor(cp.color),
+                    actions.push(AppAction::Interface(match i {
+                        0 => pages::interface::InterfaceMessage::SetPageLowColor(cp.color),
+                        1 => pages::interface::InterfaceMessage::SetHighColor(cp.color),
+                        2 => pages::interface::InterfaceMessage::SetVisualGuidesColor(cp.color),
+                        3 => pages::interface::InterfaceMessage::SetDisabledColor(cp.color),
+                        4 => pages::interface::InterfaceMessage::SetSeparatorColor(cp.color),
+                        5 => pages::interface::InterfaceMessage::SetSliderTrackColor(cp.color),
+                        6 => pages::interface::InterfaceMessage::SetColorBordersColor(cp.color),
+                        7 => pages::interface::InterfaceMessage::SetLowColor(cp.color),
+                        8 => pages::interface::InterfaceMessage::SetNormalColor(cp.color),
+                        9 => pages::interface::InterfaceMessage::SetPaginatorSidebarColor(cp.color),
+                        10 => pages::interface::InterfaceMessage::SetPrimaryHighlightColor(cp.color),
+                        11 => pages::interface::InterfaceMessage::SetPaginatorTabLabelColor(cp.color),
+                        12 => pages::interface::InterfaceMessage::SetToggleEnabledColor(cp.color),
+                        13 => pages::interface::InterfaceMessage::SetToggleDisabledColor(cp.color),
+                        _ => pages::interface::InterfaceMessage::SetLowColor(cp.color),
                     }));
                 }
             }
+            let sb = &mut self.app.interface.tab_margin_spinbox_x;
+            if !sb.hit_test(lx, ly) { sb.unfocus(); }
+            let old = sb.value;
+            if sb.mouse_input(button, state, lx, ly) && sb.value != old {
+                actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTabMarginX(sb.value as u16)));
+            }
+            let sb = &mut self.app.interface.tab_margin_spinbox_y;
+            if !sb.hit_test(lx, ly) { sb.unfocus(); }
+            let old = sb.value;
+            if sb.mouse_input(button, state, lx, ly) && sb.value != old {
+                actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTabMarginY(sb.value as u16)));
+            }
+            let sb = &mut self.app.interface.tab_padding_spinbox_x;
+            if !sb.hit_test(lx, ly) { sb.unfocus(); }
+            let old = sb.value;
+            if sb.mouse_input(button, state, lx, ly) && sb.value != old {
+                actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTabPaddingX(sb.value as u16)));
+            }
+            let sb = &mut self.app.interface.tab_padding_spinbox_y;
+            if !sb.hit_test(lx, ly) { sb.unfocus(); }
+            let old = sb.value;
+            if sb.mouse_input(button, state, lx, ly) && sb.value != old {
+                actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTabPaddingY(sb.value as u16)));
+            }
         }
         if state == clear_ui::widget::ElementState::Pressed && self.app.current_page == Page::Input {
             let sb = &mut self.app.input.rate_spinbox;
@@ -1981,6 +2046,7 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Widget, popovers: &mut Vec<(f
                 actions.push(AppAction::Screensaver(pages::screensaver::ScreensaverMessage::SetStyle(menu.selected)));
             }
         }
+
         if state == clear_ui::widget::ElementState::Pressed && self.app.current_page == Page::Audio {
             for (i, sb) in self.app.audio.sink_spinboxes.iter_mut().enumerate() {
                 if !sb.hit_test(lx, ly) { sb.unfocus(); }
@@ -2438,7 +2504,7 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Widget, popovers: &mut Vec<(f
     fn get_page_root_widget(&mut self) -> Option<*mut (dyn clear_ui::widget::Widget + 'static)> {
         match self.app.current_page {
             Page::Typefaces | Page::Services | Page::Hardware | Page::Radios |
-            Page::Layout | Page::Colors | Page::Notifications | Page::Input |
+            Page::Layout | Page::Interface | Page::Notifications | Page::Input |
             Page::Audio | Page::Display => {
                 let ptr = &mut self.page_root_container as &mut dyn clear_ui::widget::Widget as *mut dyn clear_ui::widget::Widget;
                 let static_ptr = unsafe {
@@ -2561,27 +2627,29 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Widget, popovers: &mut Vec<(f
                 return true;
             }
         }
-        if self.app.current_page == Page::Colors {
+        if self.app.current_page == Page::Interface {
             let mut changed = false;
             let mut actions = Vec::new();
-            for (i, cp) in self.app.colors.color_selectors.iter_mut().enumerate() {
+            for (i, cp) in self.app.interface.color_selectors.iter_mut().enumerate() {
                 let old = cp.color;
                 if cp.keyboard_input(event) {
                     if cp.color != old {
-                        actions.push(AppAction::Colors(match i {
-                            0 => pages::colors::ColorsMessage::SetPageLowColor(cp.color),
-                            1 => pages::colors::ColorsMessage::SetHighColor(cp.color),
-                            2 => pages::colors::ColorsMessage::SetVisualGuidesColor(cp.color),
-                            3 => pages::colors::ColorsMessage::SetDisabledColor(cp.color),
-                            4 => pages::colors::ColorsMessage::SetSeparatorColor(cp.color),
-                            5 => pages::colors::ColorsMessage::SetSliderTrackColor(cp.color),
-                            6 => pages::colors::ColorsMessage::SetColorBordersColor(cp.color),
-                            7 => pages::colors::ColorsMessage::SetLowColor(cp.color),
-                            8 => pages::colors::ColorsMessage::SetNormalColor(cp.color),
-                            9 => pages::colors::ColorsMessage::SetPaginatorSidebarColor(cp.color),
-                            10 => pages::colors::ColorsMessage::SetPrimaryHighlightColor(cp.color),
-                            11 => pages::colors::ColorsMessage::SetPaginatorTabLabelColor(cp.color),
-                            _ => pages::colors::ColorsMessage::SetLowColor(cp.color),
+                        actions.push(AppAction::Interface(match i {
+                            0 => pages::interface::InterfaceMessage::SetPageLowColor(cp.color),
+                            1 => pages::interface::InterfaceMessage::SetHighColor(cp.color),
+                            2 => pages::interface::InterfaceMessage::SetVisualGuidesColor(cp.color),
+                            3 => pages::interface::InterfaceMessage::SetDisabledColor(cp.color),
+                            4 => pages::interface::InterfaceMessage::SetSeparatorColor(cp.color),
+                            5 => pages::interface::InterfaceMessage::SetSliderTrackColor(cp.color),
+                            6 => pages::interface::InterfaceMessage::SetColorBordersColor(cp.color),
+                            7 => pages::interface::InterfaceMessage::SetLowColor(cp.color),
+                            8 => pages::interface::InterfaceMessage::SetNormalColor(cp.color),
+                            9 => pages::interface::InterfaceMessage::SetPaginatorSidebarColor(cp.color),
+                            10 => pages::interface::InterfaceMessage::SetPrimaryHighlightColor(cp.color),
+                            11 => pages::interface::InterfaceMessage::SetPaginatorTabLabelColor(cp.color),
+                            12 => pages::interface::InterfaceMessage::SetToggleEnabledColor(cp.color),
+                            13 => pages::interface::InterfaceMessage::SetToggleDisabledColor(cp.color),
+                            _ => pages::interface::InterfaceMessage::SetLowColor(cp.color),
                         }));
                     }
                     changed = true;
@@ -2594,6 +2662,50 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Widget, popovers: &mut Vec<(f
                 self.needs_rebuild = true;
                 return true;
             }
+            let sb = &mut self.app.interface.tab_margin_spinbox_x;
+            let old = sb.value;
+            if sb.keyboard_input(event) {
+                let new_val = sb.value;
+                drop(sb);
+                if new_val != old {
+                    self.handle_action(&AppAction::Interface(pages::interface::InterfaceMessage::SetTabMarginX(new_val as u16)));
+                }
+                self.needs_rebuild = true;
+                return true;
+            }
+            let sb = &mut self.app.interface.tab_margin_spinbox_y;
+            let old = sb.value;
+            if sb.keyboard_input(event) {
+                let new_val = sb.value;
+                drop(sb);
+                if new_val != old {
+                    self.handle_action(&AppAction::Interface(pages::interface::InterfaceMessage::SetTabMarginY(new_val as u16)));
+                }
+                self.needs_rebuild = true;
+                return true;
+            }
+            let sb = &mut self.app.interface.tab_padding_spinbox_x;
+            let old = sb.value;
+            if sb.keyboard_input(event) {
+                let new_val = sb.value;
+                drop(sb);
+                if new_val != old {
+                    self.handle_action(&AppAction::Interface(pages::interface::InterfaceMessage::SetTabPaddingX(new_val as u16)));
+                }
+                self.needs_rebuild = true;
+                return true;
+            }
+            let sb = &mut self.app.interface.tab_padding_spinbox_y;
+            let old = sb.value;
+            if sb.keyboard_input(event) {
+                let new_val = sb.value;
+                drop(sb);
+                if new_val != old {
+                    self.handle_action(&AppAction::Interface(pages::interface::InterfaceMessage::SetTabPaddingY(new_val as u16)));
+                }
+                self.needs_rebuild = true;
+                return true;
+            }
         }
         if self.app.current_page == Page::Screensaver {
             let sb = &mut self.app.screensaver.timeout_spinbox;
diff --git a/src/pages/accounts.rs b/src/pages/accounts.rs
index 3440982..1507aee 100644
--- a/src/pages/accounts.rs
+++ b/src/pages/accounts.rs
@@ -1,5 +1,5 @@
 use crate::app::{AppAction, PageContent};
-use clear_ui::layout::{render_widget, Section};
+use clear_ui::layout::{Section, PageLayoutBuilder, LayoutStrategy};
 use clear_ui::widget::{TextBox, Widget};
 
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
@@ -303,298 +303,301 @@ pub async fn exchange_code_for_tokens(code: String, sender: calloop::channel::Se
 
 const TEXT_DIM: [f32; 4] = [0.53, 0.53, 0.60, 1.0];
 
-pub fn view(state: &mut AccountsState, cx: f32, cy: f32, cw: f32, _ch: f32) -> PageContent {
-    let mut pc = PageContent::new();
-    let y = cy + 12.0;
-
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Online Accounts");
-
-    if !state.loaded {
-        sec.text(&mut pc, "Loading online accounts...", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec.spacing(18.0);
-    } else {
-        let usable_w = cw - 24.0;
-        let gap = 24.0;
-        let left_w = (usable_w - gap) * 0.40;
-        let right_w = (usable_w - gap) * 0.60;
-        let left_x = cx + 12.0;
-        let right_x = left_x + left_w + gap;
-
-        let mut left_y = sec.ay();
-        let row_h = 28.0;
-        let row_gap = 8.0;
-
-        if state.accounts.is_empty() {
-            pc.text("No accounts configured.", left_x + 8.0, left_y, 12.0, TEXT_DIM);
-            left_y += 20.0;
+pub fn view(state: &mut AccountsState, cx: f32, cy: f32, cw: f32, ch: f32, layout: &mut dyn LayoutStrategy) -> PageContent {
+    let mut final_pc = PageContent::new();
+    let sec_w = 320.0f32;
+    let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(2);
+
+    // ── Accounts Section ──
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec_accounts = Section::new(pc, rx, ry, sec_w, "Accounts");
+        if !state.loaded {
+            sec_accounts.text(pc, "Loading online accounts...", 12.0, 0.0, 12.0, TEXT_DIM);
+            sec_accounts.spacing(18.0);
         } else {
-            for (idx, acc) in state.accounts.iter().enumerate() {
-                let label = if acc.is_default {
-                    format!("{} [Default]", acc.email)
-                } else {
-                    acc.email.clone()
-                };
-                let is_selected = state.selected_idx == Some(idx) && !state.adding_new && !state.editing_oauth_creds;
-                let bg_col = if is_selected { [0.20, 0.40, 0.65, 0.4] } else { [0.10, 0.10, 0.16, 0.3] };
-                pc.button(
-                    &label,
-                    left_x,
-                    left_y,
-                    left_w,
-                    row_h,
-                    bg_col,
-                    [0.20, 0.20, 0.25, 0.15],
-                    [0.90, 0.90, 0.95, 1.0],
-                    AppAction::Accounts(AccountsMessage::SelectAccount(idx)),
-                );
-                left_y += row_h + row_gap;
-            }
-        }
+            let row_h = 28.0;
+            let row_gap = 8.0;
+            let item_w = sec_w - 40.0;
 
-        left_y += 12.0;
-        
-        let add_bg = if state.adding_new { [0.20, 0.40, 0.65, 0.4] } else { [0.13, 0.18, 0.14, 1.0] };
-        pc.button(
-            "Add Account",
-            left_x,
-            left_y,
-            left_w,
-            row_h,
-            add_bg,
-            [0.25, 0.30, 0.26, 1.0],
-            [1.0, 1.0, 1.0, 1.0],
-            AppAction::Accounts(AccountsMessage::AddAccountStart),
-        );
-        left_y += row_h + row_gap;
-
-        let oauth_bg = if state.editing_oauth_creds { [0.20, 0.40, 0.65, 0.4] } else { [0.15, 0.15, 0.20, 1.0] };
-        pc.button(
-            "Google API Settings",
-            left_x,
-            left_y,
-            left_w,
-            row_h,
-            oauth_bg,
-            [0.25, 0.25, 0.30, 1.0],
-            [1.0, 1.0, 1.0, 1.0],
-            AppAction::Accounts(AccountsMessage::EditOAuthCredsStart),
-        );
-        left_y += row_h + row_gap;
-
-        if let Some(selected_idx) = state.selected_idx {
-            if selected_idx < state.accounts.len() && !state.adding_new && !state.editing_oauth_creds {
-                let acc = &state.accounts[selected_idx];
-                if !acc.is_default {
+            if state.accounts.is_empty() {
+                sec_accounts.text(pc, "No accounts configured.", 12.0, 0.0, 12.0, TEXT_DIM);
+                sec_accounts.spacing(20.0);
+            } else {
+                for (idx, acc) in state.accounts.iter().enumerate() {
+                    let label = if acc.is_default {
+                        format!("{} [Default]", acc.email)
+                    } else {
+                        acc.email.clone()
+                    };
+                    let is_selected = state.selected_idx == Some(idx) && !state.adding_new && !state.editing_oauth_creds;
+                    let bg_col = if is_selected { [0.20, 0.40, 0.65, 0.4] } else { [0.10, 0.10, 0.16, 0.3] };
                     pc.button(
-                        "Make Default",
-                        left_x,
-                        left_y,
-                        left_w,
+                        &label,
+                        sec_accounts.ax(12.0),
+                        sec_accounts.ay(),
+                        item_w,
                         row_h,
-                        [0.15, 0.15, 0.25, 1.0],
-                        [0.25, 0.25, 0.35, 1.0],
-                        [1.0, 1.0, 1.0, 1.0],
-                        AppAction::Accounts(AccountsMessage::MakeDefault(selected_idx)),
+                        bg_col,
+                        [0.20, 0.20, 0.25, 0.15],
+                        [0.90, 0.90, 0.95, 1.0],
+                        AppAction::Accounts(AccountsMessage::SelectAccount(idx)),
                     );
-                    left_y += row_h + row_gap;
+                    sec_accounts.spacing(row_h + row_gap);
                 }
-                pc.button(
-                    "Delete Account",
-                    left_x,
-                    left_y,
-                    left_w,
-                    row_h,
-                    [0.33, 0.20, 0.20, 1.0],
-                    [0.45, 0.25, 0.25, 1.0],
-                    [1.0, 0.33, 0.33, 1.0],
-                    AppAction::Accounts(AccountsMessage::DeleteAccount(selected_idx)),
-                );
-                left_y += row_h + row_gap;
             }
-        }
-
-        let mut right_y = sec.ay();
-
-        if state.adding_new {
-            pc.text("Add New Account", right_x, right_y, 14.0, [0.35, 0.65, 0.90, 1.0]);
-            right_y += 24.0;
-
-            pc.text("Note: Gmail uses Google Login. iCloud requires App PW.", right_x, right_y, 11.0, TEXT_DIM);
-            right_y += 18.0;
-
-            let widget_h = 26.0;
-            let field_gap = 14.0;
 
-            // Email Address textbox
-            let email_top = state.email_box.top_room();
-            state.email_box.set_row_rect(right_x, right_w);
-            clear_ui::layout::render_widget(&mut pc, &mut state.email_box, right_x, right_y + email_top, right_w, widget_h);
-            right_y += widget_h + email_top + field_gap;
+            sec_accounts.spacing(12.0);
 
-            // Password textbox
-            let password_top = state.password_box.top_room();
-            state.password_box.set_row_rect(right_x, right_w);
-            clear_ui::layout::render_widget(&mut pc, &mut state.password_box, right_x, right_y + password_top, right_w, widget_h);
-            right_y += widget_h + password_top + field_gap;
-
-            // IMAP Server textbox
-            let imap_top = state.imap_box.top_room();
-            state.imap_box.set_row_rect(right_x, right_w);
-            clear_ui::layout::render_widget(&mut pc, &mut state.imap_box, right_x, right_y + imap_top, right_w, widget_h);
-            right_y += widget_h + imap_top + field_gap;
-
-            // SMTP Server textbox
-            let smtp_top = state.smtp_box.top_room();
-            state.smtp_box.set_row_rect(right_x, right_w);
-            clear_ui::layout::render_widget(&mut pc, &mut state.smtp_box, right_x, right_y + smtp_top, right_w, widget_h);
-            right_y += widget_h + smtp_top + field_gap;
-
-            let helper_w = (right_w - 8.0) / 2.0;
-            pc.button(
-                "Login (Google)",
-                right_x,
-                right_y,
-                helper_w,
-                row_h,
-                [0.15, 0.15, 0.25, 1.0],
-                [0.25, 0.25, 0.35, 1.0],
-                [1.0, 1.0, 1.0, 1.0],
-                AppAction::Accounts(AccountsMessage::GoogleLoginInit),
-            );
-            pc.button(
-                "Login (iCloud)",
-                right_x + helper_w + 8.0,
-                right_y,
-                helper_w,
-                row_h,
-                [0.15, 0.15, 0.25, 1.0],
-                [0.25, 0.25, 0.35, 1.0],
-                [1.0, 1.0, 1.0, 1.0],
-                AppAction::Accounts(AccountsMessage::ICloudLoginHelp),
-            );
-            right_y += row_h + 16.0;
-
-            pc.button(
-                "Save Account",
-                right_x,
-                right_y,
-                helper_w,
-                row_h,
-                [0.13, 0.18, 0.14, 1.0],
-                [0.25, 0.30, 0.26, 1.0],
-                [1.0, 1.0, 1.0, 1.0],
-                AppAction::Accounts(AccountsMessage::AddAccountSave),
-            );
-            pc.button(
-                "Cancel",
-                right_x + helper_w + 8.0,
-                right_y,
-                helper_w,
-                row_h,
-                [0.33, 0.20, 0.20, 1.0],
-                [0.45, 0.25, 0.25, 1.0],
-                [1.0, 1.0, 1.0, 1.0],
-                AppAction::Accounts(AccountsMessage::AddAccountCancel),
-            );
-            right_y += row_h + 12.0;
-        } else if state.editing_oauth_creds {
-            pc.text("Google OAuth Credentials", right_x, right_y, 14.0, [0.35, 0.65, 0.90, 1.0]);
-            right_y += 24.0;
-
-            pc.text("Configures client ID & secret from your Google Cloud Console.", right_x, right_y, 11.0, TEXT_DIM);
-            right_y += 18.0;
-            pc.text("Required: Gmail API enabled & redirect URI set to http://127.0.0.1:8080", right_x, right_y, 11.0, TEXT_DIM);
-            right_y += 18.0;
-
-            let widget_h = 26.0;
-            let field_gap = 14.0;
-
-            // Client ID textbox
-            let client_id_top = state.oauth_client_id_box.top_room();
-            state.oauth_client_id_box.set_row_rect(right_x, right_w);
-            clear_ui::layout::render_widget(&mut pc, &mut state.oauth_client_id_box, right_x, right_y + client_id_top, right_w, widget_h);
-            right_y += widget_h + client_id_top + field_gap;
-
-            // Client Secret textbox
-            let client_secret_top = state.oauth_client_secret_box.top_room();
-            state.oauth_client_secret_box.set_row_rect(right_x, right_w);
-            clear_ui::layout::render_widget(&mut pc, &mut state.oauth_client_secret_box, right_x, right_y + client_secret_top, right_w, widget_h);
-            right_y += widget_h + client_secret_top + field_gap;
-
-            let helper_w = (right_w - 8.0) / 2.0;
+            let add_bg = if state.adding_new { [0.20, 0.40, 0.65, 0.4] } else { [0.13, 0.18, 0.14, 1.0] };
             pc.button(
-                "Save Credentials",
-                right_x,
-                right_y,
-                helper_w,
+                "Add Account",
+                sec_accounts.ax(12.0),
+                sec_accounts.ay(),
+                item_w,
                 row_h,
-                [0.13, 0.18, 0.14, 1.0],
+                add_bg,
                 [0.25, 0.30, 0.26, 1.0],
                 [1.0, 1.0, 1.0, 1.0],
-                AppAction::Accounts(AccountsMessage::EditOAuthCredsSave),
+                AppAction::Accounts(AccountsMessage::AddAccountStart),
             );
+            sec_accounts.spacing(row_h + row_gap);
+
+            let oauth_bg = if state.editing_oauth_creds { [0.20, 0.40, 0.65, 0.4] } else { [0.15, 0.15, 0.20, 1.0] };
             pc.button(
-                "Cancel",
-                right_x + helper_w + 8.0,
-                right_y,
-                helper_w,
+                "Google API Settings",
+                sec_accounts.ax(12.0),
+                sec_accounts.ay(),
+                item_w,
                 row_h,
-                [0.33, 0.20, 0.20, 1.0],
-                [0.45, 0.25, 0.25, 1.0],
+                oauth_bg,
+                [0.25, 0.25, 0.30, 1.0],
                 [1.0, 1.0, 1.0, 1.0],
-                AppAction::Accounts(AccountsMessage::EditOAuthCredsCancel),
+                AppAction::Accounts(AccountsMessage::EditOAuthCredsStart),
             );
-            right_y += row_h + 12.0;
-        } else if let Some(selected_idx) = state.selected_idx {
-            if selected_idx < state.accounts.len() {
-                let acc = &state.accounts[selected_idx];
-
-                pc.text("Account Details", right_x, right_y, 14.0, [0.35, 0.65, 0.90, 1.0]);
-                right_y += 28.0;
-
-                pc.text(&format!("Email Address:   {}", acc.email), right_x + 8.0, right_y, 12.0, [0.90, 0.90, 0.95, 1.0]);
-                right_y += 18.0;
-
-                let auth_type = if acc.is_oauth { "OAuth2 (Google)" } else { "Password-based" };
-                pc.text(&format!("Authentication:  {}", auth_type), right_x + 8.0, right_y, 12.0, [0.83, 0.83, 0.83, 1.0]);
-                right_y += 18.0;
-
-                pc.text(&format!("IMAP Server:     {}", acc.imap), right_x + 8.0, right_y, 12.0, [0.83, 0.83, 0.83, 1.0]);
-                right_y += 18.0;
-
-                pc.text(&format!("SMTP Server:     {}", acc.smtp), right_x + 8.0, right_y, 12.0, [0.83, 0.83, 0.83, 1.0]);
-                right_y += 24.0;
-
-                if acc.is_oauth {
+            sec_accounts.spacing(row_h + row_gap);
+
+            if let Some(selected_idx) = state.selected_idx {
+                if selected_idx < state.accounts.len() && !state.adding_new && !state.editing_oauth_creds {
+                    let acc = &state.accounts[selected_idx];
+                    if !acc.is_default {
+                        pc.button(
+                            "Make Default",
+                            sec_accounts.ax(12.0),
+                            sec_accounts.ay(),
+                            item_w,
+                            row_h,
+                            [0.15, 0.15, 0.25, 1.0],
+                            [0.25, 0.25, 0.35, 1.0],
+                            [1.0, 1.0, 1.0, 1.0],
+                            AppAction::Accounts(AccountsMessage::MakeDefault(selected_idx)),
+                        );
+                        sec_accounts.spacing(row_h + row_gap);
+                    }
                     pc.button(
-                        "Click to Login (Browser)",
-                        right_x + 8.0,
-                        right_y,
-                        200.0,
+                        "Delete Account",
+                        sec_accounts.ax(12.0),
+                        sec_accounts.ay(),
+                        item_w,
                         row_h,
-                        [0.15, 0.15, 0.25, 1.0],
-                        [0.25, 0.25, 0.35, 1.0],
-                        [1.0, 1.0, 1.0, 1.0],
-                        AppAction::Accounts(AccountsMessage::GoogleLoginInit),
+                        [0.33, 0.20, 0.20, 1.0],
+                        [0.45, 0.25, 0.25, 1.0],
+                        [1.0, 0.33, 0.33, 1.0],
+                        AppAction::Accounts(AccountsMessage::DeleteAccount(selected_idx)),
                     );
-                    right_y += row_h + 12.0;
+                    sec_accounts.spacing(row_h + row_gap);
                 }
             }
-        } else {
-            pc.text("Select an account to view details, or click Add Account.", right_x, right_y, 12.0, TEXT_DIM);
-            right_y += 20.0;
         }
+        sec_accounts.finish(pc)
+    });
 
-        if let Some(ref msg) = state.status_msg {
-            pc.text(msg, right_x, right_y + 12.0, 12.0, [0.56, 0.83, 0.56, 1.0]);
-            right_y += 24.0;
-        }
+    // ── Modify Accounts Section ──
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec_modify = Section::new(pc, rx, ry, sec_w, "Modify Accounts");
+        let item_w = sec_w - 40.0;
+        let row_h = 28.0;
 
-        sec.content_y = left_y.max(right_y) + 24.0;
-    }
+        if state.loaded {
+            if state.adding_new {
+                sec_modify.text(pc, "Add New Account", 12.0, 0.0, 14.0, [0.35, 0.65, 0.90, 1.0]);
+                sec_modify.spacing(24.0);
+
+                sec_modify.text(pc, "Note: Gmail uses Google Login. iCloud requires App PW.", 12.0, 0.0, 11.0, TEXT_DIM);
+                sec_modify.spacing(18.0);
+
+                let widget_h = 26.0;
+                let field_gap = 14.0;
+
+                // Email Address textbox
+                let email_top = state.email_box.top_room();
+                state.email_box.set_row_rect(rx + 12.0, item_w);
+                clear_ui::layout::render_widget(pc, &mut state.email_box, rx + 12.0, sec_modify.ay() + email_top, item_w, widget_h);
+                sec_modify.spacing(widget_h + email_top + field_gap);
+
+                // Password textbox
+                let password_top = state.password_box.top_room();
+                state.password_box.set_row_rect(rx + 12.0, item_w);
+                clear_ui::layout::render_widget(pc, &mut state.password_box, rx + 12.0, sec_modify.ay() + password_top, item_w, widget_h);
+                sec_modify.spacing(widget_h + password_top + field_gap);
+
+                // IMAP Server textbox
+                let imap_top = state.imap_box.top_room();
+                state.imap_box.set_row_rect(rx + 12.0, item_w);
+                clear_ui::layout::render_widget(pc, &mut state.imap_box, rx + 12.0, sec_modify.ay() + imap_top, item_w, widget_h);
+                sec_modify.spacing(widget_h + imap_top + field_gap);
+
+                // SMTP Server textbox
+                let smtp_top = state.smtp_box.top_room();
+                state.smtp_box.set_row_rect(rx + 12.0, item_w);
+                clear_ui::layout::render_widget(pc, &mut state.smtp_box, rx + 12.0, sec_modify.ay() + smtp_top, item_w, widget_h);
+                sec_modify.spacing(widget_h + smtp_top + field_gap);
+
+                let helper_w = (item_w - 8.0) / 2.0;
+                pc.button(
+                    "Login (Google)",
+                    sec_modify.ax(12.0),
+                    sec_modify.ay(),
+                    helper_w,
+                    row_h,
+                    [0.15, 0.15, 0.25, 1.0],
+                    [0.25, 0.25, 0.35, 1.0],
+                    [1.0, 1.0, 1.0, 1.0],
+                    AppAction::Accounts(AccountsMessage::GoogleLoginInit),
+                );
+                pc.button(
+                    "Login (iCloud)",
+                    sec_modify.ax(12.0) + helper_w + 8.0,
+                    sec_modify.ay(),
+                    helper_w,
+                    row_h,
+                    [0.15, 0.15, 0.25, 1.0],
+                    [0.25, 0.25, 0.35, 1.0],
+                    [1.0, 1.0, 1.0, 1.0],
+                    AppAction::Accounts(AccountsMessage::ICloudLoginHelp),
+                );
+                sec_modify.spacing(row_h + 16.0);
+
+                pc.button(
+                    "Save Account",
+                    sec_modify.ax(12.0),
+                    sec_modify.ay(),
+                    helper_w,
+                    row_h,
+                    [0.13, 0.18, 0.14, 1.0],
+                    [0.25, 0.30, 0.26, 1.0],
+                    [1.0, 1.0, 1.0, 1.0],
+                    AppAction::Accounts(AccountsMessage::AddAccountSave),
+                );
+                pc.button(
+                    "Cancel",
+                    sec_modify.ax(12.0) + helper_w + 8.0,
+                    sec_modify.ay(),
+                    helper_w,
+                    row_h,
+                    [0.33, 0.20, 0.20, 1.0],
+                    [0.45, 0.25, 0.25, 1.0],
+                    [1.0, 1.0, 1.0, 1.0],
+                    AppAction::Accounts(AccountsMessage::AddAccountCancel),
+                );
+                sec_modify.spacing(row_h + 12.0);
+            } else if state.editing_oauth_creds {
+                sec_modify.text(pc, "Google OAuth Credentials", 12.0, 0.0, 14.0, [0.35, 0.65, 0.90, 1.0]);
+                sec_modify.spacing(24.0);
+
+                sec_modify.text(pc, "Configures client ID & secret from your Google Cloud Console.", 12.0, 0.0, 11.0, TEXT_DIM);
+                sec_modify.spacing(18.0);
+                sec_modify.text(pc, "Required: Gmail API enabled & redirect URI set to http://127.0.0.1:8080", 12.0, 0.0, 11.0, TEXT_DIM);
+                sec_modify.spacing(18.0);
+
+                let widget_h = 26.0;
+                let field_gap = 14.0;
+
+                // Client ID textbox
+                let client_id_top = state.oauth_client_id_box.top_room();
+                state.oauth_client_id_box.set_row_rect(rx + 12.0, item_w);
+                clear_ui::layout::render_widget(pc, &mut state.oauth_client_id_box, rx + 12.0, sec_modify.ay() + client_id_top, item_w, widget_h);
+                sec_modify.spacing(widget_h + client_id_top + field_gap);
+
+                // Client Secret textbox
+                let client_secret_top = state.oauth_client_secret_box.top_room();
+                state.oauth_client_secret_box.set_row_rect(rx + 12.0, item_w);
+                clear_ui::layout::render_widget(pc, &mut state.oauth_client_secret_box, rx + 12.0, sec_modify.ay() + client_secret_top, item_w, widget_h);
+                sec_modify.spacing(widget_h + client_secret_top + field_gap);
+
+                let helper_w = (item_w - 8.0) / 2.0;
+                pc.button(
+                    "Save Credentials",
+                    sec_modify.ax(12.0),
+                    sec_modify.ay(),
+                    helper_w,
+                    row_h,
+                    [0.13, 0.18, 0.14, 1.0],
+                    [0.25, 0.30, 0.26, 1.0],
+                    [1.0, 1.0, 1.0, 1.0],
+                    AppAction::Accounts(AccountsMessage::EditOAuthCredsSave),
+                );
+                pc.button(
+                    "Cancel",
+                    sec_modify.ax(12.0) + helper_w + 8.0,
+                    sec_modify.ay(),
+                    helper_w,
+                    row_h,
+                    [0.33, 0.20, 0.20, 1.0],
+                    [0.45, 0.25, 0.25, 1.0],
+                    [1.0, 1.0, 1.0, 1.0],
+                    AppAction::Accounts(AccountsMessage::EditOAuthCredsCancel),
+                );
+                sec_modify.spacing(row_h + 12.0);
+            } else if let Some(selected_idx) = state.selected_idx {
+                if selected_idx < state.accounts.len() {
+                    let acc = &state.accounts[selected_idx];
+
+                    sec_modify.text(pc, "Account Details", 12.0, 0.0, 14.0, [0.35, 0.65, 0.90, 1.0]);
+                    sec_modify.spacing(28.0);
+
+                    sec_modify.text(pc, &format!("Email Address:   {}", acc.email), 12.0, 0.0, 12.0, [0.90, 0.90, 0.95, 1.0]);
+                    sec_modify.spacing(18.0);
+
+                    let auth_type = if acc.is_oauth { "OAuth2 (Google)" } else { "Password-based" };
+                    sec_modify.text(pc, &format!("Authentication:  {}", auth_type), 12.0, 0.0, 12.0, [0.83, 0.83, 0.83, 1.0]);
+                    sec_modify.spacing(18.0);
+
+                    sec_modify.text(pc, &format!("IMAP Server:     {}", acc.imap), 12.0, 0.0, 12.0, [0.83, 0.83, 0.83, 1.0]);
+                    sec_modify.spacing(18.0);
+
+                    sec_modify.text(pc, &format!("SMTP Server:     {}", acc.smtp), 12.0, 0.0, 12.0, [0.83, 0.83, 0.83, 1.0]);
+                    sec_modify.spacing(24.0);
+
+                    if acc.is_oauth {
+                        pc.button(
+                            "Click to Login (Browser)",
+                            sec_modify.ax(12.0),
+                            sec_modify.ay(),
+                            200.0,
+                            row_h,
+                            [0.15, 0.15, 0.25, 1.0],
+                            [0.25, 0.25, 0.35, 1.0],
+                            [1.0, 1.0, 1.0, 1.0],
+                            AppAction::Accounts(AccountsMessage::GoogleLoginInit),
+                        );
+                        sec_modify.spacing(row_h + 12.0);
+                    }
+                }
+            } else {
+                sec_modify.text(pc, "Select an account to view details, or click Add Account.", 12.0, 0.0, 12.0, TEXT_DIM);
+                sec_modify.spacing(20.0);
+            }
+
+            if let Some(ref msg) = state.status_msg {
+                sec_modify.spacing(12.0);
+                sec_modify.text(pc, msg, 12.0, 0.0, 12.0, [0.56, 0.83, 0.56, 1.0]);
+                sec_modify.spacing(24.0);
+            }
+        }
+        sec_modify.finish(pc)
+    });
 
-    sec.finish(&mut pc);
-    pc
+    final_pc
 }
 
 pub fn update(state: &mut AccountsState, msg: AccountsMessage) {
diff --git a/src/pages/audio.rs b/src/pages/audio.rs
index c53dde2..9a2ff9f 100644
--- a/src/pages/audio.rs
+++ b/src/pages/audio.rs
@@ -1,5 +1,5 @@
 use crate::app::{AppAction, PageContent};
-use clear_ui::layout::{render_widget, Section};
+use clear_ui::layout::{render_widget, Section, PageLayoutBuilder, LayoutStrategy, GridLayout};
 use clear_ui::widget::{Spinbox, Widget};
 
 #[derive(Debug, Clone)]
@@ -207,127 +207,133 @@ const FILL_BAR: [f32; 4] = [0.30, 0.50, 0.32, 1.0];
 const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
 const RED: [f32; 4] = [1.0, 0.33, 0.33, 1.0];
 
-pub fn view(state: &mut AudioState, cx: f32, cy: f32, cw: f32, _ch: f32, sec_focused: &[bool]) -> PageContent {
-    let mut pc = PageContent::new();
-    let mut y = cy + 12.0;
+pub fn view(state: &mut AudioState, cx: f32, cy: f32, cw: f32, ch: f32, sec_focused: &[bool], layout: &mut dyn LayoutStrategy) -> PageContent {
+    let mut final_pc = PageContent::new();
+    let sec_w = 320.0f32;
+    let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(2);
 
     // ── Output section ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Output");
-
-    if !state.loaded {
-        sec.text(&mut pc, "Loading output devices...", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec.spacing(18.0);
-    } else if state.sinks.is_empty() {
-        sec.text(&mut pc, "No output devices found", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec.spacing(18.0);
-    }
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Output");
 
-    if state.loaded {
-        for (idx, sink) in state.sinks.iter().enumerate() {
-            let label = if !sink.active {
-                format!("{}  (inactive)", sink.name)
-            } else if sink.muted {
-                format!("{}  {:.0}%  (muted)", sink.name, sink.volume * 100.0)
-            } else {
-                format!("{}  {:.0}%", sink.name, sink.volume * 100.0)
-            };
-            let lc = if sink.muted { RED } else { TEXT_FG };
-            sec.text(&mut pc, &label, 14.0, 0.0, 13.0, lc);
+        if !state.loaded {
+            sec.text(pc, "Loading output devices...", 12.0, 0.0, 12.0, TEXT_DIM);
+            sec.spacing(18.0);
+        } else if state.sinks.is_empty() {
+            sec.text(pc, "No output devices found", 12.0, 0.0, 12.0, TEXT_DIM);
             sec.spacing(18.0);
+        }
 
-            if sink.active {
-                let bar_w = cw - 100.0;
-                let bar_x = 14.0;
-                let yt = sec.ay();
-                pc.rect(BLANK_BAR, sec.ax(bar_x), yt, bar_w, 8.0);
-                pc.rect(FILL_BAR, sec.ax(bar_x), yt, bar_w * sink.volume, 8.0);
-                sec.text(&mut pc, &format!("{:.0}%", sink.volume * 100.0), bar_x + bar_w + 8.0, -2.0, 11.0, TEXT_DIM);
-
-                let row_y = sec.ay() + 12.0;
-                let sb_w = 100.0;
-                let sb_h = 26.0;
-                let mute_w = 60.0;
-                let gap = 8.0;
-
-                state.sink_spinboxes[idx].value = (sink.volume * 100.0).round() as i32;
-                state.sink_spinboxes[idx].set_row_rect(sec.ax(8.0), cw - 16.0);
-                render_widget(&mut pc, &mut state.sink_spinboxes[idx], sec.ax(bar_x), row_y, sb_w, sb_h);
-
-                let mute_label = if sink.muted { "Unmute" } else { "Mute" };
-                let mute_col = if sink.muted { MUTED_BG } else { BTN_INACTIVE };
-                pc.button(mute_label, sec.ax(bar_x) + sb_w + gap, row_y, mute_w, sb_h,
-                    mute_col, BTN_HOVER, WHITE,
-                    AppAction::Audio(AudioMessage::SinkMute(sink.id)));
-
-                sec.content_y += 12.0 + sb_h + 6.0;
-            } else {
-                sec.content_y += 6.0;
+        if state.loaded {
+            for (idx, sink) in state.sinks.iter().enumerate() {
+                let label = if !sink.active {
+                    format!("{}  (inactive)", sink.name)
+                } else if sink.muted {
+                    format!("{}  {:.0}%  (muted)", sink.name, sink.volume * 100.0)
+                } else {
+                    format!("{}  {:.0}%", sink.name, sink.volume * 100.0)
+                };
+                let lc = if sink.muted { RED } else { TEXT_FG };
+                sec.text(pc, &label, 14.0, 0.0, 13.0, lc);
+                sec.spacing(18.0);
+
+                if sink.active {
+                    let bar_w = sec_w - 100.0;
+                    let bar_x = 14.0;
+                    let yt = sec.ay();
+                    pc.rect(BLANK_BAR, sec.ax(bar_x), yt, bar_w, 8.0);
+                    pc.rect(FILL_BAR, sec.ax(bar_x), yt, bar_w * sink.volume, 8.0);
+                    sec.text(pc, &format!("{:.0}%", sink.volume * 100.0), bar_x + bar_w + 8.0, -2.0, 11.0, TEXT_DIM);
+
+                    let row_y = sec.ay() + 12.0;
+                    let sb_w = 100.0;
+                    let sb_h = 26.0;
+                    let mute_w = 60.0;
+                    let gap = 8.0;
+
+                    state.sink_spinboxes[idx].value = (sink.volume * 100.0).round() as i32;
+                    state.sink_spinboxes[idx].set_row_rect(sec.ax(8.0), sec_w - 16.0);
+                    render_widget(pc, &mut state.sink_spinboxes[idx], sec.ax(bar_x), row_y, sb_w, sb_h);
+
+                    let mute_label = if sink.muted { "Unmute" } else { "Mute" };
+                    let mute_col = if sink.muted { MUTED_BG } else { BTN_INACTIVE };
+                    pc.button(mute_label, sec.ax(bar_x) + sb_w + gap, row_y, mute_w, sb_h,
+                        mute_col, BTN_HOVER, WHITE,
+                        AppAction::Audio(AudioMessage::SinkMute(sink.id)));
+
+                    sec.content_y += 12.0 + sb_h + 6.0;
+                } else {
+                    sec.content_y += 6.0;
+                }
             }
         }
-    }
 
-    y = sec.finish_focused(&mut pc, sec_focused.get(0).copied().unwrap_or(false));
+        sec.finish_focused(pc, sec_focused.first().copied().unwrap_or(false))
+    });
 
     // ── Input section ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Input");
-
-    if !state.loaded {
-        sec.text(&mut pc, "Loading input devices...", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec.spacing(18.0);
-    } else if state.sources.is_empty() {
-        sec.text(&mut pc, "No input devices found", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec.spacing(18.0);
-    }
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Input");
 
-    if state.loaded {
-        for (idx, src) in state.sources.iter().enumerate() {
-            let label = if !src.active {
-                format!("{}  (inactive)", src.name)
-            } else if src.muted {
-                format!("{}  {:.0}%  (muted)", src.name, src.volume * 100.0)
-            } else {
-                format!("{}  {:.0}%", src.name, src.volume * 100.0)
-            };
-            let lc = if src.muted { RED } else { TEXT_FG };
-            sec.text(&mut pc, &label, 14.0, 0.0, 13.0, lc);
+        if !state.loaded {
+            sec.text(pc, "Loading input devices...", 12.0, 0.0, 12.0, TEXT_DIM);
             sec.spacing(18.0);
+        } else if state.sources.is_empty() {
+            sec.text(pc, "No input devices found", 12.0, 0.0, 12.0, TEXT_DIM);
+            sec.spacing(18.0);
+        }
 
-            if src.active {
-                let bar_w = cw - 100.0;
-                let bar_x = 14.0;
-                let yt = sec.ay();
-                pc.rect(BLANK_BAR, sec.ax(bar_x), yt, bar_w, 8.0);
-                pc.rect(FILL_BAR, sec.ax(bar_x), yt, bar_w * src.volume, 8.0);
-                sec.text(&mut pc, &format!("{:.0}%", src.volume * 100.0), bar_x + bar_w + 8.0, -2.0, 11.0, TEXT_DIM);
-
-                let row_y = sec.ay() + 12.0;
-                let sb_w = 100.0;
-                let sb_h = 26.0;
-                let mute_w = 60.0;
-                let gap = 8.0;
-
-                state.source_spinboxes[idx].value = (src.volume * 100.0).round() as i32;
-                state.source_spinboxes[idx].set_row_rect(sec.ax(8.0), cw - 16.0);
-                render_widget(&mut pc, &mut state.source_spinboxes[idx], sec.ax(bar_x), row_y, sb_w, sb_h);
-
-                let mute_label = if src.muted { "Unmute" } else { "Mute" };
-                let mute_col = if src.muted { MUTED_BG } else { BTN_INACTIVE };
-                pc.button(mute_label, sec.ax(bar_x) + sb_w + gap, row_y, mute_w, sb_h,
-                    mute_col, BTN_HOVER, WHITE,
-                    AppAction::Audio(AudioMessage::SourceMute(src.id)));
-
-                sec.content_y += 12.0 + sb_h + 6.0;
-            } else {
-                sec.content_y += 6.0;
+        if state.loaded {
+            for (idx, src) in state.sources.iter().enumerate() {
+                let label = if !src.active {
+                    format!("{}  (inactive)", src.name)
+                } else if src.muted {
+                    format!("{}  {:.0}%  (muted)", src.name, src.volume * 100.0)
+                } else {
+                    format!("{}  {:.0}%", src.name, src.volume * 100.0)
+                };
+                let lc = if src.muted { RED } else { TEXT_FG };
+                sec.text(pc, &label, 14.0, 0.0, 13.0, lc);
+                sec.spacing(18.0);
+
+                if src.active {
+                    let bar_w = sec_w - 100.0;
+                    let bar_x = 14.0;
+                    let yt = sec.ay();
+                    pc.rect(BLANK_BAR, sec.ax(bar_x), yt, bar_w, 8.0);
+                    pc.rect(FILL_BAR, sec.ax(bar_x), yt, bar_w * src.volume, 8.0);
+                    sec.text(pc, &format!("{:.0}%", src.volume * 100.0), bar_x + bar_w + 8.0, -2.0, 11.0, TEXT_DIM);
+
+                    let row_y = sec.ay() + 12.0;
+                    let sb_w = 100.0;
+                    let sb_h = 26.0;
+                    let mute_w = 60.0;
+                    let gap = 8.0;
+
+                    state.source_spinboxes[idx].value = (src.volume * 100.0).round() as i32;
+                    state.source_spinboxes[idx].set_row_rect(sec.ax(8.0), sec_w - 16.0);
+                    render_widget(pc, &mut state.source_spinboxes[idx], sec.ax(bar_x), row_y, sb_w, sb_h);
+
+                    let mute_label = if src.muted { "Unmute" } else { "Mute" };
+                    let mute_col = if src.muted { MUTED_BG } else { BTN_INACTIVE };
+                    pc.button(mute_label, sec.ax(bar_x) + sb_w + gap, row_y, mute_w, sb_h,
+                        mute_col, BTN_HOVER, WHITE,
+                        AppAction::Audio(AudioMessage::SourceMute(src.id)));
+
+                    sec.content_y += 12.0 + sb_h + 6.0;
+                } else {
+                    sec.content_y += 6.0;
+                }
             }
         }
-    }
 
-    sec.finish_focused(&mut pc, sec_focused.get(1).copied().unwrap_or(false));
+        sec.finish_focused(pc, sec_focused.get(1).copied().unwrap_or(false))
+    });
 
-    pc
+    final_pc
 }
 
+
 pub fn update(state: &mut AudioState, msg: AudioMessage) {
     match msg {
         AudioMessage::Refreshed(new) => {
@@ -361,3 +367,17 @@ pub fn update(state: &mut AudioState, msg: AudioMessage) {
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_view_layout_grid() {
+        let mut state = AudioState::default();
+        let mut layout = GridLayout::new(320.0, 20.0);
+        let pc = view(&mut state, 10.0, 20.0, 800.0, 600.0, &[false, false], &mut layout);
+        assert!(!pc.rects.is_empty() || !pc.texts.is_empty());
+    }
+}
+
diff --git a/src/pages/backup.rs b/src/pages/backup.rs
index 1664c71..04a21d4 100644
--- a/src/pages/backup.rs
+++ b/src/pages/backup.rs
@@ -1,5 +1,5 @@
 use crate::app::{AppAction, PageContent};
-use clear_ui::layout::Section;
+use clear_ui::layout::{Section, PageLayoutBuilder, LayoutStrategy};
 use std::fs;
 
 #[derive(Debug, Clone, Default)]
@@ -96,69 +96,72 @@ const BTN_HOVER: [f32; 4] = [0.28, 0.50, 0.78, 1.0];
 const BTN_DISABLED: [f32; 4] = [0.15, 0.18, 0.22, 1.0];
 const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
 
-pub fn view(state: &BackupState, cx: f32, cy: f32, cw: f32, _ch: f32) -> PageContent {
-    let mut pc = PageContent::new();
-    let y = cy + 12.0;
-
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Full System Backup");
-
-    if !state.loaded {
-        sec.text(&mut pc, "Loading backup state...", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec.spacing(18.0);
-    } else {
-        // Status Row
-        sec.text(&mut pc, "Backup Status", 12.0, 0.0, 12.0, LABEL_FG);
-        let status_text = if state.in_progress { "Backing up..." } else { "Idle" };
-        let status_color = if state.in_progress { GREEN } else { TEXT_FG };
-        sec.text(&mut pc, status_text, 120.0, 0.0, 12.0, status_color);
-        sec.spacing(18.0);
-
-        // Last Backup Row
-        sec.text(&mut pc, "Last Backup", 12.0, 0.0, 12.0, LABEL_FG);
-        sec.text(&mut pc, &state.last_backup_time, 120.0, 0.0, 12.0, TEXT_FG);
-        sec.spacing(18.0);
-
-        // Backup Size Row
-        sec.text(&mut pc, "Archive Size", 12.0, 0.0, 12.0, LABEL_FG);
-        sec.text(&mut pc, &state.backup_size, 120.0, 0.0, 12.0, TEXT_FG);
-        sec.spacing(18.0);
-
-        // Target Directories Row
-        sec.text(&mut pc, "Backup Targets", 12.0, 0.0, 12.0, LABEL_FG);
-        sec.text(&mut pc, "Entire Filesystem (/)  [Preserving attributes]", 120.0, 0.0, 12.0, TEXT_DIM);
-        sec.spacing(18.0);
-
-        // Destination Archive Row
-        sec.text(&mut pc, "Destination", 12.0, 0.0, 12.0, LABEL_FG);
-        sec.text(&mut pc, "USB Drive (/mnt/usb or /run/media/...)", 120.0, 0.0, 12.0, TEXT_DIM);
-        sec.spacing(24.0);
-
-        // Error message if present
-        if let Some(ref err) = state.error_message {
-            sec.text(&mut pc, "Error:", 12.0, 0.0, 12.0, RED);
-            sec.text(&mut pc, err, 60.0, 0.0, 11.0, RED);
-            sec.spacing(18.0);
-        }
+pub fn view(state: &BackupState, cx: f32, cy: f32, cw: f32, ch: f32, layout: &mut dyn LayoutStrategy) -> PageContent {
+    let mut final_pc = PageContent::new();
+    let sec_w = 320.0f32;
+    let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(1);
 
-        // Action Button
-        let btn_w = 120.0;
-        let btn_h = 32.0;
-        let yt = sec.ay();
-        
-        let (btn_label, bg, hover, action) = if state.in_progress {
-            ("Backing up...", BTN_DISABLED, BTN_DISABLED, AppAction::Backup(BackupMessage::StartBackup))
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Full System Backup");
+
+        if !state.loaded {
+            sec.text(pc, "Loading backup state...", 12.0, 0.0, 12.0, TEXT_DIM);
+            sec.spacing(18.0);
         } else {
-            ("Run Backup", BTN_BG, BTN_HOVER, AppAction::Backup(BackupMessage::StartBackup))
-        };
-        
-        sec.row(1, 0.0, btn_h, |_, x, _| {
-            pc.button(btn_label, x, yt, btn_w, btn_h, bg, hover, WHITE, action.clone());
-        });
-        sec.spacing(12.0);
-    }
-    sec.finish(&mut pc);
+            // Status Row
+            sec.text(pc, "Backup Status", 12.0, 0.0, 12.0, LABEL_FG);
+            let status_text = if state.in_progress { "Backing up..." } else { "Idle" };
+            let status_color = if state.in_progress { GREEN } else { TEXT_FG };
+            sec.text(pc, status_text, 120.0, 0.0, 12.0, status_color);
+            sec.spacing(18.0);
+
+            // Last Backup Row
+            sec.text(pc, "Last Backup", 12.0, 0.0, 12.0, LABEL_FG);
+            sec.text(pc, &state.last_backup_time, 120.0, 0.0, 12.0, TEXT_FG);
+            sec.spacing(18.0);
+
+            // Backup Size Row
+            sec.text(pc, "Archive Size", 12.0, 0.0, 12.0, LABEL_FG);
+            sec.text(pc, &state.backup_size, 120.0, 0.0, 12.0, TEXT_FG);
+            sec.spacing(18.0);
+
+            // Target Directories Row
+            sec.text(pc, "Backup Targets", 12.0, 0.0, 12.0, LABEL_FG);
+            sec.text(pc, "Entire Filesystem (/)  [Preserving attributes]", 120.0, 0.0, 12.0, TEXT_DIM);
+            sec.spacing(18.0);
+
+            // Destination Archive Row
+            sec.text(pc, "Destination", 12.0, 0.0, 12.0, LABEL_FG);
+            sec.text(pc, "USB Drive (/mnt/usb or /run/media/...)", 120.0, 0.0, 12.0, TEXT_DIM);
+            sec.spacing(24.0);
+
+            // Error message if present
+            if let Some(ref err) = state.error_message {
+                sec.text(pc, "Error:", 12.0, 0.0, 12.0, RED);
+                sec.text(pc, err, 60.0, 0.0, 11.0, RED);
+                sec.spacing(18.0);
+            }
+
+            // Action Button
+            let btn_w = 120.0;
+            let btn_h = 32.0;
+            let yt = sec.ay();
+            
+            let (btn_label, bg, hover, action) = if state.in_progress {
+                ("Backing up...", BTN_DISABLED, BTN_DISABLED, AppAction::Backup(BackupMessage::StartBackup))
+            } else {
+                ("Run Backup", BTN_BG, BTN_HOVER, AppAction::Backup(BackupMessage::StartBackup))
+            };
+            
+            sec.row(1, 0.0, btn_h, |_, x, _| {
+                pc.button(btn_label, x, yt, btn_w, btn_h, bg, hover, WHITE, action.clone());
+            });
+            sec.spacing(12.0);
+        }
+        sec.finish(pc)
+    });
 
-    pc
+    final_pc
 }
 
 pub fn update(state: &mut BackupState, msg: BackupMessage) {
diff --git a/src/pages/display.rs b/src/pages/display.rs
index 804c258..eca7ba9 100644
--- a/src/pages/display.rs
+++ b/src/pages/display.rs
@@ -1,5 +1,5 @@
 use crate::app::PageContent;
-use clear_ui::layout::{render_widget, Section};
+use clear_ui::layout::{render_widget, Section, PageLayoutBuilder, LayoutStrategy, Subsection};
 use clear_ui::widget::{Spinbox, Label, Widget};
 
 #[derive(Debug, Clone)]
@@ -171,81 +171,90 @@ const TEXT_DIM: [f32; 4] = [0.53, 0.53, 0.60, 1.0];
 const BLANK_BAR: [f32; 4] = [0.15, 0.15, 0.24, 1.0];
 const FILL_BAR: [f32; 4] = [0.30, 0.50, 0.32, 1.0];
 
-pub fn view(state: &mut DisplayState, cx: f32, cy: f32, cw: f32, _ch: f32) -> PageContent {
-    let mut pc = PageContent::new();
-    let mut y = cy + 12.0;
+pub fn view(state: &mut DisplayState, cx: f32, cy: f32, cw: f32, ch: f32, layout: &mut dyn LayoutStrategy) -> PageContent {
+    let mut final_pc = PageContent::new();
+    let sec_w = 320.0f32;
+    let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(3);
 
     // ── Brightness ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Brightness");
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Brightness");
 
-    if !state.loaded {
-        sec.text(&mut pc, "Loading display settings...", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec.spacing(18.0);
-    } else {
-        let bright_pct = if state.max_brightness > 0.0 {
-            (state.brightness / state.max_brightness * 100.0).round() as i32
-        } else { 0 };
+        if !state.loaded {
+            sec.text(pc, "Loading display settings...", 12.0, 0.0, 12.0, TEXT_DIM);
+            sec.spacing(18.0);
+        } else {
+            let bright_pct = if state.max_brightness > 0.0 {
+                (state.brightness / state.max_brightness * 100.0).round() as i32
+            } else { 0 };
 
-        let bar_w = cw - 100.0;
-        let yt = sec.ay();
-        pc.rect(BLANK_BAR, sec.ax(12.0), yt, bar_w, 8.0);
-        pc.rect(FILL_BAR, sec.ax(12.0), yt, bar_w * bright_pct as f32 / 100.0, 8.0);
-        pc.text(&format!("{}%", bright_pct), sec.ax(16.0 + bar_w), yt - 2.0, 11.0, TEXT_DIM);
-        sec.content_y += 14.0;
+            let bar_w = sec_w - 100.0;
+            let yt = sec.ay();
+            pc.rect(BLANK_BAR, sec.ax(12.0), yt, bar_w, 8.0);
+            pc.rect(FILL_BAR, sec.ax(12.0), yt, bar_w * bright_pct as f32 / 100.0, 8.0);
+            pc.text(&format!("{}%", bright_pct), sec.ax(16.0 + bar_w), yt - 2.0, 11.0, TEXT_DIM);
+            sec.content_y += 14.0;
 
-        let yt = sec.ay();
-        let sb_w = 100.0;
-        let sb_h = 26.0;
-        state.brightness_spinbox.value = bright_pct;
-        state.brightness_spinbox.set_row_rect(sec.ax(8.0), cw - 16.0);
-        render_widget(&mut pc, &mut state.brightness_spinbox, sec.ax(12.0), yt, sb_w, sb_h);
-        sec.content_y += sb_h + 12.0;
-    }
-    y = sec.finish(&mut pc);
+            let yt = sec.ay();
+            let sb_w = 100.0;
+            let sb_h = 26.0;
+            state.brightness_spinbox.value = bright_pct;
+            state.brightness_spinbox.set_row_rect(sec.ax(8.0), sec_w - 16.0);
+            render_widget(pc, &mut state.brightness_spinbox, sec.ax(12.0), yt, sb_w, sb_h);
+            sec.content_y += sb_h + 12.0;
+        }
+        sec.finish(pc)
+    });
 
     // ── Night Light ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Night Light");
-    if !state.loaded {
-        sec.text(&mut pc, "Loading...", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec.spacing(18.0);
-    } else {
-        let nl_label = if state.night_light { "Night Light: ON" } else { "Night Light: OFF" };
-        state.night_light_label.set_text(nl_label);
-        sec.widget(&mut pc, &mut state.night_light_label, 12.0, cw - 24.0, 20.0);
-        sec.spacing(8.0);
-    }
-    y = sec.finish(&mut pc);
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Night Light");
+        if !state.loaded {
+            sec.text(pc, "Loading...", 12.0, 0.0, 12.0, TEXT_DIM);
+            sec.spacing(18.0);
+        } else {
+            let nl_label = if state.night_light { "Night Light: ON" } else { "Night Light: OFF" };
+            state.night_light_label.set_text(nl_label);
+            sec.widget(pc, &mut state.night_light_label, 12.0, sec_w - 24.0, 20.0);
+            sec.spacing(8.0);
+        }
+        sec.finish(pc)
+    });
 
     // ── Outputs ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Outputs");
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Outputs");
 
-    if !state.loaded {
-        sec.text(&mut pc, "Loading outputs...", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec.spacing(18.0);
-    } else {
-        for out in &mut state.outputs {
-            let yt = sec.ay();
-            
-            // Name label at x = 14.0
-            render_widget(&mut pc, &mut out.name_label, sec.ax(14.0), yt, 100.0, 20.0);
-            
-            // Resolution label at x = 120.0
-            render_widget(&mut pc, &mut out.resolution_label, sec.ax(120.0), yt, 160.0, 20.0);
-            
-            // Scale label at x = 290.0 if present
-            if let Some(ref mut scale_lbl) = out.scale_label {
-                render_widget(&mut pc, scale_lbl, sec.ax(290.0), yt, cw - 304.0, 20.0);
+        if !state.loaded {
+            sec.text(pc, "Loading outputs...", 12.0, 0.0, 12.0, TEXT_DIM);
+            sec.spacing(18.0);
+        } else {
+            for out in &mut state.outputs {
+                let mut subsec = Subsection::new(
+                    pc,
+                    sec.ax(0.0) + Section::ROW_PADDING_X,
+                    sec.content_y,
+                    sec.cw - 2.0 * Section::ROW_PADDING_X,
+                    &out.name,
+                );
+                
+                subsec.widget(pc, &mut out.resolution_label, 12.0, 240.0, 20.0);
+                
+                if let Some(ref mut scale_lbl) = out.scale_label {
+                    subsec.widget(pc, scale_lbl, 12.0, 240.0, 20.0);
+                }
+                
+                let sub_h = subsec.finish(pc);
+                sec.content_y = sub_h;
             }
-            
-            sec.content_y += 20.0;
-            sec.spacing(12.0);
         }
-    }
-    sec.finish(&mut pc);
+        sec.finish(pc)
+    });
 
-    pc
+    final_pc
 }
 
+
 pub fn update(state: &mut DisplayState, msg: DisplayMessage) {
     match msg {
         DisplayMessage::Refreshed(new) => {
@@ -281,3 +290,17 @@ pub fn update(state: &mut DisplayState, msg: DisplayMessage) {
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_view_layout_grid() {
+        let mut state = DisplayState::default();
+        let mut layout = clear_ui::layout::GridLayout::new(320.0, 20.0);
+        let pc = view(&mut state, 10.0, 20.0, 800.0, 600.0, &mut layout);
+        assert!(!pc.rects.is_empty() || !pc.texts.is_empty());
+    }
+}
+
diff --git a/src/pages/hardware.rs b/src/pages/hardware.rs
index 53d73d6..26013c5 100644
--- a/src/pages/hardware.rs
+++ b/src/pages/hardware.rs
@@ -1,5 +1,5 @@
 use crate::app::{AppAction, PageContent};
-use clear_ui::layout::Section;
+use clear_ui::layout::{Section, PageLayoutBuilder, LayoutStrategy};
 use clear_ui::widget::{Label, ScrollingList};
 
 #[derive(Debug, Clone, Default)]
@@ -352,209 +352,220 @@ const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
 const RED: [f32; 4] = [1.0, 0.33, 0.33, 1.0];
 const ORANGE: [f32; 4] = [1.0, 0.73, 0.20, 1.0];
 
-pub fn view(state: &mut HardwareState, cx: f32, cy: f32, cw: f32, _ch: f32, root_focused: bool) -> PageContent {
-    let mut pc = PageContent::new();
-    let mut y = cy + 12.0;
+pub fn view(state: &mut HardwareState, cx: f32, cy: f32, cw: f32, ch: f32, root_focused: bool, layout: &mut dyn LayoutStrategy) -> PageContent {
+    let mut final_pc = PageContent::new();
+    let sec_w = 320.0f32;
+    let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(5);
 
     // ── CPU Section ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "CPU");
-    if !state.loaded {
-        sec.text(&mut pc, "Loading CPU model and utilization...", 12.0, 0.0, 12.0, TEXT_FG);
-        sec.spacing(10.0);
-    } else {
-        // CPU Info Label
-        sec.widget(&mut pc, &mut state.cpu_label, 12.0, cw - 24.0, 26.0);
-        sec.spacing(12.0);
-
-        // Scrolling box configuration for process list
-        let list_box_x = cx + 12.0;
-        let list_box_y = sec.ay();
-        let list_box_w = cw - 24.0;
-        let list_box_h = 220.0;
-        
-        // Render the standardized ScrollBox widget
-        clear_ui::layout::render_widget(&mut pc, &mut state.cpu_list_box, list_box_x, list_box_y, list_box_w, list_box_h);
-
-        // Header for process list columns (drawn static on top of the ScrollBox background)
-        let header_h = 22.0;
-        pc.rect([0.12, 0.12, 0.16, 0.5], list_box_x + 1.0, list_box_y + 1.0, list_box_w - 2.0, header_h);
-        pc.rect([0.18, 0.18, 0.24, 1.0], list_box_x + 1.0, list_box_y + header_h, list_box_w - 2.0, 1.0); // Divider
-        
-        pc.text("PID", list_box_x + 12.0, list_box_y + 5.0, 11.0, [0.53, 0.53, 0.60, 1.0]);
-        pc.text("COMMAND", list_box_x + 80.0, list_box_y + 5.0, 11.0, [0.53, 0.53, 0.60, 1.0]);
-        pc.text("CPU %", list_box_x + list_box_w - 60.0, list_box_y + 5.0, 11.0, [0.53, 0.53, 0.60, 1.0]);
-
-        let row_h = 24.0;
-        // Update ScrollingList bounds for the scrollable viewport (which starts below the header)
-        state.cpu_list_box.update_bounds(state.processes.len(), list_box_y + header_h, list_box_h - header_h - 6.0);
-
-        // Visible process rows rendering (virtualized/clipped)
-        for (idx, (pid, cpu, comm)) in state.processes.iter().enumerate() {
-            if let Some(draw_y) = state.cpu_list_box.get_item_draw_y(idx, 4.0) {
-                // Standard row action button (transparent background, highlights on hover)
-                pc.button(
-                    "",
-                    list_box_x + 2.0,
-                    draw_y,
-                    list_box_w - 16.0,
-                    row_h,
-                    [0.0, 0.0, 0.0, 0.0],
-                    [1.0, 1.0, 1.0, 0.06],
-                    [0.0, 0.0, 0.0, 0.0],
-                    AppAction::Hardware(HardwareMessage::None),
-                );
-                
-                pc.text(pid, list_box_x + 12.0, draw_y + 6.0, 12.0, [0.80, 0.80, 0.85, 1.0]);
-                pc.text(comm, list_box_x + 80.0, draw_y + 6.0, 12.0, [0.80, 0.80, 0.85, 1.0]);
-                pc.text(&format!("{}%", cpu), list_box_x + list_box_w - 60.0, draw_y + 6.0, 12.0, [0.56, 0.83, 0.56, 1.0]);
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "CPU");
+        if !state.loaded {
+            sec.text(pc, "Loading CPU model and utilization...", 12.0, 0.0, 12.0, TEXT_FG);
+            sec.spacing(10.0);
+        } else {
+            // CPU Info Label
+            sec.widget(pc, &mut state.cpu_label, 12.0, sec_w - 24.0, 26.0);
+            sec.spacing(12.0);
+
+            // Scrolling box configuration for process list
+            let list_box_x = rx + 12.0;
+            let list_box_y = sec.ay();
+            let list_box_w = sec_w - 24.0;
+            let list_box_h = 220.0;
+            
+            // Render the standardized ScrollBox widget
+            clear_ui::layout::render_widget(pc, &mut state.cpu_list_box, list_box_x, list_box_y, list_box_w, list_box_h);
+
+            // Header for process list columns (drawn static on top of the ScrollBox background)
+            let header_h = 22.0;
+            pc.rect([0.12, 0.12, 0.16, 0.5], list_box_x + 1.0, list_box_y + 1.0, list_box_w - 2.0, header_h);
+            pc.rect([0.18, 0.18, 0.24, 1.0], list_box_x + 1.0, list_box_y + header_h, list_box_w - 2.0, 1.0); // Divider
+            
+            pc.text("PID", list_box_x + 12.0, list_box_y + 5.0, 11.0, [0.53, 0.53, 0.60, 1.0]);
+            pc.text("COMMAND", list_box_x + 80.0, list_box_y + 5.0, 11.0, [0.53, 0.53, 0.60, 1.0]);
+            pc.text("CPU %", list_box_x + list_box_w - 60.0, list_box_y + 5.0, 11.0, [0.53, 0.53, 0.60, 1.0]);
+
+            let row_h = 24.0;
+            // Update ScrollingList bounds for the scrollable viewport (which starts below the header)
+            state.cpu_list_box.update_bounds(state.processes.len(), list_box_y + header_h, list_box_h - header_h - 6.0);
+
+            // Visible process rows rendering (virtualized/clipped)
+            for (idx, (pid, cpu, comm)) in state.processes.iter().enumerate() {
+                if let Some(draw_y) = state.cpu_list_box.get_item_draw_y(idx, 4.0) {
+                    // Standard row action button (transparent background, highlights on hover)
+                    pc.button(
+                        "",
+                        list_box_x + 2.0,
+                        draw_y,
+                        list_box_w - 16.0,
+                        row_h,
+                        [0.0, 0.0, 0.0, 0.0],
+                        [1.0, 1.0, 1.0, 0.06],
+                        [0.0, 0.0, 0.0, 0.0],
+                        AppAction::Hardware(HardwareMessage::None),
+                    );
+                    
+                    pc.text(pid, list_box_x + 12.0, draw_y + 6.0, 12.0, [0.80, 0.80, 0.85, 1.0]);
+                    pc.text(comm, list_box_x + 80.0, draw_y + 6.0, 12.0, [0.80, 0.80, 0.85, 1.0]);
+                    pc.text(&format!("{}%", cpu), list_box_x + list_box_w - 60.0, draw_y + 6.0, 12.0, [0.56, 0.83, 0.56, 1.0]);
+                }
+            }
+            
+            if state.processes.is_empty() {
+                pc.text("No active processes", list_box_x + 12.0, list_box_y + header_h + 16.0, 12.0, TEXT_DIM);
             }
-        }
-        
-        if state.processes.is_empty() {
-            pc.text("No active processes", list_box_x + 12.0, list_box_y + header_h + 16.0, 12.0, TEXT_DIM);
-        }
 
-        sec.content_y += list_box_h;
-    }
-    y = sec.finish_focused(&mut pc, root_focused);
+            sec.content_y += list_box_h;
+        }
+        sec.finish_focused(pc, root_focused)
+    });
 
     // ── GPU Section ──
-    let mut sec_gpu = Section::new(&mut pc, cx, y, cw, "GPU");
-    if !state.loaded {
-        sec_gpu.text(&mut pc, "Loading GPU models...", 12.0, 0.0, 12.0, TEXT_FG);
-        sec_gpu.spacing(10.0);
-    } else {
-        for (i, gpu_lbl) in state.gpu_labels.iter_mut().enumerate() {
-            if i > 0 { sec_gpu.spacing(12.0); }
-            sec_gpu.widget(&mut pc, gpu_lbl, 12.0, cw - 24.0, 26.0);
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec_gpu = Section::new(pc, rx, ry, sec_w, "GPU");
+        if !state.loaded {
+            sec_gpu.text(pc, "Loading GPU models...", 12.0, 0.0, 12.0, TEXT_FG);
+            sec_gpu.spacing(10.0);
+        } else {
+            for (i, gpu_lbl) in state.gpu_labels.iter_mut().enumerate() {
+                if i > 0 { sec_gpu.spacing(12.0); }
+                sec_gpu.widget(pc, gpu_lbl, 12.0, sec_w - 24.0, 26.0);
+            }
         }
-    }
-    y = sec_gpu.finish(&mut pc);
+        sec_gpu.finish(pc)
+    });
 
     // ── Battery Section ──
-    let mut sec_bat = Section::new(&mut pc, cx, y, cw, "Battery");
-    if !state.loaded {
-        sec_bat.text(&mut pc, "Loading battery status...", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec_bat.spacing(18.0);
-    } else {
-        let bat = &state.battery;
-        let bat_icon = match bat.state.as_str() {
-            "charging" => "+",
-            "fully-charged" => "=",
-            _ => "",
-        };
-
-        let pct_color = if bat.percentage < 20.0 { RED }
-            else if bat.percentage < 50.0 { ORANGE }
-            else { ACCENT };
-
-        let pct_str = format!("{} {:.0}%", bat_icon, bat.percentage);
-        sec_bat.text(&mut pc, &pct_str, 12.0, 0.0, 24.0, pct_color);
-        sec_bat.spacing(30.0);
-
-        let state_str = format!("{}  •  {:.1}W  •  {:.1}/{:.1} Wh",
-            bat.state, bat.energy_rate, bat.energy, bat.energy_full);
-        sec_bat.text(&mut pc, &state_str, 12.0, 0.0, 12.0, TEXT_DIM);
-        sec_bat.spacing(18.0);
-
-        let time_str = if bat.time_to_empty > 0 {
-            format!("Time remaining: {}", format_duration(bat.time_to_empty))
-        } else if bat.time_to_full > 0 {
-            format!("Time to full: {}", format_duration(bat.time_to_full))
-        } else { String::new() };
-        if !time_str.is_empty() {
-            sec_bat.text(&mut pc, &time_str, 12.0, 0.0, 12.0, TEXT_DIM);
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec_bat = Section::new(pc, rx, ry, sec_w, "Battery");
+        if !state.loaded {
+            sec_bat.text(pc, "Loading battery status...", 12.0, 0.0, 12.0, TEXT_DIM);
+            sec_bat.spacing(18.0);
+        } else {
+            let bat = &state.battery;
+            let bat_icon = match bat.state.as_str() {
+                "charging" => "+",
+                "fully-charged" => "=",
+                _ => "",
+            };
+
+            let pct_color = if bat.percentage < 20.0 { RED }
+                else if bat.percentage < 50.0 { ORANGE }
+                else { ACCENT };
+
+            let pct_str = format!("{} {:.0}%", bat_icon, bat.percentage);
+            sec_bat.text(pc, &pct_str, 12.0, 0.0, 24.0, pct_color);
+            sec_bat.spacing(30.0);
+
+            let state_str = format!("{}  •  {:.1}W  •  {:.1}/{:.1} Wh",
+                bat.state, bat.energy_rate, bat.energy, bat.energy_full);
+            sec_bat.text(pc, &state_str, 12.0, 0.0, 12.0, TEXT_DIM);
             sec_bat.spacing(18.0);
-        }
 
-        let detail_str = format!("{}  {}", bat.vendor, bat.model);
-        sec_bat.text(&mut pc, &detail_str, 12.0, 0.0, 11.0, TEXT_DIM);
-        sec_bat.spacing(20.0);
+            let time_str = if bat.time_to_empty > 0 {
+                format!("Time remaining: {}", format_duration(bat.time_to_empty))
+            } else if bat.time_to_full > 0 {
+                format!("Time to full: {}", format_duration(bat.time_to_full))
+            } else { String::new() };
+            if !time_str.is_empty() {
+                sec_bat.text(pc, &time_str, 12.0, 0.0, 12.0, TEXT_DIM);
+                sec_bat.spacing(18.0);
+            }
 
-        let ac_str = if state.on_ac { "On AC Power" } else { "On Battery" };
-        sec_bat.text(&mut pc, ac_str, 12.0, 0.0, 14.0, TEXT_FG);
-    }
-    y = sec_bat.finish(&mut pc);
+            let detail_str = format!("{}  {}", bat.vendor, bat.model);
+            sec_bat.text(pc, &detail_str, 12.0, 0.0, 11.0, TEXT_DIM);
+            sec_bat.spacing(20.0);
+
+            let ac_str = if state.on_ac { "On AC Power" } else { "On Battery" };
+            sec_bat.text(pc, ac_str, 12.0, 0.0, 14.0, TEXT_FG);
+        }
+        sec_bat.finish(pc)
+    });
 
     // ── CPU Governor section ──
-    let mut sec_gov = Section::new(&mut pc, cx, y, cw, "CPU Governor");
-    if !state.loaded {
-        sec_gov.text(&mut pc, "Loading CPU governor...", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec_gov.spacing(18.0);
-    } else {
-        let btn_h = 44.0;
-        let yt = sec_gov.ay();
-
-        sec_gov.row(2, 8.0, btn_h, |i, x, w| {
-            if i == 0 {
-                let perf_active = !state.cpu_powersave;
-                let (perf_bg, perf_desc, perf_desc_color) = if perf_active {
-                    (BTN_ACTIVE, "Governor set to performance", ACCENT)
-                } else {
-                    (BTN_INACTIVE, "Switch to performance governor", TEXT_DIM)
-                };
-
-                pc.button("Performance", x, yt, w, btn_h,
-                    perf_bg, BTN_HOVER, WHITE,
-                    AppAction::Hardware(HardwareMessage::SetCpuPerformance));
-                pc.text(perf_desc, x + 4.0, yt + 26.0, 10.0, perf_desc_color);
-            } else {
-                let (save_bg, save_desc, save_desc_color) = if state.cpu_powersave {
-                    (BTN_ACTIVE, "Governor set to powersave — lower power, slower burst", ACCENT)
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec_gov = Section::new(pc, rx, ry, sec_w, "CPU Governor");
+        if !state.loaded {
+            sec_gov.text(pc, "Loading CPU governor...", 12.0, 0.0, 12.0, TEXT_DIM);
+            sec_gov.spacing(18.0);
+        } else {
+            let btn_h = 44.0;
+            let yt = sec_gov.ay();
+
+            sec_gov.row(2, 8.0, btn_h, |i, x, w| {
+                if i == 0 {
+                    let perf_active = !state.cpu_powersave;
+                    let (perf_bg, perf_desc, perf_desc_color) = if perf_active {
+                        (BTN_ACTIVE, "Governor set to performance", ACCENT)
+                    } else {
+                        (BTN_INACTIVE, "Switch to performance governor", TEXT_DIM)
+                    };
+
+                    pc.button("Performance", x, yt, w, btn_h,
+                        perf_bg, BTN_HOVER, WHITE,
+                        AppAction::Hardware(HardwareMessage::SetCpuPerformance));
+                    pc.text(perf_desc, x + 4.0, yt + 26.0, 10.0, perf_desc_color);
                 } else {
-                    (BTN_INACTIVE, "Switch to powersave governor (requires auth)", TEXT_DIM)
-                };
-
-                pc.button("Powersave", x, yt, w, btn_h,
-                    save_bg, BTN_HOVER, WHITE,
-                    AppAction::Hardware(HardwareMessage::SetCpuPowersave));
-                pc.text(save_desc, x + 4.0, yt + 26.0, 10.0, save_desc_color);
-            }
-        });
-        sec_gov.spacing(12.0);
-    }
-    y = sec_gov.finish(&mut pc);
+                    let (save_bg, save_desc, save_desc_color) = if state.cpu_powersave {
+                        (BTN_ACTIVE, "Governor set to powersave — lower power, slower burst", ACCENT)
+                    } else {
+                        (BTN_INACTIVE, "Switch to powersave governor (requires auth)", TEXT_DIM)
+                    };
+
+                    pc.button("Powersave", x, yt, w, btn_h,
+                        save_bg, BTN_HOVER, WHITE,
+                        AppAction::Hardware(HardwareMessage::SetCpuPowersave));
+                    pc.text(save_desc, x + 4.0, yt + 26.0, 10.0, save_desc_color);
+                }
+            });
+            sec_gov.spacing(12.0);
+        }
+        sec_gov.finish(pc)
+    });
 
     // ── GPU Power section ──
-    let mut sec_gpow = Section::new(&mut pc, cx, y, cw, "GPU Power");
-    if !state.loaded {
-        sec_gpow.text(&mut pc, "Loading GPU power status...", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec_gpow.spacing(18.0);
-    } else {
-        let btn_h = 44.0;
-        let yt = sec_gpow.ay();
-
-        sec_gpow.row(2, 8.0, btn_h, |i, x, w| {
-            if i == 0 {
-                let gpu_def_active = !state.gpu_powersave;
-                let (gpu_def_bg, gpu_def_desc, gpu_def_desc_c) = if gpu_def_active {
-                    (BTN_ACTIVE, "NVIDIA running at default power limit", ACCENT)
-                } else {
-                    (BTN_INACTIVE, "Restore default power limit (requires auth)", TEXT_DIM)
-                };
-
-                pc.button("80W Default", x, yt, w, btn_h,
-                    gpu_def_bg, BTN_HOVER, WHITE,
-                    AppAction::Hardware(HardwareMessage::SetGpuDefault));
-                pc.text(gpu_def_desc, x + 4.0, yt + 26.0, 10.0, gpu_def_desc_c);
-            } else {
-                let (gpu_cap_bg, gpu_cap_desc, gpu_cap_desc_c) = if state.gpu_powersave {
-                    (BTN_ACTIVE, "NVIDIA power limit capped at 5W — minimal draw", ACCENT)
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec_gpow = Section::new(pc, rx, ry, sec_w, "GPU Power");
+        if !state.loaded {
+            sec_gpow.text(pc, "Loading GPU power status...", 12.0, 0.0, 12.0, TEXT_DIM);
+            sec_gpow.spacing(18.0);
+        } else {
+            let btn_h = 44.0;
+            let yt = sec_gpow.ay();
+
+            sec_gpow.row(2, 8.0, btn_h, |i, x, w| {
+                if i == 0 {
+                    let gpu_def_active = !state.gpu_powersave;
+                    let (gpu_def_bg, gpu_def_desc, gpu_def_desc_c) = if gpu_def_active {
+                        (BTN_ACTIVE, "NVIDIA running at default power limit", ACCENT)
+                    } else {
+                        (BTN_INACTIVE, "Restore default power limit (requires auth)", TEXT_DIM)
+                    };
+
+                    pc.button("80W Default", x, yt, w, btn_h,
+                        gpu_def_bg, BTN_HOVER, WHITE,
+                        AppAction::Hardware(HardwareMessage::SetGpuDefault));
+                    pc.text(gpu_def_desc, x + 4.0, yt + 26.0, 10.0, gpu_def_desc_c);
                 } else {
-                    (BTN_INACTIVE, "Cap NVIDIA to 5W power limit (requires auth)", TEXT_DIM)
-                };
-
-                pc.button("5W Cap", x, yt, w, btn_h,
-                    gpu_cap_bg, BTN_HOVER, WHITE,
-                    AppAction::Hardware(HardwareMessage::SetGpuPowersave));
-                pc.text(gpu_cap_desc, x + 4.0, yt + 26.0, 10.0, gpu_cap_desc_c);
-            }
-        });
-        sec_gpow.spacing(12.0);
-    }
-    sec_gpow.finish(&mut pc);
+                    let (gpu_cap_bg, gpu_cap_desc, gpu_cap_desc_c) = if state.gpu_powersave {
+                        (BTN_ACTIVE, "NVIDIA power limit capped at 5W — minimal draw", ACCENT)
+                    } else {
+                        (BTN_INACTIVE, "Cap NVIDIA to 5W power limit (requires auth)", TEXT_DIM)
+                    };
+
+                    pc.button("5W Cap", x, yt, w, btn_h,
+                        gpu_cap_bg, BTN_HOVER, WHITE,
+                        AppAction::Hardware(HardwareMessage::SetGpuPowersave));
+                    pc.text(gpu_cap_desc, x + 4.0, yt + 26.0, 10.0, gpu_cap_desc_c);
+                }
+            });
+            sec_gpow.spacing(12.0);
+        }
+        sec_gpow.finish(pc)
+    });
 
-    pc
+    final_pc
 }
 
 pub fn update(state: &mut HardwareState, msg: HardwareMessage) {
diff --git a/src/pages/input.rs b/src/pages/input.rs
index 4cb1159..11e30a4 100644
--- a/src/pages/input.rs
+++ b/src/pages/input.rs
@@ -2,8 +2,8 @@ use std::fs;
 use std::io::Write;
 
 use crate::app::PageContent;
-use clear_ui::layout::Section;
-use clear_ui::widget::{Dropdown, Spinbox, Toggle, Widget, Finger, Trackpad};
+use clear_ui::layout::{Section, PageLayoutBuilder, LayoutStrategy};
+use clear_ui::widget::{Spinbox, Toggle, Trackpad, Dropdown, Finger, Widget};
 
 const CONFIG_PATH: &str = "/home/lsgalante/.config/ccec/config.toml";
 
@@ -389,126 +389,146 @@ fn apply_repeat_config(rate: u16, delay: u16) {
 const TEXT_FG: [f32; 4] = [0.83, 0.83, 0.83, 1.0];
 const TEXT_DIM: [f32; 4] = [0.53, 0.53, 0.60, 1.0];
 
-pub fn view(state: &mut InputState, cx: f32, cy: f32, cw: f32, _ch: f32, sec_focused: &[bool]) -> PageContent {
-    let mut pc = PageContent::new();
-    let mut y = cy + 12.0;
+pub fn view(state: &mut InputState, cx: f32, cy: f32, cw: f32, ch: f32, sec_focused: &[bool], layout: &mut dyn LayoutStrategy) -> PageContent {
+    let mut final_pc = PageContent::new();
+    let sec_w = 320.0f32;
+    let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(7);
 
     // ── Touchpad ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Touchpad");
-
-    let toggle_w = 48.0;
-    let toggle_h = 24.0;
-    state.tap_toggle.set_toggled(state.tap_to_click);
-    sec.widget(&mut pc, &mut state.tap_toggle, 14.0, toggle_w, toggle_h);
-    sec.spacing(8.0);
-
-    // Built-in trackpad visualizer widget
-    let pad_w = 280.0;
-    let pad_h = 140.0;
-    state.trackpad.set_fingers(state.fingers.clone());
-    sec.widget(&mut pc, &mut state.trackpad, 14.0, pad_w, pad_h);
-    sec.spacing(12.0);
-    y = sec.finish(&mut pc);
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Touchpad");
+
+        let toggle_w = 48.0;
+        let toggle_h = 24.0;
+        state.tap_toggle.set_toggled(state.tap_to_click);
+        sec.widget(pc, &mut state.tap_toggle, 14.0, toggle_w, toggle_h);
+        sec.spacing(8.0);
+
+        // Built-in trackpad visualizer widget
+        let pad_w = 280.0;
+        let pad_h = 140.0;
+        state.trackpad.set_fingers(state.fingers.clone());
+        sec.widget(pc, &mut state.trackpad, 14.0, pad_w, pad_h);
+        sec.spacing(12.0);
+        sec.finish(pc)
+    });
 
     // ── Trackpoint ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Trackpoint");
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Trackpoint");
 
-    state.dwtp_toggle.set_toggled(state.dwtp);
-    sec.widget(&mut pc, &mut state.dwtp_toggle, 14.0, toggle_w, toggle_h);
-    sec.spacing(12.0);
+        let toggle_w = 48.0;
+        let toggle_h = 24.0;
+        state.dwtp_toggle.set_toggled(state.dwtp);
+        sec.widget(pc, &mut state.dwtp_toggle, 14.0, toggle_w, toggle_h);
+        sec.spacing(12.0);
 
-    sec.widget(&mut pc, &mut state.trackpoint_accel_speed_spinbox, 14.0, 200.0, 26.0);
-    sec.spacing(12.0);
+        sec.widget(pc, &mut state.trackpoint_accel_speed_spinbox, 14.0, 200.0, 26.0);
+        sec.spacing(12.0);
 
-    sec.widget(&mut pc, &mut state.trackpoint_accel_profile_menu, 14.0, 200.0, 26.0);
-    sec.spacing(8.0);
-    y = sec.finish_focused(&mut pc, sec_focused.get(0).copied().unwrap_or(false));
+        sec.widget(pc, &mut state.trackpoint_accel_profile_menu, 14.0, 200.0, 26.0);
+        sec.spacing(8.0);
+        sec.finish_focused(pc, sec_focused.first().copied().unwrap_or(false))
+    });
 
     // ── Keyboard ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Keyboard");
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Keyboard");
 
-    sec.widget(&mut pc, &mut state.rate_spinbox, 14.0, 200.0, 26.0);
-    sec.spacing(8.0);
+        sec.widget(pc, &mut state.rate_spinbox, 14.0, 200.0, 26.0);
+        sec.spacing(8.0);
 
-    sec.widget(&mut pc, &mut state.delay_spinbox, 14.0, 200.0, 26.0);
-    sec.spacing(8.0);
-    y = sec.finish_focused(&mut pc, sec_focused.get(1).copied().unwrap_or(false));
+        sec.widget(pc, &mut state.delay_spinbox, 14.0, 200.0, 26.0);
+        sec.spacing(8.0);
+        sec.finish_focused(pc, sec_focused.get(1).copied().unwrap_or(false))
+    });
 
     // ── Cursor ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Cursor");
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Cursor");
 
-    sec.widget(&mut pc, &mut state.cursor_theme_menu, 14.0, 200.0, 26.0);
-    sec.spacing(12.0);
+        sec.widget(pc, &mut state.cursor_theme_menu, 14.0, 200.0, 26.0);
+        sec.spacing(12.0);
 
-    sec.widget(&mut pc, &mut state.cursor_size_spinbox, 14.0, 200.0, 26.0);
-    sec.spacing(8.0);
+        sec.widget(pc, &mut state.cursor_size_spinbox, 14.0, 200.0, 26.0);
+        sec.spacing(8.0);
 
-    y = sec.finish_focused(&mut pc, sec_focused.get(2).copied().unwrap_or(false));
+        sec.finish_focused(pc, sec_focused.get(2).copied().unwrap_or(false))
+    });
 
     // ── Scrolling ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Scrolling");
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Scrolling");
 
-    state.scroll_toggle.set_toggled(state.inertial_scroll);
-    sec.widget(&mut pc, &mut state.scroll_toggle, 14.0, toggle_w, toggle_h);
-    sec.spacing(12.0);
+        let toggle_w = 48.0;
+        let toggle_h = 24.0;
+        state.scroll_toggle.set_toggled(state.inertial_scroll);
+        sec.widget(pc, &mut state.scroll_toggle, 14.0, toggle_w, toggle_h);
+        sec.spacing(12.0);
 
-    sec.widget(&mut pc, &mut state.scroll_friction_spinbox, 14.0, 200.0, 26.0);
-    sec.spacing(12.0);
+        sec.widget(pc, &mut state.scroll_friction_spinbox, 14.0, 200.0, 26.0);
+        sec.spacing(12.0);
 
-    state.natural_toggle.set_toggled(state.natural_scroll);
-    sec.widget(&mut pc, &mut state.natural_toggle, 14.0, toggle_w, toggle_h);
-    sec.spacing(12.0);
+        state.natural_toggle.set_toggled(state.natural_scroll);
+        sec.widget(pc, &mut state.natural_toggle, 14.0, toggle_w, toggle_h);
+        sec.spacing(12.0);
 
-    sec.widget(&mut pc, &mut state.scroll_speed_spinbox, 14.0, 200.0, 26.0);
-    sec.spacing(8.0);
+        sec.widget(pc, &mut state.scroll_speed_spinbox, 14.0, 200.0, 26.0);
+        sec.spacing(8.0);
 
-    y = sec.finish_focused(&mut pc, sec_focused.get(3).copied().unwrap_or(false));
+        sec.finish_focused(pc, sec_focused.get(3).copied().unwrap_or(false))
+    });
 
     // ── Inertial Input ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Inertial Input");
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Inertial Input");
 
-    state.pointer_toggle.set_toggled(state.inertial_pointer);
-    sec.widget(&mut pc, &mut state.pointer_toggle, 14.0, toggle_w, toggle_h);
-    sec.spacing(12.0);
+        let toggle_w = 48.0;
+        let toggle_h = 24.0;
+        state.pointer_toggle.set_toggled(state.inertial_pointer);
+        sec.widget(pc, &mut state.pointer_toggle, 14.0, toggle_w, toggle_h);
+        sec.spacing(12.0);
 
-    sec.widget(&mut pc, &mut state.pointer_friction_spinbox, 14.0, 200.0, 26.0);
-    sec.spacing(16.0);
+        sec.widget(pc, &mut state.pointer_friction_spinbox, 14.0, 200.0, 26.0);
+        sec.spacing(16.0);
 
-    state.trackpad_toggle.set_toggled(state.inertial_trackpad);
-    sec.widget(&mut pc, &mut state.trackpad_toggle, 14.0, toggle_w, toggle_h);
-    sec.spacing(12.0);
+        state.trackpad_toggle.set_toggled(state.inertial_trackpad);
+        sec.widget(pc, &mut state.trackpad_toggle, 14.0, toggle_w, toggle_h);
+        sec.spacing(12.0);
 
-    sec.widget(&mut pc, &mut state.trackpad_friction_spinbox, 14.0, 200.0, 26.0);
-    sec.spacing(8.0);
+        sec.widget(pc, &mut state.trackpad_friction_spinbox, 14.0, 200.0, 26.0);
+        sec.spacing(8.0);
 
-    y = sec.finish_focused(&mut pc, sec_focused.get(4).copied().unwrap_or(false));
+        sec.finish_focused(pc, sec_focused.get(4).copied().unwrap_or(false))
+    });
 
     // ── Keybindings ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Keyboard Bindings");
-
-    for kb in &state.keybinds {
-        let binding = if kb.mods.is_empty() {
-            kb.key.clone()
-        } else {
-            format!("{}+{}", kb.mods, kb.key)
-        };
-        let action_label = if kb.command.is_empty() {
-            kb.action.clone()
-        } else {
-            format!("{}: {}", kb.action, kb.command)
-        };
-        sec.text(&mut pc, &binding, 14.0, 0.0, 12.0, TEXT_FG);
-        let label_w = cw - 200.0;
-        sec.text(&mut pc, &action_label, 14.0 + label_w.min(180.0), 0.0, 12.0, TEXT_DIM);
-        sec.spacing(18.0);
-    }
-    sec.finish(&mut pc);
-
-
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Keyboard Bindings");
+
+        for kb in &state.keybinds {
+            let binding = if kb.mods.is_empty() {
+                kb.key.clone()
+            } else {
+                format!("{}+{}", kb.mods, kb.key)
+            };
+            let action_label = if kb.command.is_empty() {
+                kb.action.clone()
+            } else {
+                format!("{}: {}", kb.action, kb.command)
+            };
+            sec.text(pc, &binding, 14.0, 0.0, 12.0, TEXT_FG);
+            let label_w = sec_w - 200.0;
+            sec.text(pc, &action_label, 14.0 + label_w.min(180.0), 0.0, 12.0, TEXT_DIM);
+            sec.spacing(18.0);
+        }
+        sec.finish(pc)
+    });
 
-    pc
+    final_pc
 }
 
+
 pub fn update(state: &mut InputState, msg: InputMessage) {
     match msg {
         InputMessage::ToggleTapToClick => {
@@ -639,5 +659,14 @@ mod tests {
         assert_eq!(parse_bool_from_default(empty_content, "natural_scroll", false), false);
         assert_eq!(parse_f32_key(empty_content, "scroll_speed", 1.0), 1.0);
     }
+
+    #[test]
+    fn test_view_layout_grid() {
+        let mut state = InputState::default();
+        let mut layout = clear_ui::layout::ColumnLayout::new(20.0);
+        let pc = view(&mut state, 10.0, 20.0, 800.0, 600.0, &[false, false, false, false, false], &mut layout);
+        assert!(!pc.rects.is_empty() || !pc.texts.is_empty());
+    }
 }
 
+
diff --git a/src/pages/colors.rs b/src/pages/interface.rs
similarity index 59%
rename from src/pages/colors.rs
rename to src/pages/interface.rs
index 0f58310..9ea2489 100644
--- a/src/pages/colors.rs
+++ b/src/pages/interface.rs
@@ -1,8 +1,9 @@
 use std::fs;
 use std::io::Write;
 use crate::app::PageContent;
-use clear_ui::layout::Section;
-use clear_ui::widget::ColorSelector;
+use crate::pages::typeface::parse_u16_from;
+use clear_ui::layout::{Section, PageLayoutBuilder, LayoutStrategy};
+use clear_ui::widget::{ColorSelector, Spinbox, Widget};
 
 const CONFIG_PATH: &str = "/home/lsgalante/.config/ccec/config.toml";
 
@@ -14,7 +15,7 @@ fn get_socket_path() -> String {
 }
 
 #[derive(Debug, Clone)]
-pub struct ColorsState {
+pub struct InterfaceState {
     pub low_color: [u8; 3],
     pub high_color: [u8; 3],
     pub disabled_color: [u8; 3],
@@ -27,10 +28,20 @@ pub struct ColorsState {
     pub paginator_sidebar_color: [u8; 3],
     pub primary_highlight_color: [u8; 3],
     pub paginator_tab_label_color: [u8; 3],
+    pub toggle_enabled_color: [u8; 3],
+    pub toggle_disabled_color: [u8; 3],
     pub color_selectors: Vec<ColorSelector>,
+    pub paginator_tab_margin_x: u16,
+    pub paginator_tab_margin_y: u16,
+    pub tab_margin_spinbox_x: Spinbox,
+    pub tab_margin_spinbox_y: Spinbox,
+    pub paginator_tab_padding_x: u16,
+    pub paginator_tab_padding_y: u16,
+    pub tab_padding_spinbox_x: Spinbox,
+    pub tab_padding_spinbox_y: Spinbox,
 }
 
-impl Default for ColorsState {
+impl Default for InterfaceState {
     fn default() -> Self {
         Self {
             low_color: [0x0a, 0x1a, 0x0e],
@@ -45,6 +56,8 @@ impl Default for ColorsState {
             paginator_sidebar_color: [90, 90, 101],
             primary_highlight_color: [255, 255, 255],
             paginator_tab_label_color: [230, 230, 242],
+            toggle_enabled_color: [104, 217, 165],
+            toggle_disabled_color: [135, 135, 148],
             color_selectors: vec![
                 ColorSelector::new([71, 71, 81]).with_label("Low Color"), // 0: Pages - Low Color
                 ColorSelector::new([0x3e, 0x3e, 0x3e]).with_label("High Color"), // 1: Layout - High Color
@@ -58,13 +71,23 @@ impl Default for ColorsState {
                 ColorSelector::new([90, 90, 101]).with_label("Paginator Sidebar"), // 9: Controls - Paginator Sidebar
                 ColorSelector::new([255, 255, 255]).with_label("Primary Highlight"), // 10: Controls - Primary Highlight
                 ColorSelector::new([230, 230, 242]).with_label("Paginator Tab Label"), // 11: Controls - Paginator Tab Label
+                ColorSelector::new([104, 217, 165]).with_label("Enabled"), // 12: Toggles - Enabled
+                ColorSelector::new([135, 135, 148]).with_label("Disabled"), // 13: Toggles - Disabled
             ],
+            paginator_tab_margin_x: 5,
+            paginator_tab_margin_y: 10,
+            tab_margin_spinbox_x: Spinbox::new(5, 0, 100, 1).with_label("Tab Margin X").with_unit("px"),
+            tab_margin_spinbox_y: Spinbox::new(10, 0, 100, 1).with_label("Tab Margin Y").with_unit("px"),
+            paginator_tab_padding_x: 10,
+            paginator_tab_padding_y: 14,
+            tab_padding_spinbox_x: Spinbox::new(10, 0, 100, 1).with_label("Tab Padding X").with_unit("px"),
+            tab_padding_spinbox_y: Spinbox::new(14, 0, 100, 1).with_label("Tab Padding Y").with_unit("px"),
         }
     }
 }
 
 #[derive(Debug, Clone)]
-pub enum ColorsMessage {
+pub enum InterfaceMessage {
     SetLowColor([u8; 3]),
     SetHighColor([u8; 3]),
     SetDisabledColor([u8; 3]),
@@ -77,6 +100,12 @@ pub enum ColorsMessage {
     SetPaginatorSidebarColor([u8; 3]),
     SetPrimaryHighlightColor([u8; 3]),
     SetPaginatorTabLabelColor([u8; 3]),
+    SetToggleEnabledColor([u8; 3]),
+    SetToggleDisabledColor([u8; 3]),
+    SetTabMarginX(u16),
+    SetTabMarginY(u16),
+    SetTabPaddingX(u16),
+    SetTabPaddingY(u16),
     PickLowColor,
     PickHighColor,
     PickDisabledColor,
@@ -89,12 +118,12 @@ pub enum ColorsMessage {
     PickPaginatorSidebarColor,
     PickPrimaryHighlightColor,
     PickPaginatorTabLabelColor,
-    Refreshed(ColorsState),
+    PickToggleEnabledColor,
+    PickToggleDisabledColor,
+    Refreshed(InterfaceState),
 }
 
-
-
-pub fn read_colors_config() -> ColorsState {
+pub fn read_interface_config() -> InterfaceState {
     let content = fs::read_to_string(CONFIG_PATH).unwrap_or_default();
     let has_low = content.lines().any(|l| l.trim().starts_with("low_color"));
     let bg = if has_low {
@@ -129,8 +158,18 @@ pub fn read_colors_config() -> ColorsState {
     let primary_highlight = parse_color_from_key(&content, "primary_highlight_color", [255, 255, 255]);
 
     let paginator_tab_label = parse_color_from_key(&content, "paginator_tab_label_color", [230, 230, 242]);
+
+    let toggle_enabled = parse_color_from_key(&content, "toggle_enabled_color", [104, 217, 165]);
+
+    let toggle_disabled = parse_color_from_key(&content, "toggle_disabled_color", [135, 135, 148]);
+    
+    let paginator_tab_margin_general = parse_u16_from(&content, "paginator_tab_margin", 999);
+    let paginator_tab_margin_x = parse_u16_from(&content, "paginator_tab_margin_x", if paginator_tab_margin_general != 999 { paginator_tab_margin_general } else { 5 });
+    let paginator_tab_margin_y = parse_u16_from(&content, "paginator_tab_margin_y", if paginator_tab_margin_general != 999 { paginator_tab_margin_general } else { 10 });
+    let paginator_tab_padding_x = parse_u16_from(&content, "paginator_tab_padding_x", 10);
+    let paginator_tab_padding_y = parse_u16_from(&content, "paginator_tab_padding_y", 14);
     
-    ColorsState {
+    InterfaceState {
         low_color: bg,
         high_color: border,
         disabled_color: disabled,
@@ -143,6 +182,8 @@ pub fn read_colors_config() -> ColorsState {
         paginator_sidebar_color: paginator_sidebar,
         primary_highlight_color: primary_highlight,
         paginator_tab_label_color: paginator_tab_label,
+        toggle_enabled_color: toggle_enabled,
+        toggle_disabled_color: toggle_disabled,
         color_selectors: vec![
             ColorSelector::new(page_low).with_label("Low Color"), // 0: Pages - Low Color
             ColorSelector::new(border).with_label("High Color"), // 1: Layout - High Color
@@ -156,7 +197,17 @@ pub fn read_colors_config() -> ColorsState {
             ColorSelector::new(paginator_sidebar).with_label("Paginator Sidebar"), // 9: Controls - Paginator Sidebar
             ColorSelector::new(primary_highlight).with_label("Primary Highlight"), // 10: Controls - Primary Highlight
             ColorSelector::new(paginator_tab_label).with_label("Paginator Tab Label"), // 11: Controls - Paginator Tab Label
+            ColorSelector::new(toggle_enabled).with_label("Enabled"), // 12: Toggles - Enabled
+            ColorSelector::new(toggle_disabled).with_label("Disabled"), // 13: Toggles - Disabled
         ],
+        paginator_tab_margin_x,
+        paginator_tab_margin_y,
+        tab_margin_spinbox_x: Spinbox::new(paginator_tab_margin_x as i32, 0, 100, 1).with_label("Tab Margin X").with_unit("px"),
+        tab_margin_spinbox_y: Spinbox::new(paginator_tab_margin_y as i32, 0, 100, 1).with_label("Tab Margin Y").with_unit("px"),
+        paginator_tab_padding_x,
+        paginator_tab_padding_y,
+        tab_padding_spinbox_x: Spinbox::new(paginator_tab_padding_x as i32, 0, 100, 1).with_label("Tab Padding X").with_unit("px"),
+        tab_padding_spinbox_y: Spinbox::new(paginator_tab_padding_y as i32, 0, 100, 1).with_label("Tab Padding Y").with_unit("px"),
     }
 }
 
@@ -165,7 +216,7 @@ fn parse_color_from_key(content: &str, key: &str, default: [u8; 3]) -> [u8; 3] {
         let trimmed = line.trim();
         if let Some(rest) = trimmed.strip_prefix(key) {
             let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
-            let hex = rest.trim_end_matches('"').trim();
+            let hex = rest.trim_end_matches('"').trim().trim_start_matches('#');
             return parse_hex(hex);
         }
     }
@@ -332,122 +383,234 @@ fn apply_paginator_tab_label_color(rgb: [u8; 3]) {
     clear_ui::color::set_paginator_tab_label_color([r, g, b, 1.0]);
 }
 
-pub fn view(state: &mut ColorsState, cx: f32, cy: f32, cw: f32, _ch: f32) -> PageContent {
-    let mut pc = PageContent::new();
-    let mut y = cy + 12.0;
+fn apply_toggle_enabled_color(rgb: [u8; 3]) {
+    let hex = format!("\"#{:02x}{:02x}{:02x}\"", rgb[0], rgb[1], rgb[2]);
+    write_config_value("toggle_enabled_color", &hex);
+    let r = clear_ui::color::srgb_to_linear(rgb[0] as f32 / 255.0);
+    let g = clear_ui::color::srgb_to_linear(rgb[1] as f32 / 255.0);
+    let b = clear_ui::color::srgb_to_linear(rgb[2] as f32 / 255.0);
+    clear_ui::color::set_toggle_on_color([r, g, b, 1.0]);
+}
+
+fn apply_toggle_disabled_color(rgb: [u8; 3]) {
+    let hex = format!("\"#{:02x}{:02x}{:02x}\"", rgb[0], rgb[1], rgb[2]);
+    write_config_value("toggle_disabled_color", &hex);
+    let r = clear_ui::color::srgb_to_linear(rgb[0] as f32 / 255.0);
+    let g = clear_ui::color::srgb_to_linear(rgb[1] as f32 / 255.0);
+    let b = clear_ui::color::srgb_to_linear(rgb[2] as f32 / 255.0);
+    clear_ui::color::set_toggle_off_color([r, g, b, 1.0]);
+}
+
+fn apply_paginator_tab_margin_x(margin: u16) {
+    write_config_value("paginator_tab_margin_x", &margin.to_string());
+    send_ipc_command(&format!("layout paginator_tab_margin_x {}", margin));
+}
+
+fn apply_paginator_tab_margin_y(margin: u16) {
+    write_config_value("paginator_tab_margin_y", &margin.to_string());
+    send_ipc_command(&format!("layout paginator_tab_margin_y {}", margin));
+}
+
+fn apply_paginator_tab_padding_x(padding: u16) {
+    write_config_value("paginator_tab_padding_x", &padding.to_string());
+    send_ipc_command(&format!("layout paginator_tab_padding_x {}", padding));
+}
+
+fn apply_paginator_tab_padding_y(padding: u16) {
+    write_config_value("paginator_tab_padding_y", &padding.to_string());
+    send_ipc_command(&format!("layout paginator_tab_padding_y {}", padding));
+}
+
+pub fn view(state: &mut InterfaceState, cx: f32, cy: f32, cw: f32, ch: f32, layout: &mut dyn LayoutStrategy) -> PageContent {
+    let mut final_pc = PageContent::new();
+    let sec_w = 320.0f32;
+    let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(6);
 
     // 1. Pages Section
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Pages");
-    sec.spacing(8.0);
-    state.color_selectors[0].color = state.page_low_color;
-    sec.widget(&mut pc, &mut state.color_selectors[0], 12.0, 220.0, 22.0);
-    sec.spacing(8.0);
-    y = sec.finish(&mut pc);
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Pages");
+        sec.spacing(8.0);
+        state.color_selectors[0].color = state.page_low_color;
+        sec.widget(pc, &mut state.color_selectors[0], 12.0, 220.0, 22.0);
+        sec.spacing(8.0);
+        sec.finish(pc)
+    });
 
     // 2. Layout Section
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Layout");
-    sec.spacing(8.0);
-    state.color_selectors[7].color = state.low_color;
-    sec.widget(&mut pc, &mut state.color_selectors[7], 12.0, 220.0, 22.0);
-    sec.spacing(8.0);
-    state.color_selectors[1].color = state.high_color;
-    sec.widget(&mut pc, &mut state.color_selectors[1], 12.0, 220.0, 22.0);
-    sec.spacing(8.0);
-    state.color_selectors[2].color = state.visual_guides_color;
-    sec.widget(&mut pc, &mut state.color_selectors[2], 12.0, 220.0, 22.0);
-    sec.spacing(8.0);
-    y = sec.finish(&mut pc);
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Layout");
+        sec.spacing(8.0);
+        state.color_selectors[7].color = state.low_color;
+        sec.widget(pc, &mut state.color_selectors[7], 12.0, 220.0, 22.0);
+        sec.spacing(8.0);
+        state.color_selectors[1].color = state.high_color;
+        sec.widget(pc, &mut state.color_selectors[1], 12.0, 220.0, 22.0);
+        sec.spacing(8.0);
+        state.color_selectors[2].color = state.visual_guides_color;
+        sec.widget(pc, &mut state.color_selectors[2], 12.0, 220.0, 22.0);
+        sec.spacing(8.0);
+        sec.finish(pc)
+    });
 
     // 3. Status Section
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Status");
-    sec.spacing(8.0);
-    state.color_selectors[8].color = state.normal_color;
-    sec.widget(&mut pc, &mut state.color_selectors[8], 12.0, 220.0, 22.0);
-    sec.spacing(8.0);
-    state.color_selectors[3].color = state.disabled_color;
-    sec.widget(&mut pc, &mut state.color_selectors[3], 12.0, 220.0, 22.0);
-    sec.spacing(8.0);
-    state.color_selectors[4].color = state.separator_color;
-    sec.widget(&mut pc, &mut state.color_selectors[4], 12.0, 220.0, 22.0);
-    sec.spacing(8.0);
-    y = sec.finish(&mut pc);
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Status");
+        sec.spacing(8.0);
+        state.color_selectors[8].color = state.normal_color;
+        sec.widget(pc, &mut state.color_selectors[8], 12.0, 220.0, 22.0);
+        sec.spacing(8.0);
+        state.color_selectors[3].color = state.disabled_color;
+        sec.widget(pc, &mut state.color_selectors[3], 12.0, 220.0, 22.0);
+        sec.spacing(8.0);
+        state.color_selectors[4].color = state.separator_color;
+        sec.widget(pc, &mut state.color_selectors[4], 12.0, 220.0, 22.0);
+        sec.spacing(8.0);
+        sec.finish(pc)
+    });
 
     // 4. Controls Section
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Controls");
-    sec.spacing(8.0);
-    state.color_selectors[5].color = state.slider_track_color;
-    sec.widget(&mut pc, &mut state.color_selectors[5], 12.0, 220.0, 22.0);
-    sec.spacing(8.0);
-    state.color_selectors[6].color = state.color_borders_color;
-    sec.widget(&mut pc, &mut state.color_selectors[6], 12.0, 220.0, 22.0);
-    sec.spacing(8.0);
-    state.color_selectors[9].color = state.paginator_sidebar_color;
-    sec.widget(&mut pc, &mut state.color_selectors[9], 12.0, 220.0, 22.0);
-    sec.spacing(8.0);
-    state.color_selectors[10].color = state.primary_highlight_color;
-    sec.widget(&mut pc, &mut state.color_selectors[10], 12.0, 220.0, 22.0);
-    sec.spacing(8.0);
-    state.color_selectors[11].color = state.paginator_tab_label_color;
-    sec.widget(&mut pc, &mut state.color_selectors[11], 12.0, 220.0, 22.0);
-    sec.spacing(8.0);
-    sec.finish(&mut pc);
-
-    pc
-}
-
-pub fn update(state: &mut ColorsState, msg: ColorsMessage) {
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Controls");
+        sec.spacing(8.0);
+        state.color_selectors[5].color = state.slider_track_color;
+        sec.widget(pc, &mut state.color_selectors[5], 12.0, 220.0, 22.0);
+        sec.spacing(8.0);
+        state.color_selectors[6].color = state.color_borders_color;
+        sec.widget(pc, &mut state.color_selectors[6], 12.0, 220.0, 22.0);
+        sec.spacing(8.0);
+        state.color_selectors[10].color = state.primary_highlight_color;
+        sec.widget(pc, &mut state.color_selectors[10], 12.0, 220.0, 22.0);
+        sec.spacing(8.0);
+        sec.finish(pc)
+    });
+
+    // 5. Paginator Section
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Paginator");
+        sec.spacing(8.0);
+        state.color_selectors[9].color = state.paginator_sidebar_color;
+        sec.widget(pc, &mut state.color_selectors[9], 12.0, 220.0, 22.0);
+        sec.spacing(8.0);
+        state.color_selectors[11].color = state.paginator_tab_label_color;
+        sec.widget(pc, &mut state.color_selectors[11], 12.0, 220.0, 22.0);
+        sec.spacing(8.0);
+        state.tab_margin_spinbox_x.value = state.paginator_tab_margin_x as i32;
+        sec.widget(pc, &mut state.tab_margin_spinbox_x, 12.0, 200.0, 26.0);
+        sec.spacing(8.0);
+        state.tab_margin_spinbox_y.value = state.paginator_tab_margin_y as i32;
+        sec.widget(pc, &mut state.tab_margin_spinbox_y, 12.0, 200.0, 26.0);
+        sec.spacing(8.0);
+        state.tab_padding_spinbox_x.value = state.paginator_tab_padding_x as i32;
+        sec.widget(pc, &mut state.tab_padding_spinbox_x, 12.0, 200.0, 26.0);
+        sec.spacing(8.0);
+        state.tab_padding_spinbox_y.value = state.paginator_tab_padding_y as i32;
+        sec.widget(pc, &mut state.tab_padding_spinbox_y, 12.0, 200.0, 26.0);
+        sec.spacing(8.0);
+        sec.finish(pc)
+    });
+
+    // 6. Toggles Section
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Toggles");
+        sec.spacing(8.0);
+        state.color_selectors[12].color = state.toggle_enabled_color;
+        sec.widget(pc, &mut state.color_selectors[12], 12.0, 220.0, 22.0);
+        sec.spacing(8.0);
+        state.color_selectors[13].color = state.toggle_disabled_color;
+        sec.widget(pc, &mut state.color_selectors[13], 12.0, 220.0, 22.0);
+        sec.spacing(8.0);
+        sec.finish(pc)
+    });
+
+    final_pc
+}
+
+
+pub fn update(state: &mut InterfaceState, msg: InterfaceMessage) {
     match msg {
-        ColorsMessage::SetLowColor(rgb) => {
+        InterfaceMessage::SetLowColor(rgb) => {
             state.low_color = rgb;
             apply_background(rgb);
         }
-        ColorsMessage::SetPageLowColor(rgb) => {
+        InterfaceMessage::SetPageLowColor(rgb) => {
             state.page_low_color = rgb;
             apply_page_low_color(rgb);
         }
-        ColorsMessage::SetHighColor(rgb) => {
+        InterfaceMessage::SetHighColor(rgb) => {
             state.high_color = rgb;
             apply_border_color(rgb);
         }
-        ColorsMessage::SetDisabledColor(rgb) => {
+        InterfaceMessage::SetDisabledColor(rgb) => {
             state.disabled_color = rgb;
             apply_disabled_color(rgb);
         }
-        ColorsMessage::SetSeparatorColor(rgb) => {
+        InterfaceMessage::SetSeparatorColor(rgb) => {
             state.separator_color = rgb;
             apply_separator_color(rgb);
         }
-        ColorsMessage::SetVisualGuidesColor(rgb) => {
+        InterfaceMessage::SetVisualGuidesColor(rgb) => {
             state.visual_guides_color = rgb;
             apply_visual_guides_color(rgb);
         }
-        ColorsMessage::SetSliderTrackColor(rgb) => {
+        InterfaceMessage::SetSliderTrackColor(rgb) => {
             state.slider_track_color = rgb;
             apply_slider_track_color(rgb);
         }
-        ColorsMessage::SetColorBordersColor(rgb) => {
+        InterfaceMessage::SetColorBordersColor(rgb) => {
             state.color_borders_color = rgb;
             apply_color_borders_color(rgb);
         }
-        ColorsMessage::SetNormalColor(rgb) => {
+        InterfaceMessage::SetNormalColor(rgb) => {
             state.normal_color = rgb;
             apply_normal_color(rgb);
         }
-        ColorsMessage::SetPaginatorSidebarColor(rgb) => {
+        InterfaceMessage::SetPaginatorSidebarColor(rgb) => {
             state.paginator_sidebar_color = rgb;
             apply_paginator_sidebar_color(rgb);
         }
-        ColorsMessage::SetPrimaryHighlightColor(rgb) => {
+        InterfaceMessage::SetPrimaryHighlightColor(rgb) => {
             state.primary_highlight_color = rgb;
             apply_primary_highlight_color(rgb);
         }
-        ColorsMessage::SetPaginatorTabLabelColor(rgb) => {
+        InterfaceMessage::SetPaginatorTabLabelColor(rgb) => {
             state.paginator_tab_label_color = rgb;
             apply_paginator_tab_label_color(rgb);
         }
-        ColorsMessage::PickLowColor | ColorsMessage::PickHighColor | ColorsMessage::PickDisabledColor | ColorsMessage::PickSeparatorColor | ColorsMessage::PickVisualGuides | ColorsMessage::PickSliderTrackColor | ColorsMessage::PickPageLowColor | ColorsMessage::PickColorBordersColor | ColorsMessage::PickNormalColor | ColorsMessage::PickPaginatorSidebarColor | ColorsMessage::PickPrimaryHighlightColor | ColorsMessage::PickPaginatorTabLabelColor => {}
-        ColorsMessage::Refreshed(new) => {
+        InterfaceMessage::SetToggleEnabledColor(rgb) => {
+            state.toggle_enabled_color = rgb;
+            apply_toggle_enabled_color(rgb);
+        }
+        InterfaceMessage::SetToggleDisabledColor(rgb) => {
+            state.toggle_disabled_color = rgb;
+            apply_toggle_disabled_color(rgb);
+        }
+        InterfaceMessage::SetTabMarginX(margin) => {
+            state.paginator_tab_margin_x = margin;
+            apply_paginator_tab_margin_x(margin);
+        }
+        InterfaceMessage::SetTabMarginY(margin) => {
+            state.paginator_tab_margin_y = margin;
+            apply_paginator_tab_margin_y(margin);
+        }
+        InterfaceMessage::SetTabPaddingX(padding) => {
+            state.paginator_tab_padding_x = padding;
+            apply_paginator_tab_padding_x(padding);
+        }
+        InterfaceMessage::SetTabPaddingY(padding) => {
+            state.paginator_tab_padding_y = padding;
+            apply_paginator_tab_padding_y(padding);
+        }
+        InterfaceMessage::PickLowColor | InterfaceMessage::PickHighColor | InterfaceMessage::PickDisabledColor | InterfaceMessage::PickSeparatorColor | InterfaceMessage::PickVisualGuides | InterfaceMessage::PickSliderTrackColor | InterfaceMessage::PickPageLowColor | InterfaceMessage::PickColorBordersColor | InterfaceMessage::PickNormalColor | InterfaceMessage::PickPaginatorSidebarColor | InterfaceMessage::PickPrimaryHighlightColor | InterfaceMessage::PickPaginatorTabLabelColor | InterfaceMessage::PickToggleEnabledColor | InterfaceMessage::PickToggleDisabledColor => {}
+        InterfaceMessage::Refreshed(new) => {
+            let was_mx_hovered = state.tab_margin_spinbox_x.hovered();
+            let was_my_hovered = state.tab_margin_spinbox_y.hovered();
+            let was_px_hovered = state.tab_padding_spinbox_x.hovered();
+            let was_py_hovered = state.tab_padding_spinbox_y.hovered();
             *state = new;
+            state.tab_margin_spinbox_x.set_hovered(was_mx_hovered);
+            state.tab_margin_spinbox_y.set_hovered(was_my_hovered);
+            state.tab_padding_spinbox_x.set_hovered(was_px_hovered);
+            state.tab_padding_spinbox_y.set_hovered(was_py_hovered);
         }
     }
 }
@@ -466,7 +629,7 @@ mod tests {
 
     #[test]
     fn test_parse_color_from_key() {
-        let content = "\n[layout]\nlow_color = \"#112233\"\nhigh_color = \"#445566\"\ndisabled_color = \"#778899\"\nstatus_separator_color = \"#aabbcc\"\nvisual_guides_color = \"#ddeeff\"\nslider_track_color = \"#123456\"\npage_low_color = \"#474751\"\ncolor_borders_color = \"#abcdef\"\nstatus_normal_color = \"#ccccd8\"\npaginator_sidebar_color = \"#5a5a65\"\nprimary_highlight_color = \"#ffffff\"\npaginator_tab_label_color = \"#e6e6f2\"\n";
+        let content = "\n[layout]\nlow_color = \"#112233\"\nhigh_color = \"#445566\"\ndisabled_color = \"#778899\"\nstatus_separator_color = \"#aabbcc\"\nvisual_guides_color = \"#ddeeff\"\nslider_track_color = \"#123456\"\npage_low_color = \"#474751\"\ncolor_borders_color = \"#abcdef\"\nstatus_normal_color = \"#ccccd8\"\npaginator_sidebar_color = \"#5a5a65\"\nprimary_highlight_color = \"#ffffff\"\npaginator_tab_label_color = \"#e6e6f2\"\ntoggle_enabled_color = \"#68d8a5\"\ntoggle_disabled_color = \"#878794\"\n";
         assert_eq!(parse_color_from_key(content, "low_color", [0, 0, 0]), [17, 34, 51]);
         assert_eq!(parse_color_from_key(content, "high_color", [0, 0, 0]), [68, 85, 102]);
         assert_eq!(parse_color_from_key(content, "disabled_color", [0, 0, 0]), [119, 136, 153]);
@@ -479,6 +642,8 @@ mod tests {
         assert_eq!(parse_color_from_key(content, "paginator_sidebar_color", [0, 0, 0]), [90, 90, 101]);
         assert_eq!(parse_color_from_key(content, "primary_highlight_color", [0, 0, 0]), [255, 255, 255]);
         assert_eq!(parse_color_from_key(content, "paginator_tab_label_color", [0, 0, 0]), [230, 230, 242]);
+        assert_eq!(parse_color_from_key(content, "toggle_enabled_color", [0, 0, 0]), [104, 216, 165]);
+        assert_eq!(parse_color_from_key(content, "toggle_disabled_color", [0, 0, 0]), [135, 135, 148]);
         assert_eq!(parse_color_from_key(content, "non_existent", [1, 2, 3]), [1, 2, 3]);
     }
 
diff --git a/src/pages/layout.rs b/src/pages/layout.rs
index ff12bdd..c38c5b6 100644
--- a/src/pages/layout.rs
+++ b/src/pages/layout.rs
@@ -2,8 +2,8 @@ use std::fs;
 use std::io::Write;
 
 use crate::app::PageContent;
-use clear_ui::layout::Section;
-use clear_ui::widget::{Dropdown, Spinbox};
+use clear_ui::layout::{render_widget, Section, PageLayoutBuilder, LayoutStrategy};
+use clear_ui::widget::{Spinbox, Dropdown};
 
 const CONFIG_PATH: &str = "/home/lsgalante/.config/ccec/config.toml";
 
@@ -504,643 +504,220 @@ fn get_short_app_name(app_id: &str) -> String {
     }
 }
 
-pub fn view(state: &mut LayoutState, cx: f32, cy: f32, cw: f32, _ch: f32, sec_focused: &[bool]) -> PageContent {
-    let mut pc = PageContent::new();
-    let mut y = cy + 12.0;
+struct SimNode {
+    x: f32,
+    y: f32,
+    w: f32,
+    h: f32,
+    label: String,
+}
+
+pub fn view(state: &mut LayoutState, cx: f32, cy: f32, cw: f32, ch: f32, sec_focused: &[bool], layout: &mut dyn LayoutStrategy) -> PageContent {
+    let mut final_pc = PageContent::new();
+    let sec_w = 320.0f32;
+    let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(5);
 
     // Current Layout Section (Read-only visual preview)
-    let mut sec_cl = Section::new(&mut pc, cx, y, cw, "Current Layout");
-    sec_cl.spacing(8.0);
-    
-    let info = read_current_layout_status();
-    
-    let card_w = (cw - 24.0) / 2.0;
-    let card_h = 135.0;
-    
-    for tag_idx in 0..4 {
-        let col = tag_idx % 2;
-        let row = tag_idx / 2;
-        let tx = cx + 8.0 + col as f32 * (card_w + 8.0);
-        let ty = sec_cl.ay() + row as f32 * (card_h + 8.0);
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec_cl = Section::new(pc, rx, ry, sec_w, "Current Layout");
+        sec_cl.spacing(8.0);
         
-        let is_active = (info.active_tags & (1 << tag_idx)) != 0;
-        let is_focused = (info.focused_tags & (1 << tag_idx)) != 0;
+        let info = read_current_layout_status();
         
-        // Draw card border and background
-        let border_color = if is_focused {
-            [0.2, 0.6, 1.0, 1.0]
-        } else if is_active {
-            [0.28, 0.28, 0.32, 1.0]
-        } else {
-            [0.16, 0.16, 0.18, 1.0]
-        };
+        let card_w = (sec_w - 24.0) / 2.0;
+        let card_h = 135.0;
         
-        let bg_color = if is_active {
-            [0.08, 0.08, 0.11, 0.9]
-        } else {
-            [0.05, 0.05, 0.07, 0.9]
-        };
-        
-        pc.rect(border_color, tx, ty, card_w, card_h);
-        pc.rect(bg_color, tx + 1.0, ty + 1.0, card_w - 2.0, card_h - 2.0);
-        
-        // Render tag label in top-left of the card
-        let tag_label = format!("T{}", tag_idx + 1);
-        let tag_label_color = if is_focused {
-            [1.0, 1.0, 1.0, 1.0]
-        } else if is_active {
-            [0.8, 0.8, 0.85, 1.0]
-        } else {
-            [0.4, 0.4, 0.45, 1.0]
-        };
-        pc.text(&tag_label, tx + 8.0, ty + 6.0, 9.5, tag_label_color);
-        
-        // Render miniature screen preview inside the box (on the left side)
-        let px = tx + 8.0;
-        let py = ty + 24.0;
-        let p_w = 70.0;
-        let p_h = 44.0;
-        
-        // Screen background
-        pc.rect([0.04, 0.04, 0.06, 1.0], px, py, p_w, p_h);
-        pc.rect([0.16, 0.16, 0.18, 1.0], px, py, p_w, 1.0); // Top border
-        pc.rect([0.16, 0.16, 0.18, 1.0], px, py + p_h - 1.0, p_w, 1.0); // Bottom border
-        pc.rect([0.16, 0.16, 0.18, 1.0], px, py, 1.0, p_h); // Left border
-        pc.rect([0.16, 0.16, 0.18, 1.0], px + p_w - 1.0, py, 1.0, p_h); // Right border
-        
-        // Find windows for this tag
-        let tag_windows: Vec<&PreviewWindow> = info.windows.iter()
-            .filter(|w| w.app_id != "clear-status-interface" && ((w.tags & (1 << tag_idx)) != 0 || w.tags == u32::MAX))
-            .collect();
+        for tag_idx in 0..4 {
+            let col = tag_idx % 2;
+            let row = tag_idx / 2;
+            let tx = rx + 8.0 + col as f32 * (card_w + 8.0);
+            let ty = sec_cl.ay() + row as f32 * (card_h + 8.0);
             
-        // Reference screen size
-        let screen_w = 1920.0;
-        let screen_h = 1200.0;
-        let scale_x = p_w / screen_w;
-        let scale_y = p_h / screen_h;
-        
-        for win in &tag_windows {
-            let wx = (px + win.x * scale_x).max(px).min(px + p_w);
-            let wy = (py + win.y * scale_y).max(py).min(py + p_h);
-            let ww = (win.w * scale_x).min(p_w - (wx - px));
-            let wh = (win.h * scale_y).min(p_h - (wy - py));
+            // Draw card background
+            let is_active = (info.active_tags & (1 << tag_idx)) != 0;
+            let bg_col = if is_active { [0.12, 0.24, 0.14, 0.55] } else { [0.08, 0.08, 0.12, 0.35] };
+            let border_col = if is_active { [0.36, 0.56, 0.38, 0.95] } else { [0.24, 0.24, 0.28, 0.45] };
+            pc.rect(bg_col, tx, ty, card_w, card_h);
+            // Card border
+            pc.rect(border_col, tx, ty, card_w, 1.0);
+            pc.rect(border_col, tx, ty + card_h - 1.0, card_w, 1.0);
+            pc.rect(border_col, tx, ty, 1.0, card_h);
+            pc.rect(border_col, tx + card_w - 1.0, ty, 1.0, card_h);
             
-            let is_win_focused = !win.title.is_empty() && win.title == info.focused_title;
-            let color = if is_win_focused {
-                [0.2, 0.6, 1.0, 1.0]
-            } else {
-                [0.4, 0.4, 0.45, 1.0]
-            };
-            let fill = if is_win_focused {
-                [0.10, 0.32, 0.55, 0.4]
-            } else {
-                [0.12, 0.12, 0.15, 0.4]
-            };
+            // Tag index text
+            pc.text(&format!("TAG {}", tag_idx + 1), tx + 8.0, ty + 8.0, 10.0, [0.55, 0.55, 0.60, 1.0]);
             
-            pc.rect(color, wx, wy, ww, wh);
-            pc.rect(fill, wx + 0.5, wy + 0.5, ww - 1.0, wh - 1.0);
-        }
-        
-        // Determine Tag layout mode
-        let layout_name = if is_focused {
-            info.focused_layout_mode.clone()
-        } else if let Some(win) = tag_windows.first() {
-            win.layout_mode.clone()
-        } else {
+            // Layout Name
             let layout_idx = state.tag_layout_menus.get(tag_idx).map(|m| m.selected).unwrap_or(0);
-            let layout_modes = ["Cascade", "Grid", "Fullscreen", "Floating", "Popup"];
-            layout_modes.get(layout_idx).copied().unwrap_or("Cascade").to_string()
-        };
-
-        // Render Visual Focus Hierarchy Tree on the right side of the card
-        let tx_tree = tx + 84.0;
-        let ty_tree = ty + 18.0;
-        let t_w = card_w - 92.0;
-        let t_h = card_h - 26.0;
-        
-        struct TreeNode {
-            label: String,
-            is_focused: bool,
-            is_layout: bool,
-            is_role: bool,
-            x: f32,
-            y: f32,
-            w: f32,
-            h: f32,
-        }
-        
-        struct TreeLine {
-            x0: f32,
-            y0: f32,
-            x1: f32,
-            y1: f32,
-            is_dotted: bool,
-        }
-        
-        let mut nodes = Vec::new();
-        let mut lines = Vec::new();
-        
-        // 1. Root Node: Layout mode
-        let layout_label = match layout_name.as_str() {
-            "Grid" => "GRID",
-            "Cascade" => "CASC",
-            "Fullscreen" => "FULL",
-            "Floating" => "FLOT",
-            "Popup" => "POP",
-            _ => "CASC",
-        };
-        nodes.push(TreeNode {
-            label: layout_label.to_string(),
-            is_focused: false,
-            is_layout: true,
-            is_role: false,
-            x: tx_tree + 2.0,
-            y: ty_tree + (t_h - 14.0) / 2.0,
-            w: 28.0,
-            h: 14.0,
-        });
-        
-        // 2. Window hierarchy
-        if tag_windows.is_empty() {
-            nodes.push(TreeNode {
-                label: "(empty)".to_string(),
-                is_focused: false,
-                is_layout: false,
-                is_role: true,
-                x: tx_tree + 46.0,
-                y: ty_tree + (t_h - 12.0) / 2.0,
-                w: 42.0,
-                h: 12.0,
-            });
-            lines.push(TreeLine {
-                x0: tx_tree + 30.0,
-                y0: ty_tree + t_h / 2.0,
-                x1: tx_tree + 46.0,
-                y1: ty_tree + t_h / 2.0,
-                is_dotted: true,
-            });
-        } else {
-            // Group transient/child windows under their parent
-            struct WinNode {
-                win: PreviewWindow,
-                children: Vec<PreviewWindow>,
-            }
-            let mut groups: Vec<WinNode> = Vec::new();
-            for w in &tag_windows {
-                if w.has_parent && !groups.is_empty() {
-                    groups.last_mut().unwrap().children.push((*w).clone());
-                } else {
-                    groups.push(WinNode {
-                        win: (*w).clone(),
-                        children: Vec::new(),
-                    });
-                }
-            }
+            let layout_name = match layout_idx {
+                1 => "Cascade",
+                2 => "Stack",
+                3 => "Grid",
+                4 => "L-Tiled",
+                5 => "R-Tiled",
+                6 => "Equal",
+                7 => "Spiral",
+                8 => "Floating",
+                _ => "Fullscreen",
+            };
+            pc.text(layout_name, tx + 8.0, ty + 20.0, 13.0, [0.90, 0.90, 0.95, 1.0]);
             
-            let num_roots = groups.len();
-            if num_roots == 1 {
-                // One root group: Layout -> Root -> Children
-                let root_g = &groups[0];
-                let rx = tx_tree + 44.0;
-                let ry = ty_tree + (t_h - 14.0) / 2.0;
-                let is_root_focused = !root_g.win.title.is_empty() && root_g.win.title == info.focused_title;
-                nodes.push(TreeNode {
-                    label: get_short_app_name(&root_g.win.app_id),
-                    is_focused: is_root_focused,
-                    is_layout: false,
-                    is_role: false,
-                    x: rx,
-                    y: ry,
-                    w: 42.0,
-                    h: 14.0,
-                });
-                lines.push(TreeLine {
-                    x0: tx_tree + 30.0,
-                    y0: ty_tree + t_h / 2.0,
-                    x1: rx,
-                    y1: ty_tree + t_h / 2.0,
-                    is_dotted: false,
-                });
-                
-                let child_count = root_g.children.len();
-                for (ci, child) in root_g.children.iter().enumerate() {
-                    let cx = tx_tree + 104.0;
-                    let cy = if child_count == 1 {
-                        ty_tree + (t_h - 12.0) / 2.0
-                    } else {
-                        ty_tree + 6.0 + (ci as f32 / (child_count - 1) as f32) * (t_h - 24.0)
-                    };
-                    let is_child_focused = !child.title.is_empty() && child.title == info.focused_title;
-                    nodes.push(TreeNode {
-                        label: get_short_app_name(&child.app_id),
-                        is_focused: is_child_focused,
-                        is_layout: false,
-                        is_role: false,
-                        x: cx,
-                        y: cy,
-                        w: 32.0,
-                        h: 12.0,
-                    });
-                    lines.push(TreeLine {
-                        x0: rx + 42.0,
-                        y0: ry + 7.0,
-                        x1: cx,
-                        y1: cy + 6.0,
-                        is_dotted: true,
-                    });
+            // Visual nodes layout preview inside card
+            let preview_x = tx + 8.0;
+            let preview_y = ty + 38.0;
+            let preview_w = card_w - 16.0;
+            let preview_h = card_h - 46.0;
+            
+            // Gray border for preview box
+            pc.rect([0.16, 0.16, 0.20, 0.6], preview_x, preview_y, preview_w, preview_h);
+            pc.rect([0.22, 0.22, 0.26, 0.8], preview_x, preview_y, preview_w, 1.0);
+            pc.rect([0.22, 0.22, 0.26, 0.8], preview_x, preview_y + preview_h - 1.0, preview_w, 1.0);
+            pc.rect([0.22, 0.22, 0.26, 0.8], preview_x, preview_y, 1.0, preview_h);
+            pc.rect([0.22, 0.22, 0.26, 0.8], preview_x + preview_w - 1.0, preview_y, 1.0, preview_h);
+            
+            // Simulate layout windows preview
+            let mut nodes = Vec::new();
+            match layout_idx {
+                0 => { // Fullscreen
+                    nodes.push(SimNode { x: 2.0, y: 2.0, w: preview_w - 4.0, h: preview_h - 4.0, label: "F".to_string() });
                 }
-            } else if num_roots == 2 {
-                // Two root groups (Master & Stack): C -> M & S -> Windows -> Children
-                let m_y = ty_tree + (t_h / 2.0) - 20.0;
-                let s_y = ty_tree + (t_h / 2.0) + 20.0;
-                
-                // M indicator
-                nodes.push(TreeNode {
-                    label: "M".to_string(),
-                    is_focused: false,
-                    is_layout: false,
-                    is_role: true,
-                    x: tx_tree + 44.0,
-                    y: m_y - 6.0,
-                    w: 12.0,
-                    h: 12.0,
-                });
-                lines.push(TreeLine {
-                    x0: tx_tree + 30.0,
-                    y0: ty_tree + t_h / 2.0,
-                    x1: tx_tree + 44.0,
-                    y1: m_y,
-                    is_dotted: false,
-                });
-                
-                // Master Window
-                let m_win = &groups[0];
-                let is_m_focused = !m_win.win.title.is_empty() && m_win.win.title == info.focused_title;
-                nodes.push(TreeNode {
-                    label: get_short_app_name(&m_win.win.app_id),
-                    is_focused: is_m_focused,
-                    is_layout: false,
-                    is_role: false,
-                    x: tx_tree + 68.0,
-                    y: m_y - 7.0,
-                    w: 36.0,
-                    h: 14.0,
-                });
-                lines.push(TreeLine {
-                    x0: tx_tree + 56.0,
-                    y0: m_y,
-                    x1: tx_tree + 68.0,
-                    y1: m_y,
-                    is_dotted: false,
-                });
-                
-                // S indicator
-                nodes.push(TreeNode {
-                    label: "S".to_string(),
-                    is_focused: false,
-                    is_layout: false,
-                    is_role: true,
-                    x: tx_tree + 44.0,
-                    y: s_y - 6.0,
-                    w: 12.0,
-                    h: 12.0,
-                });
-                lines.push(TreeLine {
-                    x0: tx_tree + 30.0,
-                    y0: ty_tree + t_h / 2.0,
-                    x1: tx_tree + 44.0,
-                    y1: s_y,
-                    is_dotted: false,
-                });
-                
-                // Stack Window
-                let s_win = &groups[1];
-                let is_s_focused = !s_win.win.title.is_empty() && s_win.win.title == info.focused_title;
-                nodes.push(TreeNode {
-                    label: get_short_app_name(&s_win.win.app_id),
-                    is_focused: is_s_focused,
-                    is_layout: false,
-                    is_role: false,
-                    x: tx_tree + 68.0,
-                    y: s_y - 7.0,
-                    w: 36.0,
-                    h: 14.0,
-                });
-                lines.push(TreeLine {
-                    x0: tx_tree + 56.0,
-                    y0: s_y,
-                    x1: tx_tree + 68.0,
-                    y1: s_y,
-                    is_dotted: false,
-                });
-                
-                // Master children
-                let m_child_count = m_win.children.len();
-                for (ci, child) in m_win.children.iter().enumerate() {
-                    let cx = tx_tree + 114.0;
-                    let cy = if m_child_count == 1 {
-                        m_y - 6.0
-                    } else {
-                        m_y - 20.0 + (ci as f32 / (m_child_count - 1) as f32) * 30.0
-                    };
-                    let is_child_focused = !child.title.is_empty() && child.title == info.focused_title;
-                    nodes.push(TreeNode {
-                        label: get_short_app_name(&child.app_id),
-                        is_focused: is_child_focused,
-                        is_layout: false,
-                        is_role: false,
-                        x: cx,
-                        y: cy,
-                        w: 28.0,
-                        h: 11.0,
-                    });
-                    lines.push(TreeLine {
-                        x0: tx_tree + 104.0,
-                        y0: m_y,
-                        x1: cx,
-                        y1: cy + 5.5,
-                        is_dotted: true,
-                    });
+                1 => { // Cascade
+                    nodes.push(SimNode { x: 2.0, y: 2.0, w: preview_w - 12.0, h: preview_h - 12.0, label: "1".to_string() });
+                    nodes.push(SimNode { x: 6.0, y: 6.0, w: preview_w - 12.0, h: preview_h - 12.0, label: "2".to_string() });
+                    nodes.push(SimNode { x: 10.0, y: 10.0, w: preview_w - 12.0, h: preview_h - 12.0, label: "3".to_string() });
                 }
-                
-                // Stack children
-                let s_child_count = s_win.children.len();
-                for (ci, child) in s_win.children.iter().enumerate() {
-                    let cx = tx_tree + 114.0;
-                    let cy = if s_child_count == 1 {
-                        s_y - 6.0
-                    } else {
-                        s_y - 20.0 + (ci as f32 / (s_child_count - 1) as f32) * 30.0
-                    };
-                    let is_child_focused = !child.title.is_empty() && child.title == info.focused_title;
-                    nodes.push(TreeNode {
-                        label: get_short_app_name(&child.app_id),
-                        is_focused: is_child_focused,
-                        is_layout: false,
-                        is_role: false,
-                        x: cx,
-                        y: cy,
-                        w: 28.0,
-                        h: 11.0,
-                    });
-                    lines.push(TreeLine {
-                        x0: tx_tree + 104.0,
-                        y0: s_y,
-                        x1: cx,
-                        y1: cy + 5.5,
-                        is_dotted: true,
-                    });
+                2 => { // Stack
+                    nodes.push(SimNode { x: 2.0, y: 2.0, w: preview_w - 4.0, h: preview_h - 4.0, label: "Stack".to_string() });
                 }
-            } else {
-                // More than 2 roots: Master & Stacks (list of stack items)
-                let m_y = ty_tree + (t_h / 2.0) - 24.0;
-                let s_y = ty_tree + (t_h / 2.0) + 20.0;
+                3 => { // Grid
+                    let hw = (preview_w - 6.0) / 2.0;
+                    let hh = (preview_h - 6.0) / 2.0;
+                    nodes.push(SimNode { x: 2.0, y: 2.0, w: hw, h: hh, label: "1".to_string() });
+                    nodes.push(SimNode { x: 4.0 + hw, y: 2.0, w: hw, h: hh, label: "2".to_string() });
+                    nodes.push(SimNode { x: 2.0, y: 4.0 + hh, w: hw, h: hh, label: "3".to_string() });
+                    nodes.push(SimNode { x: 4.0 + hw, y: 4.0 + hh, w: hw, h: hh, label: "4".to_string() });
+                }
+                4 => { // Left Tiled (Main window on left, stack on right)
+                    let mw = (preview_w - 6.0) * 0.55;
+                    let sw = (preview_w - 6.0) - mw;
+                    let sh = (preview_h - 6.0) / 2.0;
+                    nodes.push(SimNode { x: 2.0, y: 2.0, w: mw, h: preview_h - 4.0, label: "M".to_string() });
+                    nodes.push(SimNode { x: 4.0 + mw, y: 2.0, w: sw, h: sh, label: "1".to_string() });
+                    nodes.push(SimNode { x: 4.0 + mw, y: 4.0 + sh, w: sw, h: sh, label: "2".to_string() });
+                }
+                5 => { // Right Tiled (Main window on right, stack on left)
+                    let mw = (preview_w - 6.0) * 0.55;
+                    let sw = (preview_w - 6.0) - mw;
+                    let sh = (preview_h - 6.0) / 2.0;
+                    nodes.push(SimNode { x: 2.0, y: 2.0, w: sw, h: sh, label: "1".to_string() });
+                    nodes.push(SimNode { x: 2.0, y: 4.0 + sh, w: sw, h: sh, label: "2".to_string() });
+                    nodes.push(SimNode { x: 4.0 + sw, y: 2.0, w: mw, h: preview_h - 4.0, label: "M".to_string() });
+                }
+                6 => { // Equal (Split evenly horizontally)
+                    let ew = (preview_w - 8.0) / 3.0;
+                    nodes.push(SimNode { x: 2.0, y: 2.0, w: ew, h: preview_h - 4.0, label: "1".to_string() });
+                    nodes.push(SimNode { x: 4.0 + ew, y: 2.0, w: ew, h: preview_h - 4.0, label: "2".to_string() });
+                    nodes.push(SimNode { x: 6.0 + 2.0 * ew, y: 2.0, w: ew, h: preview_h - 4.0, label: "3".to_string() });
+                }
+                7 => { // Spiral (Fibonacci layout)
+                    let w1 = (preview_w - 6.0) * 0.5;
+                    let w2 = (preview_w - 6.0) - w1;
+                    let h2 = (preview_h - 6.0) * 0.5;
+                    nodes.push(SimNode { x: 2.0, y: 2.0, w: w1, h: preview_h - 4.0, label: "1".to_string() });
+                    nodes.push(SimNode { x: 4.0 + w1, y: 2.0, w: w2, h: h2, label: "2".to_string() });
+                    nodes.push(SimNode { x: 4.0 + w1, y: 4.0 + h2, w: w2 * 0.5, h: h2, label: "3".to_string() });
+                    nodes.push(SimNode { x: 4.0 + w1 + w2 * 0.5, y: 4.0 + h2, w: w2 * 0.5, h: h2, label: "4".to_string() });
+                }
+                8 => { // Floating (Scatter windows randomly)
+                    nodes.push(SimNode { x: 4.0, y: 6.0, w: preview_w * 0.45, h: preview_h * 0.5, label: "1".to_string() });
+                    nodes.push(SimNode { x: preview_w * 0.4, y: 12.0, w: preview_w * 0.5, h: preview_h * 0.45, label: "2".to_string() });
+                    nodes.push(SimNode { x: 8.0, y: preview_h * 0.4, w: preview_w * 0.55, h: preview_h * 0.5, label: "3".to_string() });
+                }
+                _ => {}
+            }
+            
+            // Draw simulated layout preview rectangles
+            for node in nodes {
+                let rect_x = preview_x + node.x;
+                let rect_y = preview_y + node.y;
                 
-                // M indicator
-                nodes.push(TreeNode {
-                    label: "M".to_string(),
-                    is_focused: false,
-                    is_layout: false,
-                    is_role: true,
-                    x: tx_tree + 44.0,
-                    y: m_y - 6.0,
-                    w: 12.0,
-                    h: 12.0,
-                });
-                lines.push(TreeLine {
-                    x0: tx_tree + 30.0,
-                    y0: ty_tree + t_h / 2.0,
-                    x1: tx_tree + 44.0,
-                    y1: m_y,
-                    is_dotted: false,
-                });
+                // Semi-transparent blue for node backgrounds, slightly highlighted if active tag
+                let node_bg = if is_active { [0.30, 0.45, 0.65, 0.45] } else { [0.20, 0.24, 0.30, 0.25] };
+                let node_border = if is_active { [0.45, 0.65, 0.90, 0.85] } else { [0.35, 0.40, 0.45, 0.55] };
                 
-                // Master Window
-                let m_win = &groups[0];
-                let is_m_focused = !m_win.win.title.is_empty() && m_win.win.title == info.focused_title;
-                nodes.push(TreeNode {
-                    label: get_short_app_name(&m_win.win.app_id),
-                    is_focused: is_m_focused,
-                    is_layout: false,
-                    is_role: false,
-                    x: tx_tree + 68.0,
-                    y: m_y - 7.0,
-                    w: 36.0,
-                    h: 14.0,
-                });
-                lines.push(TreeLine {
-                    x0: tx_tree + 56.0,
-                    y0: m_y,
-                    x1: tx_tree + 68.0,
-                    y1: m_y,
-                    is_dotted: false,
-                });
+                pc.rect(node_bg, rect_x, rect_y, node.w, node.h);
                 
-                // S indicator
-                nodes.push(TreeNode {
-                    label: "S".to_string(),
-                    is_focused: false,
-                    is_layout: false,
-                    is_role: true,
-                    x: tx_tree + 44.0,
-                    y: s_y - 6.0,
-                    w: 12.0,
-                    h: 12.0,
-                });
-                lines.push(TreeLine {
-                    x0: tx_tree + 30.0,
-                    y0: ty_tree + t_h / 2.0,
-                    x1: tx_tree + 44.0,
-                    y1: s_y,
-                    is_dotted: false,
-                });
+                // Draw node border lines
+                pc.rect(node_border, rect_x, rect_y, node.w, 1.0);
+                pc.rect(node_border, rect_x, rect_y + node.h - 1.0, node.w, 1.0);
+                pc.rect(node_border, rect_x, rect_y, 1.0, node.h);
+                pc.rect(node_border, rect_x + node.w - 1.0, rect_y, 1.0, node.h);
                 
-                // Render first 2 Stack items vertically spaced
-                let s1_win = &groups[1];
-                let is_s1_focused = !s1_win.win.title.is_empty() && s1_win.win.title == info.focused_title;
-                let s1_y = s_y - 14.0;
-                nodes.push(TreeNode {
-                    label: get_short_app_name(&s1_win.win.app_id),
-                    is_focused: is_s1_focused,
-                    is_layout: false,
-                    is_role: false,
-                    x: tx_tree + 68.0,
-                    y: s1_y - 7.0,
-                    w: 36.0,
-                    h: 14.0,
-                });
-                lines.push(TreeLine {
-                    x0: tx_tree + 56.0,
-                    y0: s_y,
-                    x1: tx_tree + 68.0,
-                    y1: s1_y,
-                    is_dotted: false,
-                });
+                // Center the label text inside the simulated node
+                let text_sz = 9.0;
+                let text_w = node.label.len() as f32 * 6.0;
+                let text_color = if is_active { [0.95, 0.95, 1.0, 0.95] } else { [0.70, 0.70, 0.75, 0.75] };
+                let tx_offset = ((node.w - text_w) / 2.0).max(1.0);
+                let ty_offset = ((node.h - text_sz) / 2.0).max(1.0);
                 
-                let s2_win = &groups[2];
-                let is_s2_focused = !s2_win.win.title.is_empty() && s2_win.win.title == info.focused_title;
-                let s2_y = s_y + 14.0;
-                nodes.push(TreeNode {
-                    label: get_short_app_name(&s2_win.win.app_id),
-                    is_focused: is_s2_focused,
-                    is_layout: false,
-                    is_role: false,
-                    x: tx_tree + 68.0,
-                    y: s2_y - 7.0,
-                    w: 36.0,
-                    h: 14.0,
-                });
-                lines.push(TreeLine {
-                    x0: tx_tree + 56.0,
-                    y0: s_y,
-                    x1: tx_tree + 68.0,
-                    y1: s2_y,
-                    is_dotted: false,
-                });
+                pc.text(&node.label, rect_x + tx_offset, rect_y + ty_offset, text_sz, text_color);
             }
         }
         
-        let line_color = if is_active {
-            [0.30, 0.30, 0.35, 0.8]
-        } else {
-            [0.18, 0.18, 0.20, 0.6]
-        };
-        let draw_line = |pc: &mut PageContent, x0: f32, y0: f32, x1: f32, y1: f32, is_dotted: bool, color: [f32; 4]| {
-            if is_dotted {
-                if (x0 - x1).abs() < 0.1 {
-                    let sy = y0.min(y1);
-                    let ey = y0.max(y1);
-                    let mut curr_y = sy;
-                    while curr_y <= ey {
-                        pc.rect(color, x0 - 0.5, curr_y, 1.0, 1.0);
-                        curr_y += 3.0;
-                    }
-                } else if (y0 - y1).abs() < 0.1 {
-                    let sx = x0.min(x1);
-                    let ex = x0.max(x1);
-                    let mut curr_x = sx;
-                    while curr_x <= ex {
-                        pc.rect(color, curr_x, y0 - 0.5, 1.0, 1.0);
-                        curr_x += 3.0;
-                    }
-                } else {
-                    pc.rect(color, x0.min(x1), y0.min(y1), (x0 - x1).abs().max(1.0), (y0 - y1).abs().max(1.0));
-                }
-            } else {
-                if (x0 - x1).abs() < 0.1 {
-                    pc.rect(color, x0 - 0.5, y0.min(y1), 1.0, (y0 - y1).abs());
-                } else if (y0 - y1).abs() < 0.1 {
-                    pc.rect(color, x0.min(x1), y0 - 0.5, (x0 - x1).abs(), 1.0);
-                } else {
-                    pc.rect(color, x0.min(x1), y0.min(y1), (x0 - x1).abs().max(1.0), (y0 - y1).abs().max(1.0));
-                }
-            }
-        };
-        
-        for line in &lines {
-            let mid_x = (line.x0 + line.x1) / 2.0;
-            draw_line(&mut pc, line.x0, line.y0, mid_x, line.y0, line.is_dotted, line_color);
-            draw_line(&mut pc, mid_x, line.y0, mid_x, line.y1, line.is_dotted, line_color);
-            draw_line(&mut pc, mid_x, line.y1, line.x1, line.y1, line.is_dotted, line_color);
-        }
-        
-        // Draw nodes
-        for node in nodes {
-            let border = if node.is_layout {
-                [0.32, 0.32, 0.38, 1.0]
-            } else if node.is_role {
-                [0.20, 0.20, 0.24, 0.8]
-            } else if node.is_focused {
-                [0.2, 0.6, 1.0, 1.0]
-            } else {
-                [0.22, 0.22, 0.26, 1.0]
-            };
-            
-            let bg = if node.is_layout {
-                [0.14, 0.14, 0.18, 1.0]
-            } else if node.is_role {
-                [0.09, 0.09, 0.11, 0.9]
-            } else if node.is_focused {
-                [0.10, 0.32, 0.55, 1.0]
-            } else {
-                [0.11, 0.11, 0.15, 1.0]
-            };
-            
-            pc.rect(border, node.x, node.y, node.w, node.h);
-            pc.rect(bg, node.x + 1.0, node.y + 1.0, node.w - 2.0, node.h - 2.0);
-            
-            let text_color = if node.is_focused {
-                [1.0, 1.0, 1.0, 1.0]
-            } else if node.is_role {
-                [0.48, 0.48, 0.52, 1.0]
-            } else {
-                [0.78, 0.78, 0.82, 1.0]
-            };
-            
-            let text_sz = if node.is_layout {
-                7.5
-            } else if node.is_role {
-                7.0
-            } else {
-                7.0
-            };
-            
-            let char_width = text_sz * 0.52;
-            let text_w = node.label.len() as f32 * char_width;
-            let tx_offset = ((node.w - text_w) / 2.0).max(1.0);
-            let ty_offset = ((node.h - text_sz) / 2.0).max(1.0);
-            
-            pc.text(&node.label, node.x + tx_offset, node.y + ty_offset, text_sz, text_color);
-        }
-    }
-    
-    sec_cl.content_y += 2.0 * (card_h + 8.0) + 4.0;
-    y = sec_cl.finish(&mut pc);
+        sec_cl.content_y += 2.0 * (card_h + 8.0) + 4.0;
+        sec_cl.finish(pc)
+    });
 
     // 1. Border Width Section
-    let mut sec_bw = Section::new(&mut pc, cx, y, cw, "Border Width");
-    sec_bw.spacing(8.0);
-    for (i, param) in WidthParam::ALL.iter().enumerate() {
-        state.spinboxes[i].set_label(param.label());
-        sec_bw.widget(&mut pc, &mut state.spinboxes[i], 14.0, 200.0, 26.0);
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec_bw = Section::new(pc, rx, ry, sec_w, "Border Width");
         sec_bw.spacing(8.0);
-    }
-    y = sec_bw.finish_focused(&mut pc, sec_focused.get(0).copied().unwrap_or(false));
+        for (i, param) in WidthParam::ALL.iter().enumerate() {
+            state.spinboxes[i].set_label(param.label());
+            sec_bw.widget(pc, &mut state.spinboxes[i], 14.0, 200.0, 26.0);
+            sec_bw.spacing(8.0);
+        }
+        sec_bw.finish_focused(pc, sec_focused.first().copied().unwrap_or(false))
+    });
 
     // 2. Cascade Section
-    let mut sec_cascade = Section::new(&mut pc, cx, y, cw, "Cascade");
-    sec_cascade.spacing(8.0);
-    state.cascade_offset_spinbox.set_label("Offset");
-    sec_cascade.widget(&mut pc, &mut state.cascade_offset_spinbox, 14.0, 200.0, 26.0);
-    sec_cascade.spacing(8.0);
-    state.edge_gap_spinbox.set_label("Edge Gap");
-    sec_cascade.widget(&mut pc, &mut state.edge_gap_spinbox, 14.0, 200.0, 26.0);
-    sec_cascade.spacing(8.0);
-    state.top_gap_spinbox.set_label("Top Gap");
-    sec_cascade.widget(&mut pc, &mut state.top_gap_spinbox, 14.0, 200.0, 26.0);
-    sec_cascade.spacing(8.0);
-    y = sec_cascade.finish_focused(&mut pc, sec_focused.get(1).copied().unwrap_or(false));
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec_cascade = Section::new(pc, rx, ry, sec_w, "Cascade");
+        sec_cascade.spacing(8.0);
+        state.cascade_offset_spinbox.set_label("Offset");
+        sec_cascade.widget(pc, &mut state.cascade_offset_spinbox, 14.0, 200.0, 26.0);
+        sec_cascade.spacing(8.0);
+        state.edge_gap_spinbox.set_label("Edge Gap");
+        sec_cascade.widget(pc, &mut state.edge_gap_spinbox, 14.0, 200.0, 26.0);
+        sec_cascade.spacing(8.0);
+        state.top_gap_spinbox.set_label("Top Gap");
+        sec_cascade.widget(pc, &mut state.top_gap_spinbox, 14.0, 200.0, 26.0);
+        sec_cascade.spacing(8.0);
+        sec_cascade.finish_focused(pc, sec_focused.get(1).copied().unwrap_or(false))
+    });
 
     // 3. Movement Section
-    let mut movement_sec = Section::new(&mut pc, cx, y, cw, "Movement");
-    movement_sec.spacing(8.0);
-    state.transition_duration_spinbox.set_label("Duration (ms)");
-    movement_sec.widget(&mut pc, &mut state.transition_duration_spinbox, 14.0, 200.0, 26.0);
-    movement_sec.spacing(8.0);
-    y = movement_sec.finish_focused(&mut pc, sec_focused.get(2).copied().unwrap_or(false));
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut movement_sec = Section::new(pc, rx, ry, sec_w, "Movement");
+        movement_sec.spacing(8.0);
+        state.transition_duration_spinbox.set_label("Duration (ms)");
+        movement_sec.widget(pc, &mut state.transition_duration_spinbox, 14.0, 200.0, 26.0);
+        movement_sec.spacing(8.0);
+        movement_sec.finish_focused(pc, sec_focused.get(2).copied().unwrap_or(false))
+    });
 
     // 4. Default Layouts Section
-    let mut default_layouts_sec = Section::new(&mut pc, cx, y, cw, "Default Layouts");
-    default_layouts_sec.spacing(8.0);
-    for i in 0..4 {
-        default_layouts_sec.widget(&mut pc, &mut state.tag_layout_menus[i], 14.0, 200.0, 26.0);
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut default_layouts_sec = Section::new(pc, rx, ry, sec_w, "Default Layouts");
         default_layouts_sec.spacing(8.0);
-    }
-    default_layouts_sec.finish_focused(&mut pc, sec_focused.get(3).copied().unwrap_or(false));
-
-
+        for i in 0..4 {
+            default_layouts_sec.widget(pc, &mut state.tag_layout_menus[i], 14.0, 200.0, 26.0);
+            default_layouts_sec.spacing(8.0);
+        }
+        default_layouts_sec.finish_focused(pc, sec_focused.get(3).copied().unwrap_or(false))
+    });
 
-    pc
+    final_pc
 }
 
 fn set_width(state: &mut LayoutState, param: WidthParam, val: u16) {
@@ -1270,4 +847,12 @@ mode = "popup"
         let modes = parse_tag_layouts_from_config(content);
         assert_eq!(modes, vec!["cascade", "cascade", "cascade", "cascade"]);
     }
+
+    #[test]
+    fn test_view_layout_grid() {
+        let mut state = LayoutState::default();
+        let mut layout = clear_ui::layout::ColumnLayout::new(20.0);
+        let pc = view(&mut state, 10.0, 20.0, 800.0, 600.0, &[false, false, false, false], &mut layout);
+        assert!(!pc.rects.is_empty() || !pc.texts.is_empty());
+    }
 }
diff --git a/src/pages/mod.rs b/src/pages/mod.rs
index de38e69..6a4f46d 100644
--- a/src/pages/mod.rs
+++ b/src/pages/mod.rs
@@ -12,7 +12,7 @@ pub mod notifications;
 pub mod backup;
 pub mod typeface;
 pub mod services;
-pub mod colors;
+pub mod interface;
 pub mod screensaver;
 pub mod accounts;
 
@@ -32,7 +32,7 @@ pub enum Page {
     Notifications,
     Backup,
     Typefaces,
-    Colors,
+    Interface,
     Screensaver,
 }
 
@@ -41,7 +41,7 @@ impl Page {
         Page::Accounts,
         Page::Audio,
         Page::Backup,
-        Page::Colors,
+        Page::Interface,
         Page::Display,
         Page::Input,
         Page::Layout,
@@ -72,7 +72,7 @@ impl Page {
             Page::Notifications => "Notifications",
             Page::Backup => "Backup",
             Page::Typefaces => "Typefaces",
-            Page::Colors => "Colors",
+            Page::Interface => "Interface",
             Page::Screensaver => "Screensaver",
         }
     }
diff --git a/src/pages/network.rs b/src/pages/network.rs
index a55db88..798bce4 100644
--- a/src/pages/network.rs
+++ b/src/pages/network.rs
@@ -1,6 +1,6 @@
 use crate::app::{AppAction, PageContent};
-use clear_ui::layout::Section;
-use clear_ui::widget::{Widget, TextLabel, ScrollBox, ScrollingList};
+use clear_ui::layout::{Section, PageLayoutBuilder, LayoutStrategy};
+use clear_ui::widget::ScrollingList;
 
 #[derive(Debug, Clone)]
 pub struct WifiNetwork {
@@ -27,6 +27,8 @@ pub struct NetworkState {
     pub ip_address: String,
     pub device: String,
     pub available: Vec<WifiNetwork>,
+    pub bt_installed: bool,
+    pub bt_service_active: bool,
     pub bt_enabled: bool,
     pub bt_devices: Vec<BluetoothDevice>,
     pub bt_scanning: bool,
@@ -42,6 +44,8 @@ pub enum NetworkMessage {
     BtConnect(String),
     BtDisconnect(String),
     BtScan,
+    InstallBtTools,
+    StartBtService,
 }
 
 pub async fn fetch_network_state() -> NetworkState {
@@ -85,12 +89,13 @@ pub async fn fetch_network_state() -> NetworkState {
         }).unwrap_or_default();
 
     let available = if wifi_enabled { fetch_wifi_list().await } else { Vec::new() };
-    let (bt_enabled, bt_devices) = fetch_bluetooth_state().await;
+    let (bt_installed, bt_service_active, bt_enabled, bt_devices) = fetch_bluetooth_state().await;
 
     NetworkState {
         loaded: true,
         wifi_enabled, connected_ssid, signal_strength: signal,
         ip_address, device, available,
+        bt_installed, bt_service_active,
         bt_enabled, bt_devices, bt_scanning: false,
         wifi_list_box: ScrollingList::new(26.0, 4.0),
     }
@@ -124,14 +129,35 @@ async fn fetch_wifi_list() -> Vec<WifiNetwork> {
     networks
 }
 
-async fn fetch_bluetooth_state() -> (bool, Vec<BluetoothDevice>) {
+async fn fetch_bluetooth_state() -> (bool, bool, bool, Vec<BluetoothDevice>) {
+    let bt_installed = tokio::process::Command::new("bluetoothctl")
+        .arg("--version")
+        .output()
+        .await
+        .is_ok();
+
+    if !bt_installed {
+        return (false, false, false, Vec::new());
+    }
+
+    let bt_service_active = tokio::process::Command::new("systemctl")
+        .args(["is-active", "bluetooth"])
+        .output()
+        .await
+        .map(|o| String::from_utf8_lossy(&o.stdout).trim() == "active")
+        .unwrap_or(false);
+
+    if !bt_service_active {
+        return (true, false, false, Vec::new());
+    }
+
     let bt_enabled = tokio::process::Command::new("bluetoothctl")
         .args(["show"]).output().await.ok()
         .map(|o| String::from_utf8_lossy(&o.stdout).lines().any(|l| l.contains("Powered: yes")))
         .unwrap_or(false);
 
     let devices = if bt_enabled { fetch_bt_devices().await } else { Vec::new() };
-    (bt_enabled, devices)
+    (true, true, bt_enabled, devices)
 }
 
 async fn fetch_bt_devices() -> Vec<BluetoothDevice> {
@@ -148,13 +174,23 @@ async fn fetch_bt_devices() -> Vec<BluetoothDevice> {
         let parts: Vec<&str> = rest.splitn(2, ' ').collect();
         if parts.len() < 2 || parts[0].is_empty() || parts[1].is_empty() { continue; }
         let mac = parts[0].to_string();
-        let name = parts[1].to_string();
+        let default_name = parts[1].to_string();
 
         let info = tokio::process::Command::new("bluetoothctl")
             .args(["info", &mac]).output().await.ok()
             .map(|o| String::from_utf8_lossy(&o.stdout).to_string())
             .unwrap_or_default();
 
+        let info_name = info.lines()
+            .find(|l| l.contains("Name:"))
+            .and_then(|l| l.splitn(2, ':').nth(1).map(|s| s.trim().to_string()));
+
+        let info_alias = info.lines()
+            .find(|l| l.contains("Alias:"))
+            .and_then(|l| l.splitn(2, ':').nth(1).map(|s| s.trim().to_string()));
+
+        let name = info_name.or(info_alias).unwrap_or(default_name);
+
         let connected = info.lines().any(|l| l.contains("Connected: yes"));
         let icon = info.lines()
             .find(|l| l.contains("Icon:"))
@@ -208,104 +244,193 @@ const NET_BTN: [f32; 4] = [0.13, 0.20, 0.27, 1.0];
 const ACT_BTN: [f32; 4] = [0.16, 0.29, 0.18, 1.0];
 const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
 
-pub fn view(state: &mut NetworkState, cx: f32, cy: f32, cw: f32, _ch: f32, root_focused: bool) -> PageContent {
-    let mut pc = PageContent::new();
-    let mut y = cy + 12.0;
+pub fn view(state: &mut NetworkState, cx: f32, cy: f32, cw: f32, ch: f32, root_focused: bool, layout: &mut dyn LayoutStrategy) -> PageContent {
+    let mut final_pc = PageContent::new();
+    let sec_w = 320.0f32;
+    let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(2);
 
     // ── WiFi ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "WiFi");
-
-    if !state.loaded {
-        sec.text(&mut pc, "Loading WiFi interfaces...", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec.spacing(18.0);
-    } else {
-        let yt = sec.ay();
-        pc.button(if state.wifi_enabled { "ON" } else { "OFF" },
-            sec.ax(cw - 80.0), yt, 60.0, 28.0,
-            if state.wifi_enabled { TOGGLE_ON } else { TOGGLE_OFF }, BTN_HOVER, WHITE,
-            AppAction::Radios(NetworkMessage::ToggleWifi));
-        sec.content_y += 34.0;
-
-        if !state.connected_ssid.is_empty() {
-            sec.text(&mut pc, &format!("Connected: {}", state.connected_ssid), 14.0, 0.0, 13.0, ACCENT);
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "WiFi");
+
+        if !state.loaded {
+            sec.text(pc, "Loading WiFi interfaces...", 12.0, 0.0, 12.0, TEXT_DIM);
             sec.spacing(18.0);
-            sec.text(&mut pc, &format!("Signal: {}%  IP: {}", state.signal_strength, state.ip_address),
-                14.0, 0.0, 12.0, TEXT_DIM);
-            sec.spacing(16.0);
-        } else if state.wifi_enabled {
-            sec.text(&mut pc, "Not connected", 14.0, 0.0, 12.0, TEXT_DIM);
-            sec.spacing(16.0);
-        }
+        } else {
+            let yt = sec.ay();
+            let wifi_btn_w = if sec_w < 200.0 { 40.0 } else { 60.0 };
+            let wifi_btn_x = sec_w - wifi_btn_w - 12.0;
+
+            pc.button(if state.wifi_enabled { "ON" } else { "OFF" },
+                sec.ax(wifi_btn_x), yt, wifi_btn_w, 28.0,
+                if state.wifi_enabled { TOGGLE_ON } else { TOGGLE_OFF }, BTN_HOVER, WHITE,
+                AppAction::Radios(NetworkMessage::ToggleWifi));
+            sec.content_y += 34.0;
+
+            if !state.connected_ssid.is_empty() {
+                let ssid_max_chars = ((sec_w - 24.0) / 7.0) as usize;
+                let ssid_truncated = if state.connected_ssid.len() > ssid_max_chars {
+                    format!("{}...", &state.connected_ssid[..ssid_max_chars.saturating_sub(3)])
+                } else {
+                    state.connected_ssid.clone()
+                };
+                sec.text(pc, &format!("Connected: {}", ssid_truncated), 14.0, 0.0, 13.0, ACCENT);
+                sec.spacing(18.0);
+
+                if sec_w < 220.0 {
+                    sec.text(pc, &format!("Signal: {}%", state.signal_strength), 14.0, 0.0, 12.0, TEXT_DIM);
+                    sec.spacing(16.0);
+                    if !state.ip_address.is_empty() {
+                        sec.text(pc, &format!("IP: {}", state.ip_address), 14.0, 0.0, 12.0, TEXT_DIM);
+                        sec.spacing(16.0);
+                    }
+                } else {
+                    sec.text(pc, &format!("Signal: {}%  IP: {}", state.signal_strength, state.ip_address),
+                        14.0, 0.0, 12.0, TEXT_DIM);
+                    sec.spacing(16.0);
+                }
+            } else if state.wifi_enabled {
+                sec.text(pc, "Not connected", 14.0, 0.0, 12.0, TEXT_DIM);
+                sec.spacing(16.0);
+            }
 
-        if state.wifi_enabled && !state.available.is_empty() {
-            let list_box_x = cx + 12.0;
-            let list_box_y = sec.ay();
-            let list_box_w = cw - 24.0;
-            let list_box_h = 160.0;
-
-            clear_ui::layout::render_widget(&mut pc, &mut state.wifi_list_box, list_box_x, list_box_y, list_box_w, list_box_h);
-
-            state.wifi_list_box.update_bounds(state.available.len(), list_box_y, list_box_h);
-
-            for (idx, net) in state.available.iter().enumerate() {
-                if let Some(draw_y) = state.wifi_list_box.get_item_draw_y(idx, 4.0) {
-                    let prefix = if net.in_use { ">" } else { " " };
-                    let label = format!("{}  {}  ({}%)", prefix, net.ssid, net.signal);
-                    let active = net.in_use;
-                    pc.button(&label, list_box_x + 4.0, draw_y, list_box_w - 24.0, 26.0,
-                        if active { ACT_BTN } else { NET_BTN }, BTN_HOVER,
-                        if active { ACCENT } else { TEXT_FG },
-                        AppAction::Radios(NetworkMessage::ConnectWifi(net.ssid.clone())));
+            if state.wifi_enabled && !state.available.is_empty() {
+                let list_box_x = rx + 12.0;
+                let list_box_y = sec.ay();
+                let list_box_w = sec_w - 24.0;
+                let list_box_h = 160.0;
+
+                clear_ui::layout::render_widget(pc, &mut state.wifi_list_box, list_box_x, list_box_y, list_box_w, list_box_h);
+
+                state.wifi_list_box.update_bounds(state.available.len(), list_box_y, list_box_h);
+
+                let btn_w = list_box_w - 24.0;
+                let max_chars = ((btn_w / 6.5) as usize).saturating_sub(10).max(5);
+
+                for (idx, net) in state.available.iter().enumerate() {
+                    if let Some(draw_y) = state.wifi_list_box.get_item_draw_y(idx, 4.0) {
+                        let prefix = if net.in_use { ">" } else { " " };
+                        let ssid_truncated = if net.ssid.len() > max_chars {
+                            format!("{}...", &net.ssid[..max_chars.saturating_sub(3)])
+                        } else {
+                            net.ssid.clone()
+                        };
+                        let label = format!("{}  {}  ({}%)", prefix, ssid_truncated, net.signal);
+                        let active = net.in_use;
+                        pc.button(&label, list_box_x + 4.0, draw_y, btn_w, 26.0,
+                            if active { ACT_BTN } else { NET_BTN }, BTN_HOVER,
+                            if active { ACCENT } else { TEXT_FG },
+                            AppAction::Radios(NetworkMessage::ConnectWifi(net.ssid.clone())));
+                    }
                 }
+                sec.content_y += list_box_h + 8.0;
             }
-            sec.content_y += list_box_h + 8.0;
         }
-    }
-
-    y = sec.finish_focused(&mut pc, root_focused);
+        sec.finish_focused(pc, root_focused)
+    });
 
     // ── Bluetooth ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Bluetooth");
-
-    if !state.loaded {
-        sec.text(&mut pc, "Loading Bluetooth status...", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec.spacing(18.0);
-    } else {
-        let yt = sec.ay();
-        pc.button(if state.bt_enabled { "ON" } else { "OFF" },
-            sec.ax(cw - 140.0), yt, 60.0, 28.0,
-            if state.bt_enabled { TOGGLE_ON } else { TOGGLE_OFF }, BTN_HOVER, WHITE,
-            AppAction::Radios(NetworkMessage::ToggleBluetooth));
-        pc.button("Scan", sec.ax(cw - 72.0), yt, 52.0, 28.0,
-            TOGGLE_OFF, BTN_HOVER, WHITE,
-            AppAction::Radios(NetworkMessage::BtScan));
-        sec.content_y += 34.0;
-
-        if state.bt_devices.is_empty() {
-            if state.bt_enabled {
-                sec.text(&mut pc, "No paired devices found", 14.0, 0.0, 12.0, TEXT_DIM);
-            }
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let bt_sec_w = sec_w * 2.0;
+        let mut sec = Section::new(pc, rx, ry, bt_sec_w, "Bluetooth");
+
+        if !state.loaded {
+            sec.text(pc, "Loading Bluetooth status...", 12.0, 0.0, 12.0, TEXT_DIM);
+            sec.spacing(18.0);
+        } else if !state.bt_installed {
+            sec.text(pc, "Bluetooth tools (bluez) not installed", 14.0, 0.0, 12.0, TEXT_DIM);
+            sec.spacing(18.0);
+            let btn_w = if bt_sec_w < 200.0 { 100.0 } else { 120.0 };
+            let yt = sec.ay();
+            pc.button("Install Tools", rx + 12.0, yt, btn_w, 28.0,
+                TOGGLE_ON, BTN_HOVER, WHITE,
+                AppAction::Radios(NetworkMessage::InstallBtTools));
+            sec.content_y += 34.0;
+        } else if !state.bt_service_active {
+            sec.text(pc, "Bluetooth service is stopped", 14.0, 0.0, 12.0, TEXT_DIM);
+            sec.spacing(18.0);
+            let btn_w = if bt_sec_w < 200.0 { 100.0 } else { 120.0 };
+            let yt = sec.ay();
+            pc.button("Start Service", rx + 12.0, yt, btn_w, 28.0,
+                TOGGLE_ON, BTN_HOVER, WHITE,
+                AppAction::Radios(NetworkMessage::StartBtService));
+            sec.content_y += 34.0;
         } else {
-            for dev in &state.bt_devices {
-                let status = if dev.connected { ">" } else { " " };
-                let label = format!("{} {} ({})", status, dev.name, dev.mac);
-                let action_label = if dev.connected { "Disconnect" } else { "Connect" };
-                let yt = sec.ay();
-                sec.text(&mut pc, &label, 14.0, 0.0, 12.0, if dev.connected { ACCENT } else { TEXT_FG });
-                pc.button(action_label, sec.ax(cw - 90.0), yt - 2.0, 70.0, 22.0,
-                    if dev.connected { TOGGLE_OFF } else { TOGGLE_ON }, BTN_HOVER, WHITE,
-                    if dev.connected {
-                        AppAction::Radios(NetworkMessage::BtDisconnect(dev.mac.clone()))
+            let yt = sec.ay();
+            let bt_btn_w = if bt_sec_w < 200.0 { 40.0 } else { 60.0 };
+            let scan_btn_w = if bt_sec_w < 200.0 { 40.0 } else { 52.0 };
+            let bt_btn_x = bt_sec_w - bt_btn_w - scan_btn_w - 20.0;
+            let scan_btn_x = bt_sec_w - scan_btn_w - 12.0;
+
+            pc.button(if state.bt_enabled { "ON" } else { "OFF" },
+                sec.ax(bt_btn_x), yt, bt_btn_w, 28.0,
+                if state.bt_enabled { TOGGLE_ON } else { TOGGLE_OFF }, BTN_HOVER, WHITE,
+                AppAction::Radios(NetworkMessage::ToggleBluetooth));
+            pc.button("Scan", sec.ax(scan_btn_x), yt, scan_btn_w, 28.0,
+                TOGGLE_OFF, BTN_HOVER, WHITE,
+                AppAction::Radios(NetworkMessage::BtScan));
+            sec.content_y += 34.0;
+
+            if state.bt_devices.is_empty() {
+                if state.bt_enabled {
+                    let no_devices_msg = if bt_sec_w < 200.0 { "No paired devices" } else { "No paired devices found" };
+                    sec.text(pc, no_devices_msg, 14.0, 0.0, 12.0, TEXT_DIM);
+                }
+            } else {
+                for dev in &state.bt_devices {
+                    let status = if dev.connected { ">" } else { " " };
+                    let btn_w = if bt_sec_w < 250.0 { 42.0 } else { 70.0 };
+                    let action_label = if dev.connected {
+                        if bt_sec_w < 250.0 { "Disc" } else { "Disconnect" }
+                    } else {
+                        if bt_sec_w < 250.0 { "Conn" } else { "Connect" }
+                    };
+
+                    let label_max_w = (bt_sec_w - btn_w - 20.0 - 14.0 - 8.0).max(20.0);
+                    let label_max_chars = ((label_max_w / 6.0) as usize).max(5);
+
+                    let is_unknown = dev.name.replace('-', ":").eq_ignore_ascii_case(&dev.mac);
+                    let label = if is_unknown {
+                        if bt_sec_w < 350.0 {
+                            format!("{} {}", status, dev.mac)
+                        } else {
+                            format!("{} Unknown Device ({})", status, dev.mac)
+                        }
                     } else {
-                        AppAction::Radios(NetworkMessage::BtConnect(dev.mac.clone()))
-                    });
-                sec.content_y += 24.0;
+                        if bt_sec_w < 350.0 {
+                            let name_truncated = if dev.name.len() > label_max_chars {
+                                format!("{}...", &dev.name[..label_max_chars.saturating_sub(3)])
+                            } else {
+                                dev.name.clone()
+                            };
+                            format!("{} {}", status, name_truncated)
+                        } else {
+                            let full_label = format!("{} {} ({})", status, dev.name, dev.mac);
+                            if full_label.len() > label_max_chars {
+                                format!("{}...", &full_label[..label_max_chars.saturating_sub(3)])
+                            } else {
+                                full_label
+                            }
+                        }
+                    };
+
+                    let yt = sec.ay();
+                    sec.text(pc, &label, 14.0, 0.0, 12.0, if dev.connected { ACCENT } else { TEXT_FG });
+                    pc.button(action_label, sec.ax(bt_sec_w - btn_w - 20.0), yt - 2.0, btn_w, 22.0,
+                        if dev.connected { TOGGLE_OFF } else { TOGGLE_ON }, BTN_HOVER, WHITE,
+                        if dev.connected {
+                            AppAction::Radios(NetworkMessage::BtDisconnect(dev.mac.clone()))
+                        } else {
+                            AppAction::Radios(NetworkMessage::BtConnect(dev.mac.clone()))
+                        });
+                    sec.content_y += 24.0;
+                }
             }
         }
-    }
-    sec.finish(&mut pc);
+        sec.finish(pc)
+    });
 
-    pc
+    final_pc
 }
 
 pub fn update(state: &mut NetworkState, msg: NetworkMessage) {
@@ -327,5 +452,28 @@ pub fn update(state: &mut NetworkState, msg: NetworkMessage) {
         NetworkMessage::BtConnect(mac) => { bt_connect(&mac); }
         NetworkMessage::BtDisconnect(mac) => { bt_disconnect(&mac); }
         NetworkMessage::BtScan => { bt_scan(); }
+        NetworkMessage::InstallBtTools => {
+            let _ = tokio::process::Command::new("pkexec")
+                .args(["sh", "-c", "pacman -S --noconfirm bluez bluez-utils && systemctl enable --now bluetooth"])
+                .spawn();
+        }
+        NetworkMessage::StartBtService => {
+            let _ = tokio::process::Command::new("pkexec")
+                .args(["systemctl", "enable", "--now", "bluetooth"])
+                .spawn();
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_view_layout_grid() {
+        let mut state = NetworkState::default();
+        let mut layout = clear_ui::layout::ColumnLayout::new(20.0);
+        let pc = view(&mut state, 10.0, 20.0, 800.0, 600.0, false, &mut layout);
+        assert!(!pc.rects.is_empty() || !pc.texts.is_empty() || !pc.buttons.is_empty());
     }
 }
diff --git a/src/pages/notifications.rs b/src/pages/notifications.rs
index 531d766..cc70ceb 100644
--- a/src/pages/notifications.rs
+++ b/src/pages/notifications.rs
@@ -2,7 +2,7 @@ use std::fs;
 use std::io::Write;
 
 use crate::app::{AppAction, PageContent};
-use clear_ui::layout::Section;
+use clear_ui::layout::{Section, PageLayoutBuilder, LayoutStrategy};
 use clear_ui::widget::{Toggle, Spinbox, Slider};
 
 const CONFIG_PATH: &str = "/home/lsgalante/.config/ccec/config.toml";
@@ -308,53 +308,60 @@ fn write_transparency_config_value(key: &str, value: &str) {
     let _ = fs::write(CONFIG_PATH, updated);
 }
 
-pub fn view(state: &mut NotificationsState, cx: f32, cy: f32, cw: f32, _ch: f32) -> PageContent {
-    let mut pc = PageContent::new();
-    let y = cy + 12.0;
-
-    let mut sec = Section::new(&mut pc, cx, y, cw, "System Notifications");
-
-    let toggle_w = 48.0;
-    let toggle_h = 24.0;
-    state.enable_toggle.set_toggled(state.enable);
-    sec.widget(&mut pc, &mut state.enable_toggle, 14.0, toggle_w, toggle_h);
-    sec.spacing(8.0);
-
-    state.bell_toggle.set_toggled(state.bell);
-    sec.widget(&mut pc, &mut state.bell_toggle, 14.0, toggle_w, toggle_h);
-    sec.spacing(16.0);
-
-    state.duration_spinbox.value = state.duration;
-    state.duration_spinbox.set_label("Notification Duration");
-    sec.widget(&mut pc, &mut state.duration_spinbox, 14.0, 200.0, 26.0);
-    sec.spacing(16.0);
-
-    let btn_w = 160.0;
-    let btn_h = 32.0;
-    let btn_y = sec.ay();
-    sec.row(1, 0.0, btn_h, |_, x, _| {
-        pc.button(
-            "Send Test Notification",
-            x,
-            btn_y,
-            btn_w,
-            btn_h,
-            BTN_BG,
-            BTN_HOVER,
-            WHITE,
-            AppAction::Notifications(NotificationsMessage::SendTestNotification),
-        );
+pub fn view(state: &mut NotificationsState, cx: f32, cy: f32, cw: f32, ch: f32, layout: &mut dyn LayoutStrategy) -> PageContent {
+    let mut final_pc = PageContent::new();
+    let sec_w = 320.0f32;
+    let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(2);
+
+    // ── System Notifications ──
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "System Notifications");
+
+        let toggle_w = 48.0;
+        let toggle_h = 24.0;
+        state.enable_toggle.set_toggled(state.enable);
+        sec.widget(pc, &mut state.enable_toggle, 14.0, toggle_w, toggle_h);
+        sec.spacing(8.0);
+
+        state.bell_toggle.set_toggled(state.bell);
+        sec.widget(pc, &mut state.bell_toggle, 14.0, toggle_w, toggle_h);
+        sec.spacing(16.0);
+
+        state.duration_spinbox.value = state.duration;
+        state.duration_spinbox.set_label("Notification Duration");
+        sec.widget(pc, &mut state.duration_spinbox, 14.0, 200.0, 26.0);
+        sec.spacing(16.0);
+
+        let btn_w = 160.0;
+        let btn_h = 32.0;
+        let btn_y = sec.ay();
+        sec.row(1, 0.0, btn_h, |_, x, _| {
+            pc.button(
+                "Send Test Notification",
+                x,
+                btn_y,
+                btn_w,
+                btn_h,
+                BTN_BG,
+                BTN_HOVER,
+                WHITE,
+                AppAction::Notifications(NotificationsMessage::SendTestNotification),
+            );
+        });
+        sec.spacing(12.0);
+        sec.finish(pc)
     });
-    sec.spacing(12.0);
-    sec.finish(&mut pc);
 
-    let mut sec2 = Section::new(&mut pc, cx, sec.ay() + 24.0, cw, "Transparency");
-    state.opacity_slider.set_value(state.opacity);
-    sec2.widget(&mut pc, &mut state.opacity_slider, 14.0, 300.0, 20.0);
-    sec2.spacing(12.0);
-    sec2.finish(&mut pc);
+    // ── Transparency ──
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec2 = Section::new(pc, rx, ry, sec_w, "Transparency");
+        state.opacity_slider.set_value(state.opacity);
+        sec2.widget(pc, &mut state.opacity_slider, 14.0, 300.0, 20.0);
+        sec2.spacing(12.0);
+        sec2.finish(pc)
+    });
 
-    pc
+    final_pc
 }
 
 pub fn update(state: &mut NotificationsState, msg: NotificationsMessage) {
@@ -451,4 +458,13 @@ duration = 10
 ";
         assert_eq!(parse_notifications_duration(content), 10);
     }
+
+    #[test]
+    fn test_view_layout_grid() {
+        let mut state = NotificationsState::default();
+        let mut layout = clear_ui::layout::ColumnLayout::new(20.0);
+        let pc = view(&mut state, 10.0, 20.0, 800.0, 600.0, &mut layout);
+        assert!(!pc.rects.is_empty() || !pc.texts.is_empty());
+    }
 }
+
diff --git a/src/pages/screensaver.rs b/src/pages/screensaver.rs
index 6f14b46..9f17ef5 100644
--- a/src/pages/screensaver.rs
+++ b/src/pages/screensaver.rs
@@ -1,6 +1,6 @@
 use std::fs;
 use crate::app::{AppAction, PageContent};
-use clear_ui::layout::Section;
+use clear_ui::layout::{Section, PageLayoutBuilder, LayoutStrategy};
 use clear_ui::widget::{Toggle, Spinbox, Dropdown};
 
 const CONFIG_PATH: &str = "/home/lsgalante/.config/ccec/config.toml";
@@ -230,50 +230,53 @@ const BTN_BG: [f32; 4] = [0.20, 0.40, 0.65, 1.0];
 const BTN_HOVER: [f32; 4] = [0.28, 0.50, 0.78, 1.0];
 const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
 
-pub fn view(state: &mut ScreensaverState, cx: f32, cy: f32, cw: f32, _ch: f32) -> PageContent {
-    let mut pc = PageContent::new();
-    let y = cy + 12.0;
+pub fn view(state: &mut ScreensaverState, cx: f32, cy: f32, cw: f32, ch: f32, layout: &mut dyn LayoutStrategy) -> PageContent {
+    let mut final_pc = PageContent::new();
+    let sec_w = 320.0f32;
+    let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(1);
 
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Screensaver Settings");
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Screensaver Settings");
 
-    let toggle_w = 48.0;
-    let toggle_h = 24.0;
-    
-    state.enable_toggle.set_toggled(state.enable);
-    sec.widget(&mut pc, &mut state.enable_toggle, 14.0, toggle_w, toggle_h);
-    sec.spacing(8.0);
+        let toggle_w = 48.0;
+        let toggle_h = 24.0;
+        
+        state.enable_toggle.set_toggled(state.enable);
+        sec.widget(pc, &mut state.enable_toggle, 14.0, toggle_w, toggle_h);
+        sec.spacing(8.0);
 
-    state.lock_screen_toggle.set_toggled(state.lock_screen);
-    sec.widget(&mut pc, &mut state.lock_screen_toggle, 14.0, toggle_w, toggle_h);
-    sec.spacing(16.0);
+        state.lock_screen_toggle.set_toggled(state.lock_screen);
+        sec.widget(pc, &mut state.lock_screen_toggle, 14.0, toggle_w, toggle_h);
+        sec.spacing(16.0);
 
-    state.timeout_spinbox.value = state.timeout;
-    sec.widget(&mut pc, &mut state.timeout_spinbox, 14.0, 200.0, 26.0);
-    sec.spacing(16.0);
+        state.timeout_spinbox.value = state.timeout;
+        sec.widget(pc, &mut state.timeout_spinbox, 14.0, 200.0, 26.0);
+        sec.spacing(16.0);
 
-    sec.widget(&mut pc, &mut state.style_menu, 14.0, 200.0, 26.0);
-    sec.spacing(24.0);
+        sec.widget(pc, &mut state.style_menu, 14.0, 200.0, 26.0);
+        sec.spacing(24.0);
 
-    let btn_w = 160.0;
-    let btn_h = 32.0;
-    let btn_y = sec.ay();
-    sec.row(1, 0.0, btn_h, |_, x, _| {
-        pc.button(
-            "Preview Screensaver",
-            x,
-            btn_y,
-            btn_w,
-            btn_h,
-            BTN_BG,
-            BTN_HOVER,
-            WHITE,
-            AppAction::Screensaver(ScreensaverMessage::StartPreview),
-        );
+        let btn_w = 160.0;
+        let btn_h = 32.0;
+        let btn_y = sec.ay();
+        sec.row(1, 0.0, btn_h, |_, x, _| {
+            pc.button(
+                "Preview Screensaver",
+                x,
+                btn_y,
+                btn_w,
+                btn_h,
+                BTN_BG,
+                BTN_HOVER,
+                WHITE,
+                AppAction::Screensaver(ScreensaverMessage::StartPreview),
+            );
+        });
+        sec.spacing(12.0);
+        sec.finish(pc)
     });
-    sec.spacing(12.0);
-    sec.finish(&mut pc);
 
-    pc
+    final_pc
 }
 
 pub fn update(state: &mut ScreensaverState, msg: ScreensaverMessage) {
diff --git a/src/pages/services.rs b/src/pages/services.rs
index 47bdcb6..8e1092f 100644
--- a/src/pages/services.rs
+++ b/src/pages/services.rs
@@ -1,9 +1,10 @@
 use crate::app::PageContent;
-use clear_ui::layout::Section;
+use clear_ui::layout::{Section, PageLayoutBuilder, LayoutStrategy};
 use clear_ui::widget::{Widget, TextLabel, ScrollBox, ScrollingList, TextBox};
 use clear_ui::widget::{ElementState, KeyEvent, MouseButton, Key, NamedKey};
 
 
+
 // ── Service Types and Page State ──
 
 #[derive(Debug, Clone)]
@@ -137,182 +138,199 @@ fn service_action(name: &str, action: &str, is_system: bool) {
 
 const TEXT_DIM: [f32; 4] = [0.53, 0.53, 0.60, 1.0];
 
-pub fn view(state: &mut ServicesState, cx: f32, cy: f32, cw: f32, _ch: f32, root_focused: bool) -> PageContent {
-    let mut pc = PageContent::new();
-    let y = cy + 12.0;
+pub fn view(state: &mut ServicesState, cx: f32, cy: f32, cw: f32, ch: f32, root_focused: bool, layout: &mut dyn LayoutStrategy) -> PageContent {
+    let mut final_pc = PageContent::new();
+    let sec_w = 320.0f32;
+    let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(1);
 
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Services");
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Services");
 
-    if !state.loaded {
-        sec.text(&mut pc, "Loading systemd services...", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec.spacing(18.0);
-    } else {
-        // Tab header buttons: System Services, User Services
-        let tab_w = 140.0;
-        let tab_h = 28.0;
-        let tab_y = sec.ay();
-        let active_bg = [0.20, 0.40, 0.65, 0.4];
-        let inactive_bg = [0.10, 0.10, 0.16, 0.3];
-        let hover_bg = [0.20, 0.20, 0.25, 0.15];
-
-        pc.button(
-            "System Services",
-            cx + 12.0,
-            tab_y,
-            tab_w,
-            tab_h,
-            if state.active_tab == ServiceTab::System { active_bg } else { inactive_bg },
-            hover_bg,
-            [0.90, 0.90, 0.95, 1.0],
-            crate::app::AppAction::Services(ServicesMessage::SetTab(ServiceTab::System)),
-        );
-
-        pc.button(
-            "User Services",
-            cx + 12.0 + tab_w + 8.0,
-            tab_y,
-            tab_w,
-            tab_h,
-            if state.active_tab == ServiceTab::User { active_bg } else { inactive_bg },
-            hover_bg,
-            [0.90, 0.90, 0.95, 1.0],
-            crate::app::AppAction::Services(ServicesMessage::SetTab(ServiceTab::User)),
-        );
-        sec.content_y += tab_h + 12.0;
-
-        // Search textbox
-        let search_y = sec.ay() + state.search_box.top_room();
-        let search_w = cw - 24.0;
-        let search_h = 28.0;
-        
-        state.search_box.set_row_rect(cx + 12.0, search_w);
-        clear_ui::layout::render_widget(
-            &mut pc,
-            &mut state.search_box,
-            cx + 12.0,
-            search_y,
-            search_w,
-            search_h,
-        );
-        sec.content_y += search_h + state.search_box.top_room() + 16.0;
-
-        // Scroll box list
-        let list_box_x = cx + 12.0;
-        let list_box_y = sec.ay();
-        let list_box_w = cw - 24.0;
-        let list_box_h = 360.0;
-        
-        clear_ui::layout::render_widget(&mut pc, &mut state.list_box, list_box_x, list_box_y, list_box_w, list_box_h);
-
-        // Filter services
-        let query = if state.search_box.editing {
-            state.search_box.edit_buffer.to_lowercase()
+        if !state.loaded {
+            sec.text(pc, "Loading systemd services...", 12.0, 0.0, 12.0, TEXT_DIM);
+            sec.spacing(18.0);
         } else {
-            state.search_box.text.to_lowercase()
-        };
-        let filtered_services: Vec<&ServiceInfo> = state.services.iter()
-            .filter(|s| s.is_system == (state.active_tab == ServiceTab::System))
-            .filter(|s| s.name.to_lowercase().contains(&query) || s.description.to_lowercase().contains(&query))
-            .collect();
-
-        // Update ScrollingList bounds
-        state.list_box.update_bounds(filtered_services.len(), list_box_y, list_box_h);
-
-        let item_h = state.list_box.item_height;
-
-        for (idx, service) in filtered_services.iter().enumerate() {
-            if let Some(draw_y) = state.list_box.get_item_draw_y(idx, 4.0) {
-                // Item background
-                let bg_color = [0.08, 0.08, 0.12, 0.2];
-                pc.rect(bg_color, list_box_x + 4.0, draw_y, list_box_w - 24.0, item_h);
-
-                // Status indicator color
-                let is_active = service.active_state == "active" || service.sub_state == "running";
-                let status_color = if service.active_state == "failed" {
-                    [0.85, 0.25, 0.25, 1.0] // failed = red
-                } else if is_active {
-                    [0.25, 0.75, 0.35, 1.0] // active = green
-                } else {
-                    [0.55, 0.55, 0.60, 1.0] // inactive/dead = gray
-                };
-
-                // Render status dot (small square)
-                pc.rect(status_color, list_box_x + 14.0, draw_y + (item_h - 10.0) / 2.0, 10.0, 10.0);
-
-                // Service Name
-                pc.text(&service.name, list_box_x + 32.0, draw_y + 4.0, 13.0, [0.90, 0.90, 0.95, 1.0]);
-
-                // Service Description
-                let desc = if service.description.is_empty() { "No description" } else { &service.description };
-                let desc_truncated = if desc.len() > 65 { format!("{}...", &desc[..62]) } else { desc.to_string() };
-                pc.text(&desc_truncated, list_box_x + 32.0, draw_y + 19.0, 11.0, [0.55, 0.55, 0.60, 1.0]);
-
-                // Control buttons: Start, Stop, Restart on the right
-                let btn_w = 46.0;
-                let r_btn_w = 54.0;
-                let btn_gap = 6.0;
-                let right_edge = list_box_x + list_box_w - 24.0 - 8.0;
-
-                let restart_x = right_edge - r_btn_w;
-                let stop_x = restart_x - btn_gap - btn_w;
-                let start_x = stop_x - btn_gap - btn_w;
-
-                let btn_y = draw_y + (item_h - 22.0) / 2.0;
-                let btn_h = 22.0;
-
-                let active_txt = [0.90, 0.90, 0.95, 1.0];
-                let disabled_txt = [0.40, 0.40, 0.45, 1.0];
-
-                // Start button
-                pc.button(
-                    "Start",
-                    start_x,
-                    btn_y,
-                    btn_w,
-                    btn_h,
-                    if !is_active { [0.16, 0.35, 0.18, 0.4] } else { [0.12, 0.12, 0.16, 0.1] },
-                    [0.22, 0.45, 0.25, 0.6],
-                    if !is_active { active_txt } else { disabled_txt },
-                    crate::app::AppAction::Services(ServicesMessage::Start(service.name.clone(), service.is_system)),
-                );
-
-                // Stop button
-                pc.button(
-                    "Stop",
-                    stop_x,
-                    btn_y,
-                    btn_w,
-                    btn_h,
-                    if is_active { [0.55, 0.16, 0.16, 0.3] } else { [0.12, 0.12, 0.16, 0.1] },
-                    [0.70, 0.22, 0.22, 0.5],
-                    if is_active { active_txt } else { disabled_txt },
-                    crate::app::AppAction::Services(ServicesMessage::Stop(service.name.clone(), service.is_system)),
-                );
-
-                // Restart button
-                pc.button(
-                    "Restart",
-                    restart_x,
-                    btn_y,
-                    r_btn_w,
-                    btn_h,
-                    [0.15, 0.28, 0.45, 0.3],
-                    [0.20, 0.38, 0.58, 0.5],
-                    active_txt,
-                    crate::app::AppAction::Services(ServicesMessage::Restart(service.name.clone(), service.is_system)),
-                );
+            // Tab header buttons: System Services, User Services
+            let tab_w = (sec_w - 24.0 - 8.0) / 2.0;
+            let tab_h = 28.0;
+            let tab_y = sec.ay();
+            let active_bg = [0.20, 0.40, 0.65, 0.4];
+            let inactive_bg = [0.10, 0.10, 0.16, 0.3];
+            let hover_bg = [0.20, 0.20, 0.25, 0.15];
+
+            let label1 = if tab_w < 110.0 { "System" } else { "System Services" };
+            let label2 = if tab_w < 110.0 { "User" } else { "User Services" };
+
+            pc.button(
+                label1,
+                rx + 12.0,
+                tab_y,
+                tab_w,
+                tab_h,
+                if state.active_tab == ServiceTab::System { active_bg } else { inactive_bg },
+                hover_bg,
+                [0.90, 0.90, 0.95, 1.0],
+                crate::app::AppAction::Services(ServicesMessage::SetTab(ServiceTab::System)),
+            );
+
+            pc.button(
+                label2,
+                rx + 12.0 + tab_w + 8.0,
+                tab_y,
+                tab_w,
+                tab_h,
+                if state.active_tab == ServiceTab::User { active_bg } else { inactive_bg },
+                hover_bg,
+                [0.90, 0.90, 0.95, 1.0],
+                crate::app::AppAction::Services(ServicesMessage::SetTab(ServiceTab::User)),
+            );
+            sec.content_y += tab_h + 12.0;
+
+            // Search textbox
+            let search_y = sec.ay() + state.search_box.top_room();
+            let search_w = sec_w - 24.0;
+            let search_h = 28.0;
+            
+            state.search_box.set_row_rect(rx + 12.0, search_w);
+            clear_ui::layout::render_widget(
+                pc,
+                &mut state.search_box,
+                rx + 12.0,
+                search_y,
+                search_w,
+                search_h,
+            );
+            sec.content_y += search_h + state.search_box.top_room() + 16.0;
+
+            // Scroll box list
+            let list_box_x = rx + 12.0;
+            let list_box_y = sec.ay();
+            let list_box_w = sec_w - 24.0;
+            let list_box_h = 360.0;
+            
+            clear_ui::layout::render_widget(pc, &mut state.list_box, list_box_x, list_box_y, list_box_w, list_box_h);
+
+            // Filter services
+            let query = if state.search_box.editing {
+                state.search_box.edit_buffer.to_lowercase()
+            } else {
+                state.search_box.text.to_lowercase()
+            };
+            let filtered_services: Vec<&ServiceInfo> = state.services.iter()
+                .filter(|s| s.is_system == (state.active_tab == ServiceTab::System))
+                .filter(|s| s.name.to_lowercase().contains(&query) || s.description.to_lowercase().contains(&query))
+                .collect();
+
+            // Update ScrollingList bounds
+            state.list_box.update_bounds(filtered_services.len(), list_box_y, list_box_h);
+
+            let item_h = state.list_box.item_height;
+
+            for (idx, service) in filtered_services.iter().enumerate() {
+                if let Some(draw_y) = state.list_box.get_item_draw_y(idx, 4.0) {
+                    // Item background
+                    let bg_color = [0.08, 0.08, 0.12, 0.2];
+                    pc.rect(bg_color, list_box_x + 4.0, draw_y, list_box_w - 24.0, item_h);
+
+                    // Status indicator color
+                    let is_active = service.active_state == "active" || service.sub_state == "running";
+                    let status_color = if service.active_state == "failed" {
+                        [0.85, 0.25, 0.25, 1.0] // failed = red
+                    } else if is_active {
+                        [0.25, 0.75, 0.35, 1.0] // active = green
+                    } else {
+                        [0.55, 0.55, 0.60, 1.0] // inactive/dead = gray
+                    };
+
+                    // Render status dot (small square)
+                    pc.rect(status_color, list_box_x + 14.0, draw_y + (item_h - 10.0) / 2.0, 10.0, 10.0);
+
+                    // Control buttons: Start, Stop, Restart on the right
+                    let is_small = sec_w < 350.0;
+                    let btn_w = if is_small { 24.0 } else { 46.0 };
+                    let r_btn_w = if is_small { 24.0 } else { 54.0 };
+                    let btn_gap = if is_small { 4.0 } else { 6.0 };
+                    let right_edge = list_box_x + list_box_w - 24.0 - 8.0;
+
+                    let restart_x = right_edge - r_btn_w;
+                    let stop_x = restart_x - btn_gap - btn_w;
+                    let start_x = stop_x - btn_gap - btn_w;
+
+                    let btn_y = draw_y + (item_h - 22.0) / 2.0;
+                    let btn_h = 22.0;
+
+                    // Service Name
+                    pc.text(&service.name, list_box_x + 32.0, draw_y + 4.0, 13.0, [0.90, 0.90, 0.95, 1.0]);
+
+                    // Service Description (Truncate dynamically based on remaining space before Start button)
+                    let text_max_w = (start_x - 8.0) - (list_box_x + 32.0);
+                    let max_chars = ((text_max_w / 6.0) as usize).max(10);
+                    let desc = if service.description.is_empty() { "No description" } else { &service.description };
+                    let desc_truncated = if desc.len() > max_chars {
+                        format!("{}...", &desc[..max_chars.saturating_sub(3)])
+                    } else {
+                        desc.to_string()
+                    };
+                    pc.text(&desc_truncated, list_box_x + 32.0, draw_y + 19.0, 11.0, [0.55, 0.55, 0.60, 1.0]);
+
+                    let active_txt = [0.90, 0.90, 0.95, 1.0];
+                    let disabled_txt = [0.40, 0.40, 0.45, 1.0];
+
+                    let start_lbl = if is_small { "▶" } else { "Start" };
+                    let stop_lbl = if is_small { "■" } else { "Stop" };
+                    let restart_lbl = if is_small { "⟳" } else { "Restart" };
+
+                    // Start button
+                    pc.button(
+                        start_lbl,
+                        start_x,
+                        btn_y,
+                        btn_w,
+                        btn_h,
+                        if !is_active { [0.16, 0.35, 0.18, 0.4] } else { [0.12, 0.12, 0.16, 0.1] },
+                        [0.22, 0.45, 0.25, 0.6],
+                        if !is_active { active_txt } else { disabled_txt },
+                        crate::app::AppAction::Services(ServicesMessage::Start(service.name.clone(), service.is_system)),
+                    );
+
+                    // Stop button
+                    pc.button(
+                        stop_lbl,
+                        stop_x,
+                        btn_y,
+                        btn_w,
+                        btn_h,
+                        if is_active { [0.55, 0.16, 0.16, 0.3] } else { [0.12, 0.12, 0.16, 0.1] },
+                        [0.70, 0.22, 0.22, 0.5],
+                        if is_active { active_txt } else { disabled_txt },
+                        crate::app::AppAction::Services(ServicesMessage::Stop(service.name.clone(), service.is_system)),
+                    );
+
+                    // Restart button
+                    pc.button(
+                        restart_lbl,
+                        restart_x,
+                        btn_y,
+                        r_btn_w,
+                        btn_h,
+                        [0.15, 0.28, 0.45, 0.3],
+                        [0.20, 0.38, 0.58, 0.5],
+                        active_txt,
+                        crate::app::AppAction::Services(ServicesMessage::Restart(service.name.clone(), service.is_system)),
+                    );
+                }
             }
-        }
 
-        if filtered_services.is_empty() {
-            pc.text("No services match the query", list_box_x + 16.0, list_box_y + 16.0, 12.0, TEXT_DIM);
-        }
+            if filtered_services.is_empty() {
+                pc.text("No services match the query", list_box_x + 16.0, list_box_y + 16.0, 12.0, TEXT_DIM);
+            }
 
-        sec.content_y += list_box_h;
-    }
+            sec.content_y += list_box_h;
+        }
+        sec.finish_focused(pc, root_focused)
+    });
 
-    sec.finish_focused(&mut pc, root_focused);
-    pc
+    final_pc
 }
 
 pub fn update(state: &mut ServicesState, msg: ServicesMessage) {
@@ -348,3 +366,17 @@ pub fn update(state: &mut ServicesState, msg: ServicesMessage) {
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_view_layout_grid() {
+        let mut state = ServicesState::default();
+        let mut layout = clear_ui::layout::ColumnLayout::new(20.0);
+        let pc = view(&mut state, 10.0, 20.0, 800.0, 600.0, false, &mut layout);
+        assert!(!pc.rects.is_empty() || !pc.texts.is_empty());
+    }
+}
+
diff --git a/src/pages/status.rs b/src/pages/status.rs
index 612f461..cc22d45 100644
--- a/src/pages/status.rs
+++ b/src/pages/status.rs
@@ -1,6 +1,7 @@
 use crate::app::{AppAction, PageContent};
-use clear_ui::layout::Section;
+use clear_ui::layout::{Section, PageLayoutBuilder, LayoutStrategy};
 use clear_ui::widget::{Label, Toggle, Widget, Spinbox};
+
 use crate::pages::typeface::parse_u16_from;
 
 #[derive(Debug, Clone)]
@@ -184,63 +185,65 @@ const BTN_INACTIVE: [f32; 4] = [0.13, 0.18, 0.14, 1.0];
 const BTN_HOVER: [f32; 4] = [0.25, 0.30, 0.26, 1.0];
 const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
 
-pub fn view(state: &mut StatusState, cx: f32, cy: f32, cw: f32, _ch: f32) -> PageContent {
-    let mut pc = PageContent::new();
-    let y = cy + 12.0;
-
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Status Interface");
-
-    if !state.loaded {
-        sec.text(&mut pc, "Loading Status Interface status...", 12.0, 0.0, 12.0, TEXT_FG);
-        sec.spacing(18.0);
-    } else {
-        // Status
-        let status_text = if state.running { "Status Interface: Running" } else { "Status Interface: Stopped" };
-        state.status_label.set_text(status_text);
-        sec.widget(&mut pc, &mut state.status_label, 12.0, cw - 24.0, 20.0);
-        sec.spacing(12.0);
-
-        // Font size
-        state.size_label.set_text(&format!("Font size: {}px", state.font_size));
-        sec.widget(&mut pc, &mut state.size_label, 12.0, cw - 24.0, 20.0);
-        sec.spacing(12.0);
-
-        let btn_h = 28.0;
-        let yt = sec.ay();
-        pc.button("-1", sec.ax(12.0), yt, 36.0, btn_h,
-            BTN_INACTIVE, BTN_HOVER, WHITE,
-            AppAction::Status(StatusMessage::FontSizeDown));
-        pc.text(&format!(" {}px ", state.font_size), sec.ax(56.0), yt + 7.0, 13.0, TEXT_FG);
-        pc.button("+1", sec.ax(12.0 + 36.0 + 8.0), yt, 36.0, btn_h,
-            BTN_ACTIVE, BTN_HOVER, WHITE,
-            AppAction::Status(StatusMessage::FontSizeUp));
-        sec.content_y += btn_h + 16.0;
-
-        // Separators toggle
-        state.separators_toggle.set_toggled(state.separators);
-        sec.widget(&mut pc, &mut state.separators_toggle, 12.0, 48.0, 24.0);
-        sec.spacing(16.0);
-
-        // Underline toggle
-        state.underline_toggle.set_toggled(state.underline);
-        sec.widget(&mut pc, &mut state.underline_toggle, 12.0, 48.0, 24.0);
-        sec.spacing(16.0);
-
-        // Padding spinbox
-        state.padding_spinbox.value = state.padding as i32;
-        sec.widget(&mut pc, &mut state.padding_spinbox, 12.0, 200.0, 26.0);
-        sec.spacing(16.0);
-
-        // Reload button
-        let yt = sec.ay();
-        let btn_w = (cw - 24.0).min(200.0);
-        pc.button("Reload Status Interface", cx + cw / 2.0 - btn_w / 2.0, yt, btn_w, 32.0,
-            BTN_INACTIVE, BTN_HOVER, WHITE,
-            AppAction::Status(StatusMessage::ReloadStatus));
-    }
-    sec.finish(&mut pc);
+pub fn view(state: &mut StatusState, cx: f32, cy: f32, cw: f32, ch: f32, layout: &mut dyn LayoutStrategy) -> PageContent {
+    let mut final_pc = PageContent::new();
+    let sec_w = 320.0f32;
+    let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(1);
+
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Status Interface");
+        if !state.loaded {
+            sec.text(pc, "Loading Status Interface status...", 12.0, 0.0, 12.0, TEXT_FG);
+            sec.spacing(18.0);
+        } else {
+            // Status
+            let status_text = if state.running { "Status Interface: Running" } else { "Status Interface: Stopped" };
+            state.status_label.set_text(status_text);
+            sec.widget(pc, &mut state.status_label, 12.0, sec_w - 24.0, 20.0);
+            sec.spacing(12.0);
+
+            // Font size
+            state.size_label.set_text(&format!("Font size: {}px", state.font_size));
+            sec.widget(pc, &mut state.size_label, 12.0, sec_w - 24.0, 20.0);
+            sec.spacing(12.0);
+
+            let btn_h = 28.0;
+            let yt = sec.ay();
+            pc.button("-1", sec.ax(12.0), yt, 36.0, btn_h,
+                BTN_INACTIVE, BTN_HOVER, WHITE,
+                AppAction::Status(StatusMessage::FontSizeDown));
+            pc.text(&format!(" {}px ", state.font_size), sec.ax(56.0), yt + 7.0, 13.0, TEXT_FG);
+            pc.button("+1", sec.ax(12.0 + 36.0 + 8.0), yt, 36.0, btn_h,
+                BTN_ACTIVE, BTN_HOVER, WHITE,
+                AppAction::Status(StatusMessage::FontSizeUp));
+            sec.content_y += btn_h + 16.0;
+
+            // Separators toggle
+            state.separators_toggle.set_toggled(state.separators);
+            sec.widget(pc, &mut state.separators_toggle, 12.0, 48.0, 24.0);
+            sec.spacing(16.0);
+
+            // Underline toggle
+            state.underline_toggle.set_toggled(state.underline);
+            sec.widget(pc, &mut state.underline_toggle, 12.0, 48.0, 24.0);
+            sec.spacing(16.0);
+
+            // Padding spinbox
+            state.padding_spinbox.value = state.padding as i32;
+            sec.widget(pc, &mut state.padding_spinbox, 12.0, 200.0, 26.0);
+            sec.spacing(16.0);
+
+            // Reload button
+            let yt = sec.ay();
+            let btn_w = (sec_w - 24.0).min(200.0);
+            pc.button("Reload Status Interface", rx + sec_w / 2.0 - btn_w / 2.0, yt, btn_w, 32.0,
+                BTN_INACTIVE, BTN_HOVER, WHITE,
+                AppAction::Status(StatusMessage::ReloadStatus));
+        }
+        sec.finish(pc)
+    });
 
-    pc
+    final_pc
 }
 
 pub fn update(state: &mut StatusState, msg: StatusMessage) {
@@ -349,4 +352,12 @@ mod tests {
 
         let _ = fs::remove_file(path);
     }
+
+    #[test]
+    fn test_view_layout_grid() {
+        let mut state = StatusState::default();
+        let mut layout = clear_ui::layout::ColumnLayout::new(20.0);
+        let pc = view(&mut state, 10.0, 20.0, 800.0, 600.0, &mut layout);
+        assert!(!pc.rects.is_empty() || !pc.texts.is_empty());
+    }
 }
diff --git a/src/pages/storage.rs b/src/pages/storage.rs
index 1e3c6ed..740413d 100644
--- a/src/pages/storage.rs
+++ b/src/pages/storage.rs
@@ -1,5 +1,5 @@
 use crate::app::PageContent;
-use clear_ui::layout::Section;
+use clear_ui::layout::{Section, PageLayoutBuilder, LayoutStrategy};
 
 #[derive(Debug, Clone, Default)]
 pub struct StorageState {
@@ -62,59 +62,61 @@ fn parse_mem(info: &str) -> (f64, f64) {
 const LABEL_FG: [f32; 4] = [0.56, 0.83, 0.56, 1.0];
 const TEXT_FG: [f32; 4] = [0.83, 0.83, 0.83, 1.0];
 
-pub fn view(state: &StorageState, cx: f32, cy: f32, cw: f32, _ch: f32) -> PageContent {
-    let mut pc = PageContent::new();
-    let y = cy + 12.0;
+pub fn view(state: &StorageState, cx: f32, cy: f32, cw: f32, ch: f32, layout: &mut dyn LayoutStrategy) -> PageContent {
+    let mut final_pc = PageContent::new();
+    let sec_w = 320.0f32;
+    let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(1);
 
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Local Storage");
-
-    if !state.loaded {
-        sec.text(&mut pc, "Loading storage and memory usage...", 12.0, 0.0, 12.0, TEXT_FG);
-        sec.spacing(18.0);
-    } else {
-        let disk_pct = if state.disk_total > 0.0 {
-            state.disk_used / state.disk_total * 100.0
-        } else {
-            0.0
-        };
-
-        sec.text(&mut pc, "Disk", 12.0, 0.0, 12.0, LABEL_FG);
-        sec.text(&mut pc,
-            &format!("{:.0} / {:.0} GiB  ({:.0}%)", state.disk_used, state.disk_total, disk_pct),
-            100.0, 0.0, 12.0, TEXT_FG,
-        );
-        sec.spacing(18.0);
-
-        let bar_w = cw - 24.0;
-        let yt = sec.ay();
-        pc.rect([0.15, 0.15, 0.25, 1.0], sec.ax(12.0), yt, bar_w, 8.0);
-        if disk_pct > 0.0 {
-            pc.rect([0.36, 0.60, 0.36, 1.0], sec.ax(12.0), yt, bar_w * (disk_pct as f32 / 100.0).min(1.0), 8.0);
-        }
-        sec.content_y += 20.0;
-
-        let ram_pct = if state.ram_total > 0.0 {
-            state.ram_used / state.ram_total * 100.0
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Local Storage");
+        if !state.loaded {
+            sec.text(pc, "Loading storage and memory usage...", 12.0, 0.0, 12.0, TEXT_FG);
+            sec.spacing(18.0);
         } else {
-            0.0
-        };
-
-        sec.text(&mut pc, "RAM", 12.0, 0.0, 12.0, LABEL_FG);
-        sec.text(&mut pc,
-            &format!("{:.1} / {:.1} GiB  ({:.0}%)", state.ram_used, state.ram_total, ram_pct),
-            100.0, 0.0, 12.0, TEXT_FG,
-        );
-        sec.spacing(18.0);
-
-        let yt = sec.ay();
-        pc.rect([0.15, 0.15, 0.25, 1.0], sec.ax(12.0), yt, bar_w, 8.0);
-        if ram_pct > 0.0 {
-            pc.rect([0.50, 0.50, 0.65, 1.0], sec.ax(12.0), yt, bar_w * (ram_pct as f32 / 100.0).min(1.0), 8.0);
+            let disk_pct = if state.disk_total > 0.0 {
+                state.disk_used / state.disk_total * 100.0
+            } else {
+                0.0
+            };
+
+            sec.text(pc, "Disk", 12.0, 0.0, 12.0, LABEL_FG);
+            sec.text(pc,
+                &format!("{:.0} / {:.0} GiB  ({:.0}%)", state.disk_used, state.disk_total, disk_pct),
+                100.0, 0.0, 12.0, TEXT_FG,
+            );
+            sec.spacing(18.0);
+
+            let bar_w = sec_w - 24.0;
+            let yt = sec.ay();
+            pc.rect([0.15, 0.15, 0.25, 1.0], sec.ax(12.0), yt, bar_w, 8.0);
+            if disk_pct > 0.0 {
+                pc.rect([0.36, 0.60, 0.36, 1.0], sec.ax(12.0), yt, bar_w * (disk_pct as f32 / 100.0).min(1.0), 8.0);
+            }
+            sec.content_y += 20.0;
+
+            let ram_pct = if state.ram_total > 0.0 {
+                state.ram_used / state.ram_total * 100.0
+            } else {
+                0.0
+            };
+
+            sec.text(pc, "RAM", 12.0, 0.0, 12.0, LABEL_FG);
+            sec.text(pc,
+                &format!("{:.1} / {:.1} GiB  ({:.0}%)", state.ram_used, state.ram_total, ram_pct),
+                100.0, 0.0, 12.0, TEXT_FG,
+            );
+            sec.spacing(18.0);
+
+            let yt = sec.ay();
+            pc.rect([0.15, 0.15, 0.25, 1.0], sec.ax(12.0), yt, bar_w, 8.0);
+            if ram_pct > 0.0 {
+                pc.rect([0.50, 0.50, 0.65, 1.0], sec.ax(12.0), yt, bar_w * (ram_pct as f32 / 100.0).min(1.0), 8.0);
+            }
         }
-    }
-    sec.finish(&mut pc);
+        sec.finish(pc)
+    });
 
-    pc
+    final_pc
 }
 
 pub fn update(state: &mut StorageState, msg: StorageMessage) {
diff --git a/src/pages/system_info.rs b/src/pages/system_info.rs
index 1b9e1cf..5335ad8 100644
--- a/src/pages/system_info.rs
+++ b/src/pages/system_info.rs
@@ -1,5 +1,5 @@
 use crate::app::{AppAction, PageContent};
-use clear_ui::layout::Section;
+use clear_ui::layout::{Section, PageLayoutBuilder, LayoutStrategy};
 
 #[derive(Debug, Clone, Default)]
 pub struct SystemState {
@@ -48,52 +48,57 @@ fn spawn_systemctl(action: &str) {
     let _ = tokio::process::Command::new("systemctl").arg(action).spawn();
 }
 
-pub fn view(state: &SystemState, cx: f32, cy: f32, cw: f32, _ch: f32) -> PageContent {
-    let mut pc = PageContent::new();
-    let mut y = cy + 12.0;
+pub fn view(state: &SystemState, cx: f32, cy: f32, cw: f32, ch: f32, layout: &mut dyn LayoutStrategy) -> PageContent {
+    let mut final_pc = PageContent::new();
+    let sec_w = 320.0f32;
+    let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(2);
 
-    let mut sec = Section::new(&mut pc, cx, y, cw, "System");
-    if !state.loaded {
-        sec.text(&mut pc, "Loading system information...", 12.0, 0.0, 14.0, TEXT_FG);
-        sec.spacing(10.0);
-    } else {
-        sec.text(&mut pc, &format!("{}  —  Linux {}", state.hostname, state.kernel), 12.0, 0.0, 14.0, TEXT_FG);
-        sec.spacing(10.0);
-        sec.text(&mut pc, &format!("Uptime: {}", state.uptime), 12.0, 0.0, 12.0, TEXT_DIM);
-    }
-    y = sec.finish(&mut pc);
-
-    // ── System Actions section ──
-    let mut sec_act = Section::new(&mut pc, cx, y, cw, "System Actions");
+    // 1. System Section
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "System");
+        if !state.loaded {
+            sec.text(pc, "Loading system information...", 12.0, 0.0, 14.0, TEXT_FG);
+            sec.spacing(10.0);
+        } else {
+            sec.text(pc, &format!("{}  —  Linux {}", state.hostname, state.kernel), 12.0, 0.0, 14.0, TEXT_FG);
+            sec.spacing(10.0);
+            sec.text(pc, &format!("Uptime: {}", state.uptime), 12.0, 0.0, 12.0, TEXT_DIM);
+        }
+        sec.finish(pc)
+    });
 
-    let yt = sec_act.ay();
-    let act_btn_h = 32.0;
+    // 2. System Actions Section
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec_act = Section::new(pc, rx, ry, sec_w, "System Actions");
+        let yt = sec_act.ay();
+        let act_btn_h = 32.0;
 
-    sec_act.row(4, 8.0, act_btn_h, |i, x, w| {
-        match i {
-            0 => {
-                pc.button("Suspend", x, yt, w, act_btn_h,
-                    SAFE_BG, BTN_HOVER, WHITE, AppAction::SystemInfo(SystemMessage::Suspend));
+        sec_act.row(4, 8.0, act_btn_h, |i, x, w| {
+            match i {
+                0 => {
+                    pc.button("Suspend", x, yt, w, act_btn_h,
+                        SAFE_BG, BTN_HOVER, WHITE, AppAction::SystemInfo(SystemMessage::Suspend));
+                }
+                1 => {
+                    pc.button("Hibernate", x, yt, w, act_btn_h,
+                        SAFE_BG, BTN_HOVER, WHITE, AppAction::SystemInfo(SystemMessage::Hibernate));
+                }
+                2 => {
+                    pc.button("Reboot", x, yt, w, act_btn_h,
+                        DANGER_BG, BTN_HOVER, WHITE, AppAction::SystemInfo(SystemMessage::Reboot));
+                }
+                3 => {
+                    pc.button("Power Off", x, yt, w, act_btn_h,
+                        DANGER_BG, BTN_HOVER, WHITE, AppAction::SystemInfo(SystemMessage::PowerOff));
+                }
+                _ => {}
             }
-            1 => {
-                pc.button("Hibernate", x, yt, w, act_btn_h,
-                    SAFE_BG, BTN_HOVER, WHITE, AppAction::SystemInfo(SystemMessage::Hibernate));
-            }
-            2 => {
-                pc.button("Reboot", x, yt, w, act_btn_h,
-                    DANGER_BG, BTN_HOVER, WHITE, AppAction::SystemInfo(SystemMessage::Reboot));
-            }
-            3 => {
-                pc.button("Power Off", x, yt, w, act_btn_h,
-                    DANGER_BG, BTN_HOVER, WHITE, AppAction::SystemInfo(SystemMessage::PowerOff));
-            }
-            _ => {}
-        }
+        });
+        sec_act.spacing(12.0);
+        sec_act.finish(pc)
     });
-    sec_act.spacing(12.0);
-    sec_act.finish(&mut pc);
 
-    pc
+    final_pc
 }
 
 pub fn update(_state: &mut SystemState, msg: SystemMessage) {
diff --git a/src/pages/typeface.rs b/src/pages/typeface.rs
index 5520975..959bc41 100644
--- a/src/pages/typeface.rs
+++ b/src/pages/typeface.rs
@@ -1,6 +1,6 @@
 use std::fs;
 use crate::app::PageContent;
-use clear_ui::layout::Section;
+use clear_ui::layout::{Section, PageLayoutBuilder, LayoutStrategy};
 use clear_ui::widget::{Widget, TextLabel, ScrollBox, ScrollingList, Dropdown, TextBox, Spinbox, Button};
 use clear_ui::widget::{ElementState, KeyEvent, MouseButton, Key, NamedKey};
 
@@ -149,7 +149,7 @@ pub fn read_preferred_fonts() -> (String, String, String, String, String, String
     let status = parse_font_for_alias(&content, "status-interface").unwrap_or_else(|| "Noto Sans".to_string());
     let fuzzel_font = parse_font_for_alias(&content, "fuzzel").unwrap_or_else(|| "Noto Sans".to_string());
     let term = parse_font_for_alias(&content, "terminal").unwrap_or_else(|| "Noto Sans Mono".to_string());
-    let paginator = parse_font_for_alias(&content, "paginator-tab-labels").unwrap_or_else(|| "Noto Sans".to_string());
+    let paginator = parse_font_for_alias(&content, "paginator-tab-labels").unwrap_or_else(|| "Noto Sans Mono".to_string());
     
     (sans, serif, mono, borders, status, fuzzel_font, term, paginator)
 }
@@ -540,313 +540,300 @@ pub async fn fetch_typeface_state() -> TypefaceState {
 
 const TEXT_DIM: [f32; 4] = [0.53, 0.53, 0.60, 1.0];
 
-pub fn view(state: &mut TypefaceState, cx: f32, cy: f32, cw: f32, _ch: f32, sec_focused: &[bool]) -> PageContent {
-    let mut pc = PageContent::new();
-    let mut y = cy + 12.0;
+pub fn view(state: &mut TypefaceState, cx: f32, cy: f32, cw: f32, ch: f32, sec_focused: &[bool], layout: &mut dyn LayoutStrategy) -> PageContent {
+    let mut final_pc = PageContent::new();
+    let sec_w = 320.0f32;
+    let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(3);
 
-    let widget_w = cw - 24.0;
     let widget_h = 26.0;
 
     // ── System Typefaces Section ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "System Typefaces");
-    sec.spacing(8.0);
-
-    if !state.loaded {
-        sec.text(&mut pc, "Loading typefaces...", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec.spacing(18.0);
-    } else {
-        // Sans-Serif
-        sec.widget(&mut pc, &mut state.sans_box, 12.0, widget_w, widget_h);
-        sec.spacing(12.0);
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "System Typefaces");
+        sec.spacing(8.0);
 
-        // Serif
-        sec.widget(&mut pc, &mut state.serif_box, 12.0, widget_w, widget_h);
-        sec.spacing(12.0);
+        if !state.loaded {
+            sec.text(pc, "Loading typefaces...", 12.0, 0.0, 12.0, TEXT_DIM);
+            sec.spacing(18.0);
+        } else {
+            let inner_w = sec_w - 24.0;
+            // Sans-Serif
+            sec.widget(pc, &mut state.sans_box, 12.0, inner_w, widget_h);
+            sec.spacing(12.0);
+
+            // Serif
+            sec.widget(pc, &mut state.serif_box, 12.0, inner_w, widget_h);
+            sec.spacing(12.0);
+
+            // Monospace
+            sec.widget(pc, &mut state.mono_box, 12.0, inner_w, widget_h);
+            sec.spacing(8.0);
+        }
+        let sys_focused = sec_focused.get(0).copied().unwrap_or(false);
+        sec.finish_focused(pc, sys_focused)
+    });
 
-        // Monospace
-        sec.widget(&mut pc, &mut state.mono_box, 12.0, widget_w, widget_h);
+    // ── Program Typefaces Section ──
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Program Typefaces");
         sec.spacing(8.0);
-    }
-    let sys_focused = sec_focused.get(0).copied().unwrap_or(false);
-    y = sec.finish_focused(&mut pc, sys_focused);
 
-    // ── Program Typefaces Section ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Program Typefaces");
-    sec.spacing(8.0);
-
-    if !state.loaded {
-        sec.text(&mut pc, "Loading typefaces...", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec.spacing(18.0);
-    } else {
-        let dropdown_w = 120.0;
-        let textbox_w = 300.0;
-        let spinbox_w = 90.0;
-
-        let menu_row_w = dropdown_w + 10.0;
-        let box_row_w = textbox_w + 10.0;
-        let spinbox_row_w = spinbox_w + 10.0;
-
-        let box_row_x = cx + 8.0 + menu_row_w;
-        let spin_row_x = box_row_x + box_row_w;
-        let spin_x = cx + 12.0 + dropdown_w + 12.0 + textbox_w + 12.0;
-
-        // Window Borders
-        let start_y = sec.ay();
-        let top_room = state.borders_box.top_room();
-        state.borders_menu.set_row_rect(cx + 8.0, menu_row_w);
-        clear_ui::layout::render_widget(&mut pc, &mut state.borders_menu, cx + 12.0, start_y + top_room, dropdown_w, widget_h);
-        state.borders_box.set_row_rect(box_row_x, box_row_w);
-        clear_ui::layout::render_widget(&mut pc, &mut state.borders_box, cx + 12.0 + dropdown_w + 12.0, start_y + top_room, textbox_w, widget_h);
-        state.borders_size_box.set_row_rect(spin_row_x, spinbox_row_w);
-        clear_ui::layout::render_widget(&mut pc, &mut state.borders_size_box, spin_x, start_y + top_room, spinbox_w, widget_h);
-        sec.spacing(widget_h + top_room + 12.0);
-
-        // Status Interface
-        let start_y = sec.ay();
-        let top_room = state.status_box.top_room();
-        state.status_menu.set_row_rect(cx + 8.0, menu_row_w);
-        clear_ui::layout::render_widget(&mut pc, &mut state.status_menu, cx + 12.0, start_y + top_room, dropdown_w, widget_h);
-        state.status_box.set_row_rect(box_row_x, box_row_w);
-        clear_ui::layout::render_widget(&mut pc, &mut state.status_box, cx + 12.0 + dropdown_w + 12.0, start_y + top_room, textbox_w, widget_h);
-        state.status_size_box.set_row_rect(spin_row_x, spinbox_row_w);
-        clear_ui::layout::render_widget(&mut pc, &mut state.status_size_box, spin_x, start_y + top_room, spinbox_w, widget_h);
-        sec.spacing(widget_h + top_room + 12.0);
-
-        // Fuzzel
-        let start_y = sec.ay();
-        let top_room = state.fuzzel_box.top_room();
-        state.fuzzel_menu.set_row_rect(cx + 8.0, menu_row_w);
-        clear_ui::layout::render_widget(&mut pc, &mut state.fuzzel_menu, cx + 12.0, start_y + top_room, dropdown_w, widget_h);
-        state.fuzzel_box.set_row_rect(box_row_x, box_row_w);
-        clear_ui::layout::render_widget(&mut pc, &mut state.fuzzel_box, cx + 12.0 + dropdown_w + 12.0, start_y + top_room, textbox_w, widget_h);
-        state.fuzzel_size_box.set_row_rect(spin_row_x, spinbox_row_w);
-        clear_ui::layout::render_widget(&mut pc, &mut state.fuzzel_size_box, spin_x, start_y + top_room, spinbox_w, widget_h);
-        sec.spacing(widget_h + top_room + 12.0);
-
-        // Terminal
-        let start_y = sec.ay();
-        let top_room = state.terminal_box.top_room();
-        state.terminal_menu.set_row_rect(cx + 8.0, menu_row_w);
-        clear_ui::layout::render_widget(&mut pc, &mut state.terminal_menu, cx + 12.0, start_y + top_room, dropdown_w, widget_h);
-        state.terminal_box.set_row_rect(box_row_x, box_row_w);
-        clear_ui::layout::render_widget(&mut pc, &mut state.terminal_box, cx + 12.0 + dropdown_w + 12.0, start_y + top_room, textbox_w, widget_h);
-        state.terminal_size_box.set_row_rect(spin_row_x, spinbox_row_w);
-        clear_ui::layout::render_widget(&mut pc, &mut state.terminal_size_box, spin_x, start_y + top_room, spinbox_w, widget_h);
-        sec.spacing(widget_h + top_room + 12.0);
-
-        // Paginator Tab Labels
-        let start_y = sec.ay();
-        let top_room = state.paginator_box.top_room();
-        state.paginator_menu.set_row_rect(cx + 8.0, menu_row_w);
-        clear_ui::layout::render_widget(&mut pc, &mut state.paginator_menu, cx + 12.0, start_y + top_room, dropdown_w, widget_h);
-        state.paginator_box.set_row_rect(box_row_x, box_row_w);
-        clear_ui::layout::render_widget(&mut pc, &mut state.paginator_box, cx + 12.0 + dropdown_w + 12.0, start_y + top_room, textbox_w, widget_h);
-        state.paginator_size_box.set_row_rect(spin_row_x, spinbox_row_w);
-        clear_ui::layout::render_widget(&mut pc, &mut state.paginator_size_box, spin_x, start_y + top_room, spinbox_w, widget_h);
-        sec.spacing(widget_h + top_room + 8.0);
-    }
-    let prog_focused = sec_focused.get(1).copied().unwrap_or(false);
-    y = sec.finish_focused(&mut pc, prog_focused);
+        if !state.loaded {
+            sec.text(pc, "Loading typefaces...", 12.0, 0.0, 12.0, TEXT_DIM);
+            sec.spacing(18.0);
+        } else {
+            let inner_w = sec_w - 24.0;
+
+            // Window Borders
+            let start_y = sec.ay();
+            sec.row(2, 10.0, widget_h, |idx, x, w| {
+                if idx == 0 {
+                    state.borders_menu.set_row_rect(x, w);
+                    clear_ui::layout::render_widget(pc, &mut state.borders_menu, x, start_y, w, widget_h);
+                } else {
+                    state.borders_size_box.set_row_rect(x, w);
+                    clear_ui::layout::render_widget(pc, &mut state.borders_size_box, x, start_y, w, widget_h);
+                }
+            });
+            sec.widget(pc, &mut state.borders_box, 12.0, inner_w, widget_h);
+            sec.spacing(16.0);
+
+            // Status Interface
+            let start_y = sec.ay();
+            sec.row(2, 10.0, widget_h, |idx, x, w| {
+                if idx == 0 {
+                    state.status_menu.set_row_rect(x, w);
+                    clear_ui::layout::render_widget(pc, &mut state.status_menu, x, start_y, w, widget_h);
+                } else {
+                    state.status_size_box.set_row_rect(x, w);
+                    clear_ui::layout::render_widget(pc, &mut state.status_size_box, x, start_y, w, widget_h);
+                }
+            });
+            sec.widget(pc, &mut state.status_box, 12.0, inner_w, widget_h);
+            sec.spacing(16.0);
+
+            // Fuzzel
+            let start_y = sec.ay();
+            sec.row(2, 10.0, widget_h, |idx, x, w| {
+                if idx == 0 {
+                    state.fuzzel_menu.set_row_rect(x, w);
+                    clear_ui::layout::render_widget(pc, &mut state.fuzzel_menu, x, start_y, w, widget_h);
+                } else {
+                    state.fuzzel_size_box.set_row_rect(x, w);
+                    clear_ui::layout::render_widget(pc, &mut state.fuzzel_size_box, x, start_y, w, widget_h);
+                }
+            });
+            sec.widget(pc, &mut state.fuzzel_box, 12.0, inner_w, widget_h);
+            sec.spacing(16.0);
+
+            // Terminal
+            let start_y = sec.ay();
+            sec.row(2, 10.0, widget_h, |idx, x, w| {
+                if idx == 0 {
+                    state.terminal_menu.set_row_rect(x, w);
+                    clear_ui::layout::render_widget(pc, &mut state.terminal_menu, x, start_y, w, widget_h);
+                } else {
+                    state.terminal_size_box.set_row_rect(x, w);
+                    clear_ui::layout::render_widget(pc, &mut state.terminal_size_box, x, start_y, w, widget_h);
+                }
+            });
+            sec.widget(pc, &mut state.terminal_box, 12.0, inner_w, widget_h);
+            sec.spacing(16.0);
+
+            // Paginator Tab Labels
+            let start_y = sec.ay();
+            sec.row(2, 10.0, widget_h, |idx, x, w| {
+                if idx == 0 {
+                    state.paginator_menu.set_row_rect(x, w);
+                    clear_ui::layout::render_widget(pc, &mut state.paginator_menu, x, start_y, w, widget_h);
+                } else {
+                    state.paginator_size_box.set_row_rect(x, w);
+                    clear_ui::layout::render_widget(pc, &mut state.paginator_size_box, x, start_y, w, widget_h);
+                }
+            });
+            sec.widget(pc, &mut state.paginator_box, 12.0, inner_w, widget_h);
+            sec.spacing(8.0);
+        }
+        let prog_focused = sec_focused.get(1).copied().unwrap_or(false);
+        sec.finish_focused(pc, prog_focused)
+    });
 
     // ── Typefaces Section (List & Preview) ──
-    let mut sec = Section::new(&mut pc, cx, y, cw, "Typefaces");
-    sec.spacing(12.0);
-
-    if !state.loaded {
-        sec.text(&mut pc, "Loading installed fonts...", 12.0, 0.0, 12.0, TEXT_DIM);
-        sec.spacing(18.0);
-    } else {
-        let start_y = sec.ay();
-        let usable_w = cw - 24.0;
-        let gap = 24.0;
-        let left_w = (usable_w - gap) * 0.40;
-        let right_w = (usable_w - gap) * 0.60;
-        let left_x = cx + 12.0;
-        let right_x = left_x + left_w + gap;
-
-        // 1. Render Left Column (Search + List Box)
-        let mut left_y = start_y;
-        
-        let top_room = state.search_box.top_room();
-        let search_box_h = widget_h + top_room;
-        state.search_box.set_row_rect(left_x, left_w);
-        clear_ui::layout::render_widget(
-            &mut pc,
-            &mut state.search_box,
-            left_x,
-            left_y + top_room,
-            left_w,
-            widget_h,
-        );
-        left_y += search_box_h + 12.0;
-
-        // Scrolling box configuration
-        let list_box_y = left_y;
-        let list_box_h = 320.0;
-        
-        // Render the standardized ScrollingList widget
-        clear_ui::layout::render_widget(&mut pc, &mut state.list_box, left_x, list_box_y, left_w, list_box_h);
-
-        let query = state.search_box.text.to_lowercase();
-        let matching_fonts: Vec<&String> = state.all_fonts.iter()
-            .filter(|font| font.to_lowercase().contains(&query))
-            .collect();
-
-        // Ensure we have exactly matching_fonts.len() buttons of each type
-        if state.font_buttons.len() != matching_fonts.len() {
-            state.font_buttons.clear();
-            state.copy_buttons.clear();
-            for _ in 0..matching_fonts.len() {
-                state.font_buttons.push(Button::new_list_row(0.0, 0.0, 0.0, 0.0));
-                state.copy_buttons.push(Button::new_copy_icon(0.0, 0.0, 0.0, 0.0));
+    builder.add_section(&mut final_pc, |pc, rx, ry| {
+        let mut sec = Section::new(pc, rx, ry, sec_w, "Typefaces");
+        sec.spacing(12.0);
+
+        if !state.loaded {
+            sec.text(pc, "Loading installed fonts...", 12.0, 0.0, 12.0, TEXT_DIM);
+            sec.spacing(18.0);
+        } else {
+            let inner_w = sec_w - 24.0;
+
+            // 1. Search Box
+            let top_room = state.search_box.top_room();
+            state.search_box.set_row_rect(rx + 12.0, inner_w);
+            clear_ui::layout::render_widget(
+                pc,
+                &mut state.search_box,
+                rx + 12.0,
+                sec.ay() + top_room,
+                inner_w,
+                widget_h,
+            );
+            sec.spacing(widget_h + top_room + 12.0);
+
+            // 2. Scrolling List Box
+            let list_box_y = sec.ay();
+            let list_box_h = 200.0;
+            
+            clear_ui::layout::render_widget(pc, &mut state.list_box, rx + 12.0, list_box_y, inner_w, list_box_h);
+
+            let query = state.search_box.text.to_lowercase();
+            let matching_fonts: Vec<&String> = state.all_fonts.iter()
+                .filter(|font| font.to_lowercase().contains(&query))
+                .collect();
+
+            if state.font_buttons.len() != matching_fonts.len() {
+                state.font_buttons.clear();
+                state.copy_buttons.clear();
+                for _ in 0..matching_fonts.len() {
+                    state.font_buttons.push(Button::new_list_row(0.0, 0.0, 0.0, 0.0));
+                    state.copy_buttons.push(Button::new_copy_icon(0.0, 0.0, 0.0, 0.0));
+                }
+            }
+
+            let btn_h = 24.0;
+            let list_inner_x = rx + 16.0;
+            let list_inner_w = inner_w - 16.0;
+
+            state.list_box.update_bounds(matching_fonts.len(), list_box_y, list_box_h);
+
+            for (idx, font_name) in matching_fonts.iter().enumerate() {
+                if let Some(draw_y) = state.list_box.get_item_draw_y(idx, 0.0) {
+                    let is_selected = state.selected_font.as_ref() == Some(*font_name);
+                    
+                    let font_btn = &mut state.font_buttons[idx];
+                    font_btn.set_text(font_name);
+                    font_btn.selected = is_selected;
+                    clear_ui::layout::render_widget(pc, font_btn, list_inner_x, draw_y, list_inner_w - 44.0, btn_h);
+
+                    let copy_btn = &mut state.copy_buttons[idx];
+                    copy_btn.set_text("📋");
+                    copy_btn.selected = is_selected;
+                    clear_ui::layout::render_widget(pc, copy_btn, list_inner_x + list_inner_w - 40.0, draw_y, 40.0, btn_h);
+                }
             }
-        }
 
-        let btn_h = 24.0;
-        let inner_x = left_x + 4.0;
-        let inner_w = left_w - 16.0; // leave room for scrollbar
-        
-        // Update ScrollingList bounds to clamp and render correctly
-        state.list_box.update_bounds(matching_fonts.len(), list_box_y, list_box_h);
-
-        // Render visible buttons inside scroll region
-        
-        for (idx, font_name) in matching_fonts.iter().enumerate() {
-            // Only render buttons that are completely within the visible area
-            if let Some(draw_y) = state.list_box.get_item_draw_y(idx, 0.0) {
-                let is_selected = state.selected_font.as_ref() == Some(*font_name);
-                
-                let font_btn = &mut state.font_buttons[idx];
-                font_btn.set_text(font_name);
-                font_btn.selected = is_selected;
-                clear_ui::layout::render_widget(&mut pc, font_btn, inner_x, draw_y, inner_w - 44.0, btn_h);
-
-                let copy_btn = &mut state.copy_buttons[idx];
-                copy_btn.set_text("📋");
-                copy_btn.selected = is_selected;
-                clear_ui::layout::render_widget(&mut pc, copy_btn, inner_x + inner_w - 40.0, draw_y, 40.0, btn_h);
+            if matching_fonts.is_empty() {
+                pc.text("No fonts match query", list_inner_x + 8.0, list_box_y + 16.0, 12.0, TEXT_DIM);
             }
-        }
-        
-        if matching_fonts.is_empty() {
-            pc.text("No fonts match query", inner_x + 8.0, list_box_y + 16.0, 12.0, TEXT_DIM);
-        }
 
-        left_y += list_box_h;
-
-        // 2. Render Right Column (Live Preview)
-        let mut right_y = start_y;
-        
-        // Render Info Box
-        let info_h = 96.0;
-        let info_bg = [0.12, 0.18, 0.28, 0.3]; // Sleek translucent blue-ish background
-        let info_border = [0.25, 0.40, 0.60, 0.5]; // Soft blue border
-        
-        pc.rect(info_bg, right_x, right_y, right_w, info_h);
-        pc.rect(info_border, right_x, right_y, right_w, 1.0);
-        pc.rect(info_border, right_x, right_y + info_h - 1.0, right_w, 1.0);
-        pc.rect(info_border, right_x, right_y, 1.0, info_h);
-        pc.rect(info_border, right_x + right_w - 1.0, right_y, 1.0, info_h);
-        
-        let text_padding_x = 16.0;
-        let mut text_y = right_y + 12.0;
-        
-        pc.text("Font Directories & Installation", right_x + text_padding_x, text_y, 12.0, [0.35, 0.65, 0.90, 1.0]);
-        text_y += 20.0;
-        
-        pc.text("• Active Directory: ~/Dropbox/Fonts", right_x + text_padding_x, text_y, 11.0, [0.80, 0.80, 0.85, 1.0]);
-        text_y += 16.0;
-        
-        pc.text("• Place TTF/OTF files there to install new fonts.", right_x + text_padding_x, text_y, 11.0, [0.80, 0.80, 0.85, 1.0]);
-        text_y += 16.0;
-
-        pc.text("• Changes will be cached automatically by fontconfig.", right_x + text_padding_x, text_y, 11.0, [0.55, 0.55, 0.60, 1.0]);
-        
-        right_y += info_h + 12.0;
-        
-        if let Some(ref font_name) = state.selected_font {
-            let card_h = 240.0;
-            pc.rect([0.10, 0.10, 0.14, 0.3], right_x, right_y, right_w, card_h);
-            pc.rect([0.25, 0.25, 0.35, 0.5], right_x, right_y, right_w, 1.0);
-            pc.rect([0.25, 0.25, 0.35, 0.5], right_x, right_y + card_h - 1.0, right_w, 1.0);
-            pc.rect([0.25, 0.25, 0.35, 0.5], right_x, right_y, 1.0, card_h);
-            pc.rect([0.25, 0.25, 0.35, 0.5], right_x + right_w - 1.0, right_y, 1.0, card_h);
-            
-            let text_padding_x = 16.0;
-            let mut text_y = right_y + 16.0;
-            
-            pc.text(&format!("Family: {}", font_name), right_x + text_padding_x, text_y, 15.0, [0.90, 0.90, 0.95, 1.0]);
-            text_y += 28.0;
-            
-            pc.rect([0.22, 0.22, 0.30, 0.8], right_x + text_padding_x, text_y, right_w - (text_padding_x * 2.0), 1.0);
-            text_y += 16.0;
-            
-            pc.text_with_font(
-                "abcdefghijklmnopqrstuvwxyz",
-                right_x + text_padding_x,
-                text_y,
-                13.0,
-                [0.75, 0.75, 0.80, 1.0],
-                font_name,
-            );
-            text_y += 22.0;
-            
-            pc.text_with_font(
-                "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
-                right_x + text_padding_x,
-                text_y,
-                13.0,
-                [0.75, 0.75, 0.80, 1.0],
-                font_name,
-            );
-            text_y += 22.0;
-            
-            pc.text_with_font(
-                "0123456789 (!@#$%&*?)",
-                right_x + text_padding_x,
-                text_y,
-                13.0,
-                [0.75, 0.75, 0.80, 1.0],
-                font_name,
-            );
-            text_y += 26.0;
-            
-            pc.text_with_font(
-                "The quick brown fox jumps over the lazy dog.",
-                right_x + text_padding_x,
-                text_y,
-                18.0,
-                [0.90, 0.90, 0.95, 1.0],
-                font_name,
-            );
-            text_y += 32.0;
-
-            pc.text_with_font(
-                "The five boxing wizards jump quickly.",
-                right_x + text_padding_x,
-                text_y,
-                24.0,
-                [0.95, 0.95, 1.0, 1.0],
-                font_name,
-            );
-            
-            right_y += card_h;
-        } else {
-            pc.text("Select a font to preview", right_x + 12.0, right_y + 20.0, 13.0, TEXT_DIM);
-            right_y += 40.0;
-        }
+            sec.spacing(list_box_h + 12.0);
 
-        sec.content_y = left_y.max(right_y);
-    }
-    let list_focused = sec_focused.get(2).copied().unwrap_or(false);
-    sec.finish_focused(&mut pc, list_focused);
+            // 3. Info Box
+            let info_h = 96.0;
+            let info_bg = [0.12, 0.18, 0.28, 0.3];
+            let info_border = [0.25, 0.40, 0.60, 0.5];
+            let info_y = sec.ay();
 
+            pc.rect(info_bg, rx + 12.0, info_y, inner_w, info_h);
+            pc.rect(info_border, rx + 12.0, info_y, inner_w, 1.0);
+            pc.rect(info_border, rx + 12.0, info_y + info_h - 1.0, inner_w, 1.0);
+            pc.rect(info_border, rx + 12.0, info_y, 1.0, info_h);
+            pc.rect(info_border, rx + 12.0 + inner_w - 1.0, info_y, 1.0, info_h);
 
+            let text_padding_x = 16.0;
+            let mut text_y = info_y + 12.0;
 
-    // Render dropdown popovers on top of all other widgets
+            pc.text("Font Directories & Installation", rx + 12.0 + text_padding_x, text_y, 12.0, [0.35, 0.65, 0.90, 1.0]);
+            text_y += 20.0;
 
+            pc.text("• Active Directory: ~/Dropbox/Fonts", rx + 12.0 + text_padding_x, text_y, 11.0, [0.80, 0.80, 0.85, 1.0]);
+            text_y += 16.0;
+
+            pc.text("• Place TTF/OTF files there to install new fonts.", rx + 12.0 + text_padding_x, text_y, 11.0, [0.80, 0.80, 0.85, 1.0]);
+            text_y += 16.0;
+
+            pc.text("• Changes will be cached automatically by fontconfig.", rx + 12.0 + text_padding_x, text_y, 11.0, [0.55, 0.55, 0.60, 1.0]);
+
+            sec.spacing(info_h + 12.0);
+
+            // 4. Preview Card
+            if let Some(ref font_name) = state.selected_font {
+                let card_h = 240.0;
+                let card_y = sec.ay();
+                pc.rect([0.10, 0.10, 0.14, 0.3], rx + 12.0, card_y, inner_w, card_h);
+                pc.rect([0.25, 0.25, 0.35, 0.5], rx + 12.0, card_y, inner_w, 1.0);
+                pc.rect([0.25, 0.25, 0.35, 0.5], rx + 12.0, card_y + card_h - 1.0, inner_w, 1.0);
+                pc.rect([0.25, 0.25, 0.35, 0.5], rx + 12.0, card_y, 1.0, card_h);
+                pc.rect([0.25, 0.25, 0.35, 0.5], rx + 12.0 + inner_w - 1.0, card_y, 1.0, card_h);
+
+                let mut p_text_y = card_y + 16.0;
+                pc.text(&format!("Family: {}", font_name), rx + 12.0 + text_padding_x, p_text_y, 15.0, [0.90, 0.90, 0.95, 1.0]);
+                p_text_y += 28.0;
+
+                pc.rect([0.22, 0.22, 0.30, 0.8], rx + 12.0 + text_padding_x, p_text_y, inner_w - (text_padding_x * 2.0), 1.0);
+                p_text_y += 16.0;
+
+                pc.text_with_font(
+                    "abcdefghijklmnopqrstuvwxyz",
+                    rx + 12.0 + text_padding_x,
+                    p_text_y,
+                    13.0,
+                    [0.75, 0.75, 0.80, 1.0],
+                    font_name,
+                );
+                p_text_y += 22.0;
+
+                pc.text_with_font(
+                    "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+                    rx + 12.0 + text_padding_x,
+                    p_text_y,
+                    13.0,
+                    [0.75, 0.75, 0.80, 1.0],
+                    font_name,
+                );
+                p_text_y += 22.0;
+
+                pc.text_with_font(
+                    "0123456789 (!@#$%&*?)",
+                    rx + 12.0 + text_padding_x,
+                    p_text_y,
+                    13.0,
+                    [0.75, 0.75, 0.80, 1.0],
+                    font_name,
+                );
+                p_text_y += 26.0;
+
+                pc.text_with_font(
+                    "The quick brown fox jumps over the lazy dog.",
+                    rx + 12.0 + text_padding_x,
+                    p_text_y,
+                    16.0,
+                    [0.90, 0.90, 0.95, 1.0],
+                    font_name,
+                );
+                p_text_y += 32.0;
+
+                pc.text_with_font(
+                    "The five boxing wizards jump quickly.",
+                    rx + 12.0 + text_padding_x,
+                    p_text_y,
+                    20.0,
+                    [0.95, 0.95, 1.0, 1.0],
+                    font_name,
+                );
+                sec.spacing(card_h + 8.0);
+            } else {
+                pc.text("Select a font to preview", rx + 24.0, sec.ay() + 20.0, 13.0, TEXT_DIM);
+                sec.spacing(40.0);
+            }
+        }
+        let list_focused = sec_focused.get(2).copied().unwrap_or(false);
+        sec.finish_focused(pc, list_focused)
+    });
 
-    pc
+    final_pc
 }
 
 pub fn update(state: &mut TypefaceState, msg: TypefaceMessage) {